Sebastian Freundt avatar Sebastian Freundt committed a96d44a Merge

Merge branch 'next'

* next:
minor, provide info and man pages for tgrep
minor, provide regression test for tgrep
minor, add section about tgrep to README
minor, provide tgrep binary in accordance to dgrep
minor, provide dt_tcmp() to compare time values
minor, include strops.h in time-core.h

Comments (0)

Files changed (11)

     =>
       754s
 
+tgrep
+-----
+  A tool to extract lines from an input stream that match certain
+  criteria, showing either the line or the match:
+
+    tgrep '>=12:00:00' <<EOF
+    fileA	11:59:58
+    fileB	11:59:59	leap second?
+    fileNOON	12:00:00	new version
+    fileC	12:03:12
+    EOF
+    =>
+      fileNOON	12:00:00	new version
+      fileC	12:03:12
+
 strptime
 --------
   A tool that brings the flexibility of strptime(3) to the command
 dateutils_TEXINFOS += tseq.texi
 dateutils_TEXINFOS += tadd.texi
 dateutils_TEXINFOS += tdiff.texi
+dateutils_TEXINFOS += tgrep.texi
 dateutils_TEXINFOS += format.texi
 
 dateutils_EXAMPLES =
 dateutils_EXAMPLES += $(tadd_EXAMPLES)
 dateutils_H2M_EX += tadd.h2m
 
+tgrep_EXAMPLES =
+tgrep_EXAMPLES += $(top_srcdir)/test/tgrep.1.dt
+dateutils_EXAMPLES += $(tgrep_EXAMPLES)
+dateutils_H2M_EX += tgrep.h2m
+
 
 dateutils_H2M_EX += format.h2m
 dateutils_H2M_EX += author.h2m
 man1_MANS += tseq.man
 man1_MANS += tadd.man
 man1_MANS += tdiff.man
+man1_MANS += tgrep.man
 
 EXTRA_DIST += $(man1_MANS)
 

info/dateutils.texi

 * dtest::               Compare dates
 * tadd::                Add durations to times
 * tdiff::               Compute durations between times
+* tgrep::               Find time matches in input stream
 * tseq::                Generate sequences of times
 @end menu
 
 @include dtest.texi
 @include tadd.texi
 @include tdiff.texi
+@include tgrep.texi
 @include tseq.texi
 
 @summarycontents
 	return dur;
 }
 
+DEFUN int
+dt_tcmp(struct dt_t_s t1, struct dt_t_s t2)
+{
+	if (t1.u < t2.u) {
+		return -1;
+	} else if (t1.u > t2.u) {
+		return 1;
+	} else {
+		return 0;
+	}
+}
+
 #endif	/* INCLUDED_time_core_c_ */
 /* time-core.c ends here */
  * result in the .sdur slot. */
 DECLF struct dt_t_s dt_tdiff(struct dt_t_s t1, struct dt_t_s t2);
 
+/**
+ * Compare two time values, yielding 0 if they are equal, -1 if T1 is older,
+ * 1 if T1 is younger than the T2. */
+DECLF int dt_tcmp(struct dt_t_s t1, struct dt_t_s t2);
+
 
 #if defined INCLUDE_TIME_CORE_IMPL
 # include "time-core.c"
 tadd_LDFLAGS = $(AM_LDFLAGS)
 BUILT_SOURCES += tadd-clo.c tadd-clo.h
 
+bin_PROGRAMS += tgrep
+tgrep_SOURCES = tgrep.c tgrep-clo.ggo
+tgrep_CPPFLAGS = $(AM_CPPFLAGS) $(DT_INCLUDES)
+tgrep_LDFLAGS = $(AM_LDFLAGS)
+BUILT_SOURCES += tgrep-clo.c tgrep-clo.h
+
 
 ## ggo rule
 %.c %.h: %.ggo $(GGO_HELPERS)

src/tgrep-clo.ggo

