chiark / gitweb /
debian: Add libcdb-dev to Build-Depends.
[misc] / stamp.c
1 /* -*-
2  *
3  * Like cat, with datestamps
4  */
5
6 #include <errno.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <time.h>
11
12 #include <unistd.h>
13
14 #include <mLib/mdwopt.h>
15 #include <mLib/quis.h>
16 #include <mLib/report.h>
17
18 static const char *fmt = "%Y-%m-%d %H:%M:%S %Z: ";
19 static struct tm *(*cvt)(const time_t *) = localtime;
20
21 static void version(void) { pquis(stdout, "$ " VERSION); }
22 static void usage(FILE *fp)
23   { pquis(fp, "Usage: $ [-z] [-f FORMAT] [FILE...]"); }
24
25 static void help(void)
26 {
27   version(); putchar('\n');
28   usage(stdout);
29   fputs("\n\
30 Copy the FILEs (or standard input) to standard output, prefixing each line\n\
31 with a datestamp.\n\
32 \n\
33 -h, --help              Show this help text.\n\
34 -v, --version           Show the program's version number.\n\
35 -u, --usage             Show a brief usage message.\n\
36 \n\
37 -f, --format=FORMAT     Write datestamps using the strftime(3) FORMAT.\n\
38 -z, --utc, --zulu       Use UTC rather than local time for datestamps.\n\
39 ", stdout);
40 }
41
42 static void cat(FILE *in)
43 {
44   unsigned ln = 1;
45   time_t t;
46   struct tm *tm;
47   char buf[256];
48   int ch;
49
50   for (;;) {
51     if ((ch = getc(in)) == EOF)
52       break;
53     if (ln) {
54       t = time(0);
55       tm = cvt(&t);
56       strftime(buf, sizeof(buf), fmt, tm);
57       fwrite(buf, 1, strlen(buf), stdout);
58     }
59     putchar(ch);
60     ln = (ch == '\n');
61   }
62   if (!ln)
63     putchar('\n');
64 }
65
66 int main(int argc, char *argv[])
67 {
68   int i;
69   FILE *fp;
70   unsigned f = 0;
71 #define F_BOGUS 1u
72
73   ego(argv[0]);
74   setvbuf(stdin, 0, _IOLBF, 0);
75
76   for (;;) {
77     static const struct option opt[] = {
78       { "help",         0,              0,      'h' },
79       { "version",      0,              0,      'v' },
80       { "usage",        0,              0,      'u' },
81       { "format",       OPTF_ARGREQ,    0,      'f' },
82       { "utc",          0,              0,      'z' },
83       { "zulu",         0,              0,      'z' },
84       { 0,              0,              0,      0 }
85     };
86
87     if ((i = mdwopt(argc, argv, "hvuf:z", opt, 0, 0, 0)) < 0)
88       break;
89     switch (i) {
90       case 'h':
91         help();
92         exit(0);
93       case 'v':
94         version();
95         exit(0);
96       case 'u':
97         usage(stdout);
98         exit(0);
99       case 'f':
100         fmt = optarg;
101         break;
102       case 'z':
103         cvt = gmtime;
104         break;
105       default:
106         f |= F_BOGUS;
107         break;
108     }
109   }
110
111   if (f & F_BOGUS) {
112     usage(stderr);
113     exit(EXIT_FAILURE);
114   }
115
116   if (optind == argc) {
117     if (isatty(STDIN_FILENO))
118       die(EXIT_FAILURE, "no arguments, and stdin is a terminal");
119     cat(stdin);
120   } else for (i = optind; i < argc; i++) {
121     if (strcmp(argv[i], "-") == 0)
122       cat(stdin);
123     else if ((fp = fopen(argv[i], "r")) == 0) {
124       moan("failed to open `%s': %s", argv[i], strerror(errno));
125       f |= F_BOGUS;
126     } else {
127       cat(fp);
128       fclose(fp);
129     }
130   }
131
132   if (ferror(stdout) || fflush(stdout) || fclose(stdout)) {
133     moan("error writing output: %s", strerror(errno));
134     f |= F_BOGUS;
135   }
136
137   return ((f & F_BOGUS) ? EXIT_FAILURE : 0);
138 }