timelocal.c revision 72167
155714Skris/*-
255714Skris * Copyright (c) 1997 FreeBSD Inc.
355714Skris * All rights reserved.
455714Skris *
555714Skris * Redistribution and use in source and binary forms, with or without
655714Skris * modification, are permitted provided that the following conditions
755714Skris * are met:
8296341Sdelphij * 1. Redistributions of source code must retain the above copyright
955714Skris *    notice, this list of conditions and the following disclaimer.
1055714Skris * 2. Redistributions in binary form must reproduce the above copyright
1155714Skris *    notice, this list of conditions and the following disclaimer in the
1255714Skris *    documentation and/or other materials provided with the distribution.
1355714Skris *
1455714Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15296341Sdelphij * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1655714Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1755714Skris * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1855714Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1955714Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2055714Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2155714Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22296341Sdelphij * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2355714Skris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2455714Skris * SUCH DAMAGE.
2555714Skris *
2655714Skris * $FreeBSD: head/lib/libc/stdtime/timelocal.c 72167 2001-02-08 17:06:37Z phantom $
2755714Skris */
2855714Skris
2955714Skris#include "namespace.h"
3055714Skris#include <sys/types.h>
3155714Skris#include <sys/stat.h>
3255714Skris#include <sys/syslimits.h>
3355714Skris#include <fcntl.h>
3455714Skris#include <locale.h>
3555714Skris#include <stddef.h>
3655714Skris#include <stdlib.h>
37296341Sdelphij#include <string.h>
3855714Skris#include <unistd.h>
3955714Skris#include "un-namespace.h"
40296341Sdelphij
4155714Skris#include "setlocale.h"
4255714Skris#include "timelocal.h"
4355714Skris
4455714Skrisstatic int split_lines(char *, const char *);
4555714Skrisstatic void set_from_buf(const char *, int);
4655714Skris
4755714Skrisstatic struct lc_time_T _time_localebuf;
4855714Skrisstatic int _time_using_locale;
4955714Skris
5055714Skris#define	LCTIME_SIZE_FULL (sizeof(struct lc_time_T) / sizeof(char *))
5155714Skris#define	LCTIME_SIZE_1 \
52296341Sdelphij	(offsetof(struct lc_time_T, alt_month[0]) / sizeof(char *))
5355714Skris#define LCTIME_SIZE_2 \
5455714Skris	(offsetof(struct lc_time_T, Ef_fmt) / sizeof(char *))
5555714Skris
5655714Skrisstatic const struct lc_time_T	_C_time_locale = {
5755714Skris	{
5855714Skris		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
5955714Skris		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
6055714Skris	}, {
6155714Skris		"January", "February", "March", "April", "May", "June",
6255714Skris		"July", "August", "September", "October", "November", "December"
6355714Skris	}, {
6455714Skris		"Sun", "Mon", "Tue", "Wed",
6555714Skris		"Thu", "Fri", "Sat"
66296341Sdelphij	}, {
6755714Skris		"Sunday", "Monday", "Tuesday", "Wednesday",
68296341Sdelphij		"Thursday", "Friday", "Saturday"
6955714Skris	},
7055714Skris
7155714Skris	/* X_fmt */
72296341Sdelphij	"%H:%M:%S",
73296341Sdelphij
74296341Sdelphij	/*
75296341Sdelphij	** x_fmt
76296341Sdelphij	** Since the C language standard calls for
77296341Sdelphij	** "date, using locale's date format," anything goes.
78296341Sdelphij	** Using just numbers (as here) makes Quakers happier;
7955714Skris	** it's also compatible with SVR4.
8055714Skris	*/
8155714Skris	"%m/%d/%y",
82296341Sdelphij
83296341Sdelphij	/*
8455714Skris	** c_fmt (ctime-compatible)
85296341Sdelphij	** Not used, just compatibility placeholder.
86296341Sdelphij	*/
87296341Sdelphij	NULL,
88296341Sdelphij
89296341Sdelphij	/* am */
90296341Sdelphij	"AM",
91296341Sdelphij
92296341Sdelphij	/* pm */
93296341Sdelphij	"PM",
94296341Sdelphij
95296341Sdelphij	/* date_fmt */
9655714Skris	"%a %Ef %X %Z %Y",
9755714Skris
98296341Sdelphij	{
99296341Sdelphij		"January", "February", "March", "April", "May", "June",
100296341Sdelphij		"July", "August", "September", "October", "November", "December"
101296341Sdelphij	},
102296341Sdelphij
103296341Sdelphij	/* Ef_fmt
104296341Sdelphij	** To determine short months / day order
105296341Sdelphij	*/
10655714Skris	"%b %e",
107296341Sdelphij
108296341Sdelphij	/* EF_fmt
10955714Skris	** To determine long months / day order
110296341Sdelphij	*/
111296341Sdelphij	"%B %e"
11255714Skris};
113296341Sdelphij
114296341Sdelphijstruct lc_time_T *
115296341Sdelphij__get_current_time_locale(void) {
116	return (_time_using_locale
117		? &_time_localebuf
118		: (struct lc_time_T *)&_C_time_locale);
119}
120
121int
122__time_load_locale(const char *name)
123{
124	static char *		locale_buf;
125	static char		locale_buf_C[] = "C";
126	static int		num_lines;
127
128	int			fd;
129	char *			lbuf;
130	char *			p;
131	const char *		plim;
132	char                    filename[PATH_MAX];
133	struct stat		st;
134	size_t			namesize;
135	size_t			bufsize;
136	int                     save_using_locale;
137
138	save_using_locale = _time_using_locale;
139	_time_using_locale = 0;
140
141	if (name == NULL)
142		goto no_locale;
143
144	if (!strcmp(name, "C") || !strcmp(name, "POSIX"))
145		return 0;
146
147	/*
148	** If the locale name is the same as our cache, use the cache.
149	*/
150	lbuf = locale_buf;
151	if (lbuf != NULL && strcmp(name, lbuf) == 0) {
152		set_from_buf(lbuf, num_lines);
153		_time_using_locale = 1;
154		return 0;
155	}
156	/*
157	** Slurp the locale file into the cache.
158	*/
159	namesize = strlen(name) + 1;
160
161	if (!_PathLocale)
162		goto no_locale;
163	/* Range checking not needed, 'name' size is limited */
164	strcpy(filename, _PathLocale);
165	strcat(filename, "/");
166	strcat(filename, name);
167	strcat(filename, "/LC_TIME");
168	fd = _open(filename, O_RDONLY);
169	if (fd < 0)
170		goto no_locale;
171	if (_fstat(fd, &st) != 0)
172		goto bad_locale;
173	if (st.st_size <= 0)
174		goto bad_locale;
175	bufsize = namesize + st.st_size;
176	locale_buf = NULL;
177	lbuf = (lbuf == NULL || lbuf == locale_buf_C) ?
178		malloc(bufsize) : reallocf(lbuf, bufsize);
179	if (lbuf == NULL)
180		goto bad_locale;
181	(void) strcpy(lbuf, name);
182	p = lbuf + namesize;
183	plim = p + st.st_size;
184	if (_read(fd, p, (size_t) st.st_size) != st.st_size)
185		goto bad_lbuf;
186	if (_close(fd) != 0)
187		goto bad_lbuf;
188	/*
189	** Parse the locale file into localebuf.
190	*/
191	if (plim[-1] != '\n')
192		goto bad_lbuf;
193	num_lines = split_lines(p, plim);
194	if (num_lines >= LCTIME_SIZE_FULL)
195		num_lines = LCTIME_SIZE_FULL;
196	else if (num_lines >= LCTIME_SIZE_2)
197		num_lines = LCTIME_SIZE_2;
198	else if (num_lines >= LCTIME_SIZE_1)
199		num_lines = LCTIME_SIZE_1;
200	else
201		goto reset_locale;
202	set_from_buf(lbuf, num_lines);
203	/*
204	** Record the successful parse in the cache.
205	*/
206	locale_buf = lbuf;
207
208	_time_using_locale = 1;
209	return 0;
210
211reset_locale:
212	/*
213	 * XXX - This may not be the correct thing to do in this case.
214	 * setlocale() assumes that we left the old locale alone.
215	 */
216	locale_buf = locale_buf_C;
217	_time_localebuf = _C_time_locale;
218	save_using_locale = 0;
219bad_lbuf:
220	free(lbuf);
221bad_locale:
222	(void)_close(fd);
223no_locale:
224	_time_using_locale = save_using_locale;
225	return -1;
226}
227
228static int
229split_lines(char *p, const char *plim)
230{
231	int i;
232
233	for (i = 0; p < plim; i++) {
234		p = strchr(p, '\n');
235		*p++ = '\0';
236	}
237	return i;
238}
239
240static void
241set_from_buf(const char *p, int num_lines)
242{
243	const char **ap;
244	int i;
245
246	for (ap = (const char **) &_time_localebuf, i = 0;
247	    i < num_lines; ++ap, ++i)
248		*ap = p += strlen(p) + 1;
249	if (num_lines >= LCTIME_SIZE_2)
250		return;
251	for (i = 0; i < 12; i++)
252		_time_localebuf.alt_month[i] = _time_localebuf.month[i];
253}
254