chiark / gitweb /
stamp: New program.
authorMark Wooding <mdw@ncipher.com>
Fri, 11 Aug 2006 13:07:13 +0000 (14:07 +0100)
committerMark Wooding <mdw@ncipher.com>
Fri, 11 Aug 2006 13:08:36 +0000 (14:08 +0100)
Like cat, but prints datestamps on input lines.

.gitignore
Makefile
debian/control
debian/inst
stamp.1 [new file with mode: 0644]
stamp.c [new file with mode: 0644]

index a11c7b3287baccb83c3f05fbce79b78ed162fb81..3ea58460cf75cbb1ef042c35ce07cf517525b90e 100644 (file)
@@ -10,4 +10,5 @@ build
 pause
 xtitle.so
 *.tar.gz
-
+*.o
+stamp
index 8d75d34b84835becef8342fc1a79d679684ead96..ae6a21305e346a05945f4ca8651991cfc6230bbb 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -10,7 +10,7 @@ BINSCRIPTS = \
        splitconf z buf create inplace
 SBINSCRIPTS = shadowfix
 SCRIPTS = $(BINSCRIPTS) $(SBINSCRIPTS)
-BINPROGS = not cdb-probe cdb-check-domain gorp locking if-mtu pause
+BINPROGS = not cdb-probe cdb-check-domain gorp locking if-mtu pause stamp
 SBINPROGS = qmail-checkspam
 PROGS = $(BINPROGS) $(SBINPROGS)
 PERLLIBS = MdwOpt.pm
@@ -18,7 +18,7 @@ LIBS = xtitle.so
 DISTMAN1 = \
        not.1 z.1 cdb-assign.1 cdb-map.1 cdb-list.1 cdb-probe.1 \
                cdb-check-domain.1 \
-       gorp.1 unfwd.1 splitconf.1 locking.1 if-mtu.1 pause.1 \
+       gorp.1 unfwd.1 splitconf.1 locking.1 if-mtu.1 pause.1 stamp.1 \
        buf.1 create.1 inplace.1
 MAN1 = $(DISTMAN1)
 DISTMAN8 = qmail-checkspam.8
@@ -98,6 +98,9 @@ locking: locking.o
 if-mtu: if-mtu.o
        $(LINK)
 
+stamp: stamp.o
+       $(LINK) -lmLib
+
 shadowfix.8: shadowfix
        pod2man --section 8 shadowfix >shadowfix.8.new
        mv shadowfix.8.new shadowfix.8
index 5fbde5278cf4f4af82f34bc4d65ad327848e1d48..f574ae6b53766a0de5b66bddbe5417f789b88b4a 100644 (file)
@@ -10,7 +10,8 @@ Package: nsict-utils
 Architecture: all
 Section: utils
 Depends: mdwopt-perl, nsict-cdb, locking, qmail-checkspam, nsict-mail,
- if-mtu, shadowfix, zz, gorp, splitconf, xtitle, pause, buf, create, inplace
+ if-mtu, shadowfix, zz, gorp, splitconf, xtitle, pause, buf, create, inplace,
+ stamp
 Description: Dummy package for convenience.
 
 Package: mdwopt-perl
@@ -100,3 +101,8 @@ Package: inplace
 Architecture: all
 Section: utils
 Description: Update files in place safely.
+
+Package: stamp
+Architecture: any
+Section: utils
+Description: Like cat, but prefixing each line with a datestamp.
index bbdbbe44f91a7c0b06e755cbf92a8fd4d337686d..d22050db694fd21cc8259d2127aee67762ac4020 100644 (file)
@@ -33,6 +33,8 @@ shadowfix shadowfix /usr/sbin
 shadowfix.8 shadowfix /usr/share/man/man8
 splitconf splitconf /usr/bin
 splitconf.1 splitconf /usr/share/man/man1
+stamp stamp /usr/bin
+stamp.1 stamp /usr/share/man/man1
 unfwd nsict-mail /usr/bin
 unfwd.1 nsict-mail /usr/share/man/man1
 xtitle.so xtitle /usr/lib/bash
diff --git a/stamp.1 b/stamp.1
new file mode 100644 (file)
index 0000000..5cbe9f0
--- /dev/null
+++ b/stamp.1
@@ -0,0 +1,54 @@
+.\" -*-nroff-*-
+.TH stamp 1 "11 August 2006" "Straylight/Edgeware"
+.SH NAME
+stamp \- copy files, prefixing each line with a datestamp
+.SH SYNOPSIS
+.B stamp
+.RB [ \-z ]
+.RB [ \-f
+.IR format ]
+.RI [ file ...]
+.SH DESCRIPTION
+Copies files, like
+.BR cat (1),
+except that it prefixes each line with a datestamp indicating when the
+line was recieved.  The datestamp is formatted using
+.BR strftime (3),
+and can use any formatting codes permitted by that function.
+.PP
+Options available:
+.TP
+.B \-h, \-\-help
+Show a help message for
+.BR stamp .
+.TP
+.B \-v, \-\-version
+Show version number for
+.BR stamp .
+.TP
+.B \-u, \-\-usage
+Show usage message for
+.BR stamp .
+.TP
+.BI "\-f, \-\-format=" format
+Use 
+.I format
+as the 
+.BR strftime (3)
+format string for the datestamps.  Note that if the datestamp is meant
+to be separated from the rest of the line by anything, then that
+separator should be part of the format string.
+.TP
+.B \-z, \-\-utc, \-\-zulu
+Use UTC (Zulu time) instead of local time for datestamps.
+.SH BUGS
+While
+.B stamp
+ensures that its input is line-buffered only, it's possible that
+whatever is providing that input delays it arbitrarily due to its own
+buffering.  There's not much to be done about this.
+.SH AUTHOR
+Mark Wooding, <mdw@distorted.org.uk>
+.SH SEE ALSO
+.BR cat (1),
+.BR strftime (3).
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);
+}