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#include <stdio.h>
36#include <stdlib.h>
37#include <errno.h>
38#include <sys/types.h>
39#include <string.h>
40#include <unistd.h>
41#include "localedef.h"
42#include "parser.h"
43#include "timelocal.h"
44
45struct lc_time_T tm;
46
47void
48init_time(void)
49{
50	(void) memset(&tm, 0, sizeof (tm));
51}
52
53void
54add_time_str(wchar_t *wcs)
55{
56	char	*str;
57
58	if ((str = to_mb_string(wcs)) == NULL) {
59		INTERR;
60		return;
61	}
62	free(wcs);
63
64	switch (last_kw) {
65	case T_D_T_FMT:
66		tm.c_fmt = str;
67		break;
68	case T_D_FMT:
69		tm.x_fmt = str;
70		break;
71	case T_T_FMT:
72		tm.X_fmt = str;
73		break;
74	case T_T_FMT_AMPM:
75		tm.ampm_fmt = str;
76		break;
77	case T_DATE_FMT:
78		/*
79		 * This one is a Solaris extension, Too bad date just
80		 * doesn't use %c, which would be simpler.
81		 */
82		tm.date_fmt = str;
83		break;
84	case T_ERA_D_FMT:
85	case T_ERA_T_FMT:
86	case T_ERA_D_T_FMT:
87		/* Silently ignore it. */
88		free(str);
89		break;
90	default:
91		free(str);
92		INTERR;
93		break;
94	}
95}
96
97static void
98add_list(const char *ptr[], char *str, int limit)
99{
100	int	i;
101	for (i = 0; i < limit; i++) {
102		if (ptr[i] == NULL) {
103			ptr[i] = str;
104			return;
105		}
106	}
107	fprintf(stderr,"too many list elements\n");
108}
109
110void
111add_time_list(wchar_t *wcs)
112{
113	char *str;
114
115	if ((str = to_mb_string(wcs)) == NULL) {
116		INTERR;
117		return;
118	}
119	free(wcs);
120
121	switch (last_kw) {
122	case T_ABMON:
123		add_list(tm.mon, str, 12);
124		break;
125	case T_MON:
126		add_list(tm.month, str, 12);
127		break;
128	case T_ABDAY:
129		add_list(tm.wday, str, 7);
130		break;
131	case T_DAY:
132		add_list(tm.weekday, str, 7);
133		break;
134	case T_AM_PM:
135		if (tm.am == NULL) {
136			tm.am = str;
137		} else if (tm.pm == NULL) {
138			tm.pm = str;
139		} else {
140			fprintf(stderr,"too many list elements\n");
141			free(str);
142		}
143		break;
144	case T_ALT_DIGITS:
145	case T_ERA:
146		free(str);
147		break;
148	default:
149		free(str);
150		INTERR;
151		break;
152	}
153}
154
155void
156check_time_list(void)
157{
158	switch (last_kw) {
159	case T_ABMON:
160		if (tm.mon[11] != NULL)
161			return;
162		break;
163	case T_MON:
164		if (tm.month[11] != NULL)
165			return;
166		break;
167	case T_ABDAY:
168		if (tm.wday[6] != NULL)
169			return;
170		break;
171	case T_DAY:
172		if (tm.weekday[6] != NULL)
173			return;
174		break;
175	case T_AM_PM:
176		if (tm.pm != NULL)
177			return;
178		break;
179	case T_ERA:
180	case T_ALT_DIGITS:
181		return;
182	default:
183		fprintf(stderr,"unknown list\n");
184		break;
185	}
186
187	fprintf(stderr,"too few items in list (%d)\n", last_kw);
188}
189
190void
191reset_time_list(void)
192{
193	int i;
194	switch (last_kw) {
195	case T_ABMON:
196		for (i = 0; i < 12; i++) {
197			free((char *)tm.mon[i]);
198			tm.mon[i] = NULL;
199		}
200		break;
201	case T_MON:
202		for (i = 0; i < 12; i++) {
203			free((char *)tm.month[i]);
204			tm.month[i] = NULL;
205		}
206		break;
207	case T_ABDAY:
208		for (i = 0; i < 7; i++) {
209			free((char *)tm.wday[i]);
210			tm.wday[i] = NULL;
211		}
212		break;
213	case T_DAY:
214		for (i = 0; i < 7; i++) {
215			free((char *)tm.weekday[i]);
216			tm.weekday[i] = NULL;
217		}
218		break;
219	case T_AM_PM:
220		free((char *)tm.am);
221		tm.am = NULL;
222		free((char *)tm.pm);
223		tm.pm = NULL;
224		break;
225	}
226}
227
228void
229dump_time(void)
230{
231	FILE *f;
232	int i;
233
234	if ((f = open_category()) == NULL) {
235		return;
236	}
237
238	for (i = 0; i < 12; i++) {
239		if (putl_category(tm.mon[i], f) == EOF) {
240			return;
241		}
242	}
243	for (i = 0; i < 12; i++) {
244		if (putl_category(tm.month[i], f) == EOF) {
245			return;
246		}
247	}
248	for (i = 0; i < 7; i++) {
249		if (putl_category(tm.wday[i], f) == EOF) {
250			return;
251		}
252	}
253	for (i = 0; i < 7; i++) {
254		if (putl_category(tm.weekday[i], f) == EOF) {
255			return;
256		}
257	}
258
259	/*
260	 * NOTE: If date_fmt is not specified, then we'll default to
261	 * using the %c for date.  This is reasonable for most
262	 * locales, although for reasons that I don't understand
263	 * Solaris historically has had a separate format for date.
264	 */
265	if ((putl_category(tm.X_fmt, f) == EOF) ||
266	    (putl_category(tm.x_fmt, f) == EOF) ||
267	    (putl_category(tm.c_fmt, f) == EOF) ||
268	    (putl_category(tm.am, f) == EOF) ||
269	    (putl_category(tm.pm, f) == EOF) ||
270	    (putl_category(tm.date_fmt ? tm.date_fmt : tm.c_fmt, f) == EOF) ||
271	    (putl_category(tm.ampm_fmt, f) == EOF)) {
272		return;
273	}
274	close_category(f);
275}
276