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