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