xref: /freebsd/lib/libc/nls/msgcat.c (revision 1e413cf93298b5b97441a21d9a50fdcd0ee9945e)
1 /***********************************************************
2 Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts.
3 
4                         All Rights Reserved
5 
6 Permission to use, copy, modify, and distribute this software and its
7 documentation for any purpose and without fee is hereby granted,
8 provided that the above copyright notice appear in all copies and that
9 both that copyright notice and this permission notice appear in
10 supporting documentation, and that Alfalfa's name not be used in
11 advertising or publicity pertaining to distribution of the software
12 without specific, written prior permission.
13 
14 ALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
15 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
16 ALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
17 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
18 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
19 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
20 SOFTWARE.
21 
22 If you make any modifications, bugfixes or other changes to this software
23 we'd appreciate it if you could send a copy to us so we can keep things
24 up-to-date.  Many thanks.
25 				Kee Hinckley
26 				Alfalfa Software, Inc.
27 				267 Allston St., #3
28 				Cambridge, MA 02139  USA
29 				nazgul@alfalfa.com
30 
31 ******************************************************************/
32 
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35 
36 #define _NLS_PRIVATE
37 
38 #include "namespace.h"
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <sys/mman.h>
42 
43 #include <arpa/inet.h>		/* for ntohl() */
44 
45 #include <errno.h>
46 #include <fcntl.h>
47 #include <limits.h>
48 #include <locale.h>
49 #include <nl_types.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <unistd.h>
54 #include "un-namespace.h"
55 
56 #include "../locale/setlocale.h"        /* for ENCODING_LEN */
57 
58 #define _DEFAULT_NLS_PATH "/usr/share/nls/%L/%N.cat:/usr/share/nls/%N/%L:/usr/local/share/nls/%L/%N.cat:/usr/local/share/nls/%N/%L"
59 
60 #define	NLERR		((nl_catd) -1)
61 #define NLRETERR(errc)  { errno = errc; return (NLERR); }
62 
63 static nl_catd load_msgcat(const char *);
64 
65 nl_catd
66 catopen(const char *name, int type)
67 {
68 	int             spcleft, saverr;
69 	char            path[PATH_MAX];
70 	char            *nlspath, *lang, *base, *cptr, *pathP, *tmpptr;
71 	char            *cptr1, *plang, *pter, *pcode;
72 	struct stat     sbuf;
73 
74 	if (name == NULL || *name == '\0')
75 		NLRETERR(EINVAL);
76 
77 	/* is it absolute path ? if yes, load immediately */
78 	if (strchr(name, '/') != NULL)
79 		return (load_msgcat(name));
80 
81 	if (type == NL_CAT_LOCALE)
82 		lang = setlocale(LC_MESSAGES, NULL);
83 	else
84 		lang = getenv("LANG");
85 
86 	if (lang == NULL || *lang == '\0' || strlen(lang) > ENCODING_LEN ||
87 	    (lang[0] == '.' &&
88 	     (lang[1] == '\0' || (lang[1] == '.' && lang[2] == '\0'))) ||
89 	    strchr(lang, '/') != NULL)
90 		lang = "C";
91 
92 	if ((plang = cptr1 = strdup(lang)) == NULL)
93 		return (NLERR);
94 	if ((cptr = strchr(cptr1, '@')) != NULL)
95 		*cptr = '\0';
96 	pter = pcode = "";
97 	if ((cptr = strchr(cptr1, '_')) != NULL) {
98 		*cptr++ = '\0';
99 		pter = cptr1 = cptr;
100 	}
101 	if ((cptr = strchr(cptr1, '.')) != NULL) {
102 		*cptr++ = '\0';
103 		pcode = cptr;
104 	}
105 
106 	if ((nlspath = getenv("NLSPATH")) == NULL || issetugid())
107 		nlspath = _DEFAULT_NLS_PATH;
108 
109 	if ((base = cptr = strdup(nlspath)) == NULL) {
110 		saverr = errno;
111 		free(plang);
112 		errno = saverr;
113 		return (NLERR);
114 	}
115 
116 	while ((nlspath = strsep(&cptr, ":")) != NULL) {
117 		pathP = path;
118 		if (*nlspath) {
119 			for (; *nlspath; ++nlspath) {
120 				if (*nlspath == '%') {
121 					switch (*(nlspath + 1)) {
122 					case 'l':
123 						tmpptr = plang;
124 						break;
125 					case 't':
126 						tmpptr = pter;
127 						break;
128 					case 'c':
129 						tmpptr = pcode;
130 						break;
131 					case 'L':
132 						tmpptr = lang;
133 						break;
134 					case 'N':
135 						tmpptr = (char *)name;
136 						break;
137 					case '%':
138 						++nlspath;
139 						/* fallthrough */
140 					default:
141 						if (pathP - path >=
142 						    sizeof(path) - 1)
143 							goto too_long;
144 						*(pathP++) = *nlspath;
145 						continue;
146 					}
147 					++nlspath;
148 			put_tmpptr:
149 					spcleft = sizeof(path) -
150 						  (pathP - path) - 1;
151 					if (strlcpy(pathP, tmpptr, spcleft) >=
152 					    spcleft) {
153 			too_long:
154 						free(plang);
155 						free(base);
156 						NLRETERR(ENAMETOOLONG);
157 					}
158 					pathP += strlen(tmpptr);
159 				} else {
160 					if (pathP - path >= sizeof(path) - 1)
161 						goto too_long;
162 					*(pathP++) = *nlspath;
163 				}
164 			}
165 			*pathP = '\0';
166 			if (stat(path, &sbuf) == 0) {
167 				free(plang);
168 				free(base);
169 				return (load_msgcat(path));
170 			}
171 		} else {
172 			tmpptr = (char *)name;
173 			--nlspath;
174 			goto put_tmpptr;
175 		}
176 	}
177 	free(plang);
178 	free(base);
179 	NLRETERR(ENOENT);
180 }
181 
182 char *
183 catgets(nl_catd catd, int set_id, int msg_id, const char *s)
184 {
185 	struct _nls_cat_hdr *cat_hdr;
186 	struct _nls_set_hdr *set_hdr;
187 	struct _nls_msg_hdr *msg_hdr;
188 	int l, u, i, r;
189 
190 	if (catd == NULL || catd == NLERR) {
191 		errno = EBADF;
192 		/* LINTED interface problem */
193 		return (char *) s;
194 }
195 
196 	cat_hdr = (struct _nls_cat_hdr *)catd->__data;
197 	set_hdr = (struct _nls_set_hdr *)(void *)((char *)catd->__data
198 			+ sizeof(struct _nls_cat_hdr));
199 
200 	/* binary search, see knuth algorithm b */
201 	l = 0;
202 	u = ntohl((u_int32_t)cat_hdr->__nsets) - 1;
203 	while (l <= u) {
204 		i = (l + u) / 2;
205 		r = set_id - ntohl((u_int32_t)set_hdr[i].__setno);
206 
207 		if (r == 0) {
208 			msg_hdr = (struct _nls_msg_hdr *)
209 			    (void *)((char *)catd->__data +
210 			    sizeof(struct _nls_cat_hdr) +
211 			    ntohl((u_int32_t)cat_hdr->__msg_hdr_offset));
212 
213 			l = ntohl((u_int32_t)set_hdr[i].__index);
214 			u = l + ntohl((u_int32_t)set_hdr[i].__nmsgs) - 1;
215 			while (l <= u) {
216 				i = (l + u) / 2;
217 				r = msg_id -
218 				    ntohl((u_int32_t)msg_hdr[i].__msgno);
219 				if (r == 0) {
220 					return ((char *) catd->__data +
221 					    sizeof(struct _nls_cat_hdr) +
222 					    ntohl((u_int32_t)
223 					    cat_hdr->__msg_txt_offset) +
224 					    ntohl((u_int32_t)
225 					    msg_hdr[i].__offset));
226 				} else if (r < 0) {
227 					u = i - 1;
228 				} else {
229 					l = i + 1;
230 				}
231 }
232 
233 			/* not found */
234 			goto notfound;
235 
236 		} else if (r < 0) {
237 			u = i - 1;
238 		} else {
239 			l = i + 1;
240 		}
241 }
242 
243 notfound:
244 	/* not found */
245 	errno = ENOMSG;
246 	/* LINTED interface problem */
247 	return (char *) s;
248 }
249 
250 int
251 catclose(nl_catd catd)
252 {
253 	if (catd == NULL || catd == NLERR) {
254 		errno = EBADF;
255 		return (-1);
256 	}
257 
258 	munmap(catd->__data, (size_t)catd->__size);
259 	free(catd);
260 	return (0);
261 }
262 
263 /*
264  * Internal support functions
265  */
266 
267 static nl_catd
268 load_msgcat(const char *path)
269 {
270 	struct stat st;
271 	nl_catd catd;
272 	void *data;
273 	int fd;
274 
275 	/* XXX: path != NULL? */
276 
277 	if ((fd = _open(path, O_RDONLY)) == -1)
278 		return (NLERR);
279 
280 	if (_fstat(fd, &st) != 0) {
281 		_close(fd);
282 		return (NLERR);
283 	}
284 
285 	data = mmap(0, (size_t)st.st_size, PROT_READ, MAP_FILE|MAP_SHARED, fd,
286 	    (off_t)0);
287 	_close(fd);
288 
289 	if (data == MAP_FAILED)
290 		return (NLERR);
291 
292 	if (ntohl((u_int32_t)((struct _nls_cat_hdr *)data)->__magic) !=
293 	    _NLS_MAGIC) {
294 		munmap(data, (size_t)st.st_size);
295 		NLRETERR(EINVAL);
296 	}
297 
298 	if ((catd = malloc(sizeof (*catd))) == NULL) {
299 		munmap(data, (size_t)st.st_size);
300 		return (NLERR);
301 	}
302 
303 	catd->__data = data;
304 	catd->__size = (int)st.st_size;
305 	return (catd);
306 }
307 
308