1 /*** dadd.c -- perform simple date arithmetic, date plus duration
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-io.h"
48 #include "dt-core-tz-glue.h"
49 #include "dt-locale.h"
50 #include "prchunk.h"
51
52 const char *prog = "dadd";
53
54
55 static bool
durs_only_d_p(struct dt_dtdur_s dur[],size_t ndur)56 durs_only_d_p(struct dt_dtdur_s dur[], size_t ndur)
57 {
58 for (size_t i = 0; i < ndur; i++) {
59 if (dur[i].durtyp >= (dt_dtdurtyp_t)DT_NDURTYP) {
60 return false;
61 }
62 }
63 return true;
64 }
65
66 static struct dt_dt_s
dadd_add(struct dt_dt_s d,struct dt_dtdur_s dur[],size_t ndur)67 dadd_add(struct dt_dt_s d, struct dt_dtdur_s dur[], size_t ndur)
68 {
69 for (size_t i = 0; i < ndur; i++) {
70 d = dt_dtadd(d, dur[i]);
71 }
72 return d;
73 }
74
75
76 struct mass_add_clo_s {
77 void *pctx;
78 const struct grep_atom_soa_s *gra;
79 struct __strpdtdur_st_s st;
80 struct dt_dt_s rd;
81 zif_t fromz;
82 zif_t hackz;
83 zif_t z;
84 const char *ofmt;
85 int sed_mode_p;
86 int quietp;
87 };
88
89 static int
proc_line(const struct mass_add_clo_s * clo,char * line,size_t llen)90 proc_line(const struct mass_add_clo_s *clo, char *line, size_t llen)
91 {
92 struct dt_dt_s d;
93 char *sp = NULL;
94 char *ep = NULL;
95 int rc = 0;
96
97 do {
98 /* check if line matches, */
99 d = dt_io_find_strpdt2(
100 line, llen, clo->gra, &sp, &ep, clo->fromz);
101
102 if (!dt_unk_p(d)) {
103 if (UNLIKELY(d.fix) && !clo->quietp) {
104 rc = 2;
105 }
106 /* perform addition now */
107 d = dadd_add(d, clo->st.durs, clo->st.ndurs);
108
109 if (clo->hackz == NULL && clo->fromz != NULL) {
110 /* fixup zone */
111 d = dtz_forgetz(d, clo->fromz);
112 }
113
114 if (clo->sed_mode_p) {
115 __io_write(line, sp - line, stdout);
116 dt_io_write(d, clo->ofmt, clo->z, '\0');
117 llen -= (ep - line);
118 line = ep;
119 } else {
120 dt_io_write(d, clo->ofmt, clo->z, '\n');
121 break;
122 }
123 } else if (clo->sed_mode_p) {
124 line[llen] = '\n';
125 __io_write(line, llen + 1, stdout);
126 break;
127 } else {
128 /* obviously unmatched, warn about it in non -q mode */
129 if (!clo->quietp) {
130 dt_io_warn_strpdt(line);
131 rc = 2;
132 }
133 break;
134 }
135 } while (1);
136 return rc;
137 }
138
139 static int
mass_add_dur(const struct mass_add_clo_s * clo)140 mass_add_dur(const struct mass_add_clo_s *clo)
141 {
142 /* read lines from stdin
143 * interpret as dates
144 * add to reference duration
145 * output */
146 size_t lno = 0;
147 int rc = 0;
148
149 for (char *line; prchunk_haslinep(clo->pctx); lno++) {
150 size_t llen = prchunk_getline(clo->pctx, &line);
151
152 rc |= proc_line(clo, line, llen);
153 }
154 return rc;
155 }
156
157 static int
mass_add_d(const struct mass_add_clo_s * clo)158 mass_add_d(const struct mass_add_clo_s *clo)
159 {
160 /* read lines from stdin
161 * interpret as durations
162 * add to reference date
163 * output */
164 size_t lno = 0;
165 struct dt_dt_s d;
166 struct __strpdtdur_st_s st = __strpdtdur_st_initialiser();
167 int rc = 0;
168
169 for (char *line; prchunk_haslinep(clo->pctx); lno++) {
170 size_t llen;
171 int has_dur_p = 1;
172
173 llen = prchunk_getline(clo->pctx, &line);
174
175 /* check for durations on this line */
176 do {
177 if (dt_io_strpdtdur(&st, line) < 0) {
178 has_dur_p = 0;
179 }
180 } while (__strpdtdur_more_p(&st));
181
182 /* finish with newline again */
183 line[llen] = '\n';
184
185 if (has_dur_p) {
186 if (UNLIKELY(clo->rd.fix) && !clo->quietp) {
187 rc = 2;
188 }
189 /* perform addition now */
190 d = dadd_add(clo->rd, st.durs, st.ndurs);
191
192 if (clo->hackz == NULL && clo->fromz != NULL) {
193 /* fixup zone */
194 d = dtz_forgetz(d, clo->fromz);
195 }
196
197 /* no sed mode here */
198 dt_io_write(d, clo->ofmt, clo->z, '\n');
199 } else if (clo->sed_mode_p) {
200 __io_write(line, llen + 1, stdout);
201 } else if (!clo->quietp) {
202 line[llen] = '\0';
203 dt_io_warn_strpdt(line);
204 rc = 2;
205 }
206 /* just reset the ndurs slot */
207 st.ndurs = 0;
208 }
209 /* free associated duration resources */
210 __strpdtdur_free(&st);
211 return rc;
212 }
213
214
215 #include "dadd.yucc"
216
217 int
main(int argc,char * argv[])218 main(int argc, char *argv[])
219 {
220 yuck_t argi[1U];
221 struct dt_dt_s d;
222 struct __strpdtdur_st_s st = __strpdtdur_st_initialiser();
223 const char *ofmt;
224 char **fmt;
225 size_t nfmt;
226 int rc = 0;
227 bool dt_given_p = false;
228 zif_t fromz = NULL;
229 zif_t z = NULL;
230 zif_t hackz = NULL;
231
232 if (yuck_parse(argi, argc, argv)) {
233 rc = 1;
234 goto out;
235 } else if (argi->nargs == 0) {
236 error("Error: DATE or DURATION must be specified\n");
237 yuck_auto_help(argi);
238 rc = 1;
239 goto out;
240 }
241 /* init and unescape sequences, maybe */
242 ofmt = argi->format_arg;
243 fmt = argi->input_format_args;
244 nfmt = argi->input_format_nargs;
245 if (argi->backslash_escapes_flag) {
246 dt_io_unescape(argi->format_arg);
247 for (size_t i = 0; i < nfmt; i++) {
248 dt_io_unescape(fmt[i]);
249 }
250 }
251
252 if (argi->from_locale_arg) {
253 setilocale(argi->from_locale_arg);
254 }
255 if (argi->locale_arg) {
256 setflocale(argi->locale_arg);
257 }
258
259 /* try and read the from and to time zones */
260 if (argi->from_zone_arg) {
261 fromz = dt_io_zone(argi->from_zone_arg);
262 }
263 if (argi->zone_arg) {
264 z = dt_io_zone(argi->zone_arg);
265 }
266 if (argi->base_arg) {
267 struct dt_dt_s base = dt_strpdt(argi->base_arg, NULL, NULL);
268 dt_set_base(base);
269 }
270
271 /* sanity checks, decide whether we're a mass date adder
272 * or a mass duration adder, or both, a date and durations are
273 * present on the command line */
274 with (const char *inp = argi->args[0U]) {
275 /* date parsing needed postponing as we need to find out
276 * about the durations */
277 if (!dt_unk_p(dt_io_strpdt(inp, fmt, nfmt, NULL))) {
278 dt_given_p = true;
279 }
280 }
281
282 /* check first arg, if it's a date the rest of the arguments are
283 * durations, if not, dates must be read from stdin */
284 for (size_t i = dt_given_p; i < argi->nargs; i++) {
285 const char *inp = argi->args[i];
286 do {
287 if (dt_io_strpdtdur(&st, inp) < 0) {
288 serror("Error: \
289 cannot parse duration string `%s'", st.istr);
290 rc = 1;
291 goto dur_out;
292 }
293 } while (__strpdtdur_more_p(&st));
294 }
295 /* check if there's only d durations */
296 hackz = durs_only_d_p(st.durs, st.ndurs) ? NULL : fromz;
297
298 /* read the first argument again in light of a completely parsed
299 * duration sequence */
300 if (dt_given_p) {
301 const char *inp = argi->args[0U];
302 if (dt_unk_p(d = dt_io_strpdt(inp, fmt, nfmt, hackz))) {
303 error("\
304 Error: cannot interpret date/time string `%s'", inp);
305 rc = 1;
306 goto dur_out;
307 }
308 }
309
310 /* start the actual work */
311 if (dt_given_p && st.ndurs) {
312 if (!dt_unk_p(d = dadd_add(d, st.durs, st.ndurs))) {
313 if (UNLIKELY(d.fix) && !argi->quiet_flag) {
314 rc = 2;
315 }
316 if (hackz == NULL && fromz != NULL) {
317 /* fixup zone */
318 d = dtz_forgetz(d, fromz);
319 }
320 dt_io_write(d, ofmt, z, '\n');
321 } else {
322 rc = 1;
323 }
324
325 } else if (st.ndurs) {
326 /* read dates from stdin */
327 struct grep_atom_s __nstk[16], *needle = __nstk;
328 size_t nneedle = countof(__nstk);
329 struct grep_atom_soa_s ndlsoa;
330 struct mass_add_clo_s clo[1];
331 void *pctx;
332
333 /* no threads reading this stream */
334 __io_setlocking_bycaller(stdout);
335
336 /* lest we overflow the stack */
337 if (nfmt >= nneedle) {
338 /* round to the nearest 8-multiple */
339 nneedle = (nfmt | 7) + 1;
340 needle = calloc(nneedle, sizeof(*needle));
341 }
342 /* and now build the needle */
343 ndlsoa = build_needle(needle, nneedle, fmt, nfmt);
344
345 /* using the prchunk reader now */
346 if ((pctx = init_prchunk(STDIN_FILENO)) == NULL) {
347 serror("could not open stdin");
348 goto ndl_free;
349 }
350
351 /* build the clo and then loop */
352 clo->pctx = pctx;
353 clo->gra = &ndlsoa;
354 clo->st = st;
355 clo->fromz = fromz;
356 clo->hackz = hackz;
357 clo->z = z;
358 clo->ofmt = ofmt;
359 clo->sed_mode_p = argi->sed_mode_flag;
360 clo->quietp = argi->quiet_flag;
361 while (prchunk_fill(pctx) >= 0) {
362 rc |= mass_add_dur(clo);
363 }
364 /* get rid of resources */
365 free_prchunk(pctx);
366 ndl_free:
367 if (needle != __nstk) {
368 free(needle);
369 }
370
371 } else {
372 /* mass-adding durations to reference date */
373 struct mass_add_clo_s clo[1];
374 void *pctx;
375
376 /* no threads reading this stream */
377 __io_setlocking_bycaller(stdout);
378
379 /* using the prchunk reader now */
380 if ((pctx = init_prchunk(STDIN_FILENO)) == NULL) {
381 serror("could not open stdin");
382 goto dur_out;
383 }
384
385 /* build the clo and then loop */
386 clo->pctx = pctx;
387 clo->rd = d;
388 clo->fromz = fromz;
389 clo->hackz = hackz;
390 clo->z = z;
391 clo->ofmt = ofmt;
392 clo->sed_mode_p = argi->sed_mode_flag;
393 clo->quietp = argi->quiet_flag;
394 while (prchunk_fill(pctx) >= 0) {
395 rc |= mass_add_d(clo);
396 }
397 /* get rid of resources */
398 free_prchunk(pctx);
399 }
400 dur_out:
401 /* free the strpdur status */
402 __strpdtdur_free(&st);
403
404 dt_io_clear_zones();
405 if (argi->from_locale_arg) {
406 setilocale(NULL);
407 }
408 if (argi->locale_arg) {
409 setflocale(NULL);
410 }
411
412 out:
413 yuck_free(argi);
414 return rc;
415 }
416
417 /* dadd.c ends here */
418