1 /*** dgrep.c -- grep for lines with dates
2  *
3  * Copyright (C) 2011-2016 Sebastian Freundt
4  *
5  * Author:  Sebastian Freundt <freundt@ga-group.nl>
6  *
7  * This file is part of dateutils.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * 3. Neither the name of the author nor the names of any contributors
21  *    may be used to endorse or promote products derived from this
22  *    software without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
25  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
26  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27  * DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
31  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
32  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
33  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
34  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35  *
36  **/
37 #if defined HAVE_CONFIG_H
38 # include "config.h"
39 #endif	/* HAVE_CONFIG_H */
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <stdint.h>
43 #include <sys/time.h>
44 #include <time.h>
45 
46 #include "dt-core.h"
47 #include "dt-core-tz-glue.h"
48 #include "dt-io.h"
49 #include "dexpr.h"
50 #include "dt-locale.h"
51 #include "prchunk.h"
52 
53 const char *prog = "dgrep";
54 
55 
56 /* dexpr subsystem */
57 #include "dexpr.c"
58 
59 struct prln_ctx_s {
60 	struct grep_atom_soa_s *ndl;
61 	dexpr_t root;
62 	zif_t fromz;
63 	zif_t z;
64 	unsigned int only_matching_p:1U;
65 	unsigned int invert_match_p:1U;
66 };
67 
68 static void
proc_line(struct prln_ctx_s ctx,char * line,size_t llen)69 proc_line(struct prln_ctx_s ctx, char *line, size_t llen)
70 {
71 	char *osp = NULL;
72 	char *oep = NULL;
73 
74 	/* check if line matches,
75 	 * there's currently no way to specify NEEDLE */
76 	for (char *lp = line, *const zp = line + llen, *sp, *ep;
77 	     /*no check*/; lp = ep, osp = sp, oep = ep) {
78 		struct dt_dt_s d =
79 			dt_io_find_strpdt2(
80 				lp, zp - lp, ctx.ndl, &sp, &ep, ctx.fromz);
81 		bool unkp = dt_unk_p(d);
82 
83 		if (unkp) {
84 			/* just plain nothing */
85 			break;
86 		} else if (ctx.z != NULL) {
87 			/* promote to zone ctx.z */
88 			d = dtz_enrichz(d, ctx.z);
89 		}
90 		/* otherwise */
91 		if (dexpr_matches_p(ctx.root, d)) {
92 			if (ctx.invert_match_p) {
93 				/* nothing must match */
94 				return;
95 			} else if (!ctx.only_matching_p) {
96 				sp = line;
97 				ep = line + llen;
98 			}
99 			/* make sure we finish the line */
100 			*ep++ = '\n';
101 			__io_write(sp, ep - sp, stdout);
102 			return;
103 		}
104 	}
105 	if (ctx.invert_match_p) {
106 		/* no match but invert_match select, print line */
107 		if (!ctx.only_matching_p) {
108 			osp = line;
109 			oep = line + llen;
110 		} else if (osp == NULL || oep == NULL) {
111 			/* no date in line and only-matching is active
112 			 * bugger off */
113 			return;
114 		}
115 		/* finish the line and bugger off */
116 		*oep++ = '\n';
117 		__io_write(osp, oep - osp, stdout);
118 	}
119 	return;
120 }
121 
122 
123 #include "dgrep.yucc"
124 
125 int
main(int argc,char * argv[])126 main(int argc, char *argv[])
127 {
128 	yuck_t argi[1U];
129 	char **fmt;
130 	size_t nfmt;
131 	dexpr_t root;
132 	oper_t o = OP_UNK;
133 	int res = 0;
134 
135 	if (yuck_parse(argi, argc, argv)) {
136 		res = 1;
137 		goto out;
138 	}
139 
140 	/* init and unescape sequences, maybe */
141 	ckv_fmt = fmt = argi->input_format_args;
142 	ckv_nfmt = nfmt = argi->input_format_nargs;
143 	if (argi->backslash_escapes_flag) {
144 		for (size_t i = 0; i < nfmt; i++) {
145 			dt_io_unescape(fmt[i]);
146 		}
147 	}
148 	if (argi->base_arg) {
149 		struct dt_dt_s base = dt_strpdt(argi->base_arg, NULL, NULL);
150 		dt_set_base(base);
151 	}
152 
153 	if (argi->eq_flag) {
154 		o = OP_EQ;
155 	} else if (argi->ne_flag) {
156 		o = OP_NE;
157 	} else if (argi->lt_flag || argi->ot_flag) {
158 		o = OP_LT;
159 	} else if (argi->le_flag) {
160 		o = OP_LE;
161 	} else if (argi->gt_flag || argi->nt_flag) {
162 		o = OP_GT;
163 	} else if (argi->ge_flag) {
164 		o = OP_GE;
165 	}
166 	/* parse the expression */
167 	if (argi->nargs == 0U ||
168 	    dexpr_parse(&root, argi->args[0U], strlen(argi->args[0U])) < 0) {
169 		res = 1;
170 		error("Error: need an expression to grep");
171 		goto out;
172 	}
173 	/* fixup o, default is OP_EQ */
174 	if (o != OP_UNK && root->type != DEX_VAL) {
175 		res = 1;
176 		error("\
177 long opt operators (--lt, --gt, ...) cannot be used in conjunction \n\
178 with complex expressions");
179 		goto out;
180 	} else if (o != OP_UNK) {
181 		/* fiddle with the operator in the expression */
182 		root->kv->op = o;
183 	}
184 
185 	if (argi->from_locale_arg) {
186 		setilocale(argi->from_locale_arg);
187 	}
188 
189 	/* otherwise bring dexpr to normal form */
190 	dexpr_simplify(root);
191 	/* beef */
192 	{
193 		/* read from stdin */
194 		size_t lno = 0;
195 		struct grep_atom_s __nstk[16], *needle = __nstk;
196 		size_t nneedle = countof(__nstk);
197 		struct grep_atom_soa_s ndlsoa;
198 		void *pctx;
199 		struct prln_ctx_s prln = {
200 			.ndl = &ndlsoa,
201 			.root = root,
202 			.fromz = dt_io_zone(argi->from_zone_arg),
203 			.z = dt_io_zone(argi->zone_arg),
204 			.only_matching_p = argi->only_matching_flag,
205 			.invert_match_p = argi->invert_match_flag,
206 		};
207 
208 		/* no threads reading this stream */
209 		__io_setlocking_bycaller(stdout);
210 
211 		/* lest we overflow the stack */
212 		if (nfmt >= nneedle) {
213 			/* round to the nearest 8-multiple */
214 			nneedle = (nfmt | 7) + 1;
215 			needle = calloc(nneedle, sizeof(*needle));
216 		}
217 		/* and now build the needle */
218 		ndlsoa = build_needle(needle, nneedle, fmt, nfmt);
219 
220 		/* using the prchunk reader now */
221 		if ((pctx = init_prchunk(STDIN_FILENO)) == NULL) {
222 			serror("Error: could not open stdin");
223 			goto ndl_free;
224 		}
225 		while (prchunk_fill(pctx) >= 0) {
226 			for (char *line; prchunk_haslinep(pctx); lno++) {
227 				size_t llen = prchunk_getline(pctx, &line);
228 
229 				proc_line(prln, line, llen);
230 			}
231 		}
232 		/* get rid of resources */
233 		free_prchunk(pctx);
234 	ndl_free:
235 		if (needle != __nstk) {
236 			free(needle);
237 		}
238 	}
239 	/* resource freeing */
240 	free_dexpr(root);
241 	dt_io_clear_zones();
242 	if (argi->from_locale_arg) {
243 		setilocale(NULL);
244 	}
245 out:
246 	yuck_free(argi);
247 	return res;
248 }
249 
250 /* dgrep.c ends here */
251