"Fossies" - the Fresh Open Source Software Archive

Member "dateutils-0.4.6/src/ddiff.c" (19 Mar 2019, 18100 Bytes) of package /linux/privat/dateutils-0.4.6.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "ddiff.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 0.4.5_vs_0.4.6.

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