ldpart.c revision 101470
1/*
2 * Copyright (c) 2000, 2001 Alexey Zelkin <phantom@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/lib/libc/locale/ldpart.c 101470 2002-08-07 16:45:23Z ache $");
29
30#include "namespace.h"
31#include <sys/types.h>
32#include <sys/stat.h>
33#include <sys/syslimits.h>
34#include <errno.h>
35#include <fcntl.h>
36#include <stdlib.h>
37#include <string.h>
38#include <unistd.h>
39#include "un-namespace.h"
40
41#include "setlocale.h"
42#include "ldpart.h"
43
44static int split_lines(char *, const char *);
45static void set_from_buf(const char *, int, const char **);
46
47int
48__part_load_locale(const char *name,
49		int *using_locale,
50		char *locale_buf,
51		const char *category_filename,
52		int locale_buf_size_max,
53		int locale_buf_size_min,
54		const char **dst_localebuf)
55{
56	static char	locale_buf_C[] = "C";
57	static int	num_lines;
58	int             saverr;
59	int		fd;
60	char		*lbuf;
61	char		*p;
62	const char 	*plim;
63	char            filename[PATH_MAX];
64	struct stat	st;
65	size_t		namesize;
66	size_t		bufsize;
67	int             save_using_locale;
68
69	save_using_locale = *using_locale;
70	*using_locale = 0;
71
72	/* 'name' must be already checked. */
73
74	if (!strcmp(name, "C") || !strcmp(name, "POSIX"))
75		return 0;
76
77	/*
78	 * If the locale name is the same as our cache, use the cache.
79	 */
80	lbuf = locale_buf;
81	if (lbuf != NULL && strcmp(name, lbuf) == 0) {
82		set_from_buf(lbuf, num_lines, dst_localebuf);
83		*using_locale = 1;
84		return 0;
85	}
86
87	/*
88	 * Slurp the locale file into the cache.
89	 */
90	namesize = strlen(name) + 1;
91
92	/* 'PathLocale' must be already set & checked. */
93
94	/* Range checking not needed, 'name' size is limited */
95	strcpy(filename, _PathLocale);
96	strcat(filename, "/");
97	strcat(filename, name);
98	strcat(filename, "/");
99	strcat(filename, category_filename);
100	fd = _open(filename, O_RDONLY);
101	if (fd < 0)
102		goto no_locale;
103	if (_fstat(fd, &st) != 0)
104		goto bad_locale;
105	if (st.st_size <= 0) {
106		errno = EFTYPE;
107		goto bad_locale;
108	}
109	bufsize = namesize + st.st_size;
110	locale_buf = NULL;
111	lbuf = (lbuf == NULL || lbuf == locale_buf_C) ?
112		malloc(bufsize) : reallocf(lbuf, bufsize);
113	if (lbuf == NULL)
114		goto bad_locale;
115	(void)strcpy(lbuf, name);
116	p = lbuf + namesize;
117	plim = p + st.st_size;
118	if (_read(fd, p, (size_t) st.st_size) != st.st_size)
119		goto bad_lbuf;
120	if (_close(fd) != 0)
121		goto bad_lbuf;
122	/*
123	 * Parse the locale file into localebuf.
124	 */
125	if (plim[-1] != '\n') {
126		errno = EFTYPE;
127		goto bad_lbuf;
128	}
129	num_lines = split_lines(p, plim);
130	if (num_lines >= locale_buf_size_max)
131		num_lines = locale_buf_size_max;
132	else if (num_lines >= locale_buf_size_min)
133		num_lines = locale_buf_size_min;
134	else {
135		errno = EFTYPE;
136		goto reset_locale;
137	}
138	set_from_buf(lbuf, num_lines, dst_localebuf);
139	/*
140	 * Record the successful parse in the cache.
141	 */
142	locale_buf = lbuf;
143
144	*using_locale = 1;
145
146	return 0;
147
148reset_locale:
149	locale_buf = locale_buf_C;
150	save_using_locale = 0;
151bad_lbuf:
152	saverr = errno;
153	free(lbuf);
154	errno = saverr;
155bad_locale:
156	saverr = errno;
157	(void)_close(fd);
158	errno = saverr;
159no_locale:
160	*using_locale = save_using_locale;
161
162	return -1;
163}
164
165static int
166split_lines(char *p, const char *plim)
167{
168	int i;
169
170	for (i = 0; p < plim; i++) {
171		p = strchr(p, '\n');
172		*p++ = '\0';
173	}
174	return (i);
175}
176
177static void
178set_from_buf(const char *p, int num_lines, const char **dst_localebuf) {
179
180	const char **ap;
181	int i;
182
183	for (ap = dst_localebuf, i = 0; i < num_lines; ++ap, ++i)
184		*ap = p += strlen(p) + 1;
185}
186
187