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