Annotation of src/usr.bin/ts/ts.c, Revision 1.10
1.10 ! cheloha 1: /* $OpenBSD: ts.c,v 1.9 2022/08/03 16:54:30 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:
1.5 deraadt 19: #include <sys/types.h>
1.10 ! cheloha 20: #include <sys/queue.h>
1.1 job 21: #include <sys/time.h>
22:
23: #include <err.h>
24: #include <stdint.h>
25: #include <stdio.h>
26: #include <stdlib.h>
27: #include <string.h>
28: #include <unistd.h>
29: #include <time.h>
30:
1.10 ! cheloha 31: SIMPLEQ_HEAD(, usec) usec_queue = SIMPLEQ_HEAD_INITIALIZER(usec_queue);
! 32: struct usec {
! 33: SIMPLEQ_ENTRY(usec) next;
! 34: char *pos;
! 35: };
! 36:
1.1 job 37: static char *format = "%b %d %H:%M:%S";
38: static char *buf;
39: static char *outbuf;
40: static size_t bufsize;
1.8 claudio 41: static size_t obsize;
1.1 job 42:
1.10 ! cheloha 43: static void fmtfmt(void);
! 44: static void fmtprint(const struct timespec *);
1.1 job 45: static void __dead usage(void);
46:
47: int
48: main(int argc, char *argv[])
49: {
1.2 job 50: int iflag, mflag, sflag;
1.1 job 51: int ch, prev;
1.7 claudio 52: struct timespec start, now, utc_offset, ts;
1.6 cheloha 53: clockid_t clock = CLOCK_REALTIME;
1.1 job 54:
55: if (pledge("stdio", NULL) == -1)
56: err(1, "pledge");
57:
1.2 job 58: iflag = mflag = sflag = 0;
1.1 job 59:
1.2 job 60: while ((ch = getopt(argc, argv, "ims")) != -1) {
1.1 job 61: switch (ch) {
62: case 'i':
63: iflag = 1;
64: format = "%H:%M:%S";
1.2 job 65: clock = CLOCK_MONOTONIC;
66: break;
67: case 'm':
68: mflag = 1;
69: clock = CLOCK_MONOTONIC;
1.1 job 70: break;
71: case 's':
72: sflag = 1;
73: format = "%H:%M:%S";
1.2 job 74: clock = CLOCK_MONOTONIC;
1.1 job 75: break;
76: default:
77: usage();
78: }
79: }
80: argc -= optind;
81: argv += optind;
1.9 job 82:
83: setvbuf(stdout, NULL, _IOLBF, 0);
1.1 job 84:
85: if ((iflag && sflag) || argc > 1)
86: usage();
87:
88: if (argc == 1)
89: format = *argv;
90:
1.8 claudio 91: bufsize = strlen(format) + 1;
1.1 job 92: if (bufsize > SIZE_MAX / 10)
93: errx(1, "format string too big");
94: bufsize *= 10;
1.8 claudio 95: obsize = bufsize;
1.1 job 96: if ((buf = calloc(1, bufsize)) == NULL)
97: err(1, NULL);
1.8 claudio 98: if ((outbuf = calloc(1, obsize)) == NULL)
1.1 job 99: err(1, NULL);
100:
1.10 ! cheloha 101: fmtfmt();
! 102:
1.3 claudio 103: /* force UTC for interval calculations */
104: if (iflag || sflag)
105: if (setenv("TZ", "UTC", 1) == -1)
106: err(1, "setenv UTC");
107:
108: clock_gettime(clock, &start);
1.7 claudio 109: clock_gettime(CLOCK_REALTIME, &utc_offset);
110: timespecsub(&utc_offset, &start, &utc_offset);
1.1 job 111:
112: for (prev = '\n'; (ch = getchar()) != EOF; prev = ch) {
113: if (prev == '\n') {
1.3 claudio 114: clock_gettime(clock, &now);
115: if (iflag || sflag)
1.7 claudio 116: timespecsub(&now, &start, &ts);
1.3 claudio 117: else if (mflag)
1.7 claudio 118: timespecadd(&now, &utc_offset, &ts);
119: else
120: ts = now;
1.10 ! cheloha 121: fmtprint(&ts);
1.3 claudio 122: if (iflag)
1.7 claudio 123: start = now;
1.1 job 124: }
125: if (putchar(ch) == EOF)
126: break;
127: }
128:
129: if (fclose(stdout))
130: err(1, "stdout");
131: return 0;
132: }
133:
134: static void __dead
135: usage(void)
136: {
1.2 job 137: fprintf(stderr, "usage: %s [-i | -s] [-m] [format]\n", getprogname());
1.1 job 138: exit(1);
139: }
140:
141: /*
142: * yo dawg, i heard you like format strings
143: * so i put format strings in your user supplied input
144: * so you can format while you format
145: */
146: static void
1.10 ! cheloha 147: fmtfmt(void)
1.1 job 148: {
1.10 ! cheloha 149: char *f;
! 150: struct usec *u;
1.1 job 151:
152: strlcpy(buf, format, bufsize);
153: f = buf;
154:
155: do {
156: while ((f = strchr(f, '%')) != NULL && f[1] == '%')
157: f += 2;
158:
159: if (f == NULL)
160: break;
161:
162: f++;
163: if (f[0] == '.' &&
164: (f[1] == 'S' || f[1] == 's' || f[1] == 'T')) {
165: size_t l;
166:
167: f[0] = f[1];
168: f[1] = '.';
169: f += 2;
1.10 ! cheloha 170: u = malloc(sizeof u);
! 171: if (u == NULL)
! 172: err(1, NULL);
! 173: u->pos = f;
! 174: SIMPLEQ_INSERT_TAIL(&usec_queue, u, next);
1.1 job 175: l = strlen(f);
176: memmove(f + 6, f, l + 1);
177: f += 6;
178: }
179: } while (*f != '\0');
1.10 ! cheloha 180: }
! 181:
! 182: static void
! 183: fmtprint(const struct timespec *ts)
! 184: {
! 185: char us[8];
! 186: struct tm *tm;
! 187: struct usec *u;
! 188:
! 189: if ((tm = localtime(&ts->tv_sec)) == NULL)
! 190: err(1, "localtime");
! 191:
! 192: /* Update any microsecond substrings in the format buffer. */
! 193: if (!SIMPLEQ_EMPTY(&usec_queue)) {
! 194: snprintf(us, sizeof(us), "%06ld", ts->tv_nsec / 1000);
! 195: SIMPLEQ_FOREACH(u, &usec_queue, next)
! 196: memcpy(u->pos, us, 6);
! 197: }
1.1 job 198:
1.8 claudio 199: *outbuf = '\0';
200: if (*buf != '\0') {
201: while (strftime(outbuf, obsize, buf, tm) == 0) {
202: if ((outbuf = reallocarray(outbuf, 2, obsize)) == NULL)
203: err(1, NULL);
204: obsize *= 2;
205: }
206: }
1.1 job 207: fprintf(stdout, "%s ", outbuf);
208: if (ferror(stdout))
209: exit(1);
210: }