Annotation of src/usr.bin/ts/ts.c, Revision 1.9
1.9 ! job 1: /* $OpenBSD: ts.c,v 1.8 2022/07/07 10:40:25 claudio 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:
1.5 deraadt 19: #include <sys/types.h>
1.1 job 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;
1.8 claudio 34: static size_t obsize;
1.1 job 35:
1.7 claudio 36: static void fmtfmt(const struct timespec *);
1.1 job 37: static void __dead usage(void);
38:
39: int
40: main(int argc, char *argv[])
41: {
1.2 job 42: int iflag, mflag, sflag;
1.1 job 43: int ch, prev;
1.7 claudio 44: struct timespec start, now, utc_offset, ts;
1.6 cheloha 45: clockid_t 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;
1.9 ! job 74:
! 75: setvbuf(stdout, NULL, _IOLBF, 0);
1.1 job 76:
77: if ((iflag && sflag) || argc > 1)
78: usage();
79:
80: if (argc == 1)
81: format = *argv;
82:
1.8 claudio 83: bufsize = strlen(format) + 1;
1.1 job 84: if (bufsize > SIZE_MAX / 10)
85: errx(1, "format string too big");
86: bufsize *= 10;
1.8 claudio 87: obsize = bufsize;
1.1 job 88: if ((buf = calloc(1, bufsize)) == NULL)
89: err(1, NULL);
1.8 claudio 90: if ((outbuf = calloc(1, obsize)) == NULL)
1.1 job 91: err(1, NULL);
92:
1.3 claudio 93: /* force UTC for interval calculations */
94: if (iflag || sflag)
95: if (setenv("TZ", "UTC", 1) == -1)
96: err(1, "setenv UTC");
97:
98: clock_gettime(clock, &start);
1.7 claudio 99: clock_gettime(CLOCK_REALTIME, &utc_offset);
100: timespecsub(&utc_offset, &start, &utc_offset);
1.1 job 101:
102: for (prev = '\n'; (ch = getchar()) != EOF; prev = ch) {
103: if (prev == '\n') {
1.3 claudio 104: clock_gettime(clock, &now);
105: if (iflag || sflag)
1.7 claudio 106: timespecsub(&now, &start, &ts);
1.3 claudio 107: else if (mflag)
1.7 claudio 108: timespecadd(&now, &utc_offset, &ts);
109: else
110: ts = now;
111: fmtfmt(&ts);
1.3 claudio 112: if (iflag)
1.7 claudio 113: start = now;
1.1 job 114: }
115: if (putchar(ch) == EOF)
116: break;
117: }
118:
119: if (fclose(stdout))
120: err(1, "stdout");
121: return 0;
122: }
123:
124: static void __dead
125: usage(void)
126: {
1.2 job 127: fprintf(stderr, "usage: %s [-i | -s] [-m] [format]\n", getprogname());
1.1 job 128: exit(1);
129: }
130:
131: /*
132: * yo dawg, i heard you like format strings
133: * so i put format strings in your user supplied input
134: * so you can format while you format
135: */
136: static void
1.7 claudio 137: fmtfmt(const struct timespec *ts)
1.1 job 138: {
1.7 claudio 139: struct tm *tm;
140: char *f, us[7];
141:
142: if ((tm = localtime(&ts->tv_sec)) == NULL)
143: err(1, "localtime");
1.1 job 144:
1.7 claudio 145: snprintf(us, sizeof(us), "%06ld", ts->tv_nsec / 1000);
1.1 job 146: strlcpy(buf, format, bufsize);
147: f = buf;
148:
149: do {
150: while ((f = strchr(f, '%')) != NULL && f[1] == '%')
151: f += 2;
152:
153: if (f == NULL)
154: break;
155:
156: f++;
157: if (f[0] == '.' &&
158: (f[1] == 'S' || f[1] == 's' || f[1] == 'T')) {
159: size_t l;
160:
161: f[0] = f[1];
162: f[1] = '.';
163: f += 2;
164: l = strlen(f);
165: memmove(f + 6, f, l + 1);
1.7 claudio 166: memcpy(f, us, 6);
1.1 job 167: f += 6;
168: }
169: } while (*f != '\0');
170:
1.8 claudio 171: *outbuf = '\0';
172: if (*buf != '\0') {
173: while (strftime(outbuf, obsize, buf, tm) == 0) {
174: if ((outbuf = reallocarray(outbuf, 2, obsize)) == NULL)
175: err(1, NULL);
176: obsize *= 2;
177: }
178: }
1.1 job 179: fprintf(stdout, "%s ", outbuf);
180: if (ferror(stdout))
181: exit(1);
182: }