timelocal.c revision 51186
128021Sjoerg/*-
228021Sjoerg * Copyright (c) 1997 FreeBSD Inc.
328021Sjoerg * All rights reserved.
428021Sjoerg *
528021Sjoerg * Redistribution and use in source and binary forms, with or without
628021Sjoerg * modification, are permitted provided that the following conditions
728021Sjoerg * are met:
828021Sjoerg * 1. Redistributions of source code must retain the above copyright
928021Sjoerg *    notice, this list of conditions and the following disclaimer.
1028021Sjoerg * 2. Redistributions in binary form must reproduce the above copyright
1128021Sjoerg *    notice, this list of conditions and the following disclaimer in the
1228021Sjoerg *    documentation and/or other materials provided with the distribution.
1328021Sjoerg *
1428021Sjoerg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1528021Sjoerg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1628021Sjoerg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1728021Sjoerg * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1828021Sjoerg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1928021Sjoerg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2028021Sjoerg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2128021Sjoerg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2228021Sjoerg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2328021Sjoerg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2428021Sjoerg * SUCH DAMAGE.
2528021Sjoerg *
2650476Speter * $FreeBSD: head/lib/libc/stdtime/timelocal.c 51186 1999-09-11 21:35:21Z dt $
2728021Sjoerg */
2828021Sjoerg
2928021Sjoerg#include <sys/types.h>
3028021Sjoerg#include <sys/stat.h>
3128021Sjoerg#include <sys/syslimits.h>
3228021Sjoerg#include <fcntl.h>
3328021Sjoerg#include <locale.h>
3451186Sdt#include <stddef.h>
3528021Sjoerg#include <stdlib.h>
3628021Sjoerg#include <string.h>
3728021Sjoerg#include "setlocale.h"
3828021Sjoerg#include "timelocal.h"
3928021Sjoerg
4051186Sdtstatic int split_lines(char *, const char *);
4151186Sdtstatic void set_from_buf(const char *, int);
4251186Sdt
4328021Sjoergstruct lc_time_T _time_localebuf;
4428021Sjoergint _time_using_locale;
4528021Sjoerg
4651186Sdt#define	LCTIME_SIZE_FULL (sizeof(struct lc_time_T) / sizeof(char *))
4751186Sdt#define	LCTIME_SIZE_1 \
4851186Sdt	(offsetof(struct lc_time_T, alt_month[0]) / sizeof(char *))
4951186Sdt
5028021Sjoergconst struct lc_time_T	_C_time_locale = {
5128021Sjoerg	{
5228021Sjoerg		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
5328021Sjoerg		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
5428021Sjoerg	}, {
5528021Sjoerg		"January", "February", "March", "April", "May", "June",
5628021Sjoerg		"July", "August", "September", "October", "November", "December"
5728021Sjoerg	}, {
5828021Sjoerg		"Sun", "Mon", "Tue", "Wed",
5928021Sjoerg		"Thu", "Fri", "Sat"
6028021Sjoerg	}, {
6128021Sjoerg		"Sunday", "Monday", "Tuesday", "Wednesday",
6228021Sjoerg		"Thursday", "Friday", "Saturday"
6328021Sjoerg	},
6428021Sjoerg
6528021Sjoerg	/* X_fmt */
6628021Sjoerg	"%H:%M:%S",
6728021Sjoerg
6828021Sjoerg	/*
6928021Sjoerg	** x_fmt
7028021Sjoerg	** Since the C language standard calls for
7128021Sjoerg	** "date, using locale's date format," anything goes.
7228021Sjoerg	** Using just numbers (as here) makes Quakers happier;
7328021Sjoerg	** it's also compatible with SVR4.
7428021Sjoerg	*/
7528021Sjoerg	"%m/%d/%y",
7628021Sjoerg
7728021Sjoerg	/*
7828021Sjoerg	** c_fmt (ctime-compatible)
7928021Sjoerg	** Note that
8028021Sjoerg	**	"%a %b %d %H:%M:%S %Y"
8128021Sjoerg	** is used by Solaris 2.3.
8228021Sjoerg	*/
8328021Sjoerg	"%a %b %e %X %Y",
8428021Sjoerg
8528021Sjoerg	/* am */
8628021Sjoerg	"AM",
8728021Sjoerg
8828021Sjoerg	/* pm */
8928021Sjoerg	"PM",
9028021Sjoerg
9128021Sjoerg	/* date_fmt */
9251186Sdt	"%a %b %e %X %Z %Y",
9351186Sdt
9451186Sdt	{
9551186Sdt		"January", "February", "March", "April", "May", "June",
9651186Sdt		"July", "August", "September", "October", "November", "December"
9751186Sdt	}
9828021Sjoerg};
9928021Sjoerg
10028021Sjoerg
10128021Sjoergint
10228021Sjoerg__time_load_locale(const char *name)
10328021Sjoerg{
10428021Sjoerg	static char *		locale_buf;
10528021Sjoerg	static char		locale_buf_C[] = "C";
10651186Sdt	static int		num_lines;
10728021Sjoerg
10828021Sjoerg	int			fd;
10928021Sjoerg	char *			lbuf;
11028021Sjoerg	char *			p;
11128021Sjoerg	const char *		plim;
11228021Sjoerg	char                    filename[PATH_MAX];
11328021Sjoerg	struct stat		st;
11428021Sjoerg	size_t			namesize;
11528021Sjoerg	size_t			bufsize;
11628021Sjoerg	int                     save_using_locale;
11728021Sjoerg
11828021Sjoerg	save_using_locale = _time_using_locale;
11928021Sjoerg	_time_using_locale = 0;
12028021Sjoerg
12128021Sjoerg	if (name == NULL)
12228021Sjoerg		goto no_locale;
12328021Sjoerg
12428021Sjoerg	if (!strcmp(name, "C") || !strcmp(name, "POSIX"))
12528021Sjoerg		return 0;
12628021Sjoerg
12728021Sjoerg	/*
12828021Sjoerg	** If the locale name is the same as our cache, use the cache.
12928021Sjoerg	*/
13028021Sjoerg	lbuf = locale_buf;
13128021Sjoerg	if (lbuf != NULL && strcmp(name, lbuf) == 0) {
13251186Sdt		set_from_buf(lbuf, num_lines);
13328021Sjoerg		_time_using_locale = 1;
13428021Sjoerg		return 0;
13528021Sjoerg	}
13628021Sjoerg	/*
13728021Sjoerg	** Slurp the locale file into the cache.
13828021Sjoerg	*/
13928021Sjoerg	namesize = strlen(name) + 1;
14028021Sjoerg
14128021Sjoerg	if (!_PathLocale)
14228021Sjoerg		goto no_locale;
14328021Sjoerg	/* Range checking not needed, 'name' size is limited */
14428021Sjoerg	strcpy(filename, _PathLocale);
14528021Sjoerg	strcat(filename, "/");
14628021Sjoerg	strcat(filename, name);
14728021Sjoerg	strcat(filename, "/LC_TIME");
14828021Sjoerg	fd = open(filename, O_RDONLY);
14928021Sjoerg	if (fd < 0)
15028021Sjoerg		goto no_locale;
15128021Sjoerg	if (fstat(fd, &st) != 0)
15228021Sjoerg		goto bad_locale;
15328021Sjoerg	if (st.st_size <= 0)
15428021Sjoerg		goto bad_locale;
15528021Sjoerg	bufsize = namesize + st.st_size;
15628021Sjoerg	locale_buf = NULL;
15728021Sjoerg	lbuf = (lbuf == NULL || lbuf == locale_buf_C) ?
15839327Simp		malloc(bufsize) : reallocf(lbuf, bufsize);
15928021Sjoerg	if (lbuf == NULL)
16028021Sjoerg		goto bad_locale;
16128021Sjoerg	(void) strcpy(lbuf, name);
16228021Sjoerg	p = lbuf + namesize;
16328021Sjoerg	plim = p + st.st_size;
16428021Sjoerg	if (read(fd, p, (size_t) st.st_size) != st.st_size)
16528021Sjoerg		goto bad_lbuf;
16628021Sjoerg	if (close(fd) != 0)
16728021Sjoerg		goto bad_lbuf;
16828021Sjoerg	/*
16928021Sjoerg	** Parse the locale file into localebuf.
17028021Sjoerg	*/
17128021Sjoerg	if (plim[-1] != '\n')
17228021Sjoerg		goto bad_lbuf;
17351186Sdt	num_lines = split_lines(p, plim);
17451186Sdt	if (num_lines >= LCTIME_SIZE_FULL)
17551186Sdt		num_lines = LCTIME_SIZE_FULL;
17651186Sdt	else if (num_lines >= LCTIME_SIZE_1)
17751186Sdt		num_lines = LCTIME_SIZE_1;
17851186Sdt	else
17951186Sdt		goto reset_locale;
18051186Sdt	set_from_buf(lbuf, num_lines);
18128021Sjoerg	/*
18228021Sjoerg	** Record the successful parse in the cache.
18328021Sjoerg	*/
18428021Sjoerg	locale_buf = lbuf;
18528021Sjoerg
18628021Sjoerg	_time_using_locale = 1;
18728021Sjoerg	return 0;
18828021Sjoerg
18928021Sjoergreset_locale:
19028021Sjoerg	/*
19128021Sjoerg	 * XXX - This may not be the correct thing to do in this case.
19228021Sjoerg	 * setlocale() assumes that we left the old locale alone.
19328021Sjoerg	 */
19428021Sjoerg	locale_buf = locale_buf_C;
19528021Sjoerg	_time_localebuf = _C_time_locale;
19628021Sjoerg	save_using_locale = 0;
19728021Sjoergbad_lbuf:
19828021Sjoerg	free(lbuf);
19928021Sjoergbad_locale:
20028021Sjoerg	(void) close(fd);
20128021Sjoergno_locale:
20228021Sjoerg	_time_using_locale = save_using_locale;
20328021Sjoerg	return -1;
20428021Sjoerg}
20551186Sdt
20651186Sdtstatic int
20751186Sdtsplit_lines(char *p, const char *plim)
20851186Sdt{
20951186Sdt	int i;
21051186Sdt
21151186Sdt	for (i = 0; p < plim; i++) {
21251186Sdt		p = strchr(p, '\n');
21351186Sdt		*p++ = '\0';
21451186Sdt	}
21551186Sdt	return i;
21651186Sdt}
21751186Sdt
21851186Sdtstatic void
21951186Sdtset_from_buf(const char *p, int num_lines)
22051186Sdt{
22151186Sdt	const char **ap;
22251186Sdt	int i;
22351186Sdt
22451186Sdt	for (ap = (const char **) &_time_localebuf, i = 0;
22551186Sdt	    i < num_lines; ++ap, ++i)
22651186Sdt		*ap = p += strlen(p) + 1;
22751186Sdt	if (num_lines == LCTIME_SIZE_FULL)
22851186Sdt		return;
22951186Sdt	for (i = 0; i < 12; i++)
23051186Sdt		_time_localebuf.alt_month[i] = _time_localebuf.month[i];
23151186Sdt}
232