/* This file is part of addts
* Copyright (C) 2018, 2019 Sergey Poznyakoff
*
* Addts is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* Addts is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with addts. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include
static char default_fmt[] = "%Y-%m-%d %H:%M:%S: ";
enum mode {
PREFIX,
SUFFIX,
NMODE
};
enum state {
SINI, /* initial */
SEOL, /* end of line */
SLIN, /* Midle of line */
STIM, /* Time to insert timestamp */
NSTATE
};
enum alpha {
ACHR,
AEOL,
NALPHA
};
int prefix_trans[NSTATE][NALPHA] = {
/* ACHR AEOL */
{ STIM, SEOL }, /* SINI */
{ STIM, SEOL }, /* SEOL */
{ SLIN, SEOL }, /* SLIN */
{ SLIN, SEOL } /* STIM */
};
int suffix_trans[NSTATE][NALPHA] = {
/* ACHR AEOL */
{ SLIN, SEOL }, /* SINI */
{ SLIN, SEOL }, /* SEOL */
{ SLIN, STIM }, /* SLIN */
{ SLIN, SEOL } /* STIM */
};
static inline int
alpha(int c)
{
return c == '\n' ? AEOL : ACHR;
}
static void
help(char *progname)
{
printf("Usage: %s [OPTION]... [FILE]\n", progname);
puts("add timestamps at the beginning of each line\n");
puts(" -a append to FILE, instead of overwriting it");
puts(" -f FORMAT strftime(3) format for timestamps");
puts(" -s add timestamp to the end of each line");
puts(" -u report times in UTC");
puts(" -w CHR replace CHR with a horizontal space in FORMAT");
putchar('\n');
printf("Default FORMAT is \"%s\"\n", default_fmt);
putchar('\n');
puts("Report bugs to ");
}
int
main(int argc, char **argv)
{
int c;
char buf[512];
int utc_opt = 0;
int append_opt = 0;
int ws_opt = 0;
int end_opt = 0;
char *p;
char *fmt = NULL;
FILE *fp;
enum state state = SINI;
int (*trans)[NALPHA] = prefix_trans;
while ((c = getopt(argc, argv, "?af:suw:")) != EOF) {
switch (c) {
case 'a':
append_opt = 1;
break;
case 'f':
fmt = optarg;
break;
case 's':
trans = suffix_trans;
break;
case 'u':
utc_opt = 1;
break;
case 'w':
ws_opt = optarg[0];
break;
default:
if (optopt == 0) {
help(argv[0]);
return 0;
}
return 1;
}
}
if (fmt) {
if (ws_opt) {
for (p = fmt; *p; p++)
if (*p == ws_opt)
*p = ' ';
}
} else
fmt = default_fmt;
argc -= optind;
argv += optind;
switch (argc) {
case 0:
fp = stdout;
break;
case 1:
fp = fopen(argv[0], append_opt ? "a" : "w");
if (!fp) {
perror(argv[0]);
return 1;
}
break;
default:
fprintf(stderr, "%s: too many arguments\n", argv[0]);
return 1;
}
setvbuf(fp, NULL, _IOLBF, 0);
while ((c = getchar()) != EOF) {
state = trans[state][alpha(c)];
if (state == STIM) {
struct timeval tv;
struct tm *tm;
size_t sz;
char *start, *p;
gettimeofday(&tv, NULL);
tm = (utc_opt ? gmtime : localtime)(&tv.tv_sec);
sz = strftime(buf, sizeof(buf), fmt, tm);
if (sz == 0 || sz == sizeof(buf)) {
strcpy(buf, "[OVERFLOW]: ");
sz = strlen(buf);
}
start = buf;
while ((p = strstr(start, "%@")) != NULL) {
fwrite(start, 1, p - start, fp);
fprintf(fp, "%06d", tv.tv_usec);
sz -= p - start + 2;
start = p + 2;
}
fwrite(start, 1, sz, fp);
}
fputc(c, fp);
}
fclose(fp);
return 0;
}