xref: /openbsd/usr.bin/ts/ts.c (revision 34a84998)
1*34a84998Scheloha /*	$OpenBSD: ts.c,v 1.10 2022/09/01 00:14:36 cheloha Exp $	*/
2acb1415eSjob /*
3acb1415eSjob  * Copyright (c) 2022 Job Snijders <job@openbsd.org>
4acb1415eSjob  * Copyright (c) 2022 Claudio Jeker <claudio@openbsd.org>
5acb1415eSjob  *
6acb1415eSjob  * Permission to use, copy, modify, and distribute this software for any
7acb1415eSjob  * purpose with or without fee is hereby granted, provided that the above
8acb1415eSjob  * copyright notice and this permission notice appear in all copies.
9acb1415eSjob  *
10acb1415eSjob  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11acb1415eSjob  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12acb1415eSjob  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13acb1415eSjob  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14acb1415eSjob  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15acb1415eSjob  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16acb1415eSjob  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17acb1415eSjob  */
18acb1415eSjob 
194cf92683Sderaadt #include <sys/types.h>
20*34a84998Scheloha #include <sys/queue.h>
21acb1415eSjob #include <sys/time.h>
22acb1415eSjob 
23acb1415eSjob #include <err.h>
24acb1415eSjob #include <stdint.h>
25acb1415eSjob #include <stdio.h>
26acb1415eSjob #include <stdlib.h>
27acb1415eSjob #include <string.h>
28acb1415eSjob #include <unistd.h>
29acb1415eSjob #include <time.h>
30acb1415eSjob 
31*34a84998Scheloha SIMPLEQ_HEAD(, usec) usec_queue = SIMPLEQ_HEAD_INITIALIZER(usec_queue);
32*34a84998Scheloha struct usec {
33*34a84998Scheloha 	SIMPLEQ_ENTRY(usec) next;
34*34a84998Scheloha 	char *pos;
35*34a84998Scheloha };
36*34a84998Scheloha 
37acb1415eSjob static char		*format = "%b %d %H:%M:%S";
38acb1415eSjob static char		*buf;
39acb1415eSjob static char		*outbuf;
40acb1415eSjob static size_t		 bufsize;
4183b8c212Sclaudio static size_t		 obsize;
42acb1415eSjob 
43*34a84998Scheloha static void		 fmtfmt(void);
44*34a84998Scheloha static void		 fmtprint(const struct timespec *);
45acb1415eSjob static void __dead	 usage(void);
46acb1415eSjob 
47acb1415eSjob int
48acb1415eSjob main(int argc, char *argv[])
49acb1415eSjob {
50c37731dfSjob 	int iflag, mflag, sflag;
51acb1415eSjob 	int ch, prev;
52f8be9accSclaudio 	struct timespec start, now, utc_offset, ts;
53a4c15d4aScheloha 	clockid_t clock = CLOCK_REALTIME;
54acb1415eSjob 
55acb1415eSjob 	if (pledge("stdio", NULL) == -1)
56acb1415eSjob 		err(1, "pledge");
57acb1415eSjob 
58c37731dfSjob 	iflag = mflag = sflag = 0;
59acb1415eSjob 
60c37731dfSjob 	while ((ch = getopt(argc, argv, "ims")) != -1) {
61acb1415eSjob 		switch (ch) {
62acb1415eSjob 		case 'i':
63acb1415eSjob 			iflag = 1;
64acb1415eSjob 			format = "%H:%M:%S";
65c37731dfSjob 			clock = CLOCK_MONOTONIC;
66c37731dfSjob 			break;
67c37731dfSjob 		case 'm':
68c37731dfSjob 			mflag = 1;
69c37731dfSjob 			clock = CLOCK_MONOTONIC;
70acb1415eSjob 			break;
71acb1415eSjob 		case 's':
72acb1415eSjob 			sflag = 1;
73acb1415eSjob 			format = "%H:%M:%S";
74c37731dfSjob 			clock = CLOCK_MONOTONIC;
75acb1415eSjob 			break;
76acb1415eSjob 		default:
77acb1415eSjob 			usage();
78acb1415eSjob 		}
79acb1415eSjob 	}
80acb1415eSjob 	argc -= optind;
81acb1415eSjob 	argv += optind;
82acb1415eSjob 
83cbfaf9cbSjob 	setvbuf(stdout, NULL, _IOLBF, 0);
84cbfaf9cbSjob 
85acb1415eSjob 	if ((iflag && sflag) || argc > 1)
86acb1415eSjob 		usage();
87acb1415eSjob 
88acb1415eSjob 	if (argc == 1)
89acb1415eSjob 		format = *argv;
90acb1415eSjob 
9183b8c212Sclaudio 	bufsize = strlen(format) + 1;
92acb1415eSjob 	if (bufsize > SIZE_MAX / 10)
93acb1415eSjob 		errx(1, "format string too big");
94acb1415eSjob 	bufsize *= 10;
9583b8c212Sclaudio 	obsize = bufsize;
96acb1415eSjob 	if ((buf = calloc(1, bufsize)) == NULL)
97acb1415eSjob 		err(1, NULL);
9883b8c212Sclaudio 	if ((outbuf = calloc(1, obsize)) == NULL)
99acb1415eSjob 		err(1, NULL);
100acb1415eSjob 
101*34a84998Scheloha 	fmtfmt();
102*34a84998Scheloha 
103ecef9ffbSclaudio 	/* force UTC for interval calculations */
104ecef9ffbSclaudio 	if (iflag || sflag)
105ecef9ffbSclaudio 		if (setenv("TZ", "UTC", 1) == -1)
106ecef9ffbSclaudio 			err(1, "setenv UTC");
107ecef9ffbSclaudio 
108ecef9ffbSclaudio 	clock_gettime(clock, &start);
109f8be9accSclaudio 	clock_gettime(CLOCK_REALTIME, &utc_offset);
110f8be9accSclaudio 	timespecsub(&utc_offset, &start, &utc_offset);
111acb1415eSjob 
112acb1415eSjob 	for (prev = '\n'; (ch = getchar()) != EOF; prev = ch) {
113acb1415eSjob 		if (prev == '\n') {
114ecef9ffbSclaudio 			clock_gettime(clock, &now);
115ecef9ffbSclaudio 			if (iflag || sflag)
116f8be9accSclaudio 				timespecsub(&now, &start, &ts);
117ecef9ffbSclaudio 			else if (mflag)
118f8be9accSclaudio 				timespecadd(&now, &utc_offset, &ts);
119f8be9accSclaudio 			else
120f8be9accSclaudio 				ts = now;
121*34a84998Scheloha 			fmtprint(&ts);
122acb1415eSjob 			if (iflag)
123f8be9accSclaudio 				start = now;
124acb1415eSjob 		}
125acb1415eSjob 		if (putchar(ch) == EOF)
126acb1415eSjob 			break;
127acb1415eSjob 	}
128acb1415eSjob 
129acb1415eSjob 	if (fclose(stdout))
130acb1415eSjob 		err(1, "stdout");
131acb1415eSjob 	return 0;
132acb1415eSjob }
133acb1415eSjob 
134acb1415eSjob static void __dead
135acb1415eSjob usage(void)
136acb1415eSjob {
137c37731dfSjob 	fprintf(stderr, "usage: %s [-i | -s] [-m] [format]\n", getprogname());
138acb1415eSjob 	exit(1);
139acb1415eSjob }
140acb1415eSjob 
141acb1415eSjob /*
142acb1415eSjob  * yo dawg, i heard you like format strings
143acb1415eSjob  * so i put format strings in your user supplied input
144acb1415eSjob  * so you can format while you format
145acb1415eSjob  */
146acb1415eSjob static void
147*34a84998Scheloha fmtfmt(void)
148acb1415eSjob {
149*34a84998Scheloha 	char *f;
150*34a84998Scheloha 	struct usec *u;
151acb1415eSjob 
152acb1415eSjob 	strlcpy(buf, format, bufsize);
153acb1415eSjob 	f = buf;
154acb1415eSjob 
155acb1415eSjob 	do {
156acb1415eSjob 		while ((f = strchr(f, '%')) != NULL && f[1] == '%')
157acb1415eSjob 			f += 2;
158acb1415eSjob 
159acb1415eSjob 		if (f == NULL)
160acb1415eSjob 			break;
161acb1415eSjob 
162acb1415eSjob 		f++;
163acb1415eSjob 		if (f[0] == '.' &&
164acb1415eSjob 		    (f[1] == 'S' || f[1] == 's' || f[1] == 'T')) {
165acb1415eSjob 			size_t l;
166acb1415eSjob 
167acb1415eSjob 			f[0] = f[1];
168acb1415eSjob 			f[1] = '.';
169acb1415eSjob 			f += 2;
170*34a84998Scheloha 			u = malloc(sizeof u);
171*34a84998Scheloha 			if (u == NULL)
172*34a84998Scheloha 				err(1, NULL);
173*34a84998Scheloha 			u->pos = f;
174*34a84998Scheloha 			SIMPLEQ_INSERT_TAIL(&usec_queue, u, next);
175acb1415eSjob 			l = strlen(f);
176acb1415eSjob 			memmove(f + 6, f, l + 1);
177acb1415eSjob 			f += 6;
178acb1415eSjob 		}
179acb1415eSjob 	} while (*f != '\0');
180*34a84998Scheloha }
181*34a84998Scheloha 
182*34a84998Scheloha static void
183*34a84998Scheloha fmtprint(const struct timespec *ts)
184*34a84998Scheloha {
185*34a84998Scheloha 	char us[8];
186*34a84998Scheloha 	struct tm *tm;
187*34a84998Scheloha 	struct usec *u;
188*34a84998Scheloha 
189*34a84998Scheloha 	if ((tm = localtime(&ts->tv_sec)) == NULL)
190*34a84998Scheloha 		err(1, "localtime");
191*34a84998Scheloha 
192*34a84998Scheloha 	/* Update any microsecond substrings in the format buffer. */
193*34a84998Scheloha 	if (!SIMPLEQ_EMPTY(&usec_queue)) {
194*34a84998Scheloha 		snprintf(us, sizeof(us), "%06ld", ts->tv_nsec / 1000);
195*34a84998Scheloha 		SIMPLEQ_FOREACH(u, &usec_queue, next)
196*34a84998Scheloha 			memcpy(u->pos, us, 6);
197*34a84998Scheloha 	}
198acb1415eSjob 
19983b8c212Sclaudio 	*outbuf = '\0';
20083b8c212Sclaudio 	if (*buf != '\0') {
20183b8c212Sclaudio 		while (strftime(outbuf, obsize, buf, tm) == 0) {
20283b8c212Sclaudio 			if ((outbuf = reallocarray(outbuf, 2, obsize)) == NULL)
20383b8c212Sclaudio 				err(1, NULL);
20483b8c212Sclaudio 			obsize *= 2;
20583b8c212Sclaudio 		}
20683b8c212Sclaudio 	}
207acb1415eSjob 	fprintf(stdout, "%s ", outbuf);
208acb1415eSjob 	if (ferror(stdout))
209acb1415eSjob 		exit(1);
210acb1415eSjob }
211