mtimeout.1: Use correct dash for number ranges.
[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 }