[BACK]Return to ts.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / ts

File: [local] / src / usr.bin / ts / ts.c (download)

Revision 1.11, Tue Oct 11 07:36:27 2022 UTC (19 months, 1 week ago) by jsg
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, OPENBSD_7_4_BASE, OPENBSD_7_4, OPENBSD_7_3_BASE, OPENBSD_7_3, HEAD
Changes since 1.10: +2 -2 lines

fix unintended sizeof pointer introduced in 1.10
ok cheloha@ miod@ matthieu@ martijn@

/*	$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);
}