ldpart.c revision 101470
191094Sdes/*
291094Sdes * Copyright (c) 2000, 2001 Alexey Zelkin <phantom@FreeBSD.org>
391094Sdes * All rights reserved.
491094Sdes *
591094Sdes * Redistribution and use in source and binary forms, with or without
691094Sdes * modification, are permitted provided that the following conditions
791094Sdes * are met:
891094Sdes * 1. Redistributions of source code must retain the above copyright
991094Sdes *    notice, this list of conditions and the following disclaimer.
1091094Sdes * 2. Redistributions in binary form must reproduce the above copyright
1191094Sdes *    notice, this list of conditions and the following disclaimer in the
1291094Sdes *    documentation and/or other materials provided with the distribution.
1391094Sdes *
1491094Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1591094Sdes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1691094Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1791094Sdes * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1891094Sdes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1991094Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2091094Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2191094Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2291094Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2391094Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2491094Sdes * SUCH DAMAGE.
2591094Sdes */
2691094Sdes
2791094Sdes#include <sys/cdefs.h>
2891094Sdes__FBSDID("$FreeBSD: head/lib/libc/locale/ldpart.c 101470 2002-08-07 16:45:23Z ache $");
2991094Sdes
3091094Sdes#include "namespace.h"
3191094Sdes#include <sys/types.h>
3291094Sdes#include <sys/stat.h>
3391094Sdes#include <sys/syslimits.h>
3491094Sdes#include <errno.h>
3591094Sdes#include <fcntl.h>
3691094Sdes#include <stdlib.h>
3791094Sdes#include <string.h>
3891094Sdes#include <unistd.h>
3991094Sdes#include "un-namespace.h"
4091094Sdes
4191094Sdes#include "setlocale.h"
4291094Sdes#include "ldpart.h"
4391094Sdes
4491094Sdesstatic int split_lines(char *, const char *);
4591094Sdesstatic void set_from_buf(const char *, int, const char **);
4691094Sdes
4791094Sdesint
4891094Sdes__part_load_locale(const char *name,
4991094Sdes		int *using_locale,
5091094Sdes		char *locale_buf,
5191094Sdes		const char *category_filename,
5291094Sdes		int locale_buf_size_max,
5391097Sdes		int locale_buf_size_min,
5491094Sdes		const char **dst_localebuf)
5591094Sdes{
5691094Sdes	static char	locale_buf_C[] = "C";
5791094Sdes	static int	num_lines;
5891094Sdes	int             saverr;
5991094Sdes	int		fd;
6091094Sdes	char		*lbuf;
6191094Sdes	char		*p;
6291094Sdes	const char 	*plim;
6391094Sdes	char            filename[PATH_MAX];
6491094Sdes	struct stat	st;
6591094Sdes	size_t		namesize;
6691094Sdes	size_t		bufsize;
6791094Sdes	int             save_using_locale;
6891100Sdes
6991100Sdes	save_using_locale = *using_locale;
7091100Sdes	*using_locale = 0;
7191100Sdes
7291100Sdes	/* 'name' must be already checked. */
7391100Sdes
7491100Sdes	if (!strcmp(name, "C") || !strcmp(name, "POSIX"))
7591100Sdes		return 0;
7691100Sdes
7791100Sdes	/*
7891100Sdes	 * If the locale name is the same as our cache, use the cache.
7991100Sdes	 */
8091100Sdes	lbuf = locale_buf;
8191100Sdes	if (lbuf != NULL && strcmp(name, lbuf) == 0) {
8291100Sdes		set_from_buf(lbuf, num_lines, dst_localebuf);
8391100Sdes		*using_locale = 1;
8491100Sdes		return 0;
8591100Sdes	}
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