xref: /freebsd/usr.bin/localedef/time.c (revision e67e85659c0de33e617e5fbf1028c6e8b49eee53)
1 /*-
2  * Copyright 2010 Nexenta Systems, Inc.  All rights reserved.
3  * Copyright 2015 John Marino <draco@marino.st>
4  *
5  * This source code is derived from the illumos localedef command, and
6  * provided under BSD-style license terms by Nexenta Systems, Inc.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 /*
32  * LC_TIME database generation routines for localedef.
33  */
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
36 
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <errno.h>
40 #include <sys/types.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include "localedef.h"
44 #include "parser.h"
45 #include "timelocal.h"
46 
47 struct lc_time_T tm;
48 
49 void
50 init_time(void)
51 {
52 	(void) memset(&tm, 0, sizeof (tm));
53 }
54 
55 void
56 add_time_str(wchar_t *wcs)
57 {
58 	char	*str;
59 
60 	if ((str = to_mb_string(wcs)) == NULL) {
61 		INTERR;
62 		return;
63 	}
64 	free(wcs);
65 
66 	switch (last_kw) {
67 	case T_D_T_FMT:
68 		tm.c_fmt = str;
69 		break;
70 	case T_D_FMT:
71 		tm.x_fmt = str;
72 		break;
73 	case T_T_FMT:
74 		tm.X_fmt = str;
75 		break;
76 	case T_T_FMT_AMPM:
77 		tm.ampm_fmt = str;
78 		break;
79 	case T_DATE_FMT:
80 		/*
81 		 * This one is a Solaris extension, Too bad date just
82 		 * doesn't use %c, which would be simpler.
83 		 */
84 		tm.date_fmt = str;
85 		break;
86 	case T_ERA_D_FMT:
87 	case T_ERA_T_FMT:
88 	case T_ERA_D_T_FMT:
89 		/* Silently ignore it. */
90 		free(str);
91 		break;
92 	default:
93 		free(str);
94 		INTERR;
95 		break;
96 	}
97 }
98 
99 static void
100 add_list(const char *ptr[], char *str, int limit)
101 {
102 	int	i;
103 	for (i = 0; i < limit; i++) {
104 		if (ptr[i] == NULL) {
105 			ptr[i] = str;
106 			return;
107 		}
108 	}
109 	fprintf(stderr,"too many list elements");
110 }
111 
112 void
113 add_time_list(wchar_t *wcs)
114 {
115 	char *str;
116 
117 	if ((str = to_mb_string(wcs)) == NULL) {
118 		INTERR;
119 		return;
120 	}
121 	free(wcs);
122 
123 	switch (last_kw) {
124 	case T_ABMON:
125 		add_list(tm.mon, str, 12);
126 		break;
127 	case T_MON:
128 		add_list(tm.month, str, 12);
129 		break;
130 	case T_ABDAY:
131 		add_list(tm.wday, str, 7);
132 		break;
133 	case T_DAY:
134 		add_list(tm.weekday, str, 7);
135 		break;
136 	case T_AM_PM:
137 		if (tm.am == NULL) {
138 			tm.am = str;
139 		} else if (tm.pm == NULL) {
140 			tm.pm = str;
141 		} else {
142 			fprintf(stderr,"too many list elements");
143 			free(str);
144 		}
145 		break;
146 	case T_ALT_DIGITS:
147 	case T_ERA:
148 		free(str);
149 		break;
150 	default:
151 		free(str);
152 		INTERR;
153 		break;
154 	}
155 }
156 
157 void
158 check_time_list(void)
159 {
160 	switch (last_kw) {
161 	case T_ABMON:
162 		if (tm.mon[11] != NULL)
163 			return;
164 		break;
165 	case T_MON:
166 		if (tm.month[11] != NULL)
167 			return;
168 		break;
169 	case T_ABDAY:
170 		if (tm.wday[6] != NULL)
171 			return;
172 		break;
173 	case T_DAY:
174 		if (tm.weekday[6] != NULL)
175 			return;
176 		break;
177 	case T_AM_PM:
178 		if (tm.pm != NULL)
179 			return;
180 		break;
181 	case T_ERA:
182 	case T_ALT_DIGITS:
183 		return;
184 	default:
185 		fprintf(stderr,"unknown list");
186 		break;
187 	}
188 
189 	fprintf(stderr,"too few items in list (%d)", last_kw);
190 }
191 
192 void
193 reset_time_list(void)
194 {
195 	int i;
196 	switch (last_kw) {
197 	case T_ABMON:
198 		for (i = 0; i < 12; i++) {
199 			free((char *)tm.mon[i]);
200 			tm.mon[i] = NULL;
201 		}
202 		break;
203 	case T_MON:
204 		for (i = 0; i < 12; i++) {
205 			free((char *)tm.month[i]);
206 			tm.month[i] = NULL;
207 		}
208 		break;
209 	case T_ABDAY:
210 		for (i = 0; i < 7; i++) {
211 			free((char *)tm.wday[i]);
212 			tm.wday[i] = NULL;
213 		}
214 		break;
215 	case T_DAY:
216 		for (i = 0; i < 7; i++) {
217 			free((char *)tm.weekday[i]);
218 			tm.weekday[i] = NULL;
219 		}
220 		break;
221 	case T_AM_PM:
222 		free((char *)tm.am);
223 		tm.am = NULL;
224 		free((char *)tm.pm);
225 		tm.pm = NULL;
226 		break;
227 	}
228 }
229 
230 void
231 dump_time(void)
232 {
233 	FILE *f;
234 	int i;
235 
236 	if ((f = open_category()) == NULL) {
237 		return;
238 	}
239 
240 	for (i = 0; i < 12; i++) {
241 		if (putl_category(tm.mon[i], f) == EOF) {
242 			return;
243 		}
244 	}
245 	for (i = 0; i < 12; i++) {
246 		if (putl_category(tm.month[i], f) == EOF) {
247 			return;
248 		}
249 	}
250 	for (i = 0; i < 7; i++) {
251 		if (putl_category(tm.wday[i], f) == EOF) {
252 			return;
253 		}
254 	}
255 	for (i = 0; i < 7; i++) {
256 		if (putl_category(tm.weekday[i], f) == EOF) {
257 			return;
258 		}
259 	}
260 
261 	/*
262 	 * NOTE: If date_fmt is not specified, then we'll default to
263 	 * using the %c for date.  This is reasonable for most
264 	 * locales, although for reasons that I don't understand
265 	 * Solaris historically has had a separate format for date.
266 	 */
267 	if ((putl_category(tm.X_fmt, f) == EOF) ||
268 	    (putl_category(tm.x_fmt, f) == EOF) ||
269 	    (putl_category(tm.c_fmt, f) == EOF) ||
270 	    (putl_category(tm.am, f) == EOF) ||
271 	    (putl_category(tm.pm, f) == EOF) ||
272 	    (putl_category(tm.date_fmt ? tm.date_fmt : tm.c_fmt, f) == EOF) ||
273 	    (putl_category(tm.ampm_fmt, f) == EOF)) {
274 		return;
275 	}
276 	close_category(f);
277 }
278