hyx

Minimalist but powerful terminal hex editor
git clone https://git.sinitax.com/yx7/hyx
Log | Files | Refs | sfeed.txt

hyx.c (6503B)


      1/*
      2 *
      3 * Copyright (c) 2016-2024 Lorenz Panny
      4 *
      5 * This is hyx version 2024.02.29.
      6 * Check for newer versions at https://yx7.cc/code.
      7 * Please report bugs to lorenz@yx7.cc.
      8 *
      9 * Contributors:
     10 *     2018, anonymous            Faster search algorithm.
     11 *     2020, Leah Neukirchen      Suspend on ^Z. File information on ^G.
     12 *     2022, Jeffrey H. Johnson   Makefile tweaks for MacOS.
     13 *     2022, Mario Haustein       Makefile tweaks for Gentoo.
     14 *     2023, Josef Schönberger    Use alternate screen. Home/End keys.
     15 *
     16 * This program is released under the MIT license; see license.txt.
     17 *
     18 */
     19
     20#include "common.h"
     21#include "blob.h"
     22#include "view.h"
     23#include "input.h"
     24#include "ansi.h"
     25
     26#include <stdlib.h>
     27#include <stdio.h>
     28#include <string.h>
     29#include <signal.h>
     30#include <setjmp.h>
     31
     32
     33struct blob blob;
     34struct view view;
     35struct input input;
     36
     37bool quit;
     38
     39jmp_buf jmp_mainloop;
     40
     41
     42void die(char const *s)
     43{
     44    view_text(&view, true);
     45    fprintf(stderr, "%s\n", s);
     46    exit(EXIT_FAILURE);
     47}
     48
     49void pdie(char const *s)
     50{
     51    view_text(&view, true);
     52    perror(s);
     53    exit(EXIT_FAILURE);
     54}
     55
     56static void sighdlr(int num)
     57{
     58    switch (num) {
     59    case SIGWINCH:
     60        view.winch = true;
     61        break;
     62    case SIGTSTP:
     63        view.tstp = true;
     64        break;
     65    case SIGCONT:
     66        view.cont = true;
     67        break;
     68    case SIGALRM:
     69        /* This is used in parsing escape sequences,
     70         * but we don't need to do anything here. */
     71        break;
     72    case SIGINT:
     73        /* ignore */
     74        break;
     75    default:
     76        die("unrecognized signal");
     77    }
     78}
     79
     80__attribute__((noreturn)) void version()
     81{
     82    printf("This is hyx version 2024.02.29.\n");
     83    exit(EXIT_SUCCESS);
     84}
     85
     86__attribute__((noreturn)) void help(int st)
     87{
     88    bool tty = isatty(fileno(stdout));
     89
     90    printf("\n");
     91    printf("    %shyx: a minimalistic hex editor%s\n",
     92            tty ? color_green : "", tty ? color_normal : "");
     93    printf("    ------------------------------\n\n");
     94
     95    printf("    %sinvocation:%s hyx [filename]\n",
     96            tty ? color_yellow : "", tty ? color_normal : "");
     97
     98    printf("    %sinvocation:%s [command] | hyx\n\n",
     99            tty ? color_yellow : "", tty ? color_normal : "");
    100
    101    printf("    %skeys:%s\n\n",
    102            tty ? color_yellow : "", tty ? color_normal : "");
    103    printf("q               quit\n");
    104    printf("\n");
    105    printf("h, j, k, l      move cursor\n");
    106    printf("(hex digits)    edit bytes (in hex mode)\n");
    107    printf("(printable)     edit bytes (in ascii mode)\n");
    108    printf("i               switch between replace and insert modes\n");
    109    printf("tab             switch between hex and ascii input\n");
    110    printf("\n");
    111    printf("u               undo\n");
    112    printf("ctrl+r          redo\n");
    113    printf("\n");
    114    printf("v               start a selection\n");
    115    printf("escape          abort a selection\n");
    116    printf("x               delete current byte or selection\n");
    117    printf("s               substitute current byte or selection\n");
    118    printf("y               copy current byte or selection to clipboard\n");
    119    printf("p               paste\n");
    120    printf("P               paste and move cursor\n");
    121    printf("\n");
    122    printf("], [            increase/decrease number of columns\n");
    123    printf("\n");
    124    printf("ctrl+u, ctrl+d  scroll up/down one page\n");
    125    printf("g, G            jump to start/end of screen or file\n");
    126    printf("^, $            jump to start/end of current line\n");
    127    printf("\n");
    128    printf(":               enter command (see below)\n");
    129    printf("\n");
    130    printf("/x (hex string) search for hexadecimal bytes\n");
    131    printf("/s (characters) search for unicode string (utf8)\n");
    132    printf("/w (characters) search for unicode string (ucs2)\n");
    133    printf("n, N            jump to next/previous match\n");
    134    printf("\n");
    135    printf("ctrl+a, ctrl+x  increment/decrement current byte\n");
    136    printf("\n");
    137    printf("ctrl+g          show file name and current position\n");
    138    printf("ctrl+z          suspend editor; use \"fg\" to continue\n");
    139    printf("\n");
    140
    141    printf("    %scommands:%s\n\n",
    142            tty ? color_yellow : "", tty ? color_normal : "");
    143    printf("$offset         jump to offset (supports hex/dec/oct)\n");
    144    printf("q               quit\n");
    145    printf("w [$filename]   save\n");
    146    printf("wq [$filename]  save and quit\n");
    147    printf("color y/n       toggle colors\n");
    148
    149    printf("\n");
    150
    151    exit(st);
    152}
    153
    154int main(int argc, char **argv)
    155{
    156    struct sigaction sigact;
    157
    158    char *filename = NULL;
    159
    160    for (size_t i = 1; i < (size_t) argc; ++i) {
    161        if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help"))
    162            help(0);
    163        else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version"))
    164            version();
    165        else if (!filename)
    166            filename = argv[i];
    167        else
    168            help(EXIT_FAILURE);
    169    }
    170
    171    blob_init(&blob);
    172    if (!isatty(fileno(stdin))) {
    173        if (filename) help(EXIT_FAILURE);
    174        blob_load_stream(&blob, stdin);
    175        if (!freopen("/dev/tty", "r", stdin))
    176            pdie("could not reopen controlling TTY");
    177    }
    178    else {
    179        blob_load(&blob, filename);
    180    }
    181
    182    view_init(&view, &blob, &input);
    183    input_init(&input, &view);
    184
    185    /* set up signal handler */
    186    memset(&sigact, 0, sizeof(sigact));
    187    sigact.sa_handler = sighdlr;
    188    sigaction(SIGWINCH, &sigact, NULL);
    189    sigaction(SIGTSTP, &sigact, NULL);
    190    sigaction(SIGCONT, &sigact, NULL);
    191    sigaction(SIGALRM, &sigact, NULL);
    192    sigaction(SIGINT, &sigact, NULL);
    193
    194    view_recompute(&view, true);
    195    view_visual(&view);
    196
    197    do {
    198        /* This is used to redraw immediately when the window size changes. */
    199        setjmp(jmp_mainloop);
    200
    201        if (view.winch) {
    202            view_recompute(&view, true);
    203            view.winch = false;
    204        }
    205        if (view.tstp) {
    206            view_text(&view, true);
    207            view.tstp = false;
    208            raise(SIGSTOP);
    209            /* should continue with view.cont == true */
    210        }
    211        if (view.cont) {
    212            view_recompute(&view, true);
    213            view_dirty_from(&view, 0);
    214            view_visual(&view);
    215            view.cont = false;
    216        }
    217        assert(input.cur >= view.start && input.cur < view.start + view.rows * view.cols);
    218        view_update(&view);
    219
    220        input_get(&input, &quit);
    221
    222    } while (!quit);
    223
    224    view_text(&view, true);
    225
    226    input_free(&input);
    227    view_free(&view);
    228    blob_free(&blob);
    229}
    230