timelocal.c revision 53940
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 53940 1999-11-30 07:33:37Z 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 \
5053940Sache	(offsetof(struct lc_time_T, Ex_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	*/
8553940Sache	"%a %Ex %X %Y",
8628021Sjoerg
8728021Sjoerg	/* am */
8828021Sjoerg	"AM",
8928021Sjoerg
9028021Sjoerg	/* pm */
9128021Sjoerg	"PM",
9228021Sjoerg
9328021Sjoerg	/* date_fmt */
9453940Sache	"%a %Ex %X %Z %Y",
9551186Sdt
9651186Sdt	{
9751186Sdt		"January", "February", "March", "April", "May", "June",
9851186Sdt		"July", "August", "September", "October", "November", "December"
9953940Sache	},
10053940Sache
10153940Sache	/* Ex_fmt
10253940Sache	** To determine months / day order
10353940Sache	*/
10453940Sache	"%b %e"
10528021Sjoerg};
10628021Sjoerg
10728021Sjoerg
10828021Sjoergint
10928021Sjoerg__time_load_locale(const char *name)
11028021Sjoerg{
11128021Sjoerg	static char *		locale_buf;
11228021Sjoerg	static char		locale_buf_C[] = "C";
11351186Sdt	static int		num_lines;
11428021Sjoerg
11528021Sjoerg	int			fd;
11628021Sjoerg	char *			lbuf;
11728021Sjoerg	char *			p;
11828021Sjoerg	const char *		plim;
11928021Sjoerg	char                    filename[PATH_MAX];
12028021Sjoerg	struct stat		st;
12128021Sjoerg	size_t			namesize;
12228021Sjoerg	size_t			bufsize;
12328021Sjoerg	int                     save_using_locale;
12428021Sjoerg
12528021Sjoerg	save_using_locale = _time_using_locale;
12628021Sjoerg	_time_using_locale = 0;
12728021Sjoerg
12828021Sjoerg	if (name == NULL)
12928021Sjoerg		goto no_locale;
13028021Sjoerg
13128021Sjoerg	if (!strcmp(name, "C") || !strcmp(name, "POSIX"))
13228021Sjoerg		return 0;
13328021Sjoerg
13428021Sjoerg	/*
13528021Sjoerg	** If the locale name is the same as our cache, use the cache.
13628021Sjoerg	*/
13728021Sjoerg	lbuf = locale_buf;
13828021Sjoerg	if (lbuf != NULL && strcmp(name, lbuf) == 0) {
13951186Sdt		set_from_buf(lbuf, num_lines);
14028021Sjoerg		_time_using_locale = 1;
14128021Sjoerg		return 0;
14228021Sjoerg	}
14328021Sjoerg	/*
14428021Sjoerg	** Slurp the locale file into the cache.
14528021Sjoerg	*/
14628021Sjoerg	namesize = strlen(name) + 1;
14728021Sjoerg
14828021Sjoerg	if (!_PathLocale)
14928021Sjoerg		goto no_locale;
15028021Sjoerg	/* Range checking not needed, 'name' size is limited */
15128021Sjoerg	strcpy(filename, _PathLocale);
15228021Sjoerg	strcat(filename, "/");
15328021Sjoerg	strcat(filename, name);
15428021Sjoerg	strcat(filename, "/LC_TIME");
15528021Sjoerg	fd = open(filename, O_RDONLY);
15628021Sjoerg	if (fd < 0)
15728021Sjoerg		goto no_locale;
15828021Sjoerg	if (fstat(fd, &st) != 0)
15928021Sjoerg		goto bad_locale;
16028021Sjoerg	if (st.st_size <= 0)
16128021Sjoerg		goto bad_locale;
16228021Sjoerg	bufsize = namesize + st.st_size;
16328021Sjoerg	locale_buf = NULL;
16428021Sjoerg	lbuf = (lbuf == NULL || lbuf == locale_buf_C) ?
16539327Simp		malloc(bufsize) : reallocf(lbuf, bufsize);
16628021Sjoerg	if (lbuf == NULL)
16728021Sjoerg		goto bad_locale;
16828021Sjoerg	(void) strcpy(lbuf, name);
16928021Sjoerg	p = lbuf + namesize;
17028021Sjoerg	plim = p + st.st_size;
17128021Sjoerg	if (read(fd, p, (size_t) st.st_size) != st.st_size)
17228021Sjoerg		goto bad_lbuf;
17328021Sjoerg	if (close(fd) != 0)
17428021Sjoerg		goto bad_lbuf;
17528021Sjoerg	/*
17628021Sjoerg	** Parse the locale file into localebuf.
17728021Sjoerg	*/
17828021Sjoerg	if (plim[-1] != '\n')
17928021Sjoerg		goto bad_lbuf;
18051186Sdt	num_lines = split_lines(p, plim);
18151186Sdt	if (num_lines >= LCTIME_SIZE_FULL)
18251186Sdt		num_lines = LCTIME_SIZE_FULL;
18353940Sache	else if (num_lines >= LCTIME_SIZE_2)
18453940Sache		num_lines = LCTIME_SIZE_2;
18551186Sdt	else if (num_lines >= LCTIME_SIZE_1)
18651186Sdt		num_lines = LCTIME_SIZE_1;
18751186Sdt	else
18851186Sdt		goto reset_locale;
18951186Sdt	set_from_buf(lbuf, num_lines);
19028021Sjoerg	/*
19128021Sjoerg	** Record the successful parse in the cache.
19228021Sjoerg	*/
19328021Sjoerg	locale_buf = lbuf;
19428021Sjoerg
19528021Sjoerg	_time_using_locale = 1;
19628021Sjoerg	return 0;
19728021Sjoerg
19828021Sjoergreset_locale:
19928021Sjoerg	/*
20028021Sjoerg	 * XXX - This may not be the correct thing to do in this case.
20128021Sjoerg	 * setlocale() assumes that we left the old locale alone.
20228021Sjoerg	 */
20328021Sjoerg	locale_buf = locale_buf_C;
20428021Sjoerg	_time_localebuf = _C_time_locale;
20528021Sjoerg	save_using_locale = 0;
20628021Sjoergbad_lbuf:
20728021Sjoerg	free(lbuf);
20828021Sjoergbad_locale:
20928021Sjoerg	(void) close(fd);
21028021Sjoergno_locale:
21128021Sjoerg	_time_using_locale = save_using_locale;
21228021Sjoerg	return -1;
21328021Sjoerg}
21451186Sdt
21551186Sdtstatic int
21651186Sdtsplit_lines(char *p, const char *plim)
21751186Sdt{
21851186Sdt	int i;
21951186Sdt
22051186Sdt	for (i = 0; p < plim; i++) {
22151186Sdt		p = strchr(p, '\n');
22251186Sdt		*p++ = '\0';
22351186Sdt	}
22451186Sdt	return i;
22551186Sdt}
22651186Sdt
22751186Sdtstatic void
22851186Sdtset_from_buf(const char *p, int num_lines)
22951186Sdt{
23051186Sdt	const char **ap;
23151186Sdt	int i;
23251186Sdt
23351186Sdt	for (ap = (const char **) &_time_localebuf, i = 0;
23451186Sdt	    i < num_lines; ++ap, ++i)
23551186Sdt		*ap = p += strlen(p) + 1;
23651186Sdt	if (num_lines == LCTIME_SIZE_FULL)
23751186Sdt		return;
23851186Sdt	for (i = 0; i < 12; i++)
23951186Sdt		_time_localebuf.alt_month[i] = _time_localebuf.month[i];
24051186Sdt}
241