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