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