diff options
Diffstat (limited to 'src/usr.bin/ts/ts.c')
-rw-r--r-- | src/usr.bin/ts/ts.c | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/src/usr.bin/ts/ts.c b/src/usr.bin/ts/ts.c new file mode 100644 index 0000000..f8e3cb0 --- /dev/null +++ b/src/usr.bin/ts/ts.c @@ -0,0 +1,210 @@ +/* $OpenBSD: ts.c,v 1.11 2022/10/11 07:36:27 jsg Exp $ */ +/* + * Copyright (c) 2022 Job Snijders <job@openbsd.org> + * Copyright (c) 2022 Claudio Jeker <claudio@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/queue.h> +#include <sys/time.h> + +#include <err.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <time.h> + +SIMPLEQ_HEAD(, usec) usec_queue = SIMPLEQ_HEAD_INITIALIZER(usec_queue); +struct usec { + SIMPLEQ_ENTRY(usec) next; + char *pos; +}; + +static char *format = "%b %d %H:%M:%S"; +static char *buf; +static char *outbuf; +static size_t bufsize; +static size_t obsize; + +static void fmtfmt(void); +static void fmtprint(const struct timespec *); +static void __dead usage(void); + +int +main(int argc, char *argv[]) +{ + int iflag, mflag, sflag; + int ch, prev; + struct timespec start, now, utc_offset, ts; + clockid_t clock = CLOCK_REALTIME; + + if (pledge("stdio", NULL) == -1) + err(1, "pledge"); + + iflag = mflag = sflag = 0; + + while ((ch = getopt(argc, argv, "ims")) != -1) { + switch (ch) { + case 'i': + iflag = 1; + format = "%H:%M:%S"; + clock = CLOCK_MONOTONIC; + break; + case 'm': + mflag = 1; + clock = CLOCK_MONOTONIC; + break; + case 's': + sflag = 1; + format = "%H:%M:%S"; + clock = CLOCK_MONOTONIC; + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + + setvbuf(stdout, NULL, _IOLBF, 0); + + if ((iflag && sflag) || argc > 1) + usage(); + + if (argc == 1) + format = *argv; + + bufsize = strlen(format) + 1; + if (bufsize > SIZE_MAX / 10) + errx(1, "format string too big"); + bufsize *= 10; + obsize = bufsize; + if ((buf = calloc(1, bufsize)) == NULL) + err(1, NULL); + if ((outbuf = calloc(1, obsize)) == NULL) + err(1, NULL); + + fmtfmt(); + + /* force UTC for interval calculations */ + if (iflag || sflag) + if (setenv("TZ", "UTC", 1) == -1) + err(1, "setenv UTC"); + + clock_gettime(clock, &start); + clock_gettime(CLOCK_REALTIME, &utc_offset); + timespecsub(&utc_offset, &start, &utc_offset); + + for (prev = '\n'; (ch = getchar()) != EOF; prev = ch) { + if (prev == '\n') { + clock_gettime(clock, &now); + if (iflag || sflag) + timespecsub(&now, &start, &ts); + else if (mflag) + timespecadd(&now, &utc_offset, &ts); + else + ts = now; + fmtprint(&ts); + if (iflag) + start = now; + } + if (putchar(ch) == EOF) + break; + } + + if (fclose(stdout)) + err(1, "stdout"); + return 0; +} + +static void __dead +usage(void) +{ + fprintf(stderr, "usage: %s [-i | -s] [-m] [format]\n", getprogname()); + exit(1); +} + +/* + * yo dawg, i heard you like format strings + * so i put format strings in your user supplied input + * so you can format while you format + */ +static void +fmtfmt(void) +{ + char *f; + struct usec *u; + + strlcpy(buf, format, bufsize); + f = buf; + + do { + while ((f = strchr(f, '%')) != NULL && f[1] == '%') + f += 2; + + if (f == NULL) + break; + + f++; + if (f[0] == '.' && + (f[1] == 'S' || f[1] == 's' || f[1] == 'T')) { + size_t l; + + f[0] = f[1]; + f[1] = '.'; + f += 2; + u = malloc(sizeof *u); + if (u == NULL) + err(1, NULL); + u->pos = f; + SIMPLEQ_INSERT_TAIL(&usec_queue, u, next); + l = strlen(f); + memmove(f + 6, f, l + 1); + f += 6; + } + } while (*f != '\0'); +} + +static void +fmtprint(const struct timespec *ts) +{ + char us[8]; + struct tm *tm; + struct usec *u; + + if ((tm = localtime(&ts->tv_sec)) == NULL) + err(1, "localtime"); + + /* Update any microsecond substrings in the format buffer. */ + if (!SIMPLEQ_EMPTY(&usec_queue)) { + snprintf(us, sizeof(us), "%06ld", ts->tv_nsec / 1000); + SIMPLEQ_FOREACH(u, &usec_queue, next) + memcpy(u->pos, us, 6); + } + + *outbuf = '\0'; + if (*buf != '\0') { + while (strftime(outbuf, obsize, buf, tm) == 0) { + if ((outbuf = reallocarray(outbuf, 2, obsize)) == NULL) + err(1, NULL); + obsize *= 2; + } + } + fprintf(stdout, "%s ", outbuf); + if (ferror(stdout)) + exit(1); +} |