summaryrefslogblamecommitdiffstats
path: root/tools/catcho.c
blob: 3426e50f6490c2cbec20faa4c5f26152b3b5670a (plain) (tree)











































































































































































































                                                                               
/*
 * catcho - outputing text to the standard output
 *
 * Usage
 * -----
 *  catcho [options]
 *
 * Options
 * -------
 *  -m --mode MODE      Set the initial text mode (Default: passthrough)
 *  -v --version        Display program information and exit
 *  -V --verbose        Duplicate stdout on stderr
 *
 * Modes
 * -----
 *  passthru / passthrough / normal / n - Input is copied to output verbatim.
 *  null / z - Input is dropped.
 *  hb - Input (interpreted as hex) is compressed to bytes on the output.
 *  bh - The opposite of hb.
 *  ib - Input (interpreted as bit-bytes) is compressed to bytes on the output.
 *  bi - The opposite of ib.
 *
 *  Append 'f' to any mode (eg:  bhf):  Input is interpreted as '\n'-delimited
 *  filenames.  File contents are filtered based on mode and sent to output.
 *
 * Switching modes
 * ---------------
 *  When catcho receives signal SIGINT (ctrl-C from the terminal, for example),
 *  any pending input/output is flushed and catcho then interprets the input
 *  as a mode name terminated by a '\n'.  Once the input provides a string\n,
 *  it switches the desired mode and input processing resumes.
 *
 * Exiting catcho
 * --------------
 *  Since SIGINT is used for mode switching, the 'normal' way to terminate
 *  catcho is by sending it an End-Of-File byte on its input. (ctrl-D on your
 *  terminal)
 */

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <signal.h>

#define LAST_STR_CHAR(s) (s[strlen(s)-1])

/* modes */
enum mode
{
    N,
    Z,
    HB,
    BH,
    IB,
    BI
};

/* global state */
static enum mode current_mode = N;
static int  current_mode_f = 0;
static int  current_verbose = 0;

static size_t ccfwrite(const void *ptr, size_t size, size_t nmemb)
{
    size_t ret;
    ret = fwrite(ptr, size, nmemb, stdout);

    if (current_verbose)
        fwrite(ptr, size, nmemb, stderr);

    return ret;
}

static void sigint(int sig)
{
    (void)sig;
    ccfwrite("in signal handler\n", 18, 1);
}

static int set_mode(const char *mode)
{
    if (!mode)
        return 0;

    if (!strcmp(mode, "passthru") || // these don't support 'f' suffix
        !strcmp(mode, "passthrough") ||
        !strcmp(mode, "normal"))
    {
        current_mode = N;
        current_mode_f = 0;
    }
    else if (!strcmp(mode, "null")) // this doesn't support 'f' suffix
    {
        current_mode = Z;
        current_mode_f = 0;
    }
    else if (!strncmp(mode, "n", 1))
    {
        current_mode = N;
        current_mode_f = (LAST_STR_CHAR(mode) == 'f');
    }
    else if (!strncmp(mode, "z", 1))
    {
        current_mode = Z;
        current_mode_f = (LAST_STR_CHAR(mode) == 'f');
    }
    else if (!strncmp(mode, "hb", 2))
    {
        current_mode = HB;
        current_mode_f = (LAST_STR_CHAR(mode) == 'f');
    }
    else if (!strncmp(mode, "bh", 2))
    {
        current_mode = BH;
        current_mode_f = (LAST_STR_CHAR(mode) == 'f');
    }
    else if (!strncmp(mode, "ib", 2))
    {
        current_mode = IB;
        current_mode_f = (LAST_STR_CHAR(mode) == 'f');
    }
    else if (!strncmp(mode, "bi", 2))
    {
        current_mode = BI;
        current_mode_f = (LAST_STR_CHAR(mode) == 'f');
    }
    else
    {
        fprintf(stderr, "Mode string '%s' not recognized\n", mode);
        return 0;
    }

    return 1;
}

void show_version(const char *name)
{
    printf("%s version 1.0\n", name);
}

int main(int argc, char **argv)
{
    /*
     * handling command-line arguments:
     * i'm going to leave opterr alone (defaults to non-zero)
     * \this causes getopt to print its own error messages to
     *  stderr if things go wrong, which is fine.
     */

    const char *shortopts = "m:vV";

    const struct option longopts[] = {
        { "mode",    required_argument, NULL, 'm' },
        { "version", no_argument,       NULL, 'v' },
        { "verbose", no_argument,       NULL, 'V' },
        { NULL,      0,                 NULL,  0  }
    };

    int opt;

    while ((opt = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1)
    {
        switch (opt)
        {
            case '?':
                return 1;

            case 'm':
                if (!set_mode(optarg))
                    return 1;
                break;

            case 'v':
                show_version(argv[0]);
                return 0;

            case 'V':
                current_verbose = 1;
                break;
        }
    }

    /* setup signals (mode switch) */
    signal(SIGINT, &sigint);


    char memes[4];
    fread(memes, 4, 1, stdin);
    switch (current_mode)
    {
        case N:  printf("NORMAL\n"); break;
        case Z:  printf("NULL\n"); break;
        case HB: printf("hex to bytes\n"); break;
        case BH: printf("bytes to hex\n"); break;
        case IB: printf("bits to bytes\n"); break;
        case BI: printf("bytes to bits\n"); break;
    }

    printf("file mode: %i\n", current_mode_f);
    printf("verbose mode: %i\n", current_verbose);

    return 0;
}