xref: /freebsd/lib/libc/stdtime/timelocal.c (revision 11cd0d324175da41324f7f35ac3e08146bb8577f)
137486f03SJoerg Wunsch /*-
237486f03SJoerg Wunsch  * Copyright (c) 1997 FreeBSD Inc.
337486f03SJoerg Wunsch  * All rights reserved.
437486f03SJoerg Wunsch  *
537486f03SJoerg Wunsch  * Redistribution and use in source and binary forms, with or without
637486f03SJoerg Wunsch  * modification, are permitted provided that the following conditions
737486f03SJoerg Wunsch  * are met:
837486f03SJoerg Wunsch  * 1. Redistributions of source code must retain the above copyright
937486f03SJoerg Wunsch  *    notice, this list of conditions and the following disclaimer.
1037486f03SJoerg Wunsch  * 2. Redistributions in binary form must reproduce the above copyright
1137486f03SJoerg Wunsch  *    notice, this list of conditions and the following disclaimer in the
1237486f03SJoerg Wunsch  *    documentation and/or other materials provided with the distribution.
1337486f03SJoerg Wunsch  *
1437486f03SJoerg Wunsch  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1537486f03SJoerg Wunsch  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1637486f03SJoerg Wunsch  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1737486f03SJoerg Wunsch  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1837486f03SJoerg Wunsch  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1937486f03SJoerg Wunsch  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2037486f03SJoerg Wunsch  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2137486f03SJoerg Wunsch  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2237486f03SJoerg Wunsch  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2337486f03SJoerg Wunsch  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2437486f03SJoerg Wunsch  * SUCH DAMAGE.
2537486f03SJoerg Wunsch  *
267f3dea24SPeter Wemm  * $FreeBSD$
2737486f03SJoerg Wunsch  */
2837486f03SJoerg Wunsch 
2937486f03SJoerg Wunsch #include <sys/types.h>
3037486f03SJoerg Wunsch #include <sys/stat.h>
3137486f03SJoerg Wunsch #include <sys/syslimits.h>
3237486f03SJoerg Wunsch #include <fcntl.h>
3337486f03SJoerg Wunsch #include <locale.h>
34da3785efSDmitrij Tejblum #include <stddef.h>
3537486f03SJoerg Wunsch #include <stdlib.h>
3637486f03SJoerg Wunsch #include <string.h>
3737486f03SJoerg Wunsch #include "setlocale.h"
3837486f03SJoerg Wunsch #include "timelocal.h"
3937486f03SJoerg Wunsch 
40da3785efSDmitrij Tejblum static int split_lines(char *, const char *);
41da3785efSDmitrij Tejblum static void set_from_buf(const char *, int);
42da3785efSDmitrij Tejblum 
4337486f03SJoerg Wunsch struct lc_time_T _time_localebuf;
4437486f03SJoerg Wunsch int _time_using_locale;
4537486f03SJoerg Wunsch 
46da3785efSDmitrij Tejblum #define	LCTIME_SIZE_FULL (sizeof(struct lc_time_T) / sizeof(char *))
47da3785efSDmitrij Tejblum #define	LCTIME_SIZE_1 \
48da3785efSDmitrij Tejblum 	(offsetof(struct lc_time_T, alt_month[0]) / sizeof(char *))
4911cd0d32SAndrey A. Chernov #define LCTIME_SIZE_2 \
5011cd0d32SAndrey A. Chernov 	(offsetof(struct lc_time_T, Ex_fmt) / sizeof(char *))
51da3785efSDmitrij Tejblum 
5237486f03SJoerg Wunsch const struct lc_time_T	_C_time_locale = {
5337486f03SJoerg Wunsch 	{
5437486f03SJoerg Wunsch 		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
5537486f03SJoerg Wunsch 		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
5637486f03SJoerg Wunsch 	}, {
5737486f03SJoerg Wunsch 		"January", "February", "March", "April", "May", "June",
5837486f03SJoerg Wunsch 		"July", "August", "September", "October", "November", "December"
5937486f03SJoerg Wunsch 	}, {
6037486f03SJoerg Wunsch 		"Sun", "Mon", "Tue", "Wed",
6137486f03SJoerg Wunsch 		"Thu", "Fri", "Sat"
6237486f03SJoerg Wunsch 	}, {
6337486f03SJoerg Wunsch 		"Sunday", "Monday", "Tuesday", "Wednesday",
6437486f03SJoerg Wunsch 		"Thursday", "Friday", "Saturday"
6537486f03SJoerg Wunsch 	},
6637486f03SJoerg Wunsch 
6737486f03SJoerg Wunsch 	/* X_fmt */
6837486f03SJoerg Wunsch 	"%H:%M:%S",
6937486f03SJoerg Wunsch 
7037486f03SJoerg Wunsch 	/*
7137486f03SJoerg Wunsch 	** x_fmt
7237486f03SJoerg Wunsch 	** Since the C language standard calls for
7337486f03SJoerg Wunsch 	** "date, using locale's date format," anything goes.
7437486f03SJoerg Wunsch 	** Using just numbers (as here) makes Quakers happier;
7537486f03SJoerg Wunsch 	** it's also compatible with SVR4.
7637486f03SJoerg Wunsch 	*/
7737486f03SJoerg Wunsch 	"%m/%d/%y",
7837486f03SJoerg Wunsch 
7937486f03SJoerg Wunsch 	/*
8037486f03SJoerg Wunsch 	** c_fmt (ctime-compatible)
8137486f03SJoerg Wunsch 	** Note that
8237486f03SJoerg Wunsch 	**	"%a %b %d %H:%M:%S %Y"
8337486f03SJoerg Wunsch 	** is used by Solaris 2.3.
8437486f03SJoerg Wunsch 	*/
8511cd0d32SAndrey A. Chernov 	"%a %Ex %X %Y",
8637486f03SJoerg Wunsch 
8737486f03SJoerg Wunsch 	/* am */
8837486f03SJoerg Wunsch 	"AM",
8937486f03SJoerg Wunsch 
9037486f03SJoerg Wunsch 	/* pm */
9137486f03SJoerg Wunsch 	"PM",
9237486f03SJoerg Wunsch 
9337486f03SJoerg Wunsch 	/* date_fmt */
9411cd0d32SAndrey A. Chernov 	"%a %Ex %X %Z %Y",
95da3785efSDmitrij Tejblum 
96da3785efSDmitrij Tejblum 	{
97da3785efSDmitrij Tejblum 		"January", "February", "March", "April", "May", "June",
98da3785efSDmitrij Tejblum 		"July", "August", "September", "October", "November", "December"
9911cd0d32SAndrey A. Chernov 	},
10011cd0d32SAndrey A. Chernov 
10111cd0d32SAndrey A. Chernov 	/* Ex_fmt
10211cd0d32SAndrey A. Chernov 	** To determine months / day order
10311cd0d32SAndrey A. Chernov 	*/
10411cd0d32SAndrey A. Chernov 	"%b %e"
10537486f03SJoerg Wunsch };
10637486f03SJoerg Wunsch 
10737486f03SJoerg Wunsch 
10837486f03SJoerg Wunsch int
10937486f03SJoerg Wunsch __time_load_locale(const char *name)
11037486f03SJoerg Wunsch {
11137486f03SJoerg Wunsch 	static char *		locale_buf;
11237486f03SJoerg Wunsch 	static char		locale_buf_C[] = "C";
113da3785efSDmitrij Tejblum 	static int		num_lines;
11437486f03SJoerg Wunsch 
11537486f03SJoerg Wunsch 	int			fd;
11637486f03SJoerg Wunsch 	char *			lbuf;
11737486f03SJoerg Wunsch 	char *			p;
11837486f03SJoerg Wunsch 	const char *		plim;
11937486f03SJoerg Wunsch 	char                    filename[PATH_MAX];
12037486f03SJoerg Wunsch 	struct stat		st;
12137486f03SJoerg Wunsch 	size_t			namesize;
12237486f03SJoerg Wunsch 	size_t			bufsize;
12337486f03SJoerg Wunsch 	int                     save_using_locale;
12437486f03SJoerg Wunsch 
12537486f03SJoerg Wunsch 	save_using_locale = _time_using_locale;
12637486f03SJoerg Wunsch 	_time_using_locale = 0;
12737486f03SJoerg Wunsch 
12837486f03SJoerg Wunsch 	if (name == NULL)
12937486f03SJoerg Wunsch 		goto no_locale;
13037486f03SJoerg Wunsch 
13137486f03SJoerg Wunsch 	if (!strcmp(name, "C") || !strcmp(name, "POSIX"))
13237486f03SJoerg Wunsch 		return 0;
13337486f03SJoerg Wunsch 
13437486f03SJoerg Wunsch 	/*
13537486f03SJoerg Wunsch 	** If the locale name is the same as our cache, use the cache.
13637486f03SJoerg Wunsch 	*/
13737486f03SJoerg Wunsch 	lbuf = locale_buf;
13837486f03SJoerg Wunsch 	if (lbuf != NULL && strcmp(name, lbuf) == 0) {
139da3785efSDmitrij Tejblum 		set_from_buf(lbuf, num_lines);
14037486f03SJoerg Wunsch 		_time_using_locale = 1;
14137486f03SJoerg Wunsch 		return 0;
14237486f03SJoerg Wunsch 	}
14337486f03SJoerg Wunsch 	/*
14437486f03SJoerg Wunsch 	** Slurp the locale file into the cache.
14537486f03SJoerg Wunsch 	*/
14637486f03SJoerg Wunsch 	namesize = strlen(name) + 1;
14737486f03SJoerg Wunsch 
14837486f03SJoerg Wunsch 	if (!_PathLocale)
14937486f03SJoerg Wunsch 		goto no_locale;
15037486f03SJoerg Wunsch 	/* Range checking not needed, 'name' size is limited */
15137486f03SJoerg Wunsch 	strcpy(filename, _PathLocale);
15237486f03SJoerg Wunsch 	strcat(filename, "/");
15337486f03SJoerg Wunsch 	strcat(filename, name);
15437486f03SJoerg Wunsch 	strcat(filename, "/LC_TIME");
15537486f03SJoerg Wunsch 	fd = open(filename, O_RDONLY);
15637486f03SJoerg Wunsch 	if (fd < 0)
15737486f03SJoerg Wunsch 		goto no_locale;
15837486f03SJoerg Wunsch 	if (fstat(fd, &st) != 0)
15937486f03SJoerg Wunsch 		goto bad_locale;
16037486f03SJoerg Wunsch 	if (st.st_size <= 0)
16137486f03SJoerg Wunsch 		goto bad_locale;
16237486f03SJoerg Wunsch 	bufsize = namesize + st.st_size;
16337486f03SJoerg Wunsch 	locale_buf = NULL;
16437486f03SJoerg Wunsch 	lbuf = (lbuf == NULL || lbuf == locale_buf_C) ?
165e8420087SWarner Losh 		malloc(bufsize) : reallocf(lbuf, bufsize);
16637486f03SJoerg Wunsch 	if (lbuf == NULL)
16737486f03SJoerg Wunsch 		goto bad_locale;
16837486f03SJoerg Wunsch 	(void) strcpy(lbuf, name);
16937486f03SJoerg Wunsch 	p = lbuf + namesize;
17037486f03SJoerg Wunsch 	plim = p + st.st_size;
17137486f03SJoerg Wunsch 	if (read(fd, p, (size_t) st.st_size) != st.st_size)
17237486f03SJoerg Wunsch 		goto bad_lbuf;
17337486f03SJoerg Wunsch 	if (close(fd) != 0)
17437486f03SJoerg Wunsch 		goto bad_lbuf;
17537486f03SJoerg Wunsch 	/*
17637486f03SJoerg Wunsch 	** Parse the locale file into localebuf.
17737486f03SJoerg Wunsch 	*/
17837486f03SJoerg Wunsch 	if (plim[-1] != '\n')
17937486f03SJoerg Wunsch 		goto bad_lbuf;
180da3785efSDmitrij Tejblum 	num_lines = split_lines(p, plim);
181da3785efSDmitrij Tejblum 	if (num_lines >= LCTIME_SIZE_FULL)
182da3785efSDmitrij Tejblum 		num_lines = LCTIME_SIZE_FULL;
18311cd0d32SAndrey A. Chernov 	else if (num_lines >= LCTIME_SIZE_2)
18411cd0d32SAndrey A. Chernov 		num_lines = LCTIME_SIZE_2;
185da3785efSDmitrij Tejblum 	else if (num_lines >= LCTIME_SIZE_1)
186da3785efSDmitrij Tejblum 		num_lines = LCTIME_SIZE_1;
187da3785efSDmitrij Tejblum 	else
18837486f03SJoerg Wunsch 		goto reset_locale;
189da3785efSDmitrij Tejblum 	set_from_buf(lbuf, num_lines);
19037486f03SJoerg Wunsch 	/*
19137486f03SJoerg Wunsch 	** Record the successful parse in the cache.
19237486f03SJoerg Wunsch 	*/
19337486f03SJoerg Wunsch 	locale_buf = lbuf;
19437486f03SJoerg Wunsch 
19537486f03SJoerg Wunsch 	_time_using_locale = 1;
19637486f03SJoerg Wunsch 	return 0;
19737486f03SJoerg Wunsch 
19837486f03SJoerg Wunsch reset_locale:
19937486f03SJoerg Wunsch 	/*
20037486f03SJoerg Wunsch 	 * XXX - This may not be the correct thing to do in this case.
20137486f03SJoerg Wunsch 	 * setlocale() assumes that we left the old locale alone.
20237486f03SJoerg Wunsch 	 */
20337486f03SJoerg Wunsch 	locale_buf = locale_buf_C;
20437486f03SJoerg Wunsch 	_time_localebuf = _C_time_locale;
20537486f03SJoerg Wunsch 	save_using_locale = 0;
20637486f03SJoerg Wunsch bad_lbuf:
20737486f03SJoerg Wunsch 	free(lbuf);
20837486f03SJoerg Wunsch bad_locale:
20937486f03SJoerg Wunsch 	(void) close(fd);
21037486f03SJoerg Wunsch no_locale:
21137486f03SJoerg Wunsch 	_time_using_locale = save_using_locale;
21237486f03SJoerg Wunsch 	return -1;
21337486f03SJoerg Wunsch }
214da3785efSDmitrij Tejblum 
215da3785efSDmitrij Tejblum static int
216da3785efSDmitrij Tejblum split_lines(char *p, const char *plim)
217da3785efSDmitrij Tejblum {
218da3785efSDmitrij Tejblum 	int i;
219da3785efSDmitrij Tejblum 
220da3785efSDmitrij Tejblum 	for (i = 0; p < plim; i++) {
221da3785efSDmitrij Tejblum 		p = strchr(p, '\n');
222da3785efSDmitrij Tejblum 		*p++ = '\0';
223da3785efSDmitrij Tejblum 	}
224da3785efSDmitrij Tejblum 	return i;
225da3785efSDmitrij Tejblum }
226da3785efSDmitrij Tejblum 
227da3785efSDmitrij Tejblum static void
228da3785efSDmitrij Tejblum set_from_buf(const char *p, int num_lines)
229da3785efSDmitrij Tejblum {
230da3785efSDmitrij Tejblum 	const char **ap;
231da3785efSDmitrij Tejblum 	int i;
232da3785efSDmitrij Tejblum 
233da3785efSDmitrij Tejblum 	for (ap = (const char **) &_time_localebuf, i = 0;
234da3785efSDmitrij Tejblum 	    i < num_lines; ++ap, ++i)
235da3785efSDmitrij Tejblum 		*ap = p += strlen(p) + 1;
236da3785efSDmitrij Tejblum 	if (num_lines == LCTIME_SIZE_FULL)
237da3785efSDmitrij Tejblum 		return;
238da3785efSDmitrij Tejblum 	for (i = 0; i < 12; i++)
239da3785efSDmitrij Tejblum 		_time_localebuf.alt_month[i] = _time_localebuf.month[i];
240da3785efSDmitrij Tejblum }
241