stamp: New program.
[misc] / stamp.c
diff --git a/stamp.c b/stamp.c
new file mode 100644 (file)
index 0000000..a5655a6
--- /dev/null
+++ b/stamp.c
@@ -0,0 +1,138 @@
+/* -*-
+ *
+ * Like cat, with datestamps
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <unistd.h>
+
+#include <mLib/mdwopt.h>
+#include <mLib/quis.h>
+#include <mLib/report.h>
+
+static const char *fmt = "%Y-%m-%d %H:%M:%S %Z: ";
+static struct tm *(*cvt)(const time_t *) = localtime;
+
+static void version(void) { pquis(stdout, "$ " VERSION); }
+static void usage(FILE *fp)
+  { pquis(fp, "Usage: $ [-z] [-f FORMAT] [FILE...]"); }
+
+static void help(void)
+{
+  version(); putchar('\n');
+  usage(stdout);
+  fputs("\n\
+Copy the FILEs (or standard input) to standard output, prefixing each line\n\
+with a datestamp.\n\
+\n\
+-h, --help             Show this help text.\n\
+-v, --version          Show the program's version number.\n\
+-u, --usage            Show a brief usage message.\n\
+\n\
+-f, --format=FORMAT    Write datestamps using the strftime(3) FORMAT.\n\
+-z, --utc, --zulu      Use UTC rather than local time for datestamps.\n\
+", stdout);
+}
+
+static void cat(FILE *in)
+{
+  unsigned ln = 1;
+  time_t t;
+  struct tm *tm;
+  char buf[256];
+  int ch;
+
+  for (;;) {
+    if ((ch = getc(in)) == EOF)
+      break;
+    if (ln) {
+      t = time(0);
+      tm = cvt(&t);
+      strftime(buf, sizeof(buf), fmt, tm);
+      fwrite(buf, 1, strlen(buf), stdout);
+    }
+    putchar(ch);
+    ln = (ch == '\n');
+  }
+  if (!ln)
+    putchar('\n');
+}
+
+int main(int argc, char *argv[])
+{
+  int i;
+  FILE *fp;
+  unsigned f = 0;
+#define F_BOGUS 1u
+
+  ego(argv[0]);
+  setvbuf(stdin, 0, _IOLBF, 0);
+
+  for (;;) {
+    static const struct option opt[] = {
+      { "help",                0,              0,      'h' },
+      { "version",     0,              0,      'v' },
+      { "usage",       0,              0,      'u' },
+      { "format",      OPTF_ARGREQ,    0,      'f' },
+      { "utc",         0,              0,      'z' },
+      { "zulu",                0,              0,      'z' },
+      { 0,             0,              0,      0 }
+    };
+
+    if ((i = mdwopt(argc, argv, "hvuf:z", opt, 0, 0, 0)) < 0)
+      break;
+    switch (i) {
+      case 'h':
+       help();
+       exit(0);
+      case 'v':
+       version();
+       exit(0);
+      case 'u':
+       usage(stdout);
+       exit(0);
+      case 'f':
+       fmt = optarg;
+       break;
+      case 'z':
+       cvt = gmtime;
+       break;
+      default:
+       f |= F_BOGUS;
+       break;
+    }
+  }
+
+  if (f & F_BOGUS) {
+    usage(stderr);
+    exit(EXIT_FAILURE);
+  }
+
+  if (optind == argc) {
+    if (isatty(STDIN_FILENO))
+      die(EXIT_FAILURE, "no arguments, and stdin is a terminal");
+    cat(stdin);
+  } else for (i = optind; i < argc; i++) {
+    if (strcmp(argv[i], "-") == 0)
+      cat(stdin);
+    else if ((fp = fopen(argv[i], "r")) == 0) {
+      moan("failed to open `%s': %s", argv[i], strerror(errno));
+      f |= F_BOGUS;
+    } else {
+      cat(fp);
+      fclose(fp);
+    }
+  }
+
+  if (ferror(stdout) || fflush(stdout) || fclose(stdout)) {
+    moan("error writing output: %s", strerror(errno));
+    f |= F_BOGUS;
+  }
+
+  return ((f & F_BOGUS) ? EXIT_FAILURE : 0);
+}