Dear all, The below changeset is a from scratch & pledged C implementation of the 'ts' perl utility found in the moreutils collection by Joey Hess.
OK to add to /usr/bin? Kind regards, Job Index: ts/Makefile =================================================================== RCS file: ts/Makefile diff -N ts/Makefile --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ ts/Makefile 28 Jun 2022 22:32:32 -0000 @@ -0,0 +1,5 @@ +# $OpenBSD: Makefile,v 1.4 2017/02/19 00:46:57 jca Exp $ + +PROG= ts + +.include <bsd.prog.mk> Index: ts/ts.1 =================================================================== RCS file: ts/ts.1 diff -N ts/ts.1 --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ ts/ts.1 28 Jun 2022 22:32:32 -0000 @@ -0,0 +1,93 @@ +.\" $OpenBSD$ +.\" +.\" Copyright (c) 2022 Job Snijders <[email protected]> +.\" +.\" 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. +.\" +.Dd $Mdocdate: June 28 2022 $ +.Dt TS 1 +.Os +.Sh NAME +.Nm ts +.Nd timestamp input +.Sh SYNOPSIS +.Nm ts +.Op Fl i | s +.Op Ar format +.Sh DESCRIPTION +When invoked, the +.Nm +utility adds a timestamp to the beginning of each line of input. +The optional +.Ar format +argument controls how the timestamp is displayed, according to the conversion +specifications described in the +.Xr strftime 3 +manual page. +The default format is +.Qq %b %d %H:%M:%S . +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl i +Display time elapsed since the last timestamp. +.It Fl s +Display time elapsed since the start of the program. +.El +.Pp +When an option is used, the default format changes to +.Qq %H:%M:%S . +.Sh EXAMPLES +.Bd -literal -offset indent +$ (echo foo; sleep 2; echo bar) | ts +Jun 28 12:13:38 foo +Jun 28 12:13:40 bar + +$ ls | ts %.s +1656455386.515542 Makefile +1656455386.516082 ts.1 +1656455386.516089 ts.c +.Ed +.Sh STANDARDS +The following non-standard conversion specifications append millisecond +resolution: +.Cm \&%.S , +.Cm \&%.s , +and +.Cm \&%.T ; +which are similar to +.Cm \&%S , +.Cm \&%s , +and +.Cm \&%T . +Examples: +.Qq 10.00001 , +.Qq 1656427781.00001 , +and +.Qq 4:20:00.00001 . +.Sh HISTORY +A +.Nm +utility first appeared in the moreutils collection by Joey Hess, and was +rewritten from scratch for +.Ox 7.2 +for licensing reasons. +.Sh AUTHORS +This +.Nm +utility was written by +.An Job Snijders Aq Mt [email protected] +and +.An Claudio Jeker Aq Mt [email protected] +while burning midnight fuel at +.Em r2k22 . Index: ts/ts.c =================================================================== RCS file: ts/ts.c diff -N ts/ts.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ ts/ts.c 28 Jun 2022 22:32:32 -0000 @@ -0,0 +1,164 @@ +/* $OpenBSD$ */ +/* + * Copyright (c) 2022 Job Snijders <[email protected]> + * Copyright (c) 2022 Claudio Jeker <[email protected]> + * + * 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/stat.h> +#include <sys/types.h> + +#include <err.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +static char *format = "%b %d %H:%M:%S"; +static char *buf; +static char *outbuf; +static size_t bufsize; + +static void fmtfmt(struct tm *, long); +static void __dead usage(void); + +int +main(int argc, char *argv[]) +{ + int iflag, sflag; + int ch, prev; + struct timespec start, now, elapsed; + struct tm *lt, tm; + + if (pledge("stdio", NULL) == -1) + err(1, "pledge"); + + iflag = sflag = 0; + + while ((ch = getopt(argc, argv, "is")) != -1) { + switch (ch) { + case 'i': + iflag = 1; + format = "%H:%M:%S"; + break; + case 's': + sflag = 1; + format = "%H:%M:%S"; + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (iflag && sflag) + usage(); + + if (argc == 1) + format = *argv; + + bufsize = strlen(format); + if (bufsize > SIZE_MAX / 10) + errx(1, "format string too big"); + + bufsize *= 10; + if ((buf = malloc(bufsize)) == NULL) + err(1, NULL); + if ((outbuf = malloc(bufsize)) == NULL) + err(1, NULL); + + clock_gettime(CLOCK_MONOTONIC, &start); + + for (prev = '\n'; (ch = getchar()) != EOF; prev = ch) { + if (prev == '\n') { + if (iflag || sflag) { + if (clock_gettime(CLOCK_MONOTONIC, &now)) + err(1, "clock_gettime"); + timespecsub(&now, &start, &elapsed); + if (gmtime_r(&elapsed.tv_sec, &tm) == NULL) + err(1, "gmtime_r"); + if (iflag) + clock_gettime(CLOCK_MONOTONIC, &start); + fmtfmt(&tm, elapsed.tv_nsec); + } else { + if (clock_gettime(CLOCK_REALTIME, &now)) + err(1, "clock_gettime"); + lt = localtime(&now.tv_sec); + if (lt == NULL) + err(1, "localtime"); + fmtfmt(lt, now.tv_nsec); + } + } + if (putchar(ch) == EOF) + break; + } + + if (fclose(stdout)) + err(1, "stdout"); + return 0; +} + +static void __dead +usage(void) +{ + fprintf(stderr, "usage: %s [-i | -s] [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(struct tm *tm, long tv_nsec) +{ + char *f, ms[7]; + + snprintf(ms, sizeof(ms), "%06ld", tv_nsec / 1000); + 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; + l = strlen(f); + memmove(f + 6, f, l + 1); + memcpy(f, ms, 6); + f += 6; + } + } while (*f != '\0'); + + if (strftime(outbuf, bufsize, buf, tm) == 0) + errx(1, "strftime"); + + fprintf(stdout, "%s ", outbuf); + if (ferror(stdout)) + exit(1); +}
