/* * 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 #include #include #include #include #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; }