#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();
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);
}
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");
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);
}