The -m flag is not compliant to either the OpenBSD or the moreutils implementation of ts(1), but closer to the toybox one. It allows the user to add in nanoseconds, like toybox, but allows to specify how many digits are printed. --- .gitignore | 1 + Makefile | 1 + README | 1 + ts.1 | 38 +++++++++++++++++++++++ ts.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 132 insertions(+) create mode 100644 ts.1 create mode 100644 ts.c
diff --git a/.gitignore b/.gitignore index e591886..001b055 100644 --- a/.gitignore +++ b/.gitignore @@ -88,6 +88,7 @@ /touch /tr /true +/ts /tsort /tty /uname diff --git a/Makefile b/Makefile index 55e00d9..7bd9626 100644 --- a/Makefile +++ b/Makefile @@ -177,6 +177,7 @@ BIN =\ touch\ tr\ true\ + ts\ tsort\ tty\ uname\ diff --git a/README b/README index 91a60f9..5555f8b 100644 --- a/README +++ b/README @@ -129,6 +129,7 @@ The following tools are implemented: 0=*|o touch . 0#*|o tr . 0=*|o true . +0=* x ts . 0=* o tsort . 0=*|o tty . 0=*|o uname . diff --git a/ts.1 b/ts.1 new file mode 100644 index 0000000..dcc04ac --- /dev/null +++ b/ts.1 @@ -0,0 +1,38 @@ +.Dd 2024-02-25 +.Dt TS 1 +.Os sbase +.Sh NAME +.Nm ts +.Nd timestamp input +.Sh SYNOPSIS +.Nm +.Op Fl i | Fl s +.Op Fl m Ar n +.Op Ar format +.Sh DESCRIPTION +.Nm +prepends a timestamp to each line of standard input and +writes it to standard output. +The timestamp is formatted using +.Ar format , +according to the conversion specifications described in +.Xf strftime 3 . +The default format is "%b %d %H:%M:%S"; or "%H:%M:%S" +if one of the +.Fl i +or +.Fl s +options is used. +.Sh OPTIONS +.Bl -tag -width Ds +.It Fl i +Display time elapsed since the last timestamp. +.It Fl m +Append to the timestamp +.Ar n +digits (up to 9) of nanoseconds. +.It Fl s +Display time elapsed since the start of the program. +.El +.Sh SEE ALSO +.Xr strftime 3 diff --git a/ts.c b/ts.c new file mode 100644 index 0000000..d419660 --- /dev/null +++ b/ts.c @@ -0,0 +1,91 @@ +#include <stdio.h> +#include <stdlib.h> +#include <time.h> + +#include "util.h" + +#define timespecsub(tsp, usp, vsp) \ + do { \ + (vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec; \ + (vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec; \ + if ((vsp)->tv_nsec < 0) { \ + (vsp)->tv_sec--; \ + (vsp)->tv_nsec += 1000000000L; \ + } \ + } while (0) + +static void +usage(void) +{ + eprintf("usage: %s [-i | -s] [-m] [format]\n", argv0); +} + +int +main(int argc, char *argv[]) +{ + int iflag, mflag, sflag, div; + ssize_t len; + size_t n; + char *fmt, *line = NULL, buf[256], mm[11]; + struct tm *tm; + struct timespec start, now, ts; + clockid_t clock = CLOCK_REALTIME; + + fmt = "%b %d %H:%M:%S"; + *mm = '\0'; + iflag = mflag = sflag = 0; + + ARGBEGIN { + case 'i': + iflag = 1; + fmt = "%H:%M:%S"; + clock = CLOCK_MONOTONIC; + break; + case 'm': + mflag = estrtonum(EARGF(usage()), 1, 9); + break; + case 's': + sflag = 1; + fmt = "%H:%M:%S"; + clock = CLOCK_MONOTONIC; + break; + default: + usage(); + } ARGEND + + /* divisor factor for nanosecs */ + div = 1000000000; + for (int i = mflag; i > 0; i--) + div /= 10; + + setvbuf(stdout, NULL, _IOLBF, 0); + + clock_gettime(clock, &start); + + if (argc > 1) + usage(); + else if (argc == 1) + fmt = *argv; + + while ((len = getline(&line, &n, stdin)) > 0) { + clock_gettime(clock, &now); + if (iflag || sflag) + timespecsub(&now, &start, &ts); + else + ts = now; + if (mflag) + sprintf(mm, ".%0*ld", mflag, ts.tv_nsec/div); + + tm = (iflag || sflag) ? gmtime(&ts.tv_sec) : localtime(&ts.tv_sec); + + strftime(buf, sizeof(buf), fmt, tm); + printf("%s%s ", buf, mm); + fwrite(line, 1, len, stdout); + + if (iflag) + start = now; + } + free(line); + + return 0; +} -- 2.43.0