1 /*** dt-io-zone.c -- abstract from raw zone interface
2  *
3  * Copyright (C) 2010-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 <string.h>
41 #include "tzmap.h"
42 #include "dt-io.h"
43 #include "dt-io-zone.h"
44 #include "alist.h"
45 
46 #if defined TZMAP_DIR
47 static const char tmdir[] = TZMAP_DIR;
48 #else  /* !TZMAP_DIR */
49 static const char tmdir[] = ".";
50 #endif	/* TZMAP_DIR */
51 #define TZMAP_SUF	".tzmcc"
52 
53 static size_t
xstrlncpy(char * restrict dst,size_t dsz,const char * src,size_t ssz)54 xstrlncpy(char *restrict dst, size_t dsz, const char *src, size_t ssz)
55 {
56 	if (ssz > dsz) {
57 		ssz = dsz - 1U;
58 	}
59 	memcpy(dst, src, ssz);
60 	dst[ssz] = '\0';
61 	return ssz;
62 }
63 
64 static size_t
xstrlcpy(char * restrict dst,const char * src,size_t dsz)65 xstrlcpy(char *restrict dst, const char *src, size_t dsz)
66 {
67 	size_t ssz = strlen(src);
68 	if (ssz > dsz) {
69 		ssz = dsz - 1U;
70 	}
71 	memcpy(dst, src, ssz);
72 	dst[ssz] = '\0';
73 	return ssz;
74 }
75 
76 
77 /* extended zone handling, tzmaps and stuff */
78 #if !defined PATH_MAX
79 # define PATH_MAX	256U
80 #endif	/* !PATH_MAX */
81 
82 static struct alist_s zones[1U];
83 static struct alist_s tzmaps[1U];
84 
85 static tzmap_t
find_tzmap(const char * mnm,size_t mnz)86 find_tzmap(const char *mnm, size_t mnz)
87 {
88 	static const char tzmap_suffix[] = TZMAP_SUF;
89 	char tzmfn[PATH_MAX];
90 	char *tp = tzmfn;
91 	size_t tz = sizeof(tzmfn);
92 	const char *p;
93 	size_t z;
94 
95 	/* prefer TZMAP_DIR */
96 	if ((p = getenv("TZMAP_DIR")) != NULL) {
97 		z = xstrlcpy(tp, p, tz);
98 	} else {
99 		z = xstrlncpy(tp, tz, tmdir, sizeof(tmdir) - 1U);
100 	}
101 	tp += z, tz -= z;
102 	*tp++ = '/', tz--;
103 
104 	/* try and find it the hard way */
105 	xstrlncpy(tp, tz, mnm, mnz);
106 	tp += mnz, tz -= mnz;
107 	xstrlncpy(tp, tz, tzmap_suffix, sizeof(tzmap_suffix) - 1U);
108 
109 	/* try and open the thing, then try and look up SPEC */
110 	return tzm_open(tzmfn);
111 }
112 
113 static zif_t
__io_zone(const char * spec)114 __io_zone(const char *spec)
115 {
116 	zif_t res;
117 
118 	/* try looking up SPEC first */
119 	if ((res = alist_assoc(zones, spec)) == NULL) {
120 		/* open 'im */
121 		if ((res = zif_open(spec)) != NULL) {
122 			/* cache 'im */
123 			alist_put(zones, spec, res);
124 		}
125 	}
126 	return res;
127 }
128 
129 zif_t
dt_io_zone(const char * spec)130 dt_io_zone(const char *spec)
131 {
132 	char *p;
133 
134 	if (spec == NULL) {
135 		/* safety net */
136 		return NULL;
137 	}
138 	/* see if SPEC is a MAP:KEY */
139 	if ((p = strchr(spec, ':')) != NULL) {
140 		char tzmfn[PATH_MAX];
141 		tzmap_t tzm;
142 
143 		xstrlncpy(tzmfn, sizeof(tzmfn), spec, p - spec);
144 
145 		/* check tzmaps alist first */
146 		if ((tzm = alist_assoc(tzmaps, tzmfn)) != NULL) {
147 			;
148 		} else if ((tzm = find_tzmap(tzmfn, p - spec)) != NULL) {
149 			/* cache the instance */
150 			alist_put(tzmaps, tzmfn, tzm);
151 		} else {
152 			error("\
153 Cannot find `%s" TZMAP_SUF "' in the tzmaps search path\n\
154 Set TZMAP_DIR environment variable to where " TZMAP_SUF " files reside", tzmfn);
155 			return NULL;
156 		}
157 		/* look up key bit in tzmap and use that if found */
158 		if ((spec = tzm_find(tzm, ++p)) == NULL) {
159 			return NULL;
160 		}
161 	}
162 	return __io_zone(spec);
163 }
164 
165 void
dt_io_clear_zones(void)166 dt_io_clear_zones(void)
167 {
168 	if (tzmaps->data != NULL) {
169 		for (acons_t c; (c = alist_next(tzmaps)).val;) {
170 			tzm_close(c.val);
171 		}
172 		free_alist(tzmaps);
173 	}
174 	if (zones->data != NULL) {
175 		for (acons_t c; (c = alist_next(zones)).val;) {
176 			zif_close(c.val);
177 		}
178 		free_alist(zones);
179 	}
180 	return;
181 }
182 
183 /* dt-io-zone.c ends here */
184