1 /*** dzone.c -- convert date/times between timezones
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 "tzraw.h"
51 
52 struct ztr_s {
53 	int32_t trns;
54 	int32_t offs;
55 };
56 
57 /* fwd decld in tzraw.h */
58 struct ztrdtl_s {
59 	int32_t offs;
60 	uint8_t dstp;
61 	uint8_t abbr;
62 } __attribute__((packed));
63 
64 const char *prog = "dzone";
65 static char gbuf[256U];
66 
67 static const char never[] = "never";
68 static const char nindi[] = " -> ";
69 static const char pindi[] = " <- ";
70 
71 
72 static size_t
xstrlcpy(char * restrict dst,const char * src,size_t dsz)73 xstrlcpy(char *restrict dst, const char *src, size_t dsz)
74 {
75 	size_t ssz = strlen(src);
76 	if (ssz > dsz) {
77 		ssz = dsz - 1U;
78 	}
79 	memcpy(dst, src, ssz);
80 	dst[ssz] = '\0';
81 	return ssz;
82 }
83 
84 static int
dz_io_write(struct dt_dt_s d,zif_t zone,const char * name)85 dz_io_write(struct dt_dt_s d, zif_t zone, const char *name)
86 {
87 	static const char fmt[] = "%FT%T%Z";
88 	char *restrict bp = gbuf;
89 	const char *const ep = gbuf + sizeof(gbuf);
90 
91 	if (LIKELY(zone != NULL)) {
92 		d = dtz_enrichz(d, zone);
93 	}
94 	bp += dt_strfdt(bp, ep - bp, fmt, d);
95 	/* append name */
96 	if (LIKELY(name != NULL)) {
97 		*bp++ = '\t';
98 		bp += xstrlcpy(bp, name, ep - bp);
99 	}
100 	*bp++ = '\n';
101 	__io_write(gbuf, bp - gbuf, stdout);
102 	return (bp > gbuf) - 1;
103 }
104 
105 static size_t
dz_strftr(char * restrict buf,size_t bsz,struct ztr_s t)106 dz_strftr(char *restrict buf, size_t bsz, struct ztr_s t)
107 {
108 	static const char fmt[] = "%FT%T%Z";
109 	struct dt_dt_s d = dt_dt_initialiser();
110 
111 	d.typ = DT_SEXY;
112 	d.sexy = t.trns + t.offs;
113 	if (t.offs > 0) {
114 		d.zdiff = (uint16_t)(t.offs / ZDIFF_RES);
115 	} else if (t.offs < 0) {
116 		d.neg = 1;
117 		d.zdiff = (uint16_t)(-t.offs / ZDIFF_RES);
118 	}
119 	return dt_strfdt(buf, bsz, fmt, d);
120 }
121 
122 static int
dz_write_nxtr(struct zrng_s r,zif_t z,const char * zn)123 dz_write_nxtr(struct zrng_s r, zif_t z, const char *zn)
124 {
125 	char *restrict bp = gbuf;
126 	const char *const ep = gbuf + sizeof(gbuf);
127 	size_t ntr = zif_ntrans(z);
128 
129 	if (r.next == INT_MAX) {
130 		bp += xstrlcpy(bp, never, bp - ep);
131 	} else {
132 		bp += dz_strftr(bp, ep - bp, (struct ztr_s){r.next, r.offs});
133 	}
134 	/* append next indicator */
135 	bp += xstrlcpy(bp, nindi, bp - ep);
136 	if (r.trno + 1U < ntr) {
137 		/* thank god there's another one */
138 		struct ztrdtl_s zd = zif_trdtl(z, r.trno + 1);
139 
140 		bp += dz_strftr(bp, ep - bp, (struct ztr_s){r.next, zd.offs});
141 	} else {
142 		bp += xstrlcpy(bp, never, bp - ep);
143 	}
144 
145 	/* append name */
146 	if (LIKELY(zn != NULL)) {
147 		*bp++ = '\t';
148 		bp += xstrlcpy(bp, zn, ep - bp);
149 	}
150 	*bp++ = '\n';
151 	__io_write(gbuf, bp - gbuf, stdout);
152 	return (bp > gbuf) - 1;
153 }
154 
155 static int
dz_write_prtr(struct zrng_s r,zif_t UNUSED (z),const char * zn)156 dz_write_prtr(struct zrng_s r, zif_t UNUSED(z), const char *zn)
157 {
158 	char *restrict bp = gbuf;
159 	const char *const ep = gbuf + sizeof(gbuf);
160 
161 	if (r.trno >= 1) {
162 		/* there's one before that */
163 		struct ztrdtl_s zd = zif_trdtl(z, r.trno - 1);
164 
165 		bp += dz_strftr(bp, ep - bp, (struct ztr_s){r.prev, zd.offs});
166 	} else {
167 		bp += xstrlcpy(bp, never, bp - ep);
168 	}
169 	/* append prev indicator */
170 	bp += xstrlcpy(bp, pindi, bp - ep);
171 	if (r.prev == INT_MIN) {
172 		bp += xstrlcpy(bp, never, bp - ep);
173 	} else {
174 		bp += dz_strftr(bp, ep - bp, (struct ztr_s){r.prev, r.offs});
175 	}
176 
177 	/* append name */
178 	if (LIKELY(zn != NULL)) {
179 		*bp++ = '\t';
180 		bp += xstrlcpy(bp, zn, ep - bp);
181 	}
182 	*bp++ = '\n';
183 	__io_write(gbuf, bp - gbuf, stdout);
184 	return (bp > gbuf) - 1;
185 }
186 
187 
188 #include "dzone.yucc"
189 
190 int
main(int argc,char * argv[])191 main(int argc, char *argv[])
192 {
193 	yuck_t argi[1U];
194 	int rc = 0;
195 	zif_t fromz = NULL;
196 	char **fmt;
197 	size_t nfmt;
198 	/* all them zones to consider */
199 	struct {
200 		zif_t zone;
201 		const char *name;
202 	} *z = NULL;
203 	size_t nz = 0U;
204 	/* all them datetimes to consider */
205 	struct dt_dt_s *d = NULL;
206 	size_t nd = 0U;
207 	bool trnsp = false;
208 
209 	if (yuck_parse(argi, argc, argv)) {
210 		rc = 1;
211 		goto out;
212 	} else if (argi->nargs == 0U) {
213 		error("Need at least a ZONENAME or a DATE/TIME");
214 		rc = 1;
215 		goto out;
216 	}
217 
218 	if (argi->from_locale_arg) {
219 		setilocale(argi->from_locale_arg);
220 	}
221 
222 	/* try and read the from and to time zones */
223 	if (argi->from_zone_arg) {
224 		fromz = dt_io_zone(argi->from_zone_arg);
225 	}
226 	if (argi->next_flag || argi->prev_flag) {
227 		trnsp = true;
228 	}
229 	if (argi->base_arg) {
230 		struct dt_dt_s base = dt_strpdt(argi->base_arg, NULL, NULL);
231 		dt_set_base(base);
232 	}
233 
234 	/* very well then */
235 	fmt = argi->input_format_args;
236 	nfmt = argi->input_format_nargs;
237 
238 	/* initially size the two beef arrays as large as there is input
239 	 * we'll then sort them by traversing the input args and ass'ing
240 	 * to the one or the other */
241 	nz = 0U;
242 	if (UNLIKELY((z = malloc(argi->nargs * sizeof(*z))) == NULL)) {
243 		error("failed to allocate space for zone info");
244 		goto out;
245 	}
246 	nd = 0U;
247 	if (UNLIKELY((d = malloc(argi->nargs * sizeof(*d))) == NULL)) {
248 		error("failed to allocate space for date/times");
249 		goto out;
250 	}
251 
252 	for (size_t i = 0U; i < argi->nargs; i++) {
253 		const char *inp = argi->args[i];
254 
255 		/* try dt_strp'ing the input or assume it's a zone  */
256 		if (!dt_unk_p(d[nd] = dt_io_strpdt(inp, fmt, nfmt, fromz))) {
257 			if (UNLIKELY(d[nd].fix) && !argi->quiet_flag) {
258 				rc = 2;
259 			}
260 			nd++;
261 		} else if ((z[nz].zone = dt_io_zone(inp)) != NULL) {
262 			z[nz].name = inp;
263 			nz++;
264 		} else if (!argi->quiet_flag) {
265 			/* just bollocks */
266 			error("\
267 Cannot use `%s', it does not appear to be a zonename\n\
268 nor a date/time corresponding to the given input formats", inp);
269 			rc = 2;
270 		}
271 	}
272 	/* operate with default zone UTC and default time now */
273 	if (nz == 0U) {
274 		z[nz].zone = NULL;
275 		z[nz].name = NULL;
276 		nz++;
277 	}
278 	if (nd == 0U && !trnsp) {
279 		d[nd++] = dt_datetime((dt_dttyp_t)DT_YMD);
280 	} else if (nd == 0U) {
281 		d[nd++] = dt_datetime((dt_dttyp_t)DT_SEXY);
282 	}
283 
284 	/* just go through them all now */
285 	if (LIKELY(!trnsp)) {
286 		for (size_t i = 0U; !trnsp && i < nd; i++) {
287 			for (size_t j = 0U; j < nz; j++) {
288 				dz_io_write(d[i], z[j].zone, z[j].name);
289 			}
290 		}
291 	} else {
292 		/* otherwise traverse the zones and determine transitions */
293 		for (size_t i = 0U; i < nd; i++) {
294 			struct dt_dt_s di = dt_dtconv(DT_SEXY, d[i]);
295 
296 			for (size_t j = 0U; j < nz; j++) {
297 				const zif_t zj = z[j].zone;
298 				const char *zn = z[j].name;
299 				struct zrng_s r;
300 
301 				if (UNLIKELY(zj == NULL)) {
302 					/* don't bother */
303 					continue;
304 				}
305 				/* otherwise find the range */
306 				r = zif_find_zrng(zj, di.sexy);
307 
308 				if (argi->next_flag) {
309 					dz_write_nxtr(r, zj, zn);
310 				}
311 
312 				if (argi->prev_flag) {
313 					dz_write_prtr(r, zj, zn);
314 				}
315 			}
316 		}
317 	}
318 
319 out:
320 	/* release the zones */
321 	dt_io_clear_zones();
322 	/* release those arrays */
323 	if (LIKELY(z != NULL)) {
324 		free(z);
325 	}
326 	if (LIKELY(d != NULL)) {
327 		free(d);
328 	}
329 
330 	if (fromz != NULL) {
331 		zif_close(fromz);
332 	}
333 	if (argi->from_locale_arg) {
334 		setilocale(NULL);
335 	}
336 
337 	yuck_free(argi);
338 	return rc;
339 }
340 
341 /* dzone.c ends here */
342