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
47struct lc_time_T tm;
48
49void
50init_time(void)
51{
52	(void) memset(&tm, 0, sizeof (tm));
53}
54
55void
56add_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
99static void
100add_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
112void
113add_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
157void
158check_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
192void
193reset_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
230void
231dump_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