1 /*** ddiff.c -- perform simple date arithmetic, date minus date
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 "date-core-private.h"
47 #include "dt-core-private.h"
48 #include "dt-io.h"
49 #include "dt-locale.h"
50 #include "prchunk.h"
51
52 #if !defined UNUSED
53 # define UNUSED(_x) __attribute__((unused)) _x
54 #endif /* !UNUSED */
55
56 typedef union {
57 unsigned int flags;
58 struct {
59 unsigned int has_year:1;
60 unsigned int has_mon:1;
61 unsigned int has_week:1;
62 unsigned int has_day:1;
63 unsigned int has_biz:1;
64
65 unsigned int has_hour:1;
66 unsigned int has_min:1;
67 unsigned int has_sec:1;
68 unsigned int has_nano:1;
69
70 unsigned int has_tai:1;
71 };
72 } durfmt_t;
73
74 const char *prog = "ddiff";
75
76
77 static durfmt_t
determine_durfmt(const char * fmt)78 determine_durfmt(const char *fmt)
79 {
80 durfmt_t res = {0};
81 dt_dtyp_t special;
82
83 if (fmt == NULL) {
84 /* decide later on */
85 ;
86 } else if (UNLIKELY((special = __trans_dfmt_special(fmt)) != DT_DUNK)) {
87 switch (special) {
88 default:
89 case DT_DUNK:
90 break;
91 case DT_YMD:
92 res.has_year = 1;
93 res.has_mon = 1;
94 res.has_day = 1;
95 break;
96 case DT_YMCW:
97 res.has_year = 1;
98 res.has_mon = 1;
99 res.has_week = 1;
100 res.has_day = 1;
101 break;
102 case DT_BIZDA:
103 res.has_year = 1;
104 res.has_mon = 1;
105 res.has_day = 1;
106 res.has_biz = 1;
107 break;
108 case DT_DAISY:
109 res.has_day = 1;
110 break;
111 case DT_BIZSI:
112 res.has_day = 1;
113 res.has_biz = 1;
114 break;
115 case DT_YWD:
116 res.has_year = 1;
117 res.has_week = 1;
118 res.has_day = 1;
119 break;
120 case DT_YD:
121 res.has_year = 1;
122 res.has_day = 1;
123 break;
124 }
125
126 /* all special types have %0H:%0M:%0S */
127 res.has_hour = 1;
128 res.has_min = 1;
129 res.has_sec = 1;
130 } else {
131 /* go through the fmt specs */
132 for (const char *fp = fmt; *fp;) {
133 const char *fp_sav = fp;
134 struct dt_spec_s spec = __tok_spec(fp_sav, &fp);
135
136 switch (spec.spfl) {
137 case DT_SPFL_UNK:
138 default:
139 /* nothing changes */
140 break;
141 case DT_SPFL_N_YEAR:
142 res.has_year = 1;
143 break;
144 case DT_SPFL_N_MON:
145 case DT_SPFL_S_MON:
146 res.has_mon = 1;
147 break;
148 case DT_SPFL_N_DCNT_MON:
149 if (spec.bizda) {
150 res.has_biz = 1;
151 }
152 case DT_SPFL_N_DSTD:
153 case DT_SPFL_N_DCNT_YEAR:
154 res.has_day = 1;
155 break;
156 case DT_SPFL_N_WCNT_MON:
157 case DT_SPFL_N_WCNT_YEAR:
158 case DT_SPFL_N_DCNT_WEEK:
159 case DT_SPFL_S_WDAY:
160 res.has_week = 1;
161 break;
162
163 case DT_SPFL_N_TSTD:
164 case DT_SPFL_N_SEC:
165 if (spec.tai) {
166 res.has_tai = 1;
167 }
168 res.has_sec = 1;
169 break;
170 case DT_SPFL_N_HOUR:
171 res.has_hour = 1;
172 break;
173 case DT_SPFL_N_MIN:
174 res.has_min = 1;
175 break;
176 case DT_SPFL_N_NANO:
177 res.has_nano = 1;
178 break;
179 }
180 }
181 }
182 return res;
183 }
184
185 static dt_dtdurtyp_t
determine_durtype(struct dt_dt_s d1,struct dt_dt_s d2,durfmt_t f)186 determine_durtype(struct dt_dt_s d1, struct dt_dt_s d2, durfmt_t f)
187 {
188 /* the type-multiplication table looks like:
189 *
190 * - D T DT
191 * D d x d
192 * T x t x
193 * DT d x s
194 *
195 * where d means a ddur type, t a tdur type and s is DT_SEXY */
196
197 if (UNLIKELY(dt_sandwich_only_t_p(d1) && dt_sandwich_only_t_p(d2))) {
198 /* time only duration */
199 ;
200 } else if (dt_sandwich_only_t_p(d1) || dt_sandwich_only_t_p(d2)) {
201 /* isn't defined */
202 return (dt_dtdurtyp_t)DT_DURUNK;
203 } else if (f.has_week && f.has_mon) {
204 return (dt_dtdurtyp_t)DT_DURYMCW;
205 } else if (f.has_week && f.has_year) {
206 return (dt_dtdurtyp_t)DT_DURYWD;
207 } else if (f.has_mon) {
208 return (dt_dtdurtyp_t)DT_DURYMD;
209 } else if (f.has_year && f.has_day) {
210 return (dt_dtdurtyp_t)DT_DURYD;
211 } else if (f.has_day && f.has_biz) {
212 return (dt_dtdurtyp_t)DT_DURBD;
213 } else if (f.has_year) {
214 return (dt_dtdurtyp_t)DT_DURYMD;
215 } else if (dt_sandwich_only_d_p(d1) || dt_sandwich_only_d_p(d2)) {
216 /* default date-only type */
217 return (dt_dtdurtyp_t)DT_DURD;
218 } else if (UNLIKELY(f.has_tai)) {
219 /* we has tais */
220 return (dt_dtdurtyp_t)0xffU;
221 }
222 /* otherwise */
223 return DT_DURS;
224 }
225
226
227 static size_t
ltostr(char * restrict buf,size_t bsz,long int v,int range,unsigned int pad)228 ltostr(char *restrict buf, size_t bsz, long int v,
229 int range, unsigned int pad)
230 {
231 #define C(x) (char)((x) + '0')
232 char *restrict bp = buf;
233 const char *const ep = buf + bsz;
234 bool negp;
235
236 if (UNLIKELY((negp = v < 0))) {
237 v = -v;
238 } else if (!v) {
239 *bp++ = C(0U);
240 range--;
241 }
242 /* write the mantissa right to left */
243 for (; v && bp < ep; range--) {
244 register unsigned int x = v % 10U;
245
246 v /= 10U;
247 *bp++ = C(x);
248 }
249 /* fill up with padding */
250 if (UNLIKELY(pad)) {
251 static const char pads[] = " 0";
252 const char p = pads[2U - pad];
253
254 while (range-- > 0) {
255 *bp++ = p;
256 }
257 }
258 /* write the sign */
259 if (UNLIKELY(negp)) {
260 *bp++ = '-';
261 }
262
263 /* reverse the string */
264 for (char *ip = buf, *jp = bp - 1; ip < jp; ip++, jp--) {
265 register char tmp = *ip;
266 *ip = *jp;
267 *jp = tmp;
268 }
269 #undef C
270 return bp - buf;
271 }
272
273 static inline void
dt_io_warn_dur(const char * d1,const char * d2)274 dt_io_warn_dur(const char *d1, const char *d2)
275 {
276 error("\
277 duration between `%s' and `%s' is not defined", d1, d2);
278 return;
279 }
280
281 static __attribute__((pure)) long int
__strf_tot_secs(struct dt_dtdur_s dur)282 __strf_tot_secs(struct dt_dtdur_s dur)
283 {
284 /* return time portion of duration in UTC seconds */
285 long int s = dur.dv;
286
287 if (UNLIKELY(dur.tai) && dur.durtyp == DT_DURS) {
288 return dur.soft - dur.corr;
289 }
290
291 switch (dur.durtyp) {
292 default:
293 /* all the date types */
294 return dur.t.sdur;
295 case DT_DURH:
296 s *= MINS_PER_HOUR;
297 /*@fallthrough@*/
298 case DT_DURM:
299 s *= SECS_PER_MIN;
300 /*@fallthrough@*/
301 case DT_DURS:
302 break;
303 case DT_DURNANO:
304 s /= NANOS_PER_SEC;
305 break;
306 }
307 return s;
308 }
309
310 static __attribute__((pure)) long int
__strf_tot_corr(struct dt_dtdur_s dur)311 __strf_tot_corr(struct dt_dtdur_s dur)
312 {
313 if (dur.durtyp == DT_DURS && dur.tai) {
314 return dur.corr;
315 }
316 /* otherwise no corrections */
317 return 0;
318 }
319
320 static __attribute__((pure)) int
__strf_tot_days(struct dt_dtdur_s dur)321 __strf_tot_days(struct dt_dtdur_s dur)
322 {
323 /* return date portion of DURation in days */
324 int d;
325
326 switch (dur.d.durtyp) {
327 case DT_DURD:
328 case DT_DURBD:
329 d = dur.d.dv;
330 break;
331 case DT_DURBIZDA:
332 d = dur.d.bizda.bd;
333 break;
334 case DT_DURYMD:
335 d = dur.d.ymd.d;
336 break;
337 case DT_DURYD:
338 d = dur.d.yd.d;
339 break;
340 case DT_DURYMCW:
341 d = dur.d.ymcw.w + dur.d.ymcw.c * (int)GREG_DAYS_P_WEEK;
342 break;
343 case DT_DURYWD:
344 d = dur.d.ywd.w + dur.d.ywd.c * (int)GREG_DAYS_P_WEEK;
345 break;
346 default:
347 d = 0;
348 break;
349 }
350 return d;
351 }
352
353 static __attribute__((pure)) int
__strf_tot_mon(struct dt_dtdur_s dur)354 __strf_tot_mon(struct dt_dtdur_s dur)
355 {
356 /* DUR expressed as month and days */
357 int m;
358
359 switch (dur.d.durtyp) {
360 case DT_DURBIZDA:
361 m = dur.d.bizda.m + dur.d.bizda.y * (int)GREG_MONTHS_P_YEAR;
362 break;
363 case DT_DURYMD:
364 m = dur.d.ymd.m + dur.d.ymd.y * (int)GREG_MONTHS_P_YEAR;
365 break;
366 case DT_DURYMCW:
367 m = dur.d.ymcw.m + dur.d.ymcw.y * (int)GREG_MONTHS_P_YEAR;
368 break;
369 case DT_DURYD:
370 m = dur.d.yd.y * (int)GREG_MONTHS_P_YEAR;
371 break;
372 case DT_DURYWD:
373 m = dur.d.ywd.y * (int)GREG_MONTHS_P_YEAR;
374 break;
375 default:
376 m = 0;
377 break;
378 }
379 return m;
380 }
381
382 static __attribute__((pure)) int
__strf_ym_mon(struct dt_dtdur_s dur)383 __strf_ym_mon(struct dt_dtdur_s dur)
384 {
385 return __strf_tot_mon(dur) % (int)GREG_MONTHS_P_YEAR;
386 }
387
388 static __attribute__((pure)) int
__strf_tot_years(struct dt_dtdur_s dur)389 __strf_tot_years(struct dt_dtdur_s dur)
390 {
391 return __strf_tot_mon(dur) / (int)GREG_MONTHS_P_YEAR;
392 }
393
394 static struct precalc_s {
395 int Y;
396 int m;
397 int w;
398 int d;
399 int db;
400
401 long int H;
402 long int M;
403 long int S;
404 long int N;
405
406 long int rS;
precalc(durfmt_t f,struct dt_dtdur_s dur)407 } precalc(durfmt_t f, struct dt_dtdur_s dur)
408 {
409 #define MINS_PER_DAY (MINS_PER_HOUR * HOURS_PER_DAY)
410 #define SECS_PER_WEEK (SECS_PER_DAY * GREG_DAYS_P_WEEK)
411 #define MINS_PER_WEEK (MINS_PER_DAY * GREG_DAYS_P_WEEK)
412 #define HOURS_PER_WEEK (HOURS_PER_DAY * GREG_DAYS_P_WEEK)
413 struct precalc_s res = {0};
414 long long int us;
415
416 /* date specs */
417 if (f.has_year) {
418 /* just years */
419 res.Y = __strf_tot_years(dur);
420 }
421 if (f.has_year && f.has_mon) {
422 /* years and months */
423 res.m = __strf_ym_mon(dur);
424 } else if (f.has_mon) {
425 /* just months */
426 res.m = __strf_tot_mon(dur);
427 }
428
429 /* the other units are easily converted as their factors are fixed.
430 * we operate on clean seconds and attribute leap seconds only
431 * to the S slot, so 59 seconds plus a leap second != 1 minute */
432 with (long long int S = __strf_tot_secs(dur), d = __strf_tot_days(dur)) {
433 us = d * (int)SECS_PER_DAY + S;
434 }
435
436 if (f.has_week) {
437 /* week shadows days in the hierarchy */
438 res.w = us / (int)SECS_PER_WEEK;
439 us %= (int)SECS_PER_WEEK;
440 }
441 if (f.has_day) {
442 res.d += us / (int)SECS_PER_DAY;
443 us %= (int)SECS_PER_DAY;
444 }
445 if (f.has_hour) {
446 res.H = us / (long int)SECS_PER_HOUR;
447 us %= (long int)SECS_PER_HOUR;
448 }
449 if (f.has_min) {
450 /* minutes and seconds */
451 res.M = us / (long int)SECS_PER_MIN;
452 us %= (long int)SECS_PER_MIN;
453 }
454 if (f.has_sec) {
455 res.S = us + __strf_tot_corr(dur);
456 }
457
458 /* just in case the duration iss negative jump through all
459 * the hoops again, backwards */
460 if (res.w < 0 || res.d < 0 ||
461 res.H < 0 || res.M < 0 || res.S < 0) {
462 if (0) {
463 fixup_d:
464 res.d = -res.d;
465 fixup_H:
466 res.H = -res.H;
467 fixup_M:
468 res.M = -res.M;
469 fixup_S:
470 res.S = -res.S;
471 } else if (f.has_week) {
472 goto fixup_d;
473 } else if (f.has_day) {
474 goto fixup_H;
475 } else if (f.has_hour) {
476 goto fixup_M;
477 } else if (f.has_min) {
478 goto fixup_S;
479 }
480 }
481 return res;
482 }
483
484 static size_t
__strfdtdur(char * restrict buf,size_t bsz,const char * fmt,struct dt_dtdur_s dur,durfmt_t f,bool only_d_p)485 __strfdtdur(
486 char *restrict buf, size_t bsz, const char *fmt,
487 struct dt_dtdur_s dur, durfmt_t f, bool only_d_p)
488 {
489 /* like strfdtdur() but do some calculations based on F on the way there */
490 static const char sexy_dflt_dur[] = "%0T";
491 static const char ddur_dflt_dur[] = "%d";
492 struct precalc_s pre;
493 const char *fp;
494 char *bp;
495
496 if (UNLIKELY(buf == NULL || bsz == 0)) {
497 bp = buf;
498 goto out;
499 }
500
501 /* translate high-level format names */
502 if (fmt == NULL && dur.durtyp >= (dt_dtdurtyp_t)DT_NDURTYP) {
503 fmt = sexy_dflt_dur;
504 f.has_sec = 1U;
505 } else if (fmt == NULL) {
506 fmt = ddur_dflt_dur;
507 f.has_day = 1U;
508 } else if (only_d_p) {
509 __trans_ddurfmt(&fmt);
510 } else {
511 __trans_dtdurfmt(&fmt);
512 }
513
514 /* precompute */
515 pre = precalc(f, dur);
516
517 /* assign and go */
518 bp = buf;
519 fp = fmt;
520 if (dur.neg) {
521 *bp++ = '-';
522 }
523 for (char *const eo = buf + bsz; *fp && bp < eo;) {
524 const char *fp_sav = fp;
525 struct dt_spec_s spec = __tok_spec(fp_sav, &fp);
526
527 if (spec.spfl == DT_SPFL_UNK) {
528 /* must be literal then */
529 *bp++ = *fp_sav;
530 } else if (UNLIKELY(spec.rom)) {
531 continue;
532 }
533 /* otherwise switch over spec.spfl */
534 switch (spec.spfl) {
535 case DT_SPFL_LIT_PERCENT:
536 /* literal % */
537 *bp++ = '%';
538 break;
539 case DT_SPFL_LIT_TAB:
540 /* literal tab */
541 *bp++ = '\t';
542 break;
543 case DT_SPFL_LIT_NL:
544 /* literal \n */
545 *bp++ = '\n';
546 break;
547
548 case DT_SPFL_N_DSTD:
549 bp += ltostr(bp, eo - bp, pre.d, -1, DT_SPPAD_NONE);
550 *bp++ = 'd';
551 goto bizda_suffix;
552
553 case DT_SPFL_N_DCNT_MON: {
554 int rng = 2;
555
556 if (!f.has_mon && !f.has_week && f.has_year) {
557 rng++;
558 }
559 bp += ltostr(bp, eo - bp, pre.d, rng, spec.pad);
560 }
561 bizda_suffix:
562 if (spec.bizda) {
563 /* don't print the b after an ordinal */
564 dt_bizda_param_t bprm;
565
566 bprm.bs = dur.d.param;
567 switch (bprm.ab) {
568 case BIZDA_AFTER:
569 *bp++ = 'b';
570 break;
571 case BIZDA_BEFORE:
572 *bp++ = 'B';
573 break;
574 }
575 }
576 break;
577
578 case DT_SPFL_N_WCNT_MON:
579 case DT_SPFL_N_DCNT_WEEK:
580 bp += ltostr(bp, eo - bp, pre.w, 2, spec.pad);
581 break;
582
583 case DT_SPFL_N_MON:
584 bp += ltostr(bp, eo - bp, pre.m, 2, spec.pad);
585 break;
586
587 case DT_SPFL_N_YEAR:
588 bp += ltostr(bp, eo - bp, pre.Y, -1, DT_SPPAD_NONE);
589 break;
590
591 /* time specs */
592 case DT_SPFL_N_TSTD:
593 if (UNLIKELY(spec.tai)) {
594 pre.S += __strf_tot_corr(dur);
595 }
596 bp += ltostr(bp, eo - bp, pre.S, -1, DT_SPPAD_NONE);
597 *bp++ = 's';
598 break;
599
600 case DT_SPFL_N_SEC:
601 if (UNLIKELY(spec.tai)) {
602 pre.S += __strf_tot_corr(dur);
603 }
604
605 bp += ltostr(bp, eo - bp, pre.S, 2, spec.pad);
606 break;
607
608 case DT_SPFL_N_MIN:
609 bp += ltostr(bp, eo - bp, pre.M, 2, spec.pad);
610 break;
611
612 case DT_SPFL_N_HOUR:
613 bp += ltostr(bp, eo - bp, pre.H, 2, spec.pad);
614 break;
615
616 default:
617 break;
618 }
619 }
620 out:
621 if (bp < buf + bsz) {
622 *bp = '\0';
623 }
624 return bp - buf;
625 }
626
627 static int
ddiff_prnt(struct dt_dtdur_s dur,const char * fmt,durfmt_t f,bool only_d_p)628 ddiff_prnt(struct dt_dtdur_s dur, const char *fmt, durfmt_t f, bool only_d_p)
629 {
630 /* this is mainly a better dt_strfdtdur() */
631 char buf[256];
632 size_t res = __strfdtdur(buf, sizeof(buf), fmt, dur, f, only_d_p);
633
634 if (res > 0 && buf[res - 1] != '\n') {
635 /* auto-newline */
636 buf[res++] = '\n';
637 }
638 if (res > 0) {
639 __io_write(buf, res, stdout);
640 }
641 return (res > 0) - 1;
642 }
643
644
645 #include "ddiff.yucc"
646
647 int
main(int argc,char * argv[])648 main(int argc, char *argv[])
649 {
650 yuck_t argi[1U];
651 struct dt_dt_s d;
652 const char *ofmt;
653 const char *refinp;
654 char **fmt;
655 size_t nfmt;
656 int rc = 0;
657 durfmt_t dfmt;
658 dt_dtdurtyp_t dtyp;
659 zif_t fromz = NULL;
660
661 if (yuck_parse(argi, argc, argv)) {
662 rc = 1;
663 goto out;
664 }
665 /* unescape sequences, maybe */
666 if (argi->backslash_escapes_flag) {
667 dt_io_unescape(argi->format_arg);
668 }
669
670 if (argi->from_locale_arg) {
671 setilocale(argi->from_locale_arg);
672 }
673
674 /* try and read the from and to time zones */
675 if (argi->from_zone_arg) {
676 fromz = dt_io_zone(argi->from_zone_arg);
677 }
678 if (argi->base_arg) {
679 struct dt_dt_s base = dt_strpdt(argi->base_arg, NULL, NULL);
680 dt_set_base(base);
681 }
682
683 ofmt = argi->format_arg;
684 fmt = argi->input_format_args;
685 nfmt = argi->input_format_nargs;
686
687 if (argi->nargs == 0 ||
688 (refinp = argi->args[0U],
689 dt_unk_p(d = dt_io_strpdt(refinp, fmt, nfmt, fromz)) &&
690 dt_unk_p(d = dt_io_strpdt(refinp, NULL, 0U, fromz)))) {
691 error("Error: reference DATE must be specified\n");
692 yuck_auto_help(argi);
693 rc = 1;
694 goto out;
695 } else if (UNLIKELY(d.fix) && !argi->quiet_flag) {
696 rc = 2;
697 }
698
699 /* try and guess the diff tgttype most suitable for user's FMT */
700 dfmt = determine_durfmt(ofmt);
701
702 if (argi->nargs > 1) {
703 for (size_t i = 1; i < argi->nargs; i++) {
704 struct dt_dt_s d2;
705 struct dt_dtdur_s dur;
706 const char *inp = argi->args[i];
707 bool onlydp;
708
709 d2 = dt_io_strpdt(inp, fmt, nfmt, fromz);
710 if (dt_unk_p(d2)) {
711 if (!argi->quiet_flag) {
712 dt_io_warn_strpdt(inp);
713 rc = 2;
714 }
715 continue;
716 } else if (UNLIKELY(d2.fix) && !argi->quiet_flag) {
717 rc = 2;
718 }
719 /* guess the diff type */
720 onlydp = dt_sandwich_only_d_p(d) ||
721 dt_sandwich_only_d_p(d2);
722 if (!(dtyp = determine_durtype(d, d2, dfmt))) {
723 if (!argi->quiet_flag) {
724 dt_io_warn_dur(refinp, inp);
725 rc = 2;
726 }
727 continue;
728 }
729 /* subtraction and print */
730 dur = dt_dtdiff(dtyp, d, d2);
731 ddiff_prnt(dur, ofmt, dfmt, onlydp);
732 }
733 } else {
734 /* read from stdin */
735 size_t lno = 0;
736 void *pctx;
737
738 /* no threads reading this stream */
739 __io_setlocking_bycaller(stdout);
740
741 /* using the prchunk reader now */
742 if ((pctx = init_prchunk(STDIN_FILENO)) == NULL) {
743 serror("Error: could not open stdin");
744 goto out;
745 }
746 while (prchunk_fill(pctx) >= 0) {
747 for (char *line; prchunk_haslinep(pctx); lno++) {
748 struct dt_dt_s d2;
749 struct dt_dtdur_s dur;
750 bool onlydp;
751
752 (void)prchunk_getline(pctx, &line);
753 d2 = dt_io_strpdt(line, fmt, nfmt, fromz);
754
755 if (dt_unk_p(d2)) {
756 if (!argi->quiet_flag) {
757 dt_io_warn_strpdt(line);
758 rc = 2;
759 }
760 if (argi->skip_illegal_flag) {
761 /* empty line */
762 __io_write("\n", 1U, stdout);
763 }
764 continue;
765 } else if (UNLIKELY(d2.fix) &&
766 !argi->quiet_flag) {
767 rc = 2;
768 }
769 /* guess the diff type */
770 onlydp = dt_sandwich_only_d_p(d) ||
771 dt_sandwich_only_d_p(d2);
772 if (!(dtyp = determine_durtype(d, d2, dfmt))) {
773 if (!argi->quiet_flag) {
774 dt_io_warn_dur(refinp, line);
775 rc = 2;
776 }
777 continue;
778 }
779 /* perform subtraction now */
780 dur = dt_dtdiff(dtyp, d, d2);
781 ddiff_prnt(dur, ofmt, dfmt, onlydp);
782 }
783 }
784 /* get rid of resources */
785 free_prchunk(pctx);
786 }
787
788 dt_io_clear_zones();
789 if (argi->from_locale_arg) {
790 setilocale(NULL);
791 }
792
793 out:
794 yuck_free(argi);
795 return rc;
796 }
797
798 /* ddiff.c ends here */
799