source: zzuf/trunk/src/zzcat.c @ 4246

Last change on this file since 4246 was 4246, checked in by sam, 5 years ago

Improve zzcat documentation and add commandline flags, including --help
and --version.

  • Property svn:keywords set to Id
File size: 19.9 KB
Line 
1/*
2 *  zzcat - various cat reimplementations for testing purposes
3 *  Copyright (c) 2006-2010 Sam Hocevar <sam@hocevar.net>
4 *                All Rights Reserved
5 *
6 *  $Id: zzcat.c 4246 2010-01-12 23:53:18Z sam $
7 *
8 *  This program is free software. It comes without any warranty, to
9 *  the extent permitted by applicable law. You can redistribute it
10 *  and/or modify it under the terms of the Do What The Fuck You Want
11 *  To Public License, Version 2, as published by Sam Hocevar. See
12 *  http://sam.zoy.org/wtfpl/COPYING for more details.
13 */
14
15/*
16 * TODO: fsetpos64, fgetln
17 */
18
19#include "config.h"
20
21/* Needed for lseek64() */
22#define _LARGEFILE64_SOURCE
23/* Needed for O_RDONLY on HP-UX */
24#define _INCLUDE_POSIX_SOURCE
25/* Needed for fgets_unlocked() */
26#define _GNU_SOURCE
27
28#if defined HAVE_STDINT_H
29#   include <stdint.h>
30#elif defined HAVE_INTTYPES_H
31#   include <inttypes.h>
32#endif
33#include <sys/types.h>
34#include <sys/stat.h>
35#include <fcntl.h>
36#if defined HAVE_UNISTD_H
37#   include <unistd.h>
38#endif
39#if defined HAVE_SYS_MMAN_H
40#   include <sys/mman.h>
41#endif
42#include <stdlib.h>
43#include <stdio.h>
44#include <string.h>
45
46#if !defined HAVE_GETOPT_LONG
47#   include "mygetopt.h"
48#elif defined HAVE_GETOPT_H
49#   include <getopt.h>
50#endif
51
52#if defined HAVE_GETOPT_LONG
53#   define mygetopt getopt_long
54#   define myoptind optind
55#   define myoptarg optarg
56#   define myoption option
57#endif
58
59static int run(char const *sequence, char const *file);
60
61static void syntax(void);
62static void version(void);
63static void usage(void);
64
65/*
66 * Main program.
67 */
68
69int main(int argc, char *argv[])
70{
71    char const *sequence = "repeat(-1, fgetc(), feof(1))";
72    int i;
73
74    for (;;)
75    {
76#define OPTSTR "+x:lhV"
77#define MOREINFO "Try `%s --help' for more information.\n"
78        int option_index = 0;
79        static struct myoption long_options[] =
80        {
81            { "execute",     1, NULL, 'x' },
82            { "list",        0, NULL, 'l' },
83            { "help",        0, NULL, 'h' },
84            { "version",     0, NULL, 'V' },
85            { NULL,          0, NULL,  0  }
86        };
87        int c = mygetopt(argc, argv, OPTSTR, long_options, &option_index);
88
89        if (c == -1)
90            break;
91
92        switch (c)
93        {
94        case 'x': /* --execute */
95            if (myoptarg[0] == '=')
96                myoptarg++;
97            sequence = myoptarg;
98            break;
99        case 'l': /* --list */
100            syntax();
101            return 0;
102        case 'h': /* --help */
103            usage();
104            return 0;
105        case 'V': /* --version */
106            version();
107            return 0;
108        default:
109            fprintf(stderr, "%s: invalid option -- %c\n", argv[0], c);
110            printf(MOREINFO, argv[0]);
111            return EXIT_FAILURE;
112        }
113    }
114
115    if (myoptind >= argc)
116    {
117        fprintf(stderr, "E: zzcat: too few arguments\n");
118        return EXIT_FAILURE;
119    }
120
121    for (i = myoptind; i < argc; i++)
122    {
123        int ret = run(sequence, argv[i]);
124        if (ret)
125            return ret;
126    }
127
128    return EXIT_SUCCESS;
129}
130
131/*
132 * Command intepreter
133 */
134
135#define MY_FOPEN(cmd) \
136    do { \
137        cmd; \
138        if (!f) \
139        { \
140            fprintf(stderr, "E: zzcat: cannot open `%s'\n", file); \
141            return EXIT_FAILURE; \
142        } \
143        retoff = 0; \
144        sequence = strchr(sequence, ')') + 1; \
145    } while(0)
146
147#define MY_FCLOSE(cmd) \
148    do { \
149        cmd; \
150        f = NULL; \
151        sequence = strchr(sequence, ')') + 1; \
152    } while(0)
153
154#define MERGE(address, cnt, off) \
155    do { \
156        size_t _cnt = cnt, _off = off; \
157        if (_cnt && retoff + _cnt > retlen) \
158        { \
159            retlen = retoff + _cnt; \
160            retbuf = realloc(retbuf, retlen); \
161        } \
162        if (_cnt > 0) \
163            memcpy(retbuf + retoff, address, _cnt); \
164        retoff += _off; \
165    } while(0)
166
167#define MY_FREAD(cmd, buf, cnt) MY_FCALL(cmd, buf, cnt, cnt)
168#define MY_FSEEK(cmd, off) MY_FCALL(cmd, /* unused */ "", 0, off)
169
170#define MY_FCALL(cmd, buf, cnt, off) \
171    do { \
172        if (!f) \
173        { \
174            f = fopen(file, "r"); \
175            if (!f) \
176            { \
177                fprintf(stderr, "E: zzcat: cannot open `%s'\n", file); \
178                return EXIT_FAILURE; \
179            } \
180        } \
181        /* fprintf(stderr, "debug: %s\n", #cmd); */ \
182        cmd; \
183        MERGE(buf, cnt, off); \
184        sequence = strchr(sequence, ')') + 1; \
185    } while(0)
186
187#define MY_FEOF() \
188    do { \
189        if (!f) \
190        { \
191            f = fopen(file, "r"); \
192            if (!f) \
193            { \
194                fprintf(stderr, "E: zzcat: cannot open `%s'\n", file); \
195                return EXIT_FAILURE; \
196            } \
197        } \
198        if (feof(f)) \
199            feofs++; \
200        if (feofs >= l1) \
201            finish = 1; \
202        sequence = strchr(sequence, ')') + 1; \
203    } while(0)
204
205/*
206 * Command parser. We rewrite fmt by replacing the last character with
207 * '%c' and check that the sscanf() call returns the expected number of
208 * matches plus one (for the last character). We use this macro trick to
209 * avoid using vsscanf() which does not exist on all platforms.
210 */
211
212struct parser
213{
214    char tmpfmt[1024], ch, lastch;
215};
216
217static int make_fmt(struct parser *p, char const *fmt)
218{
219    char const *tmp;
220    size_t len;
221    int ret = 0;
222
223    len = strlen(fmt);
224    p->lastch = fmt[len - 1];
225
226    memcpy(p->tmpfmt, fmt, len - 1);
227    p->tmpfmt[len - 1] = '%';
228    p->tmpfmt[len] = 'c';
229    p->tmpfmt[len + 1] = '\0';
230
231    for (tmp = p->tmpfmt; *tmp; tmp++)
232        if (*tmp == '%')
233            tmp++, ret++;
234
235    return ret;
236}
237
238#define PARSECMD(fmt, arg...) \
239    (make_fmt(&parser, fmt) == sscanf(sequence, parser.tmpfmt, \
240                                      ##arg, &parser.ch) \
241         && parser.ch == parser.lastch)
242
243/*
244 * File reader. We parse a command line and perform all the operations it
245 * contains on the specified file.
246 */
247
248static int run(char const *sequence, char const *file)
249{
250    struct { char const *p; int count; } loops[128];
251    char *retbuf = NULL, *tmp;
252    FILE *f = NULL;
253    size_t retlen = 0, retoff = 0;
254    int nloops = 0, fd = -1, feofs = 0, finish = 0;
255
256    /* Allocate 32MB for our temporary buffer. Any larger value will crash. */
257    tmp = malloc(32 * 1024 * 1024);
258
259    while (*sequence)
260    {
261        struct parser parser;
262        long int l1, l2;
263        char *s, *lineptr = NULL;
264        size_t k;
265        ssize_t l;
266        int n;
267        char ch;
268
269        (void)k;
270
271        /* Ignore punctuation */
272        if (strchr(" \t,;\r\n", *sequence))
273            sequence++;
274
275        /* Loop handling */
276        else if (PARSECMD("repeat ( %li ,", &l1))
277        {
278            sequence = strchr(sequence, ',') + 1;
279            loops[nloops].p = sequence;
280            loops[nloops].count = l1;
281            nloops++;
282        }
283        else if (PARSECMD(")"))
284        {
285            if (nloops == 0)
286            {
287                fprintf(stderr, "E: zzcat: ')' outside a loop\n");
288                return EXIT_FAILURE;
289            }
290            if (loops[nloops - 1].count == 1 || finish)
291            {
292                nloops--;
293                sequence = strchr(sequence, ')') + 1;
294            }
295            else
296            {
297                loops[nloops - 1].count--;
298                sequence = loops[nloops - 1].p;
299            }
300
301            finish = 0;
302        }
303
304        /* FILE * opening functions */
305        else if (PARSECMD("fopen ( )"))
306            MY_FOPEN(f = fopen(file, "r"));
307#if defined HAVE_FOPEN64
308        else if (PARSECMD("fopen64 ( )"))
309            MY_FOPEN(f = fopen64(file, "r"));
310#endif
311#if defined HAVE___FOPEN64
312        else if (PARSECMD("__fopen64 ( )"))
313            MY_FOPEN(f = __fopen64(file, "r"));
314#endif
315        else if (PARSECMD("freopen ( )"))
316            MY_FOPEN(f = freopen(file, "r", f));
317#if defined HAVE_FREOPEN64
318        else if (PARSECMD("freopen64 ( )"))
319            MY_FOPEN(f = freopen64(file, "r", f));
320#endif
321#if defined HAVE___FREOPEN64
322        else if (PARSECMD("__freopen64 ( )"))
323            MY_FOPEN(f = __freopen64(file, "r", f));
324#endif
325
326        /* FILE * EOF detection */
327        else if (PARSECMD("feof ( %li )", &l1))
328            MY_FEOF();
329
330        /* FILE * closing functions */
331        else if (PARSECMD("fclose ( )"))
332            MY_FCLOSE(fclose(f));
333
334        /* FILE * reading functions */
335        else if (PARSECMD("fread ( %li , %li )", &l1, &l2))
336            MY_FREAD(l = fread(tmp, l1, l2, f), tmp, l > 0 ? l * l1 : 0);
337        else if (PARSECMD("getc ( )"))
338            MY_FREAD(ch = (n = getc(f)), &ch, (n != EOF));
339        else if (PARSECMD("fgetc ( )"))
340            MY_FREAD(ch = (n = fgetc(f)), &ch, (n != EOF));
341        else if (PARSECMD("fgets ( %li )", &l1))
342            MY_FREAD(s = fgets(tmp, l1, f), tmp, s ? strlen(tmp) : 0);
343#if defined HAVE__IO_GETC
344        else if (PARSECMD("_IO_getc ( )"))
345            MY_FREAD(ch = (n = _IO_getc(f)), &ch, (n != EOF));
346#endif
347#if defined HAVE_FREAD_UNLOCKED
348        else if (PARSECMD("fread_unlocked ( %li , %li )", &l1, &l2))
349            MY_FREAD(l = fread_unlocked(tmp, l1, l2, f), tmp, l > 0 ? l * l1 : 0);
350#endif
351#if defined HAVE_FGETS_UNLOCKED
352        else if (PARSECMD("fgets_unlocked ( %li )", &l1))
353            MY_FREAD(s = fgets_unlocked(tmp, l1, f), tmp, s ? strlen(tmp) : 0);
354#endif
355#if defined HAVE_GETC_UNLOCKED
356        else if (PARSECMD("getc_unlocked ( )"))
357            MY_FREAD(ch = (n = getc_unlocked(f)), &ch, (n != EOF));
358#endif
359#if defined HAVE_FGETC_UNLOCKED
360        else if (PARSECMD("fgetc_unlocked ( )"))
361            MY_FREAD(ch = (n = fgetc_unlocked(f)), &ch, (n != EOF));
362#endif
363
364        /* FILE * getdelim functions */
365#if defined HAVE_GETLINE
366        else if (PARSECMD("getline ( )"))
367            MY_FREAD(l = getline(&lineptr, &k, f), lineptr, l >= 0 ? l : 0);
368#endif
369#if defined HAVE_GETDELIM
370        else if (PARSECMD("getdelim ( '%c' )", &ch))
371            MY_FREAD(l = getdelim(&lineptr, &k, ch, f), lineptr, l >= 0 ? l : 0);
372        else if (PARSECMD("getdelim ( %i )", &n))
373            MY_FREAD(l = getdelim(&lineptr, &k, n, f), lineptr, l >= 0 ? l : 0);
374#endif
375#if defined HAVE___GETDELIM
376        else if (PARSECMD("__getdelim ( '%c' )", &ch))
377            MY_FREAD(l = __getdelim(&lineptr, &k, ch, f), lineptr, l >= 0 ? l : 0);
378        else if (PARSECMD("__getdelim ( %i )", &n))
379            MY_FREAD(l = __getdelim(&lineptr, &k, n, f), lineptr, l >= 0 ? l : 0);
380#endif
381
382        /* FILE * seeking functions */
383        else if (PARSECMD("fseek ( %li , SEEK_CUR )", &l1))
384            MY_FSEEK(l = fseek(f, l1, SEEK_CUR),
385                     ftell(f) >= 0 ? ftell(f) - retoff : 0);
386        else if (PARSECMD("fseek ( %li , SEEK_SET )", &l1))
387            MY_FSEEK(l = fseek(f, l1, SEEK_SET),
388                     ftell(f) >= 0 ? ftell(f) - retoff : 0);
389        else if (PARSECMD("fseek ( %li , SEEK_END )", &l1))
390            MY_FSEEK(l = fseek(f, l1, SEEK_END),
391                     ftell(f) >= 0 ? ftell(f) - retoff : 0);
392#if defined HAVE_FSEEKO
393        else if (PARSECMD("fseeko ( %li , SEEK_CUR )", &l1))
394            MY_FSEEK(l = fseeko(f, l1, SEEK_CUR),
395                     ftell(f) >= 0 ? ftell(f) - retoff : 0);
396        else if (PARSECMD("fseeko ( %li , SEEK_SET )", &l1))
397            MY_FSEEK(l = fseeko(f, l1, SEEK_SET),
398                     ftell(f) >= 0 ? ftell(f) - retoff : 0);
399        else if (PARSECMD("fseeko ( %li , SEEK_END )", &l1))
400            MY_FSEEK(l = fseeko(f, l1, SEEK_END),
401                     ftell(f) >= 0 ? ftell(f) - retoff : 0);
402#endif
403#if defined HAVE_FSEEKO64
404        else if (PARSECMD("fseeko64 ( %li , SEEK_CUR )", &l1))
405            MY_FSEEK(l = fseeko64(f, l1, SEEK_CUR),
406                     ftell(f) >= 0 ? ftell(f) - retoff : 0);
407        else if (PARSECMD("fseeko64 ( %li , SEEK_SET )", &l1))
408            MY_FSEEK(l = fseeko64(f, l1, SEEK_SET),
409                     ftell(f) >= 0 ? ftell(f) - retoff : 0);
410        else if (PARSECMD("fseeko64 ( %li , SEEK_END )", &l1))
411            MY_FSEEK(l = fseeko64(f, l1, SEEK_END),
412                     ftell(f) >= 0 ? ftell(f) - retoff : 0);
413#endif
414#if defined HAVE___FSEEKO64
415        else if (PARSECMD("__fseeko64 ( %li , SEEK_CUR )", &l1))
416            MY_FSEEK(l = __fseeko64(f, l1, SEEK_CUR),
417                     ftell(f) >= 0 ? ftell(f) - retoff : 0);
418        else if (PARSECMD("__fseeko64 ( %li , SEEK_SET )", &l1))
419            MY_FSEEK(l = __fseeko64(f, l1, SEEK_SET),
420                     ftell(f) >= 0 ? ftell(f) - retoff : 0);
421        else if (PARSECMD("__fseeko64 ( %li , SEEK_END )", &l1))
422            MY_FSEEK(l = __fseeko64(f, l1, SEEK_END),
423                     ftell(f) >= 0 ? ftell(f) - retoff : 0);
424#endif
425        else if (PARSECMD("rewind ( )"))
426            MY_FSEEK(rewind(f), -retlen);
427        else if (PARSECMD("ungetc ( )"))
428            MY_FSEEK(if(retoff) ungetc((unsigned char)retbuf[retoff - 1], f),
429                     retoff ? -1 : 0);
430
431        /* Unrecognised sequence */
432        else
433        {
434            char buf[16];
435            snprintf(buf, 16, strlen(sequence) < 16 ? "%s" : "%.12s...",
436                     sequence);
437            fprintf(stderr, "E: zzcat: syntax error near `%s'\n", buf);
438            return EXIT_FAILURE;
439        }
440
441        /* Clean up our mess */
442        if (lineptr)
443            free(lineptr);
444
445        if (finish && !nloops)
446            break;
447    }
448
449    if (f)
450        fclose(f);
451
452    if (fd >= 0)
453        close(fd);
454
455    fwrite(retbuf, retlen, 1, stdout);
456
457    free(retbuf);
458    free(tmp);
459
460    return EXIT_SUCCESS;
461}
462
463#if 0
464/* Only read() calls */
465static int zzcat_read(char const *name, unsigned char *data, int64_t len,
466                      int64_t chunk)
467{
468    int i, fd = open(name, O_RDONLY);
469    if(fd < 0)
470        return EXIT_FAILURE;
471    for(i = 0; i < len; i += chunk)
472        read(fd, data + i, chunk);
473    close(fd);
474    return EXIT_SUCCESS;
475}
476
477/* Socket seeks and reads */
478static int zzcat_random_socket(char const *name, unsigned char *data,
479                               int64_t len)
480{
481    int i, j, fd = open(name, O_RDONLY);
482    if(fd < 0)
483        return EXIT_FAILURE;
484    for(i = 0; i < 128; i++)
485    {
486        lseek(fd, myrand() % len, SEEK_SET);
487        for(j = 0; j < 4; j++)
488            read(fd, data + lseek(fd, 0, SEEK_CUR), myrand() % 4096);
489#ifdef HAVE_LSEEK64
490        lseek64(fd, myrand() % len, SEEK_SET);
491        for(j = 0; j < 4; j++)
492            read(fd, data + lseek(fd, 0, SEEK_CUR), myrand() % 4096);
493#endif
494    }
495    close(fd);
496    return EXIT_SUCCESS;
497}
498
499/* Standard stream seeks and reads */
500static int zzcat_random_stream(char const *name, unsigned char *data,
501                               int64_t len)
502{
503    FILE *stream = fopen(name, "r");
504    int i, j;
505    if(!stream)
506        return EXIT_FAILURE;
507    for(i = 0; i < 128; i++)
508    {
509        long int now;
510        fseek(stream, myrand() % len, SEEK_SET);
511        for(j = 0; j < 4; j++)
512            fread(data + ftell(stream),
513                  myrand() % (len - ftell(stream)), 1, stream);
514        fseek(stream, myrand() % len, SEEK_SET);
515        now = ftell(stream);
516        for(j = 0; j < 16; j++)
517            data[now + j] = getc(stream);
518        now = ftell(stream);
519        for(j = 0; j < 16; j++)
520            data[now + j] = fgetc(stream);
521    }
522    fclose(stream);
523    return EXIT_SUCCESS;
524}
525
526#ifdef HAVE_MMAP
527/* mmap() followed by random memory reads */
528static int zzcat_random_mmap(char const *name, unsigned char *data,
529                               int64_t len)
530{
531    int i, j, fd = open(name, O_RDONLY);
532    if(fd < 0)
533        return EXIT_FAILURE;
534    for(i = 0; i < 128; i++)
535    {
536        char *map;
537        int moff, mlen, pgsz = len + 1;
538#ifdef HAVE_GETPAGESIZE
539        pgsz = getpagesize();
540#endif
541        moff = len < pgsz ? 0 : (myrand() % (len / pgsz)) * pgsz;
542        mlen = 1 + (myrand() % (len - moff));
543        map = mmap(NULL, mlen, PROT_READ, MAP_PRIVATE, fd, moff);
544        if(map == MAP_FAILED)
545            return EXIT_FAILURE;
546        for(j = 0; j < 128; j++)
547        {
548            int x = myrand() % mlen;
549            data[moff + x] = map[x];
550        }
551        munmap(map, mlen);
552    }
553    close(fd);
554    return EXIT_SUCCESS;
555}
556#endif
557#endif
558
559static char const *keyword_list[] =
560{
561    "repeat", "(<int>,<sequence>)", "loop <int> times through <sequence>",
562    "feof", "(<int>)", "break out of loop or sequence after <int> EOFs",
563    NULL
564};
565
566static char const *function_list[] =
567{
568    "fopen", "()", "open file",
569#if defined HAVE_FOPEN64
570    "fopen64", "()", "same as fopen()",
571#endif
572#if defined HAVE___FOPEN64
573    "__fopen64", "()", "same as fopen()",
574#endif
575    "freopen", "()", "reopen file",
576#if defined HAVE_FREOPEN64
577    "freopen64", "()", "same as reopen()",
578#endif
579#if defined HAVE___FREOPEN64
580    "__freopen64", "()", "same as reopen()",
581#endif
582    "fclose", "()", "close file",
583    "fread", "(<inta>,<intb>)", "read <intb> chunks of <inta> bytes",
584    "getc", "()", "get one character (can be a macro)",
585    "fgetc", "()", "get one character",
586    "fgets", "(<int>)", "read one line no longer than <int> bytes",
587#if defined HAVE__IO_GETC
588    "_IO_getc", "()", "get one character",
589#endif
590#if defined HAVE_FREAD_UNLOCKED
591    "fread_unlocked", "(<inta>,<intb>)", "same as fread(), unlocked I/O version",
592#endif
593#if defined HAVE_FGETS_UNLOCKED
594    "fgets_unlocked", "(<int>)", "same as fgets(), unlocked I/O version",
595#endif
596#if defined HAVE_GETC_UNLOCKED
597    "getc_unlocked", "()", "same as getc(), unlocked I/O version",
598#endif
599#if defined HAVE_FGETC_UNLOCKED
600    "fgetc_unlocked", "()", "same as fgetc(), unlocked I/O version",
601#endif
602#if defined HAVE_GETLINE
603    "getline", "()", "read one complete line of text",
604#endif
605#if defined HAVE_GETDELIM
606    "getdelim", "('<char>')", "read all data until delimiter character <char>",
607    "getdelim", "(<int>)", "read all data until delimiter character <int>",
608#endif
609#if defined HAVE___GETDELIM
610    "__getdelim", "('<char>')", "same as getdelim()",
611    "__getdelim", "(<int>)", "same as getdelim()",
612#endif
613    "fseek", "(<int>,<whence>)", "seek using SEEK_CUR, SEEK_SET or SEEK_END",
614#if defined HAVE_FSEEKO
615    "fseeko", "(<int>,<whence>)", "same as fseek()",
616#endif
617#if defined HAVE_FSEEKO64
618    "fseeko64", "(<int>,<whence>)", "same as fseek()",
619#endif
620#if defined HAVE___FSEEKO64
621    "__fseeko64", "(<int>,<whence>)", "same as fseek()",
622#endif
623    "rewind", "()", "rewind to the beginning of the stream",
624    "ungetc", "()", "put one byte back in the stream",
625    NULL
626};
627
628static void print_list(char const **list)
629{
630    static char const spaces[] = "                                ";
631
632    while (*list)
633    {
634        size_t len = printf("  %s%s", list[0], list[1]);
635        if (len < strlen(spaces))
636            printf("%s", spaces + len);
637        printf("%s\n", list[2]);
638        list += 3;
639    }
640}
641
642static void syntax(void)
643{
644    printf("Available control keywords:\n");
645    print_list(keyword_list);
646    printf("\n");
647    printf("Available functions:\n");
648    print_list(function_list);
649}
650
651static void version(void)
652{
653    printf("zzcat %s\n", PACKAGE_VERSION);
654    printf("Copyright (C) 2002-2010 Sam Hocevar <sam@hocevar.net>\n");
655    printf("This program is free software. It comes without any warranty, to the extent\n");
656    printf("permitted by applicable law. You can redistribute it and/or modify it under\n");
657    printf("the terms of the Do What The Fuck You Want To Public License, Version 2, as\n");
658    printf("published by Sam Hocevar. See <http://sam.zoy.org/wtfpl/> for more details.\n");
659    printf("\n");
660    printf("Written by Sam Hocevar. Report bugs to <sam@hocevar.net>.\n");
661}
662
663static void usage(void)
664{
665    printf("Usage: zzcat [-x sequence] [FILE...]\n");
666    printf("       zzcat -l | --list\n");
667    printf("       zzcat -h | --help\n");
668    printf("       zzcat -V | --version\n");
669    printf("Read FILE using a sequence of various I/O methods.\n");
670    printf("\n");
671    printf("Mandatory arguments to long options are mandatory for short options too.\n");
672    printf("  -x, --execute <sequence>  execute commands in <sequence>\n");
673    printf("  -l, --list                list available program functions\n");
674    printf("  -h, --help                display this help and exit\n");
675    printf("  -V, --version             output version information and exit\n");
676    printf("\n");
677    printf("Written by Sam Hocevar. Report bugs to <sam@hocevar.net>.\n");
678}
679
Note: See TracBrowser for help on using the repository browser.