xref: /freebsd/lib/libc/stdtime/timelocal.c (revision 18f3e1e4004b7454ec7fe0fa0f2800de4f4d4423)
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 
29d201fe46SDaniel Eischen #include "namespace.h"
3037486f03SJoerg Wunsch #include <sys/types.h>
3137486f03SJoerg Wunsch #include <sys/stat.h>
3237486f03SJoerg Wunsch #include <sys/syslimits.h>
3337486f03SJoerg Wunsch #include <fcntl.h>
3437486f03SJoerg Wunsch #include <locale.h>
35da3785efSDmitrij Tejblum #include <stddef.h>
3637486f03SJoerg Wunsch #include <stdlib.h>
3737486f03SJoerg Wunsch #include <string.h>
38d201fe46SDaniel Eischen #include <unistd.h>
39d201fe46SDaniel Eischen #include "un-namespace.h"
40d201fe46SDaniel Eischen 
4137486f03SJoerg Wunsch #include "setlocale.h"
4237486f03SJoerg Wunsch #include "timelocal.h"
4337486f03SJoerg Wunsch 
44da3785efSDmitrij Tejblum static int split_lines(char *, const char *);
45da3785efSDmitrij Tejblum static void set_from_buf(const char *, int);
46da3785efSDmitrij Tejblum 
4718f3e1e4SAlexey Zelkin static struct lc_time_T _time_localebuf;
4818f3e1e4SAlexey Zelkin static int _time_using_locale;
4937486f03SJoerg Wunsch 
50da3785efSDmitrij Tejblum #define	LCTIME_SIZE_FULL (sizeof(struct lc_time_T) / sizeof(char *))
51da3785efSDmitrij Tejblum #define	LCTIME_SIZE_1 \
52da3785efSDmitrij Tejblum 	(offsetof(struct lc_time_T, alt_month[0]) / sizeof(char *))
5311cd0d32SAndrey A. Chernov #define LCTIME_SIZE_2 \
54c63a4303SAndrey A. Chernov 	(offsetof(struct lc_time_T, Ef_fmt) / sizeof(char *))
55da3785efSDmitrij Tejblum 
5618f3e1e4SAlexey Zelkin static const struct lc_time_T	_C_time_locale = {
5737486f03SJoerg Wunsch 	{
5837486f03SJoerg Wunsch 		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
5937486f03SJoerg Wunsch 		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
6037486f03SJoerg Wunsch 	}, {
6137486f03SJoerg Wunsch 		"January", "February", "March", "April", "May", "June",
6237486f03SJoerg Wunsch 		"July", "August", "September", "October", "November", "December"
6337486f03SJoerg Wunsch 	}, {
6437486f03SJoerg Wunsch 		"Sun", "Mon", "Tue", "Wed",
6537486f03SJoerg Wunsch 		"Thu", "Fri", "Sat"
6637486f03SJoerg Wunsch 	}, {
6737486f03SJoerg Wunsch 		"Sunday", "Monday", "Tuesday", "Wednesday",
6837486f03SJoerg Wunsch 		"Thursday", "Friday", "Saturday"
6937486f03SJoerg Wunsch 	},
7037486f03SJoerg Wunsch 
7137486f03SJoerg Wunsch 	/* X_fmt */
7237486f03SJoerg Wunsch 	"%H:%M:%S",
7337486f03SJoerg Wunsch 
7437486f03SJoerg Wunsch 	/*
7537486f03SJoerg Wunsch 	** x_fmt
7637486f03SJoerg Wunsch 	** Since the C language standard calls for
7737486f03SJoerg Wunsch 	** "date, using locale's date format," anything goes.
7837486f03SJoerg Wunsch 	** Using just numbers (as here) makes Quakers happier;
7937486f03SJoerg Wunsch 	** it's also compatible with SVR4.
8037486f03SJoerg Wunsch 	*/
8137486f03SJoerg Wunsch 	"%m/%d/%y",
8237486f03SJoerg Wunsch 
8337486f03SJoerg Wunsch 	/*
8437486f03SJoerg Wunsch 	** c_fmt (ctime-compatible)
85aabf7d45SAndrey A. Chernov 	** Not used, just compatibility placeholder.
8637486f03SJoerg Wunsch 	*/
87aabf7d45SAndrey A. Chernov 	NULL,
8837486f03SJoerg Wunsch 
8937486f03SJoerg Wunsch 	/* am */
9037486f03SJoerg Wunsch 	"AM",
9137486f03SJoerg Wunsch 
9237486f03SJoerg Wunsch 	/* pm */
9337486f03SJoerg Wunsch 	"PM",
9437486f03SJoerg Wunsch 
9537486f03SJoerg Wunsch 	/* date_fmt */
96c63a4303SAndrey A. Chernov 	"%a %Ef %X %Z %Y",
97da3785efSDmitrij Tejblum 
98da3785efSDmitrij Tejblum 	{
99da3785efSDmitrij Tejblum 		"January", "February", "March", "April", "May", "June",
100da3785efSDmitrij Tejblum 		"July", "August", "September", "October", "November", "December"
10111cd0d32SAndrey A. Chernov 	},
10211cd0d32SAndrey A. Chernov 
103c63a4303SAndrey A. Chernov 	/* Ef_fmt
104c63a4303SAndrey A. Chernov 	** To determine short months / day order
10511cd0d32SAndrey A. Chernov 	*/
106c63a4303SAndrey A. Chernov 	"%b %e",
107c63a4303SAndrey A. Chernov 
108c63a4303SAndrey A. Chernov 	/* EF_fmt
109c63a4303SAndrey A. Chernov 	** To determine long months / day order
110c63a4303SAndrey A. Chernov 	*/
111c63a4303SAndrey A. Chernov 	"%B %e"
11237486f03SJoerg Wunsch };
11337486f03SJoerg Wunsch 
11418f3e1e4SAlexey Zelkin struct lc_time_T *
11518f3e1e4SAlexey Zelkin __get_current_time_locale(void) {
11618f3e1e4SAlexey Zelkin 	return (_time_using_locale
11718f3e1e4SAlexey Zelkin 		? &_time_localebuf
11818f3e1e4SAlexey Zelkin 		: (struct lc_time_T *)&_C_time_locale);
11918f3e1e4SAlexey Zelkin }
12037486f03SJoerg Wunsch 
12137486f03SJoerg Wunsch int
12237486f03SJoerg Wunsch __time_load_locale(const char *name)
12337486f03SJoerg Wunsch {
12437486f03SJoerg Wunsch 	static char *		locale_buf;
12537486f03SJoerg Wunsch 	static char		locale_buf_C[] = "C";
126da3785efSDmitrij Tejblum 	static int		num_lines;
12737486f03SJoerg Wunsch 
12837486f03SJoerg Wunsch 	int			fd;
12937486f03SJoerg Wunsch 	char *			lbuf;
13037486f03SJoerg Wunsch 	char *			p;
13137486f03SJoerg Wunsch 	const char *		plim;
13237486f03SJoerg Wunsch 	char                    filename[PATH_MAX];
13337486f03SJoerg Wunsch 	struct stat		st;
13437486f03SJoerg Wunsch 	size_t			namesize;
13537486f03SJoerg Wunsch 	size_t			bufsize;
13637486f03SJoerg Wunsch 	int                     save_using_locale;
13737486f03SJoerg Wunsch 
13837486f03SJoerg Wunsch 	save_using_locale = _time_using_locale;
13937486f03SJoerg Wunsch 	_time_using_locale = 0;
14037486f03SJoerg Wunsch 
14137486f03SJoerg Wunsch 	if (name == NULL)
14237486f03SJoerg Wunsch 		goto no_locale;
14337486f03SJoerg Wunsch 
14437486f03SJoerg Wunsch 	if (!strcmp(name, "C") || !strcmp(name, "POSIX"))
14537486f03SJoerg Wunsch 		return 0;
14637486f03SJoerg Wunsch 
14737486f03SJoerg Wunsch 	/*
14837486f03SJoerg Wunsch 	** If the locale name is the same as our cache, use the cache.
14937486f03SJoerg Wunsch 	*/
15037486f03SJoerg Wunsch 	lbuf = locale_buf;
15137486f03SJoerg Wunsch 	if (lbuf != NULL && strcmp(name, lbuf) == 0) {
152da3785efSDmitrij Tejblum 		set_from_buf(lbuf, num_lines);
15337486f03SJoerg Wunsch 		_time_using_locale = 1;
15437486f03SJoerg Wunsch 		return 0;
15537486f03SJoerg Wunsch 	}
15637486f03SJoerg Wunsch 	/*
15737486f03SJoerg Wunsch 	** Slurp the locale file into the cache.
15837486f03SJoerg Wunsch 	*/
15937486f03SJoerg Wunsch 	namesize = strlen(name) + 1;
16037486f03SJoerg Wunsch 
16137486f03SJoerg Wunsch 	if (!_PathLocale)
16237486f03SJoerg Wunsch 		goto no_locale;
16337486f03SJoerg Wunsch 	/* Range checking not needed, 'name' size is limited */
16437486f03SJoerg Wunsch 	strcpy(filename, _PathLocale);
16537486f03SJoerg Wunsch 	strcat(filename, "/");
16637486f03SJoerg Wunsch 	strcat(filename, name);
16737486f03SJoerg Wunsch 	strcat(filename, "/LC_TIME");
1689233c4d9SJason Evans 	fd = _open(filename, O_RDONLY);
16937486f03SJoerg Wunsch 	if (fd < 0)
17037486f03SJoerg Wunsch 		goto no_locale;
171d201fe46SDaniel Eischen 	if (_fstat(fd, &st) != 0)
17237486f03SJoerg Wunsch 		goto bad_locale;
17337486f03SJoerg Wunsch 	if (st.st_size <= 0)
17437486f03SJoerg Wunsch 		goto bad_locale;
17537486f03SJoerg Wunsch 	bufsize = namesize + st.st_size;
17637486f03SJoerg Wunsch 	locale_buf = NULL;
17737486f03SJoerg Wunsch 	lbuf = (lbuf == NULL || lbuf == locale_buf_C) ?
178e8420087SWarner Losh 		malloc(bufsize) : reallocf(lbuf, bufsize);
17937486f03SJoerg Wunsch 	if (lbuf == NULL)
18037486f03SJoerg Wunsch 		goto bad_locale;
18137486f03SJoerg Wunsch 	(void) strcpy(lbuf, name);
18237486f03SJoerg Wunsch 	p = lbuf + namesize;
18337486f03SJoerg Wunsch 	plim = p + st.st_size;
1849233c4d9SJason Evans 	if (_read(fd, p, (size_t) st.st_size) != st.st_size)
18537486f03SJoerg Wunsch 		goto bad_lbuf;
1869233c4d9SJason Evans 	if (_close(fd) != 0)
18737486f03SJoerg Wunsch 		goto bad_lbuf;
18837486f03SJoerg Wunsch 	/*
18937486f03SJoerg Wunsch 	** Parse the locale file into localebuf.
19037486f03SJoerg Wunsch 	*/
19137486f03SJoerg Wunsch 	if (plim[-1] != '\n')
19237486f03SJoerg Wunsch 		goto bad_lbuf;
193da3785efSDmitrij Tejblum 	num_lines = split_lines(p, plim);
194da3785efSDmitrij Tejblum 	if (num_lines >= LCTIME_SIZE_FULL)
195da3785efSDmitrij Tejblum 		num_lines = LCTIME_SIZE_FULL;
19611cd0d32SAndrey A. Chernov 	else if (num_lines >= LCTIME_SIZE_2)
19711cd0d32SAndrey A. Chernov 		num_lines = LCTIME_SIZE_2;
198da3785efSDmitrij Tejblum 	else if (num_lines >= LCTIME_SIZE_1)
199da3785efSDmitrij Tejblum 		num_lines = LCTIME_SIZE_1;
200da3785efSDmitrij Tejblum 	else
20137486f03SJoerg Wunsch 		goto reset_locale;
202da3785efSDmitrij Tejblum 	set_from_buf(lbuf, num_lines);
20337486f03SJoerg Wunsch 	/*
20437486f03SJoerg Wunsch 	** Record the successful parse in the cache.
20537486f03SJoerg Wunsch 	*/
20637486f03SJoerg Wunsch 	locale_buf = lbuf;
20737486f03SJoerg Wunsch 
20837486f03SJoerg Wunsch 	_time_using_locale = 1;
20937486f03SJoerg Wunsch 	return 0;
21037486f03SJoerg Wunsch 
21137486f03SJoerg Wunsch reset_locale:
21237486f03SJoerg Wunsch 	/*
21337486f03SJoerg Wunsch 	 * XXX - This may not be the correct thing to do in this case.
21437486f03SJoerg Wunsch 	 * setlocale() assumes that we left the old locale alone.
21537486f03SJoerg Wunsch 	 */
21637486f03SJoerg Wunsch 	locale_buf = locale_buf_C;
21737486f03SJoerg Wunsch 	_time_localebuf = _C_time_locale;
21837486f03SJoerg Wunsch 	save_using_locale = 0;
21937486f03SJoerg Wunsch bad_lbuf:
22037486f03SJoerg Wunsch 	free(lbuf);
22137486f03SJoerg Wunsch bad_locale:
2229233c4d9SJason Evans 	(void)_close(fd);
22337486f03SJoerg Wunsch no_locale:
22437486f03SJoerg Wunsch 	_time_using_locale = save_using_locale;
22537486f03SJoerg Wunsch 	return -1;
22637486f03SJoerg Wunsch }
227da3785efSDmitrij Tejblum 
228da3785efSDmitrij Tejblum static int
229da3785efSDmitrij Tejblum split_lines(char *p, const char *plim)
230da3785efSDmitrij Tejblum {
231da3785efSDmitrij Tejblum 	int i;
232da3785efSDmitrij Tejblum 
233da3785efSDmitrij Tejblum 	for (i = 0; p < plim; i++) {
234da3785efSDmitrij Tejblum 		p = strchr(p, '\n');
235da3785efSDmitrij Tejblum 		*p++ = '\0';
236da3785efSDmitrij Tejblum 	}
237da3785efSDmitrij Tejblum 	return i;
238da3785efSDmitrij Tejblum }
239da3785efSDmitrij Tejblum 
240da3785efSDmitrij Tejblum static void
241da3785efSDmitrij Tejblum set_from_buf(const char *p, int num_lines)
242da3785efSDmitrij Tejblum {
243da3785efSDmitrij Tejblum 	const char **ap;
244da3785efSDmitrij Tejblum 	int i;
245da3785efSDmitrij Tejblum 
246da3785efSDmitrij Tejblum 	for (ap = (const char **) &_time_localebuf, i = 0;
247da3785efSDmitrij Tejblum 	    i < num_lines; ++ap, ++i)
248da3785efSDmitrij Tejblum 		*ap = p += strlen(p) + 1;
249849c64f5SAndrey A. Chernov 	if (num_lines >= LCTIME_SIZE_2)
250da3785efSDmitrij Tejblum 		return;
251da3785efSDmitrij Tejblum 	for (i = 0; i < 12; i++)
252da3785efSDmitrij Tejblum 		_time_localebuf.alt_month[i] = _time_localebuf.month[i];
253da3785efSDmitrij Tejblum }
254