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