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 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 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 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