diff --git a/doc/zzcat.1.in b/doc/zzcat.1.in index 1e7958e..d6b6e29 100644 --- a/doc/zzcat.1.in +++ b/doc/zzcat.1.in @@ -2,14 +2,41 @@ .SH NAME zzcat \- debugging tool for zzuf .SH SYNOPSIS -\fBzzcat\fR PROGRAM FILE +\fBzzcat\fR [\fB\-x\fR \fIsequence\fR] [\fIFILE\fR]... +.br +\fBzzcat \-l\fR | \fB\-\-list\fR +.br +\fBzzcat \-h\fR | \fB\-\-help\fR +.br +\fBzzcat \-V\fR | \fB\-\-version\fR .SH DESCRIPTION .PP -\fBzzcat\fR is a tool to read files using various strategies. It is -primarily used as a debugging tool for \fBzzuf\fR. +The \fBzzcat\fR utility reads files sequentially, writing them to the +standard output. The user can decide which sequence of library calls is +used to seek and read data. + +\fBzzcat\fR is primarily used as a debugging tool for \fBzzuf\fR. .SH USAGE .PP -\fB zzcat "fread(1,10000)" /dev/zero\fR +\fB zzcat -x "fread(1,10000)" /dev/zero\fR +.SH OPTIONS +.TP +\fB\-x\fR, \fB\-\-execute\fR=\fIsequence\fR +Read a file or a stream using the instruction sequence specified in +\fIsequence\fR. + +If no sequence is specified, the following default sequence is used: + +\fB repeat(\-1, fgetc(), feof(1)) +.TP +\fB\-l\fR, \fB\-\-list\fR +Display the list of supported keywords and functions and exit. +.TP +\fB\-h\fR, \fB\-\-help\fR +Display a short help message and exit. +.TP +\fB\-V\fR, \fB\-\-version\fR +Output version information and exit. .SH EXAMPLES .PP To do. @@ -18,7 +45,7 @@ To do. \fBzzuf(3)\fR, \fBlibzzuf(3)\fR .SH AUTHOR .PP -Copyright \(co 2002, 2007\-2010 Sam Hocevar . +Copyright \(co 2002\-2010 Sam Hocevar . .PP \fBzzcat\fR and this manual page are free software. They come without any warranty, to the extent permitted by applicable law. You can redistribute diff --git a/src/zzcat.c b/src/zzcat.c index c1d4b88..93fa6ab 100644 --- a/src/zzcat.c +++ b/src/zzcat.c @@ -43,16 +43,95 @@ #include #include -static inline unsigned int myrand(void) +#if !defined HAVE_GETOPT_LONG +# include "mygetopt.h" +#elif defined HAVE_GETOPT_H +# include +#endif + +#if defined HAVE_GETOPT_LONG +# define mygetopt getopt_long +# define myoptind optind +# define myoptarg optarg +# define myoption option +#endif + +static int run(char const *sequence, char const *file); + +static void syntax(void); +static void version(void); +static void usage(void); + +/* + * Main program. + */ + +int main(int argc, char *argv[]) { - static int seed = 1; - int x, y; - x = (seed + 0x12345678) << 11; - y = (seed + 0xfedcba98) >> 21; - seed = x * 1010101 + y * 343434; - return seed; + char const *sequence = "repeat(-1, fgetc(), feof(1))"; + int i; + + for (;;) + { +#define OPTSTR "+x:lhV" +#define MOREINFO "Try `%s --help' for more information.\n" + int option_index = 0; + static struct myoption long_options[] = + { + { "execute", 1, NULL, 'x' }, + { "list", 0, NULL, 'l' }, + { "help", 0, NULL, 'h' }, + { "version", 0, NULL, 'V' }, + { NULL, 0, NULL, 0 } + }; + int c = mygetopt(argc, argv, OPTSTR, long_options, &option_index); + + if (c == -1) + break; + + switch (c) + { + case 'x': /* --execute */ + if (myoptarg[0] == '=') + myoptarg++; + sequence = myoptarg; + break; + case 'l': /* --list */ + syntax(); + return 0; + case 'h': /* --help */ + usage(); + return 0; + case 'V': /* --version */ + version(); + return 0; + default: + fprintf(stderr, "%s: invalid option -- %c\n", argv[0], c); + printf(MOREINFO, argv[0]); + return EXIT_FAILURE; + } + } + + if (myoptind >= argc) + { + fprintf(stderr, "E: zzcat: too few arguments\n"); + return EXIT_FAILURE; + } + + for (i = myoptind; i < argc; i++) + { + int ret = run(sequence, argv[i]); + if (ret) + return ret; + } + + return EXIT_SUCCESS; } +/* + * Command intepreter + */ + #define MY_FOPEN(cmd) \ do { \ cmd; \ @@ -62,14 +141,14 @@ static inline unsigned int myrand(void) return EXIT_FAILURE; \ } \ retoff = 0; \ - p = strchr(p, ')') + 1; \ + sequence = strchr(sequence, ')') + 1; \ } while(0) #define MY_FCLOSE(cmd) \ do { \ cmd; \ f = NULL; \ - p = strchr(p, ')') + 1; \ + sequence = strchr(sequence, ')') + 1; \ } while(0) #define MERGE(address, cnt, off) \ @@ -99,9 +178,10 @@ static inline unsigned int myrand(void) return EXIT_FAILURE; \ } \ } \ + /* fprintf(stderr, "debug: %s\n", #cmd); */ \ cmd; \ MERGE(buf, cnt, off); \ - p = strchr(p, ')') + 1; \ + sequence = strchr(sequence, ')') + 1; \ } while(0) #define MY_FEOF() \ @@ -119,7 +199,7 @@ static inline unsigned int myrand(void) feofs++; \ if (feofs >= l1) \ finish = 1; \ - p = strchr(p, ')') + 1; \ + sequence = strchr(sequence, ')') + 1; \ } while(0) /* @@ -156,15 +236,16 @@ static int make_fmt(struct parser *p, char const *fmt) } #define PARSECMD(fmt, arg...) \ - make_fmt(&parser, fmt) == sscanf(p, parser.tmpfmt, ##arg, &parser.ch) \ - && parser.ch == parser.lastch + (make_fmt(&parser, fmt) == sscanf(sequence, parser.tmpfmt, \ + ##arg, &parser.ch) \ + && parser.ch == parser.lastch) /* * File reader. We parse a command line and perform all the operations it * contains on the specified file. */ -static int cat_file(char const *p, char const *file) +static int run(char const *sequence, char const *file) { struct { char const *p; int count; } loops[128]; char *retbuf = NULL, *tmp; @@ -175,7 +256,7 @@ static int cat_file(char const *p, char const *file) /* Allocate 32MB for our temporary buffer. Any larger value will crash. */ tmp = malloc(32 * 1024 * 1024); - while (*p) + while (*sequence) { struct parser parser; long int l1, l2; @@ -188,14 +269,14 @@ static int cat_file(char const *p, char const *file) (void)k; /* Ignore punctuation */ - if (strchr(" \t,;\r\n", *p)) - p++; + if (strchr(" \t,;\r\n", *sequence)) + sequence++; /* Loop handling */ else if (PARSECMD("repeat ( %li ,", &l1)) { - p = strchr(p, ',') + 1; - loops[nloops].p = p; + sequence = strchr(sequence, ',') + 1; + loops[nloops].p = sequence; loops[nloops].count = l1; nloops++; } @@ -206,15 +287,15 @@ static int cat_file(char const *p, char const *file) fprintf(stderr, "E: zzcat: ')' outside a loop\n"); return EXIT_FAILURE; } - loops[nloops - 1].count--; - if (loops[nloops - 1].count <= 0 || finish) + if (loops[nloops - 1].count == 1 || finish) { nloops--; - p = strchr(p, ')') + 1; + sequence = strchr(sequence, ')') + 1; } else { - p = loops[nloops - 1].p; + loops[nloops - 1].count--; + sequence = loops[nloops - 1].p; } finish = 0; @@ -351,7 +432,8 @@ static int cat_file(char const *p, char const *file) else { char buf[16]; - snprintf(buf, 16, strlen(p) < 16 ? "%s" : "%.12s...", p); + snprintf(buf, 16, strlen(sequence) < 16 ? "%s" : "%.12s...", + sequence); fprintf(stderr, "E: zzcat: syntax error near `%s'\n", buf); return EXIT_FAILURE; } @@ -378,33 +460,6 @@ static int cat_file(char const *p, char const *file) return EXIT_SUCCESS; } -/* - * Main program. - */ - -int main(int argc, char *argv[]) -{ - int i; - - if (argc < 2) - { - fprintf(stderr, "E: zzcat: too few arguments\n"); - return EXIT_FAILURE; - } - - if (argc == 2) - return cat_file("fread(1,33554432)", argv[1]); - - for (i = 2; i < argc; i++) - { - int ret = cat_file(argv[1], argv[i]); - if (ret) - return ret; - } - - return EXIT_SUCCESS; -} - #if 0 /* Only read() calls */ static int zzcat_read(char const *name, unsigned char *data, int64_t len, @@ -501,3 +556,124 @@ static int zzcat_random_mmap(char const *name, unsigned char *data, #endif #endif +static char const *keyword_list[] = +{ + "repeat", "(,)", "loop times through ", + "feof", "()", "break out of loop or sequence after EOFs", + NULL +}; + +static char const *function_list[] = +{ + "fopen", "()", "open file", +#if defined HAVE_FOPEN64 + "fopen64", "()", "same as fopen()", +#endif +#if defined HAVE___FOPEN64 + "__fopen64", "()", "same as fopen()", +#endif + "freopen", "()", "reopen file", +#if defined HAVE_FREOPEN64 + "freopen64", "()", "same as reopen()", +#endif +#if defined HAVE___FREOPEN64 + "__freopen64", "()", "same as reopen()", +#endif + "fclose", "()", "close file", + "fread", "(,)", "read chunks of bytes", + "getc", "()", "get one character (can be a macro)", + "fgetc", "()", "get one character", + "fgets", "()", "read one line no longer than bytes", +#if defined HAVE__IO_GETC + "_IO_getc", "()", "get one character", +#endif +#if defined HAVE_FREAD_UNLOCKED + "fread_unlocked", "(,)", "same as fread(), unlocked I/O version", +#endif +#if defined HAVE_FGETS_UNLOCKED + "fgets_unlocked", "()", "same as fgets(), unlocked I/O version", +#endif +#if defined HAVE_GETC_UNLOCKED + "getc_unlocked", "()", "same as getc(), unlocked I/O version", +#endif +#if defined HAVE_FGETC_UNLOCKED + "fgetc_unlocked", "()", "same as fgetc(), unlocked I/O version", +#endif +#if defined HAVE_GETLINE + "getline", "()", "read one complete line of text", +#endif +#if defined HAVE_GETDELIM + "getdelim", "('')", "read all data until delimiter character ", + "getdelim", "()", "read all data until delimiter character ", +#endif +#if defined HAVE___GETDELIM + "__getdelim", "('')", "same as getdelim()", + "__getdelim", "()", "same as getdelim()", +#endif + "fseek", "(,)", "seek using SEEK_CUR, SEEK_SET or SEEK_END", +#if defined HAVE_FSEEKO + "fseeko", "(,)", "same as fseek()", +#endif +#if defined HAVE_FSEEKO64 + "fseeko64", "(,)", "same as fseek()", +#endif +#if defined HAVE___FSEEKO64 + "__fseeko64", "(,)", "same as fseek()", +#endif + "rewind", "()", "rewind to the beginning of the stream", + "ungetc", "()", "put one byte back in the stream", + NULL +}; + +static void print_list(char const **list) +{ + static char const spaces[] = " "; + + while (*list) + { + size_t len = printf(" %s%s", list[0], list[1]); + if (len < strlen(spaces)) + printf("%s", spaces + len); + printf("%s\n", list[2]); + list += 3; + } +} + +static void syntax(void) +{ + printf("Available control keywords:\n"); + print_list(keyword_list); + printf("\n"); + printf("Available functions:\n"); + print_list(function_list); +} + +static void version(void) +{ + printf("zzcat %s\n", PACKAGE_VERSION); + printf("Copyright (C) 2002-2010 Sam Hocevar \n"); + printf("This program is free software. It comes without any warranty, to the extent\n"); + printf("permitted by applicable law. You can redistribute it and/or modify it under\n"); + printf("the terms of the Do What The Fuck You Want To Public License, Version 2, as\n"); + printf("published by Sam Hocevar. See for more details.\n"); + printf("\n"); + printf("Written by Sam Hocevar. Report bugs to .\n"); +} + +static void usage(void) +{ + printf("Usage: zzcat [-x sequence] [FILE...]\n"); + printf(" zzcat -l | --list\n"); + printf(" zzcat -h | --help\n"); + printf(" zzcat -V | --version\n"); + printf("Read FILE using a sequence of various I/O methods.\n"); + printf("\n"); + printf("Mandatory arguments to long options are mandatory for short options too.\n"); + printf(" -x, --execute execute commands in \n"); + printf(" -l, --list list available program functions\n"); + printf(" -h, --help display this help and exit\n"); + printf(" -V, --version output version information and exit\n"); + printf("\n"); + printf("Written by Sam Hocevar. Report bugs to .\n"); +} + diff --git a/test/check-utils b/test/check-utils index 0206177..63df05c 100755 --- a/test/check-utils +++ b/test/check-utils @@ -54,9 +54,9 @@ checkutils() ;; esac # Regression tests for stuff that used to break - check "$ZZOPTS" "$ZZCAT \"fread(1,33000) fseek(1,SEEK_SET) fread(1,1) fseek(4093,SEEK_CUR) fread(1,1) fseek(1,SEEK_CUR) fread(1,1)\" $file" \ + check "$ZZOPTS" "$ZZCAT -x \"fread(1,33000) fseek(1,SEEK_SET) fread(1,1) fseek(4093,SEEK_CUR) fread(1,1) fseek(1,SEEK_CUR) fread(1,1)\" $file" \ "eglibc (2.9-21) bug regression" - check "$ZZOPTS" "$ZZCAT \"repeat(33000,getc_unlocked() ungetc() getline() feof(10))\" $file" \ + check "$ZZOPTS" "$ZZCAT -x \"repeat(33000,getc_unlocked() ungetc() getline() feof(10))\" $file" \ "sed getc_unlocked() bug regression" # Misc tests for n in \ @@ -95,7 +95,7 @@ checkutils() "fread(1,33000) fseek(1000,SEEK_CUR) repeat(10000,fgetc(),feof(10))" \ "fread(1,33000) fseek(1000,SEEK_CUR) repeat(10000,getc_unlocked(),feof(10))" \ ; do - check "$ZZOPTS" "$ZZCAT \"$n\" $file" "$n" + check "$ZZOPTS" "$ZZCAT -x \"$n\" $file" "$n" done done }