bin/102609: [PATCH] Add filtering capability to date(1)

Oliver Fromme olli at secnetix.de
Mon Aug 28 15:40:24 UTC 2006


>Number:         102609
>Category:       bin
>Synopsis:       [PATCH] Add filtering capability to date(1)
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Mon Aug 28 15:40:18 GMT 2006
>Closed-Date:
>Last-Modified:
>Originator:     Oliver Fromme
>Release:        FreeBSD 6.1-STABLE i386
>Organization:
secnetix GmbH & Co. KG
		http://www.secnetix.de/bsd
>Environment:

The patch below is against src/bin/date.c rev. 1.47
which is the current revision in 7-current and 6-stable.
The patch also applies cleanly in 5-stable and works fine.
(I haven't tested 4-stable, but it should work there, too.)

A proposed patch for the date(1) manpage is also included.
It is against the 6-stable version of the manpage.
(Note that I'm not a native English speaker, so the patch
might require some review.)

>Description:

Julian Elischer started a thread in the -current mailing list,
proposing an addition to date(1) that turns it into a filter,
reading lines from stdin, adding a time stamp, and writing it
to stdout.  This is Julian's mail that started the thread:

http://lists.freebsd.org/pipermail/freebsd-current/2006-August/065003.html

This PR contains a somewhat different approach to the problem.
It has the same capability as Julian's patch, but it doesn't
require a new command line option letter.  It also provides
better control over the placement of the time stamps, and it
handles arbitrarily long lines.  A description can be found
here:

http://lists.freebsd.org/pipermail/freebsd-current/2006-August/065424.html

Basically, it implements a new conversion specification "%*"
(it can be escaped with a double percent "%%*" sign as usual).
If it occurs in the format string, filter mode is enabled:
A loop is entered, reading lines from stdin and writing to
stdout.  For each line, the format string is expanded as
usual, and the line read from stdin is inserted in the place
of the "%*" character seqeuence.  The result is written to
stdout.

>How-To-Repeat:

	n/a

>Fix:

--- src/bin/date/date.c.orig	Mon Jan 10 09:39:21 2005
+++ src/bin/date/date.c	Mon Aug 28 16:20:15 2006
@@ -66,6 +66,8 @@
 int retval;
 
 static void setthetime(const char *, const char *, int, int);
+static char *strndup(const char *, int);
+static int  parseformat(const char *, const char **, const char **);
 static void badformat(void);
 static void usage(void);
 
@@ -76,6 +78,10 @@
 	int ch, rflag;
 	int jflag, nflag;
 	const char *format;
+	const char *format2;	/* Format part to right of "%*", if any. */
+	int filtermode;		/* True if "%*" occured. */
+	size_t linelen;
+	char *line;
 	char buf[1024];
 	char *endptr, *fmt;
 	char *tmp;
@@ -144,10 +150,13 @@
 		err(1, "time");
 
 	format = "%+";
+	format2 = "";
+	line = NULL;
+	filtermode = 0;
 
 	/* allow the operands in any order */
 	if (*argv && **argv == '+') {
-		format = *argv + 1;
+		filtermode = parseformat(*argv + 1, &format, &format2);
 		++argv;
 	}
 
@@ -158,22 +167,75 @@
 		usage();
 
 	if (*argv && **argv == '+')
-		format = *argv + 1;
+		filtermode = parseformat(*argv + 1, &format, &format2);
 
-	lt = *localtime(&tval);
-	badv = vary_apply(v, &lt);
-	if (badv) {
-		fprintf(stderr, "%s: Cannot apply date adjustment\n",
-			badv->arg);
-		vary_destroy(v);
-		usage();
-	}
-	vary_destroy(v);
-	(void)strftime(buf, sizeof(buf), format, &lt);
-	(void)printf("%s\n", buf);
-	if (fflush(stdout))
-		err(1, "stdout");
+	do {
+		if (filtermode) {
+			if ((line = fgetln(stdin, &linelen)) == NULL)
+				break;
+			if (linelen != 0 && line[linelen - 1] == '\n')
+				linelen--;
+			if (!rflag)
+				tval = time(NULL);
+		}
+		lt = *localtime(&tval);
+		badv = vary_apply(v, &lt);
+		if (badv) {
+			fprintf(stderr, "%s: Cannot apply date adjustment\n",
+				badv->arg);
+			vary_destroy(v);
+			usage();
+		}
+		if (*format) {
+			strftime(buf, sizeof(buf), format, &lt);
+			printf("%s", buf);
+		}
+		if (filtermode) {
+			fwrite(line, linelen, 1, stdout);
+			if (*format2) {
+				strftime(buf, sizeof(buf), format2, &lt);
+				printf("%s", buf);
+			}
+		}
+		printf("\n");
+		if (fflush(stdout))
+			err(1, "stdout");
+	} while (filtermode);
 	exit(retval);
+}
+
+static char *
+strndup(const char *src, int num)
+{
+	char *dst;
+
+	if ((dst = malloc(num + 1)) == NULL)
+		return (NULL);
+	dst[num] = '\0';
+	return (strncpy(dst, src, num));
+}
+
+static int
+parseformat(const char *src, const char **prefix, const char **suffix)
+{
+	const char *fptr;
+	int percent;
+
+	percent = 0;
+	for (fptr = src; *fptr; fptr++) {	/* Scan string for "%*". */
+		if (*fptr == '%')
+			percent = !percent;
+		else if (*fptr == '*' && percent) {
+			if ((*prefix = strndup(src, fptr - src - 1)) == NULL
+			    || (*suffix = strdup(fptr + 1)) == NULL)
+				err(1, "malloc");
+			return (1);	/* filtermode on */
+		}
+		else
+			percent = 0;
+	}
+	*prefix = src;
+	return (0);	/* filtermode off */
 }
 
 #define	ATOI2(s)	((s) += 2, ((s)[-2] - '0') * 10 + ((s)[-1] - '0'))
--- src/bin/date/date.1.orig	Sun Feb 13 23:25:09 2005
+++ src/bin/date/date.1	Mon Aug 28 17:31:10 2006
@@ -253,6 +253,17 @@
 The format string for the default display is
 .Dq +%+ .
 .Pp
+As a special extension, if the format string contains the specification
+.Dq %* ,
+.Nm
+enters filter mode.
+In that mode, lines are read from stdin.  For each line,
+a current time stamp is created according to the format string,
+the line read from stdin is inserted in place of the specification
+.Dq %* ,
+and the result is written to stdout.
+This feature can be used to add time stamps to log files on the fly.
+.Pp
 If an operand does not have a leading plus sign, it is interpreted as
 a value for setting the system's notion of the current date and time.
 The canonical representation for setting the date and time is:
>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list