ldpart.c revision 101307
11541Srgrimes/*
21541Srgrimes * Copyright (c) 2000, 2001 Alexey Zelkin <phantom@FreeBSD.org>
31541Srgrimes * All rights reserved.
41541Srgrimes *
51541Srgrimes * Redistribution and use in source and binary forms, with or without
61541Srgrimes * modification, are permitted provided that the following conditions
71541Srgrimes * are met:
81541Srgrimes * 1. Redistributions of source code must retain the above copyright
91541Srgrimes *    notice, this list of conditions and the following disclaimer.
101541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111541Srgrimes *    notice, this list of conditions and the following disclaimer in the
121541Srgrimes *    documentation and/or other materials provided with the distribution.
131541Srgrimes *
141541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
151541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
161541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
171541Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
181541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
191541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
201541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
211541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
221541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
231541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
241541Srgrimes * SUCH DAMAGE.
251541Srgrimes */
261541Srgrimes
271541Srgrimes#include <sys/cdefs.h>
281541Srgrimes__FBSDID("$FreeBSD: head/lib/libc/locale/ldpart.c 101307 2002-08-04 09:37:28Z ache $");
291541Srgrimes
301541Srgrimes#include "namespace.h"
311541Srgrimes#include <sys/types.h>
321541Srgrimes#include <sys/stat.h>
331541Srgrimes#include <sys/syslimits.h>
341541Srgrimes#include <errno.h>
351541Srgrimes#include <fcntl.h>
361541Srgrimes#include <stdlib.h>
371541Srgrimes#include <string.h>
381541Srgrimes#include <unistd.h>
3940435Speter#include "un-namespace.h"
401541Srgrimes
411541Srgrimes#include "setlocale.h"
421541Srgrimes#include "ldpart.h"
431541Srgrimes
442112Swollmanstatic int split_lines(char *, const char *);
452946Swollmanstatic void set_from_buf(const char *, int, const char **);
461541Srgrimes
4738869Sbdeint
481541Srgrimes__part_load_locale(const char *name,
491541Srgrimes		int *using_locale,
5029653Sdyson		char *locale_buf,
511541Srgrimes		const char *category_filename,
5212913Sphk		int locale_buf_size_max,
5312577Sbde		int locale_buf_size_min,
5410653Sdg		const char **dst_localebuf)
5510358Sjulian{
5610358Sjulian	static char		locale_buf_C[] = "C";
5730354Sphk	static int		num_lines;
5830354Sphk	int                     saverr;
5910358Sjulian	int			 fd;
601541Srgrimes	char			*lbuf;
611541Srgrimes	char			*p;
621541Srgrimes	const char 		*plim;
631541Srgrimes	char                     filename[PATH_MAX];
641541Srgrimes	struct stat		 st;
651541Srgrimes	size_t			 namesize;
661541Srgrimes	size_t			 bufsize;
671541Srgrimes	int                      save_using_locale;
6833181Seivind
692946Swollman	save_using_locale = *using_locale;
7040435Speter	*using_locale = 0;
712946Swollman
722946Swollman	/* 'name' must be already checked. */
7340435Speter
742946Swollman	if (!strcmp(name, "C") || !strcmp(name, "POSIX"))
751541Srgrimes		return 0;
761541Srgrimes
771541Srgrimes	/*
781541Srgrimes	 * If the locale name is the same as our cache, use the cache.
7940435Speter	 */
8040435Speter	lbuf = locale_buf;
8140435Speter	if (lbuf != NULL && strcmp(name, lbuf) == 0) {
8240435Speter		set_from_buf(lbuf, num_lines, dst_localebuf);
8340435Speter		*using_locale = 1;
8440435Speter		return 0;
8540435Speter	}
8640435Speter
8740435Speter	/*
8840435Speter	 * Slurp the locale file into the cache.
8940435Speter	 */
9029653Sdyson	namesize = strlen(name) + 1;
9129653Sdyson
9229653Sdyson	/* 'PathLocale' must be already set & checked. */
9329653Sdyson
9429653Sdyson	/* Range checking not needed, 'name' size is limited */
951541Srgrimes	strcpy(filename, _PathLocale);
961541Srgrimes	strcat(filename, "/");
971541Srgrimes	strcat(filename, name);
981541Srgrimes	strcat(filename, "/");
991541Srgrimes	strcat(filename, category_filename);
1001541Srgrimes	fd = _open(filename, O_RDONLY);
1011541Srgrimes	if (fd < 0)
1021541Srgrimes		goto no_locale;
1031541Srgrimes	if (_fstat(fd, &st) != 0)
1041541Srgrimes		goto bad_locale;
1051541Srgrimes	if (st.st_size <= 0) {
1061541Srgrimes		errno = EFTYPE;
1071541Srgrimes		goto bad_locale;
1081541Srgrimes	}
10929290Sphk	bufsize = namesize + st.st_size;
11029290Sphk	locale_buf = NULL;
11129290Sphk	lbuf = (lbuf == NULL || lbuf == locale_buf_C) ?
11229290Sphk		malloc(bufsize) : reallocf(lbuf, bufsize);
11329290Sphk	if (lbuf == NULL)
11429290Sphk		goto bad_locale;
1151541Srgrimes	(void) strcpy(lbuf, name);
1161541Srgrimes	p = lbuf + namesize;
11740435Speter	plim = p + st.st_size;
1181541Srgrimes	if (_read(fd, p, (size_t) st.st_size) != st.st_size)
11940435Speter		goto bad_lbuf;
12012158Sbde	if (_close(fd) != 0)
12112158Sbde		goto bad_lbuf;
1221541Srgrimes	/*
1231541Srgrimes	 * Parse the locale file into localebuf.
1241541Srgrimes	 */
1251541Srgrimes	if (plim[-1] != '\n') {
1261541Srgrimes		errno = EFTYPE;
12740435Speter		goto bad_lbuf;
12840435Speter	}
12940435Speter	num_lines = split_lines(p, plim);
13040435Speter	if (num_lines >= locale_buf_size_max)
13140435Speter		num_lines = locale_buf_size_max;
13240435Speter	else if (num_lines >= locale_buf_size_min)
13340435Speter		num_lines = locale_buf_size_min;
13440435Speter	else {
13540435Speter		errno = EFTYPE;
13640435Speter		goto reset_locale;
13740435Speter	}
13840435Speter	set_from_buf(lbuf, num_lines, dst_localebuf);
13940435Speter	/*
14040435Speter	 * Record the successful parse in the cache.
14140435Speter	 */
14240435Speter	locale_buf = lbuf;
14340435Speter
14440435Speter	*using_locale = 1;
14540435Speter	return 0;
1461541Srgrimes
14740435Speterreset_locale:
14840435Speter	locale_buf = locale_buf_C;
14940435Speter	save_using_locale = 0;
15040435Speterbad_lbuf:
15140435Speter	saverr = errno; free(lbuf); errno = saverr;
15240435Speterbad_locale:
15340435Speter	saverr = errno; (void)_close(fd); errno = saverr;
15440435Speterno_locale:
15540435Speter	*using_locale = save_using_locale;
15640435Speter	return -1;
15740435Speter}
15840435Speter
15940435Speterstatic int
16040435Spetersplit_lines(char *p, const char *plim) {
16140435Speter
1621541Srgrimes	int i;
16340435Speter
16440435Speter	for (i = 0; p < plim; i++) {
16540435Speter		p = strchr(p, '\n');
16640435Speter		*p++ = '\0';
16740435Speter	}
16840435Speter	return i;
16940435Speter}
1701541Srgrimes
17140435Speterstatic void
17240435Speterset_from_buf(const char *p, int num_lines, const char **dst_localebuf) {
17340435Speter
17440435Speter	const char **ap;
17540435Speter	int i;
1761541Srgrimes
1771541Srgrimes	for (ap = dst_localebuf, i = 0; i < num_lines; ++ap, ++i)
1781541Srgrimes		*ap = p += strlen(p) + 1;
1791541Srgrimes}
1801541Srgrimes
1811541Srgrimes