xref: /freebsd/lib/libc/locale/ldpart.c (revision 74f2b975)
190423eceSAlexey Zelkin /*
274f2b975SAlexey Zelkin  * Copyright (c) 2000, 2001 Alexey Zelkin <phantom@FreeBSD.org>
390423eceSAlexey Zelkin  * All rights reserved.
490423eceSAlexey Zelkin  *
590423eceSAlexey Zelkin  * Redistribution and use in source and binary forms, with or without
690423eceSAlexey Zelkin  * modification, are permitted provided that the following conditions
790423eceSAlexey Zelkin  * are met:
890423eceSAlexey Zelkin  * 1. Redistributions of source code must retain the above copyright
990423eceSAlexey Zelkin  *    notice, this list of conditions and the following disclaimer.
1090423eceSAlexey Zelkin  * 2. Redistributions in binary form must reproduce the above copyright
1190423eceSAlexey Zelkin  *    notice, this list of conditions and the following disclaimer in the
1290423eceSAlexey Zelkin  *    documentation and/or other materials provided with the distribution.
1390423eceSAlexey Zelkin  *
1490423eceSAlexey Zelkin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1590423eceSAlexey Zelkin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1690423eceSAlexey Zelkin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1790423eceSAlexey Zelkin  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1890423eceSAlexey Zelkin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1990423eceSAlexey Zelkin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2090423eceSAlexey Zelkin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2190423eceSAlexey Zelkin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2290423eceSAlexey Zelkin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2390423eceSAlexey Zelkin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2490423eceSAlexey Zelkin  * SUCH DAMAGE.
2590423eceSAlexey Zelkin  *
2690423eceSAlexey Zelkin  * $FreeBSD$
2790423eceSAlexey Zelkin  */
2890423eceSAlexey Zelkin 
297d2cc62bSAlexey Zelkin #include "namespace.h"
3090423eceSAlexey Zelkin #include <sys/types.h>
3190423eceSAlexey Zelkin #include <sys/stat.h>
3290423eceSAlexey Zelkin #include <sys/syslimits.h>
3390423eceSAlexey Zelkin #include <fcntl.h>
3490423eceSAlexey Zelkin #include <stdlib.h>
3590423eceSAlexey Zelkin #include <string.h>
36278d1a20SDaniel Eischen #include <unistd.h>
37278d1a20SDaniel Eischen #include "un-namespace.h"
3874f2b975SAlexey Zelkin 
3990423eceSAlexey Zelkin #include "setlocale.h"
4090423eceSAlexey Zelkin #include "ldpart.h"
4190423eceSAlexey Zelkin 
4290423eceSAlexey Zelkin static int split_lines(char *, const char *);
4390423eceSAlexey Zelkin static void set_from_buf(const char *, int, const char **);
4490423eceSAlexey Zelkin 
4590423eceSAlexey Zelkin int
4690423eceSAlexey Zelkin __part_load_locale(const char *name,
4790423eceSAlexey Zelkin 		int* using_locale,
4890423eceSAlexey Zelkin 		char *locale_buf,
4990423eceSAlexey Zelkin 		const char *category_name,
50fc38c1e5SAlexey Zelkin 		int locale_buf_size_max,
51fc38c1e5SAlexey Zelkin 		int locale_buf_size_min,
5290423eceSAlexey Zelkin 		const char **dst_localebuf) {
5390423eceSAlexey Zelkin 
5490423eceSAlexey Zelkin 	static char		locale_buf_C[] = "C";
5590423eceSAlexey Zelkin 	static int		num_lines;
5690423eceSAlexey Zelkin 
5790423eceSAlexey Zelkin 	int			fd;
5890423eceSAlexey Zelkin 	char *			lbuf;
5990423eceSAlexey Zelkin 	char *			p;
6090423eceSAlexey Zelkin 	const char *		plim;
6190423eceSAlexey Zelkin 	char                    filename[PATH_MAX];
6290423eceSAlexey Zelkin 	struct stat		st;
6390423eceSAlexey Zelkin 	size_t			namesize;
6490423eceSAlexey Zelkin 	size_t			bufsize;
6590423eceSAlexey Zelkin 	int                     save_using_locale;
6690423eceSAlexey Zelkin 
6790423eceSAlexey Zelkin 	save_using_locale = *using_locale;
6890423eceSAlexey Zelkin 	*using_locale = 0;
6990423eceSAlexey Zelkin 
7090423eceSAlexey Zelkin 	if (name == NULL)
7190423eceSAlexey Zelkin 		goto no_locale;
7290423eceSAlexey Zelkin 
7390423eceSAlexey Zelkin 	if (!strcmp(name, "C") || !strcmp(name, "POSIX"))
7490423eceSAlexey Zelkin 		return 0;
7590423eceSAlexey Zelkin 
7690423eceSAlexey Zelkin 	/*
7774f2b975SAlexey Zelkin 	 * If the locale name is the same as our cache, use the cache.
7890423eceSAlexey Zelkin 	 */
7990423eceSAlexey Zelkin 	lbuf = locale_buf;
8090423eceSAlexey Zelkin 	if (lbuf != NULL && strcmp(name, lbuf) == 0) {
8190423eceSAlexey Zelkin 		set_from_buf(lbuf, num_lines, dst_localebuf);
8290423eceSAlexey Zelkin 		*using_locale = 1;
8390423eceSAlexey Zelkin 		return 0;
8490423eceSAlexey Zelkin 	}
8590423eceSAlexey Zelkin 	/*
8674f2b975SAlexey Zelkin 	 * Slurp the locale file into the cache.
8790423eceSAlexey Zelkin 	 */
8890423eceSAlexey Zelkin 	namesize = strlen(name) + 1;
8990423eceSAlexey Zelkin 
9090423eceSAlexey Zelkin 	if (!_PathLocale)
9190423eceSAlexey Zelkin 		goto no_locale;
9290423eceSAlexey Zelkin 	/* Range checking not needed, 'name' size is limited */
9390423eceSAlexey Zelkin 	strcpy(filename, _PathLocale);
9490423eceSAlexey Zelkin 	strcat(filename, "/");
9590423eceSAlexey Zelkin 	strcat(filename, name);
9690423eceSAlexey Zelkin 	strcat(filename, "/");
9790423eceSAlexey Zelkin 	strcat(filename, category_name);
9890423eceSAlexey Zelkin 	fd = _open(filename, O_RDONLY);
9990423eceSAlexey Zelkin 	if (fd < 0)
10090423eceSAlexey Zelkin 		goto no_locale;
1013b5b529fSDaniel Eischen 	if (_fstat(fd, &st) != 0)
10290423eceSAlexey Zelkin 		goto bad_locale;
10390423eceSAlexey Zelkin 	if (st.st_size <= 0)
10490423eceSAlexey Zelkin 		goto bad_locale;
10590423eceSAlexey Zelkin 	bufsize = namesize + st.st_size;
10690423eceSAlexey Zelkin 	locale_buf = NULL;
10790423eceSAlexey Zelkin 	lbuf = (lbuf == NULL || lbuf == locale_buf_C) ?
10890423eceSAlexey Zelkin 		malloc(bufsize) : reallocf(lbuf, bufsize);
10990423eceSAlexey Zelkin 	if (lbuf == NULL)
11090423eceSAlexey Zelkin 		goto bad_locale;
11190423eceSAlexey Zelkin 	(void) strcpy(lbuf, name);
11290423eceSAlexey Zelkin 	p = lbuf + namesize;
11390423eceSAlexey Zelkin 	plim = p + st.st_size;
11490423eceSAlexey Zelkin 	if (_read(fd, p, (size_t) st.st_size) != st.st_size)
11590423eceSAlexey Zelkin 		goto bad_lbuf;
11690423eceSAlexey Zelkin 	if (_close(fd) != 0)
11790423eceSAlexey Zelkin 		goto bad_lbuf;
11890423eceSAlexey Zelkin 	/*
11974f2b975SAlexey Zelkin 	 * Parse the locale file into localebuf.
12090423eceSAlexey Zelkin 	 */
12190423eceSAlexey Zelkin 	if (plim[-1] != '\n')
12290423eceSAlexey Zelkin 		goto bad_lbuf;
12390423eceSAlexey Zelkin 	num_lines = split_lines(p, plim);
124fc38c1e5SAlexey Zelkin 	if (num_lines >= locale_buf_size_max)
125fc38c1e5SAlexey Zelkin 		num_lines = locale_buf_size_max;
126fc38c1e5SAlexey Zelkin 	else if (num_lines >= locale_buf_size_min)
127fc38c1e5SAlexey Zelkin 		num_lines = locale_buf_size_min;
12890423eceSAlexey Zelkin 	else
12990423eceSAlexey Zelkin 		goto reset_locale;
13090423eceSAlexey Zelkin 	set_from_buf(lbuf, num_lines, dst_localebuf);
13190423eceSAlexey Zelkin 	/*
13290423eceSAlexey Zelkin 	** Record the successful parse in the cache.
13390423eceSAlexey Zelkin 	*/
13490423eceSAlexey Zelkin 	locale_buf = lbuf;
13590423eceSAlexey Zelkin 
13690423eceSAlexey Zelkin 	*using_locale = 1;
13790423eceSAlexey Zelkin 	return 0;
13890423eceSAlexey Zelkin 
13990423eceSAlexey Zelkin reset_locale:
14090423eceSAlexey Zelkin 	locale_buf = locale_buf_C;
14190423eceSAlexey Zelkin 	save_using_locale = 0;
14290423eceSAlexey Zelkin bad_lbuf:
14390423eceSAlexey Zelkin 	free(lbuf);
14490423eceSAlexey Zelkin bad_locale:
14590423eceSAlexey Zelkin 	(void)_close(fd);
14690423eceSAlexey Zelkin no_locale:
14790423eceSAlexey Zelkin 	*using_locale = save_using_locale;
14890423eceSAlexey Zelkin 	return -1;
14990423eceSAlexey Zelkin }
15090423eceSAlexey Zelkin 
15190423eceSAlexey Zelkin static int
15290423eceSAlexey Zelkin split_lines(char *p, const char *plim)
15390423eceSAlexey Zelkin {
15490423eceSAlexey Zelkin 	int i;
15590423eceSAlexey Zelkin 
15690423eceSAlexey Zelkin 	for (i = 0; p < plim; i++) {
15790423eceSAlexey Zelkin 		p = strchr(p, '\n');
15890423eceSAlexey Zelkin 		*p++ = '\0';
15990423eceSAlexey Zelkin 	}
16090423eceSAlexey Zelkin 	return i;
16190423eceSAlexey Zelkin }
16290423eceSAlexey Zelkin 
16390423eceSAlexey Zelkin static void
16490423eceSAlexey Zelkin set_from_buf(const char *p, int num_lines, const char **dst_localebuf)
16590423eceSAlexey Zelkin {
16690423eceSAlexey Zelkin 	const char **ap;
16790423eceSAlexey Zelkin 	int i;
16890423eceSAlexey Zelkin 
16990423eceSAlexey Zelkin 	for (ap = dst_localebuf, i = 0; i < num_lines; ++ap, ++i)
17090423eceSAlexey Zelkin 		*ap = p += strlen(p) + 1;
17190423eceSAlexey Zelkin }
17290423eceSAlexey Zelkin 
173