+args "--unamed-opts --long-help"
+package "tgrep"
+usage "tgrep [OPTION]... TIME"
+description "Grep TIME in stdin.
+TIME may be prefixed with an operator \
+`<', `<=', '=', '>=', '>', `!=', `<>' \
+meaning, also find lines whose times are older, older-equal, equal, \
+newer-equal, newer, or not equal respectively.  By default tgrep looks
+for equality."
+
+QUIET
+INPUT_FORMAT
+BACKSLASH_ESCAPES
+
+option "only-matching" o
+	"Show only the part of a line matching TIME."
+	optional
+
+defgroup "op" groupdesc="Operations can be specified by options as well"
+
+groupoption "eq" -
+	"TIME1 is the same as TIME2"
+	group="op"
+
+groupoption "ne" -
+	"TIME1 is not the same as TIME2"
+	group="op"
+
+groupoption "gt" -
+	"TIME1 is newer than TIME2"
+	group="op"
+
+groupoption "lt" -
+	"TIME1 is older than TIME2"
+	group="op"
+
+groupoption "ge" -
+	"TIME1 is newer than or equals TIME2"
+	group="op"
+
+groupoption "le" -
+	"TIME1 is older than or equals TIME2"
+	group="op"
+
+## convenience
+groupoption "nt" -
+	"TIME1 is newer than TIME2"
+	group="op"
+
+groupoption "ot" -
+	"TIME1 is older than TIME2"
+	group="op"
+/*** tgrep.c -- grep for lines with time values
+ *
+ * Copyright (C) 2011 Sebastian Freundt
+ *
+ * Author:  Sebastian Freundt <freundt@ga-group.nl>
+ *
+ * This file is part of dateutils.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the author nor the names of any contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ **/
+#include <stdio.h>
+#include <stdio_ext.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/time.h>
+#include <time.h>
+#include "time-core.h"
+#include "time-io.h"
+
+typedef uint32_t oper_t;
+
+enum {
+	OP_UNK = 0,
+	/* bit 1 set */
+	OP_EQ,
+	/* bit 2 set */
+	OP_LT,
+	OP_LE,
+	/* bit 3 set */
+	OP_GT,
+	OP_GE,
+	/* bits 2 and 3 set */
+	OP_NE,
+	/* bits 1, 2 and 3 set */
+	OP_FUCKED,
+};
+
+
+static oper_t
+find_oper(const char *s, char **ep)
+{
+	oper_t res = OP_UNK;
+
+	switch (*s) {
+	default:
+		break;
+	case '<':
+		switch (*++s) {
+		default:
+			res = OP_LT;
+			break;
+		case '=':
+			res = OP_LE;
+			s++;
+			break;
+		case '>':
+			res = OP_NE;
+			s++;
+			break;
+		}
+		break;
+	case '>':
+		switch (*++s) {
+		default:
+			res = OP_GT;
+			break;
+		case '=':
+			res = OP_GE;
+			s++;
+			break;
+		}
+		break;
+	case '=':
+		switch (*++s) {
+		case '=':
+			s++;
+		default:
+			res = OP_EQ;
+			break;
+		case '>':
+			res = OP_GE;
+			s++;
+			break;
+		}
+		break;
+	case '!':
+		switch (*++s) {
+		case '=':
+			s++;
+		default:
+			res = OP_NE;
+			break;
+		}
+		break;
+	}
+	if (ep) {
+		*ep = (char*)s;
+	}
+	return res;
+}
+
+static bool
+matchp(struct dt_t_s linet, struct dt_t_s reft, oper_t o)
+{
+	int cmp = dt_tcmp(linet, reft);
+
+	switch (o) {
+	case OP_EQ:
+		return cmp == 0;
+	case OP_LT:
+		return cmp < 0;
+	case OP_LE:
+		return cmp <= 0;
+	case OP_GT:
+		return cmp > 0;
+	case OP_GE:
+		return cmp >= 0;
+	case OP_NE:
+	case OP_FUCKED:
+		return cmp != 0;
+	case OP_UNK:
+	default:
+		return false;
+	}
+}
+
+
+#if defined __INTEL_COMPILER
+# pragma warning (disable:593)
+# pragma warning (disable:181)
+#endif	/* __INTEL_COMPILER */
+#include "tgrep-clo.h"
+#include "tgrep-clo.c"
+#if defined __INTEL_COMPILER
+# pragma warning (default:593)
+# pragma warning (default:181)
+#endif	/* __INTEL_COMPILER */
+
+int
+main(int argc, char *argv[])
+{
+	struct gengetopt_args_info argi[1];
+	struct dt_t_s reft;
+	char **fmt;
+	size_t nfmt;
+	char *inp;
+	oper_t o = OP_UNK;
+	int res = 0;
+
+	if (cmdline_parser(argc, argv, argi)) {
+		res = 1;
+		goto out;
+	}
+
+	/* init and unescape sequences, maybe */
+	fmt = argi->input_format_arg;
+	nfmt = argi->input_format_given;
+	if (argi->backslash_escapes_given) {
+		for (size_t i = 0; i < nfmt; i++) {
+			dt_io_unescape(fmt[i]);
+		}
+	}
+
+	if (argi->eq_given) {
+		o = OP_EQ;
+	} else if (argi->ne_given) {
+		o = OP_NE;
+	} else if (argi->lt_given || argi->ot_given) {
+		o = OP_LT;
+	} else if (argi->le_given) {
+		o = OP_LE;
+	} else if (argi->gt_given || argi->nt_given) {
+		o = OP_GT;
+	} else if (argi->ge_given) {
+		o = OP_GE;
+	}
+	if (argi->inputs_num != 1 ||
+	    (o |= find_oper(argi->inputs[0], &inp)) == OP_FUCKED ||
+	    (reft = dt_io_strpt(inp, fmt, nfmt)).s < 0) {
+		res = 1;
+		fputs("need a TIME to grep\n", stderr);
+		goto out;
+	}
+
+	/* fixup o, default is OP_EQ */
+	o = o ?: OP_EQ;
+	{
+		/* read from stdin */
+		FILE *fp = stdin;
+		char *line;
+		size_t lno = 0;
+		struct grep_atom_s __nstk[16], *needle = __nstk;
+		size_t nneedle = countof(__nstk);
+		struct grep_atom_soa_s ndlsoa;
+
+		/* no threads reading this stream */
+		__fsetlocking(fp, FSETLOCKING_BYCALLER);
+		/* no threads reading this stream */
+		__fsetlocking(stdout, FSETLOCKING_BYCALLER);
+
+		/* lest we overflow the stack */
+		if (nfmt >= nneedle) {
+			/* round to the nearest 8-multiple */
+			nneedle = (nfmt | 7) + 1;
+			needle = calloc(nneedle, sizeof(*needle));
+		}
+		/* and now build the needle */
+		ndlsoa = build_needle(needle, nneedle, fmt, nfmt);
+
+		for (line = NULL; !feof_unlocked(fp); lno++) {
+			ssize_t n;
+			size_t len;
+			struct dt_t_s t;
+			const char *sp = NULL;
+			const char *ep = NULL;
+
+			n = getline(&line, &len, fp);
+			if (n < 0) {
+				break;
+			}
+			/* check if line matches,
+			 * there is currently no way to specify NEEDLE */
+			t = dt_io_find_strpt2(
+				line, &ndlsoa, (char**)&sp, (char**)&ep);
+			if (t.s >= 0 && matchp(t, reft, o)) {
+				if (!argi->only_matching_given) {
+					sp = line;
+					ep = line + n - 1;
+				}
+				fwrite(sp, sizeof(*sp), ep - sp, stdout);
+				fputc('\n', stdout);
+			}
+		}
+		/* get rid of resources */
+		free(line);
+		if (needle != __nstk) {
+			free(needle);
+		}
+	}
+
+out:
+	cmdline_parser_free(argi);
+	return res;
+}
+
+/* tgrep.c ends here */
 #include <stdio.h>
 #include <stdio_ext.h>
 #include "time-core.h"
+#include "strops.h"
 
 #if !defined LIKELY
 # define LIKELY(_x)	__builtin_expect(!!(_x), 1)
 TESTS += tadd.4.dt
 TESTS += tadd.5.dt
 
+TESTS += tgrep.1.dt
+
 ## Makefile.am ends here
+## -*- shell-script -*-
+
+TOOL=tgrep
+CMDLINE="'>=12:00:00'"
+
+## STDIN
+stdin=$(mktemp)
+cat > "${stdin}" <<EOF
+fileA	11:59:58
+fileB	11:59:59	leap second?
+fileNOON	12:00:00	new version
+fileC	12:03:12
+EOF
+
+## STDOUT
+stdout=$(mktemp)
+cat > "${stdout}" <<EOF
+fileNOON	12:00:00	new version
+fileC	12:03:12
+EOF
+
+## tgrep.1.dt ends here
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.