timelocal.c revision 53960
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 53960 1999-11-30 19:24:07Z ache $
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 *))
4953940Sache#define LCTIME_SIZE_2 \
5053960Sache	(offsetof(struct lc_time_T, Ef_fmt) / sizeof(char *))
5151186Sdt
5228021Sjoergconst struct lc_time_T	_C_time_locale = {
5328021Sjoerg	{
5428021Sjoerg		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
5528021Sjoerg		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
5628021Sjoerg	}, {
5728021Sjoerg		"January", "February", "March", "April", "May", "June",
5828021Sjoerg		"July", "August", "September", "October", "November", "December"
5928021Sjoerg	}, {
6028021Sjoerg		"Sun", "Mon", "Tue", "Wed",
6128021Sjoerg		"Thu", "Fri", "Sat"
6228021Sjoerg	}, {
6328021Sjoerg		"Sunday", "Monday", "Tuesday", "Wednesday",
6428021Sjoerg		"Thursday", "Friday", "Saturday"
6528021Sjoerg	},
6628021Sjoerg
6728021Sjoerg	/* X_fmt */
6828021Sjoerg	"%H:%M:%S",
6928021Sjoerg
7028021Sjoerg	/*
7128021Sjoerg	** x_fmt
7228021Sjoerg	** Since the C language standard calls for
7328021Sjoerg	** "date, using locale's date format," anything goes.
7428021Sjoerg	** Using just numbers (as here) makes Quakers happier;
7528021Sjoerg	** it's also compatible with SVR4.
7628021Sjoerg	*/
7728021Sjoerg	"%m/%d/%y",
7828021Sjoerg
7928021Sjoerg	/*
8028021Sjoerg	** c_fmt (ctime-compatible)
8128021Sjoerg	** Note that
8228021Sjoerg	**	"%a %b %d %H:%M:%S %Y"
8328021Sjoerg	** is used by Solaris 2.3.
8428021Sjoerg	*/
8553960Sache	"%a %Ef %X %Y",
8628021Sjoerg
8728021Sjoerg	/* am */
8828021Sjoerg	"AM",
8928021Sjoerg
9028021Sjoerg	/* pm */
9128021Sjoerg	"PM",
9228021Sjoerg
9328021Sjoerg	/* date_fmt */
9453960Sache	"%a %Ef %X %Z %Y",
9551186Sdt
9651186Sdt	{
9751186Sdt		"January", "February", "March", "April", "May", "June",
9851186Sdt		"July", "August", "September", "October", "November", "December"
9953940Sache	},
10053940Sache
10153960Sache	/* Ef_fmt
10253960Sache	** To determine short months / day order
10353940Sache	*/
10453960Sache	"%b %e",
10553960Sache
10653960Sache	/* EF_fmt
10753960Sache	** To determine long months / day order
10853960Sache	*/
10953960Sache	"%B %e"
11028021Sjoerg};
11128021Sjoerg
11228021Sjoerg
11328021Sjoergint
11428021Sjoerg__time_load_locale(const char *name)
11528021Sjoerg{
11628021Sjoerg	static char *		locale_buf;
11728021Sjoerg	static char		locale_buf_C[] = "C";
11851186Sdt	static int		num_lines;
11928021Sjoerg
12028021Sjoerg	int			fd;
12128021Sjoerg	char *			lbuf;
12228021Sjoerg	char *			p;
12328021Sjoerg	const char *		plim;
12428021Sjoerg	char                    filename[PATH_MAX];
12528021Sjoerg	struct stat		st;
12628021Sjoerg	size_t			namesize;
12728021Sjoerg	size_t			bufsize;
12828021Sjoerg	int                     save_using_locale;
12928021Sjoerg
13028021Sjoerg	save_using_locale = _time_using_locale;
13128021Sjoerg	_time_using_locale = 0;
13228021Sjoerg
13328021Sjoerg	if (name == NULL)
13428021Sjoerg		goto no_locale;
13528021Sjoerg
13628021Sjoerg	if (!strcmp(name, "C") || !strcmp(name, "POSIX"))
13728021Sjoerg		return 0;
13828021Sjoerg
13928021Sjoerg	/*
14028021Sjoerg	** If the locale name is the same as our cache, use the cache.
14128021Sjoerg	*/
14228021Sjoerg	lbuf = locale_buf;
14328021Sjoerg	if (lbuf != NULL && strcmp(name, lbuf) == 0) {
14451186Sdt		set_from_buf(lbuf, num_lines);
14528021Sjoerg		_time_using_locale = 1;
14628021Sjoerg		return 0;
14728021Sjoerg	}
14828021Sjoerg	/*
14928021Sjoerg	** Slurp the locale file into the cache.
15028021Sjoerg	*/
15128021Sjoerg	namesize = strlen(name) + 1;
15228021Sjoerg
15328021Sjoerg	if (!_PathLocale)
15428021Sjoerg		goto no_locale;
15528021Sjoerg	/* Range checking not needed, 'name' size is limited */
15628021Sjoerg	strcpy(filename, _PathLocale);
15728021Sjoerg	strcat(filename, "/");
15828021Sjoerg	strcat(filename, name);
15928021Sjoerg	strcat(filename, "/LC_TIME");
16028021Sjoerg	fd = open(filename, O_RDONLY);
16128021Sjoerg	if (fd < 0)
16228021Sjoerg		goto no_locale;
16328021Sjoerg	if (fstat(fd, &st) != 0)
16428021Sjoerg		goto bad_locale;
16528021Sjoerg	if (st.st_size <= 0)
16628021Sjoerg		goto bad_locale;
16728021Sjoerg	bufsize = namesize + st.st_size;
16828021Sjoerg	locale_buf = NULL;
16928021Sjoerg	lbuf = (lbuf == NULL || lbuf == locale_buf_C) ?
17039327Simp		malloc(bufsize) : reallocf(lbuf, bufsize);
17128021Sjoerg	if (lbuf == NULL)
17228021Sjoerg		goto bad_locale;
17328021Sjoerg	(void) strcpy(lbuf, name);
17428021Sjoerg	p = lbuf + namesize;
17528021Sjoerg	plim = p + st.st_size;
17628021Sjoerg	if (read(fd, p, (size_t) st.st_size) != st.st_size)
17728021Sjoerg		goto bad_lbuf;
17828021Sjoerg	if (close(fd) != 0)
17928021Sjoerg		goto bad_lbuf;
18028021Sjoerg	/*
18128021Sjoerg	** Parse the locale file into localebuf.
18228021Sjoerg	*/
18328021Sjoerg	if (plim[-1] != '\n')
18428021Sjoerg		goto bad_lbuf;
18551186Sdt	num_lines = split_lines(p, plim);
18651186Sdt	if (num_lines >= LCTIME_SIZE_FULL)
18751186Sdt		num_lines = LCTIME_SIZE_FULL;
18853940Sache	else if (num_lines >= LCTIME_SIZE_2)
18953940Sache		num_lines = LCTIME_SIZE_2;
19051186Sdt	else if (num_lines >= LCTIME_SIZE_1)
19151186Sdt		num_lines = LCTIME_SIZE_1;
19251186Sdt	else
19351186Sdt		goto reset_locale;
19451186Sdt	set_from_buf(lbuf, num_lines);
19528021Sjoerg	/*
19628021Sjoerg	** Record the successful parse in the cache.
19728021Sjoerg	*/
19828021Sjoerg	locale_buf = lbuf;
19928021Sjoerg
20028021Sjoerg	_time_using_locale = 1;
20128021Sjoerg	return 0;
20228021Sjoerg
20328021Sjoergreset_locale:
20428021Sjoerg	/*
20528021Sjoerg	 * XXX - This may not be the correct thing to do in this case.
20628021Sjoerg	 * setlocale() assumes that we left the old locale alone.
20728021Sjoerg	 */
20828021Sjoerg	locale_buf = locale_buf_C;
20928021Sjoerg	_time_localebuf = _C_time_locale;
21028021Sjoerg	save_using_locale = 0;
21128021Sjoergbad_lbuf:
21228021Sjoerg	free(lbuf);
21328021Sjoergbad_locale:
21428021Sjoerg	(void) close(fd);
21528021Sjoergno_locale:
21628021Sjoerg	_time_using_locale = save_using_locale;
21728021Sjoerg	return -1;
21828021Sjoerg}
21951186Sdt
22051186Sdtstatic int
22151186Sdtsplit_lines(char *p, const char *plim)
22251186Sdt{
22351186Sdt	int i;
22451186Sdt
22551186Sdt	for (i = 0; p < plim; i++) {
22651186Sdt		p = strchr(p, '\n');
22751186Sdt		*p++ = '\0';
22851186Sdt	}
22951186Sdt	return i;
23051186Sdt}
23151186Sdt
23251186Sdtstatic void
23351186Sdtset_from_buf(const char *p, int num_lines)
23451186Sdt{
23551186Sdt	const char **ap;
23651186Sdt	int i;
23751186Sdt
23851186Sdt	for (ap = (const char **) &_time_localebuf, i = 0;
23951186Sdt	    i < num_lines; ++ap, ++i)
24051186Sdt		*ap = p += strlen(p) + 1;
24151186Sdt	if (num_lines == LCTIME_SIZE_FULL)
24251186Sdt		return;
24351186Sdt	for (i = 0; i < 12; i++)
24451186Sdt		_time_localebuf.alt_month[i] = _time_localebuf.month[i];
24551186Sdt}
246