1 /*** dseq.c -- like seq(1) but for dates
2  *
3  * Copyright (C) 2009 - 2011 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 
38 #if defined HAVE_CONFIG_H
39 # include "config.h"
40 #endif	/* HAVE_CONFIG_H */
41 #include <stdlib.h>
42 #include <stdio.h>
43 #include <stdint.h>
44 #include <stdbool.h>
45 #include <time.h>
46 #include <string.h>
47 
48 #include "dt-core.h"
49 #include "dt-io.h"
50 #include "dt-locale.h"
51 #include "tzraw.h"
52 
53 typedef uint8_t __skipspec_t;
54 
55 /* generic closure */
56 struct dseq_clo_s {
57 	struct dt_dt_s fst;
58 	struct dt_dt_s lst;
59 	struct dt_dtdur_s *ite;
60 	size_t nite;
61 	struct dt_dtdur_s *altite;
62 	__skipspec_t ss;
63 	size_t naltite;
64 	/* direction, >0 if increasing, <0 if decreasing, 0 if undefined */
65 	int dir;
66 	int flags;
67 #define CLO_FL_FREE_ITE		(1)
68 };
69 
70 const char *prog = "dseq";
71 
72 
73 /* skip system */
74 static int
skipp(__skipspec_t ss,struct dt_dt_s dt)75 skipp(__skipspec_t ss, struct dt_dt_s dt)
76 {
77 	dt_dow_t dow;
78 	/* common case first */
79 	if (ss == 0) {
80 		return 0;
81 	}
82 	dow = dt_get_wday(dt.d);
83 	/* just check if the bit in the bitset `skip' is set */
84 	return (ss & (1 << dow)) != 0;
85 }
86 
87 #define SKIP_MON	(1 << DT_MONDAY)
88 #define SKIP_TUE	(1 << DT_TUESDAY)
89 #define SKIP_WED	(1 << DT_WEDNESDAY)
90 #define SKIP_THU	(1 << DT_THURSDAY)
91 #define SKIP_FRI	(1 << DT_FRIDAY)
92 #define SKIP_SAT	(1 << DT_SATURDAY)
93 #define SKIP_SUN	(1 << DT_SUNDAY)
94 
95 static inline int
__toupper(int c)96 __toupper(int c)
97 {
98 	return c & ~0x20;
99 }
100 
101 static dt_dow_t
__parse_wd(const char * str)102 __parse_wd(const char *str)
103 {
104 #define ILEA(a, b)	(((a) << 8) | (b))
105 	int s1 = __toupper(str[0]);
106 	int s2 = __toupper(str[1]);
107 
108 	switch (ILEA(s1, s2)) {
109 	case ILEA('M', 'O'):
110 	case ILEA('M', 0):
111 		/* monday */
112 		return DT_MONDAY;
113 	case ILEA('T', 'U'):
114 		/* tuesday */
115 		return DT_TUESDAY;
116 	case ILEA('W', 'E'):
117 	case ILEA('W', 0):
118 		/* wednesday */
119 		return DT_WEDNESDAY;
120 	case ILEA('T', 'H'):
121 		/* thursday */
122 		return DT_THURSDAY;
123 	case ILEA('F', 'R'):
124 	case ILEA('F', 0):
125 		/* friday */
126 		return DT_FRIDAY;
127 	case ILEA('S', 'A'):
128 	case ILEA('A', 0):
129 		/* saturday */
130 		return DT_SATURDAY;
131 	case ILEA('S', 'U'):
132 	case ILEA('S', 0):
133 		/* sunday */
134 		return DT_SUNDAY;
135 	default:
136 		return DT_MIRACLEDAY;
137 	}
138 }
139 
140 static __skipspec_t
__skip_dow(__skipspec_t ss,unsigned int wd)141 __skip_dow(__skipspec_t ss, unsigned int wd)
142 {
143 	if (wd > GREG_DAYS_P_WEEK) {
144 		wd -= GREG_DAYS_P_WEEK;
145 	}
146 
147 	switch (wd) {
148 	case DT_MONDAY:
149 		/* monday */
150 		ss |= SKIP_MON;
151 		break;
152 	case DT_TUESDAY:
153 		/* tuesday */
154 		ss |= SKIP_TUE;
155 		break;
156 	case DT_WEDNESDAY:
157 		/* wednesday */
158 		ss |= SKIP_WED;
159 		break;
160 	case DT_THURSDAY:
161 		/* thursday */
162 		ss |= SKIP_THU;
163 		break;
164 	case DT_FRIDAY:
165 		/* friday */
166 		ss |= SKIP_FRI;
167 		break;
168 	case DT_SATURDAY:
169 		/* saturday */
170 		ss |= SKIP_SAT;
171 		break;
172 	case DT_SUNDAY:
173 		/* sunday */
174 		ss |= SKIP_SUN;
175 		break;
176 	default:
177 	case DT_MIRACLEDAY:
178 		break;
179 	}
180 	return ss;
181 }
182 
183 static __skipspec_t
__skip_str(__skipspec_t ss,const char * str)184 __skip_str(__skipspec_t ss, const char *str)
185 {
186 	dt_dow_t tmp;
187 
188 	if ((tmp = __parse_wd(str)) != DT_MIRACLEDAY) {
189 		ss = __skip_dow(ss, tmp);
190 	} else {
191 		int s1 = __toupper(str[0]);
192 		int s2 = __toupper(str[1]);
193 
194 		if (ILEA(s1, s2) == ILEA('S', 'S')) {
195 			/* weekend */
196 			ss |= SKIP_SAT;
197 			ss |= SKIP_SUN;
198 		}
199 	}
200 	return ss;
201 }
202 
203 static __skipspec_t
__skip_1spec(__skipspec_t ss,char * spec)204 __skip_1spec(__skipspec_t ss, char *spec)
205 {
206 	char *tmp;
207 	dt_dow_t from, till;
208 
209 	if ((tmp = strchr(spec, '-')) == NULL) {
210 		return __skip_str(ss, spec);
211 	}
212 	/* otherwise it's a range */
213 	*tmp = '\0';
214 	from = __parse_wd(spec);
215 	till = __parse_wd(tmp + 1);
216 	for (int d = from, e = till >= from ? till : till + 7; d <= e; d++) {
217 		ss = __skip_dow(ss, d);
218 	}
219 	return ss;
220 }
221 
222 static __skipspec_t
set_skip(__skipspec_t ss,char * spec)223 set_skip(__skipspec_t ss, char *spec)
224 {
225 	char *tmp, *tm2;
226 
227 	if ((tmp = strchr(spec, ',')) == NULL) {
228 		return __skip_1spec(ss, spec);
229 	}
230 	/* const violation */
231 	*tmp++ = '\0';
232 	ss = __skip_1spec(ss, spec);
233 	while ((tmp = strchr(tm2 = tmp, ','))) {
234 		*tmp++ = '\0';
235 		ss = __skip_1spec(ss, tm2);
236 	}
237 	return __skip_1spec(ss, tm2);
238 }
239 
240 static struct dt_dt_s
date_add(struct dt_dt_s d,struct dt_dtdur_s dur[],size_t ndur)241 date_add(struct dt_dt_s d, struct dt_dtdur_s dur[], size_t ndur)
242 {
243 	int32_t carries = d.d.u;
244 
245 	for (size_t i = 0; i < ndur; i++) {
246 		d = dt_dtadd(d, dur[i]);
247 		/* keep track of carries */
248 		carries += d.t.carry;
249 	}
250 	if (UNLIKELY(dt_sandwich_only_t_p(d))) {
251 		d.d.u = carries;
252 	}
253 	return d;
254 }
255 
256 static void
date_neg_dur(struct dt_dtdur_s dur[],size_t ndur)257 date_neg_dur(struct dt_dtdur_s dur[], size_t ndur)
258 {
259 	for (size_t i = 0; i < ndur; i++) {
260 		dur[i] = dt_neg_dtdur(dur[i]);
261 	}
262 	return;
263 }
264 
265 static bool
__daisy_feasible_p(struct dt_dtdur_s dur[],size_t ndur)266 __daisy_feasible_p(struct dt_dtdur_s dur[], size_t ndur)
267 {
268 	if (ndur != 1) {
269 		return false;
270 	}
271 
272 	switch (dur->d.durtyp) {
273 	case DT_DURYMD:
274 		return !(dur->d.ymd.y || dur->d.ymd.m);
275 	case DT_DURBIZDA:
276 		return !dur->d.bizda.bd;
277 	case DT_DURD:
278 	case DT_DURBD:
279 		/* definitley, they're daisy already */
280 		return true;
281 	case DT_DURWK:
282 		/* borderline, could be less efficient for ywd dates */
283 		return true;
284 	case DT_DURMO:
285 	case DT_DURQU:
286 	case DT_DURYR:
287 		/* most definitely not */
288 	default:
289 		/* all the time durs make it infeasible as well */
290 		return false;
291 	}
292 	/* not reached */
293 }
294 
295 static bool
__dur_naught_p(struct dt_dtdur_s dur)296 __dur_naught_p(struct dt_dtdur_s dur)
297 {
298 	/* we use the fact that dur.dv overlaps dur.d.dv */
299 	return dur.dv == 0;
300 }
301 
302 static bool
__durstack_naught_p(struct dt_dtdur_s dur[],size_t ndur)303 __durstack_naught_p(struct dt_dtdur_s dur[], size_t ndur)
304 {
305 	if (ndur == 0) {
306 		return true;
307 	} else if (ndur == 1) {
308 		return __dur_naught_p(dur[0]);
309 	}
310 	for (size_t i = 0; i < ndur; i++) {
311 		if (!__dur_naught_p(dur[i])) {
312 		    return false;
313 		}
314 	}
315 	return true;
316 }
317 
318 static bool
__in_range_p(struct dt_dt_s now,const struct dseq_clo_s * clo)319 __in_range_p(struct dt_dt_s now, const struct dseq_clo_s *clo)
320 {
321 	if (!dt_sandwich_only_t_p(now)) {
322 		if (clo->dir > 0) {
323 			return dt_dt_in_range_p(now, clo->fst, clo->lst) == 1;
324 		} else if (clo->dir < 0) {
325 			return dt_dt_in_range_p(now, clo->lst, clo->fst) == 1;
326 		}
327 	}
328 	/* otherwise perform a simple range check */
329 	if (clo->dir > 0) {
330 		if (clo->fst.t.u < clo->lst.t.u) {
331 			/* dseq A B  with A < B */
332 			return now.t.u >= clo->fst.t.u &&
333 				now.t.u <= clo->lst.t.u;
334 		} else {
335 			/* dseq A B  with A > B and wrap-around,
336 			 * carries have kindly been stored in d.u */
337 			return now.t.u <= clo->lst.t.u || now.d.u == 0U;
338 		}
339 	} else if (clo->dir < 0) {
340 		if (clo->fst.t.u > clo->lst.t.u) {
341 			/* counting down from A to B */
342 			return now.t.u <= clo->fst.t.u &&
343 				now.t.u >= clo->lst.t.u;
344 		} else {
345 			/* count down from A to B with wrap around,
346 			 * carries have kindly been stored in d.u */
347 			return now.t.u >= clo->lst.t.u || now.d.u == 0U;
348 		}
349 	}
350 	return false;
351 }
352 
353 static struct dt_dt_s
__seq_altnext(struct dt_dt_s now,const struct dseq_clo_s * clo)354 __seq_altnext(struct dt_dt_s now, const struct dseq_clo_s *clo)
355 {
356 	do {
357 		now = date_add(now, clo->altite, clo->naltite);
358 	} while (skipp(clo->ss, now) && __in_range_p(now, clo));
359 	return now;
360 }
361 
362 static struct dt_dt_s
__seq_this(struct dt_dt_s now,const struct dseq_clo_s * clo)363 __seq_this(struct dt_dt_s now, const struct dseq_clo_s *clo)
364 {
365 /* if NOW is on a skip date, find the next date according to ALTITE, then ITE */
366 	if (!skipp(clo->ss, now) && __in_range_p(now, clo)) {
367 		return now;
368 	} else if (clo->naltite > 0) {
369 		return __seq_altnext(now, clo);
370 	} else if (clo->nite) {
371 		/* advance until it goes out of range */
372 		for (;
373 		     skipp(clo->ss, now) && __in_range_p(now, clo);
374 		     now = date_add(now, clo->ite, clo->nite));
375 	} else {
376 		/* good question */
377 		;
378 	}
379 	return now;
380 }
381 
382 static struct dt_dt_s
__seq_next(struct dt_dt_s now,const struct dseq_clo_s * clo)383 __seq_next(struct dt_dt_s now, const struct dseq_clo_s *clo)
384 {
385 /* advance NOW, then fix it */
386 	struct dt_dt_s tmp = date_add(now, clo->ite, clo->nite);
387 	return __seq_this(tmp, clo);
388 }
389 
390 static int
__get_dir(struct dt_dt_s d,const struct dseq_clo_s * clo)391 __get_dir(struct dt_dt_s d, const struct dseq_clo_s *clo)
392 {
393 	if (!dt_sandwich_only_t_p(d)) {
394 		/* trial addition to to see where it goes */
395 		struct dt_dt_s tmp = __seq_next(d, clo);
396 		return dt_dtcmp(tmp, d);
397 	}
398 	if (clo->ite->dv > 0) {
399 		return 1;
400 	} else if (clo->ite->dv < 0) {
401 		return -1;
402 	}
403 	return 0;
404 }
405 
406 static struct dt_dt_s
__fixup_fst(struct dseq_clo_s * clo)407 __fixup_fst(struct dseq_clo_s *clo)
408 {
409 	struct dt_dt_s tmp;
410 	struct dt_dt_s old;
411 
412 	/* assume clo->dir has been computed already */
413 	old = tmp = clo->lst;
414 	date_neg_dur(clo->ite, clo->nite);
415 	while (__in_range_p(tmp, clo)) {
416 		old = tmp;
417 		tmp = __seq_next(tmp, clo);
418 	}
419 	/* final checks */
420 	old = __seq_this(old, clo);
421 	date_neg_dur(clo->ite, clo->nite);
422 	/* fixup again with negated dur */
423 	old = __seq_this(old, clo);
424 	return old;
425 }
426 
427 static struct dt_dtdur_s
tseq_guess_ite(struct dt_t_s beg,struct dt_t_s end)428 tseq_guess_ite(struct dt_t_s beg, struct dt_t_s end)
429 {
430 	struct dt_dtdur_s res = {(dt_dtdurtyp_t)DT_DURUNK};
431 
432 	if (beg.hms.h != end.hms.h &&
433 	    beg.hms.m == 0 && end.hms.m == 0 &&
434 	    beg.hms.s == 0 && end.hms.s == 0) {
435 		res.durtyp = DT_DURH;
436 		res.dv = (beg.u < end.u) ? 1 : -1;
437 	} else if (beg.hms.m != end.hms.m &&
438 		   beg.hms.s == 0 && end.hms.s == 0) {
439 		res.durtyp = DT_DURM;
440 		res.dv = (beg.u < end.u) ? 1 : -1;
441 	} else {
442 		res.durtyp = DT_DURS;
443 		res.dv = (beg.u < end.u) ? 1 : -1;
444 	}
445 	return res;
446 }
447 
448 
449 #include "dseq.yucc"
450 
451 int
main(int argc,char * argv[])452 main(int argc, char *argv[])
453 {
454 	static struct dt_dtdur_s ite_p1;
455 	yuck_t argi[1U];
456 	struct dt_dt_s tmp;
457 	char **ifmt;
458 	size_t nifmt;
459 	char *ofmt;
460 	dt_dttyp_t tgttyp;
461 	int rc = 0;
462 	struct dseq_clo_s clo = {
463 		.ite = &ite_p1,
464 		.nite = 1,
465 		.altite = NULL,
466 		.naltite = 0,
467 		.ss = 0,
468 		.dir = 0,
469 		.flags = 0,
470 	};
471 
472 	if (yuck_parse(argi, argc, argv)) {
473 		rc = 1;
474 		goto out;
475 	}
476 	/* assign ofmt/ifmt */
477 	ofmt = argi->format_arg;
478 	if (argi->backslash_escapes_flag) {
479 		dt_io_unescape(ofmt);
480 	}
481 	nifmt = argi->input_format_nargs;
482 	ifmt = argi->input_format_args;
483 
484 	if (argi->from_locale_arg) {
485 		setilocale(argi->from_locale_arg);
486 	}
487 	if (argi->locale_arg) {
488 		setflocale(argi->locale_arg);
489 	}
490 
491 	if (argi->base_arg) {
492 		struct dt_dt_s base = dt_strpdt(argi->base_arg, NULL, NULL);
493 		dt_set_base(base);
494 	}
495 
496 	for (size_t i = 0; i < argi->skip_nargs; i++) {
497 		clo.ss = set_skip(clo.ss, argi->skip_args[i]);
498 	}
499 
500 	if (argi->alt_inc_arg) {
501 		struct __strpdtdur_st_s st = __strpdtdur_st_initialiser();
502 
503 		do {
504 			if (dt_io_strpdtdur(&st, argi->alt_inc_arg) < 0) {
505 				if (!argi->quiet_flag) {
506 					error("Error: \
507 cannot parse duration string `%s'", argi->alt_inc_arg);
508 				}
509 				rc = 1;
510 				goto out;
511 			}
512 		} while (__strpdtdur_more_p(&st));
513 		/* assign values */
514 		clo.altite = st.durs;
515 		clo.naltite = st.ndurs;
516 	}
517 
518 	switch (argi->nargs) {
519 		struct dt_dt_s fst, lst;
520 	default:
521 		yuck_auto_help(argi);
522 		rc = 1;
523 		goto out;
524 
525 	case 2:
526 		lst = dt_io_strpdt(argi->args[1U], ifmt, nifmt, NULL);
527 		if (dt_unk_p(lst)) {
528 			if (!argi->quiet_flag) {
529 				dt_io_warn_strpdt(argi->args[1U]);
530 			}
531 			rc = 1;
532 			goto out;
533 		} else if (UNLIKELY(lst.fix) && !argi->quiet_flag) {
534 			rc = 2;
535 		}
536 		/* fallthrough */
537 	case 1:
538 		fst = dt_io_strpdt(argi->args[0U], ifmt, nifmt, NULL);
539 		if (dt_unk_p(fst)) {
540 			if (!argi->quiet_flag) {
541 				dt_io_warn_strpdt(argi->args[0U]);
542 			}
543 			rc = 1;
544 			goto out;
545 		} else if (UNLIKELY(fst.fix) && !argi->quiet_flag) {
546 			rc = 2;
547 		}
548 
549 		/* check the input arguments and do the sane thing now
550 		 * if it's all dates, use DURD iterator
551 		 * if it's all times, use DURS/DURM/DURH iterators
552 		 * if one of them is a dt, promote the other */
553 		if (dt_sandwich_only_d_p(fst)) {
554 			/* emulates old dseq(1) */
555 			if (argi->nargs == 1U) {
556 				lst.d = dt_date(fst.d.typ);
557 				dt_make_d_only(&lst, fst.d.typ);
558 			}
559 			clo.ite->d = dt_make_ddur(DT_DURD, 1);
560 		} else if (dt_sandwich_only_t_p(fst)) {
561 			/* emulates old tseq(1) */
562 			if (argi->nargs == 1U) {
563 				lst.t = dt_time();
564 				dt_make_t_only(&lst, DT_HMS);
565 			}
566 		} else if (dt_sandwich_p(fst)) {
567 			if (argi->nargs == 1U) {
568 				lst = dt_datetime(fst.typ);
569 				dt_make_sandwich(&lst, fst.d.typ, DT_HMS);
570 			}
571 			clo.ite->d = dt_make_ddur(DT_DURD, 1);
572 		} else {
573 			error("\
574 don't know how to handle single argument case");
575 			rc = 1;
576 			goto out;
577 		}
578 		goto make_compat;
579 
580 	case 3: {
581 		struct __strpdtdur_st_s st = __strpdtdur_st_initialiser();
582 
583 		/* get lower bound */
584 		fst = dt_io_strpdt(argi->args[0U], ifmt, nifmt, NULL);
585 		if (dt_unk_p(fst)) {
586 			if (!argi->quiet_flag) {
587 				dt_io_warn_strpdt(argi->args[0U]);
588 			}
589 			rc = 1;
590 			goto out;
591 		} else if (UNLIKELY(fst.fix) && !argi->quiet_flag) {
592 			rc = 2;
593 		}
594 
595 		/* get increment */
596 		do {
597 			if (dt_io_strpdtdur(&st, argi->args[1U]) < 0) {
598 				error("Error: \
599 cannot parse duration string `%s'", argi->args[1U]);
600 				rc = 1;
601 				goto out;
602 			}
603 		} while (__strpdtdur_more_p(&st));
604 		/* assign values */
605 		clo.ite = st.durs;
606 		clo.nite = st.ndurs;
607 		clo.flags |= CLO_FL_FREE_ITE;
608 
609 		/* get upper bound */
610 		lst = dt_io_strpdt(argi->args[2U], ifmt, nifmt, NULL);
611 		if (dt_unk_p(lst)) {
612 			if (!argi->quiet_flag) {
613 				dt_io_warn_strpdt(argi->args[2U]);
614 			}
615 			rc = 1;
616 			goto out;
617 		} else if (UNLIKELY(lst.fix) && !argi->quiet_flag) {
618 			rc = 2;
619 		}
620 		goto make_compat;
621 	}
622 
623 	make_compat:
624 		if (LIKELY(fst.typ == lst.typ)) {
625 			clo.fst = fst;
626 			clo.lst = lst;
627 		} else {
628 			clo.fst = fst;
629 			clo.lst = dt_dtconv(fst.typ, lst);
630 		}
631 		break;
632 	}
633 
634 	/* promote the args maybe */
635 	if ((dt_sandwich_only_d_p(clo.fst) && dt_sandwich_only_t_p(clo.lst)) ||
636 	    (dt_sandwich_only_t_p(clo.fst) && dt_sandwich_only_d_p(clo.lst))) {
637 		error("\
638 cannot mix dates and times as arguments");
639 		rc = 1;
640 		goto out;
641 	} else if (dt_sandwich_only_d_p(clo.fst) && dt_sandwich_p(clo.lst)) {
642 		/* promote clo.fst */
643 		clo.fst.t = clo.lst.t;
644 		dt_make_sandwich(&clo.fst, clo.fst.d.typ, clo.lst.t.typ);
645 	} else if (dt_sandwich_p(clo.fst) && dt_sandwich_only_d_p(clo.lst)) {
646 		/* promote clo.lst */
647 		clo.lst.t = clo.fst.t;
648 		dt_make_sandwich(&clo.lst, clo.lst.d.typ, clo.fst.t.typ);
649 	} else if (dt_sandwich_only_t_p(clo.fst) && dt_sandwich_p(clo.lst)) {
650 		/* promote clo.fst */
651 		clo.fst.d = clo.lst.d;
652 		dt_make_sandwich(&clo.fst, clo.fst.d.typ, clo.lst.t.typ);
653 	} else if (dt_sandwich_p(clo.fst) && dt_sandwich_only_t_p(clo.lst)) {
654 		/* promote clo.lst */
655 		clo.lst.d = clo.fst.d;
656 		dt_make_sandwich(&clo.lst, clo.lst.d.typ, clo.fst.t.typ);
657 	}
658 
659 #define _DAISY	((dt_dttyp_t)DT_DAISY)
660 	tgttyp = clo.fst.typ;
661 	if ((dt_sandwich_p(clo.fst) || dt_sandwich_only_d_p(clo.fst)) &&
662 	    clo.fst.d.typ == DT_YMD && clo.fst.d.ymd.m == 0) {
663 		/* iterate year-wise */
664 		clo.ite->d = dt_make_ddur(DT_DURYR, 1);
665 	} else if ((dt_sandwich_p(clo.fst) || dt_sandwich_only_d_p(clo.fst)) &&
666 		   clo.fst.d.typ == DT_YMD && clo.fst.d.ymd.d == 0) {
667 		/* iterate month-wise */
668 		clo.ite->d = dt_make_ddur(DT_DURMO, 1);
669 	} else if (dt_sandwich_only_d_p(clo.fst) &&
670 		   __daisy_feasible_p(clo.ite, clo.nite) &&
671 		   clo.fst.d.typ == DT_YMD &&
672 		   /* convert to daisies */
673 		   ((clo.fst = dt_dtconv(_DAISY, clo.fst)).d.typ != DT_DAISY ||
674 		    (clo.lst = dt_dtconv(_DAISY, clo.lst)).d.typ != DT_DAISY)) {
675 		if (!argi->quiet_flag) {
676 			error("\
677 cannot convert calendric system internally");
678 		}
679 		rc = 1;
680 		goto out;
681 	} else if (dt_sandwich_only_t_p(clo.fst) && clo.ite->dv == 0) {
682 		*clo.ite = tseq_guess_ite(clo.fst.t, clo.lst.t);
683 	}
684 
685 	if (__durstack_naught_p(clo.ite, clo.nite) ||
686 	    !(clo.dir = __get_dir(clo.fst, &clo))) {
687 		if (!argi->quiet_flag) {
688 			error("\
689 increment must not be naught");
690 		}
691 		rc = 1;
692 		goto out;
693 	} else if (argi->compute_from_last_flag) {
694 		tmp = __fixup_fst(&clo);
695 	} else {
696 		tmp = __seq_this(clo.fst, &clo);
697 	}
698 
699 	for (; __in_range_p(dt_fixup(tmp), &clo); tmp = __seq_next(tmp, &clo)) {
700 		struct dt_dt_s tgt = tmp;
701 
702 		if (LIKELY(ofmt == NULL)) {
703 			tgt = dt_dtconv(tgttyp, tmp);
704 		}
705 		dt_io_write(tgt, ofmt, NULL, '\n');
706 	}
707 
708 out:
709 	/* free strpdur resources */
710 	if (clo.ite && clo.flags & CLO_FL_FREE_ITE) {
711 		free(clo.ite);
712 	}
713 	if (clo.altite != NULL) {
714 		free(clo.altite);
715 	}
716 	if (argi->from_locale_arg) {
717 		setilocale(NULL);
718 	}
719 	if (argi->locale_arg) {
720 		setflocale(NULL);
721 	}
722 	yuck_free(argi);
723 	return rc;
724 }
725 
726 /* dseq.c ends here */
727