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