Annotation of src/usr.bin/ts/ts.c, Revision 1.2
1.2 ! job 1: /* $OpenBSD: ts.c,v 1.1 2022/06/29 08:39:49 job Exp $ */
1.1 job 2: /*
3: * Copyright (c) 2022 Job Snijders <job@openbsd.org>
4: * Copyright (c) 2022 Claudio Jeker <claudio@openbsd.org>
5: *
6: * Permission to use, copy, modify, and distribute this software for any
7: * purpose with or without fee is hereby granted, provided that the above
8: * copyright notice and this permission notice appear in all copies.
9: *
10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17: */
18:
19: #include <sys/cdefs.h>
20: #include <sys/time.h>
21:
22: #include <err.h>
23: #include <stdint.h>
24: #include <stdio.h>
25: #include <stdlib.h>
26: #include <string.h>
27: #include <unistd.h>
28: #include <time.h>
29:
30: static char *format = "%b %d %H:%M:%S";
31: static char *buf;
32: static char *outbuf;
33: static size_t bufsize;
34:
35: static void fmtfmt(struct tm *, long);
36: static void __dead usage(void);
37:
38: int
39: main(int argc, char *argv[])
40: {
1.2 ! job 41: int iflag, mflag, sflag;
1.1 job 42: int ch, prev;
1.2 ! job 43: struct timespec rstart, start, now, elapsed;
1.1 job 44: struct tm *lt, tm;
1.2 ! job 45: int clock = CLOCK_REALTIME;
1.1 job 46:
47: if (pledge("stdio", NULL) == -1)
48: err(1, "pledge");
49:
1.2 ! job 50: iflag = mflag = sflag = 0;
1.1 job 51:
1.2 ! job 52: while ((ch = getopt(argc, argv, "ims")) != -1) {
1.1 job 53: switch (ch) {
54: case 'i':
55: iflag = 1;
56: format = "%H:%M:%S";
1.2 ! job 57: clock = CLOCK_MONOTONIC;
! 58: break;
! 59: case 'm':
! 60: mflag = 1;
! 61: clock = CLOCK_MONOTONIC;
1.1 job 62: break;
63: case 's':
64: sflag = 1;
65: format = "%H:%M:%S";
1.2 ! job 66: clock = CLOCK_MONOTONIC;
1.1 job 67: break;
68: default:
69: usage();
70: }
71: }
72: argc -= optind;
73: argv += optind;
74:
75: if ((iflag && sflag) || argc > 1)
76: usage();
77:
78: if (argc == 1)
79: format = *argv;
80:
81: bufsize = strlen(format);
82: if (bufsize > SIZE_MAX / 10)
83: errx(1, "format string too big");
84:
85: bufsize *= 10;
86: if ((buf = calloc(1, bufsize)) == NULL)
87: err(1, NULL);
88: if ((outbuf = calloc(1, bufsize)) == NULL)
89: err(1, NULL);
90:
1.2 ! job 91: clock_gettime(CLOCK_REALTIME, &rstart);
1.1 job 92: clock_gettime(CLOCK_MONOTONIC, &start);
93:
94: for (prev = '\n'; (ch = getchar()) != EOF; prev = ch) {
95: if (prev == '\n') {
1.2 ! job 96: if (clock_gettime(clock, &now))
! 97: err(1, "clock_gettime");
1.1 job 98: if (iflag || sflag) {
99: timespecsub(&now, &start, &elapsed);
100: if (gmtime_r(&elapsed.tv_sec, &tm) == NULL)
101: err(1, "gmtime_r");
102: if (iflag)
1.2 ! job 103: if (clock_gettime(clock, &start))
! 104: err(1, "clock_gettime");
1.1 job 105: fmtfmt(&tm, elapsed.tv_nsec);
1.2 ! job 106: } else if (mflag) {
! 107: timespecsub(&now, &start, &elapsed);
! 108: timespecadd(&rstart, &elapsed, &now);
! 109: lt = localtime(&now.tv_sec);
! 110: if (lt == NULL)
! 111: err(1, "localtime");
! 112: fmtfmt(lt, now.tv_nsec);
1.1 job 113: } else {
114: lt = localtime(&now.tv_sec);
115: if (lt == NULL)
116: err(1, "localtime");
117: fmtfmt(lt, now.tv_nsec);
118: }
119: }
120: if (putchar(ch) == EOF)
121: break;
122: }
123:
124: if (fclose(stdout))
125: err(1, "stdout");
126: return 0;
127: }
128:
129: static void __dead
130: usage(void)
131: {
1.2 ! job 132: fprintf(stderr, "usage: %s [-i | -s] [-m] [format]\n", getprogname());
1.1 job 133: exit(1);
134: }
135:
136: /*
137: * yo dawg, i heard you like format strings
138: * so i put format strings in your user supplied input
139: * so you can format while you format
140: */
141: static void
142: fmtfmt(struct tm *tm, long tv_nsec)
143: {
144: char *f, ms[7];
145:
146: snprintf(ms, sizeof(ms), "%06ld", tv_nsec / 1000);
147: strlcpy(buf, format, bufsize);
148: f = buf;
149:
150: do {
151: while ((f = strchr(f, '%')) != NULL && f[1] == '%')
152: f += 2;
153:
154: if (f == NULL)
155: break;
156:
157: f++;
158: if (f[0] == '.' &&
159: (f[1] == 'S' || f[1] == 's' || f[1] == 'T')) {
160: size_t l;
161:
162: f[0] = f[1];
163: f[1] = '.';
164: f += 2;
165: l = strlen(f);
166: memmove(f + 6, f, l + 1);
167: memcpy(f, ms, 6);
168: f += 6;
169: }
170: } while (*f != '\0');
171:
172: if (strftime(outbuf, bufsize, buf, tm) == 0)
173: errx(1, "strftime");
174:
175: fprintf(stdout, "%s ", outbuf);
176: if (ferror(stdout))
177: exit(1);
178: }