kernel: Remove the FFS_ROOT option. It was a no-op since 4.9.
[dragonfly.git] / lib / libc / nls / msgcat.c
blobc2b06c1b5370a0a0c1921f4166c8675ea4a33e89
1 /***********************************************************
2 Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts.
3 Copyright 2010, Gabor Kovesdan <gabor@FreeBSD.org>
5 All Rights Reserved
7 Permission to use, copy, modify, and distribute this software and its
8 documentation for any purpose and without fee is hereby granted,
9 provided that the above copyright notice appear in all copies and that
10 both that copyright notice and this permission notice appear in
11 supporting documentation, and that Alfalfa's name not be used in
12 advertising or publicity pertaining to distribution of the software
13 without specific, written prior permission.
15 ALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
16 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
17 ALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
18 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
19 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
20 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
21 SOFTWARE.
23 If you make any modifications, bugfixes or other changes to this software
24 we'd appreciate it if you could send a copy to us so we can keep things
25 up-to-date. Many thanks.
26 Kee Hinckley
27 Alfalfa Software, Inc.
28 267 Allston St., #3
29 Cambridge, MA 02139 USA
30 nazgul@alfalfa.com
32 $FreeBSD: head/lib/libc/nls/msgcat.c 304755 2016-08-24 16:44:27Z ache $
33 ******************************************************************/
35 #define _NLS_PRIVATE
37 #include "namespace.h"
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <sys/mman.h>
41 #include <sys/queue.h>
43 #include <arpa/inet.h> /* for ntohl() */
45 #include <errno.h>
46 #include <fcntl.h>
47 #include <limits.h>
48 #include <nl_types.h>
49 #include <pthread.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <unistd.h>
54 #include "un-namespace.h"
56 #include "../locale/xlocale_private.h"
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"
60 #define RLOCK(fail) { int ret; \
61 if (__isthreaded && \
62 ((ret = _pthread_rwlock_rdlock(&rwlock)) != 0)) { \
63 errno = ret; \
64 return (fail); \
66 #define WLOCK(fail) { int ret; \
67 if (__isthreaded && \
68 ((ret = _pthread_rwlock_wrlock(&rwlock)) != 0)) { \
69 errno = ret; \
70 return (fail); \
72 #define UNLOCK { if (__isthreaded) \
73 _pthread_rwlock_unlock(&rwlock); }
75 #define NLERR ((nl_catd) -1)
76 #define NLRETERR(errc) { errno = errc; return (NLERR); }
77 #define SAVEFAIL(n, l, e) { WLOCK(NLERR); \
78 np = malloc(sizeof(struct catentry)); \
79 if (np != NULL) { \
80 np->name = strdup(n); \
81 np->path = NULL; \
82 np->catd = NLERR; \
83 np->refcount = 0; \
84 np->lang = (l == NULL) ? NULL : \
85 strdup(l); \
86 np->caterrno = e; \
87 SLIST_INSERT_HEAD(&cache, np, list); \
88 } \
89 UNLOCK; \
90 errno = e; \
93 static nl_catd load_msgcat(const char *, const char *, const char *);
95 static pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
97 struct catentry {
98 SLIST_ENTRY(catentry) list;
99 char *name;
100 char *path;
101 int caterrno;
102 nl_catd catd;
103 char *lang;
104 int refcount;
107 SLIST_HEAD(listhead, catentry) cache =
108 SLIST_HEAD_INITIALIZER(cache);
110 nl_catd
111 catopen(const char *name, int type)
113 struct stat sbuf;
114 struct catentry *np;
115 char *base, *cptr, *cptr1, *nlspath, *pathP, *pcode;
116 char *plang, *pter;
117 int saverr, spcleft;
118 const char *lang, *tmpptr;
119 char path[PATH_MAX];
121 /* sanity checking */
122 if (name == NULL || *name == '\0')
123 NLRETERR(EINVAL);
125 if (strchr(name, '/') != NULL)
126 /* have a pathname */
127 lang = NULL;
128 else {
129 if (type == NL_CAT_LOCALE)
130 lang = querylocale(LC_MESSAGES_MASK, __get_locale());
131 else
132 lang = getenv("LANG");
134 if (lang == NULL || *lang == '\0' || strlen(lang) > ENCODING_LEN ||
135 (lang[0] == '.' &&
136 (lang[1] == '\0' || (lang[1] == '.' && lang[2] == '\0'))) ||
137 strchr(lang, '/') != NULL)
138 lang = "C";
141 /* Try to get it from the cache first */
142 RLOCK(NLERR);
143 SLIST_FOREACH(np, &cache, list) {
144 if ((strcmp(np->name, name) == 0) &&
145 ((lang != NULL && np->lang != NULL &&
146 strcmp(np->lang, lang) == 0) || (np->lang == lang))) {
147 if (np->caterrno != 0) {
148 /* Found cached failing entry */
149 UNLOCK;
150 NLRETERR(np->caterrno);
151 } else {
152 /* Found cached successful entry */
153 np->refcount++;
154 UNLOCK;
155 return (np->catd);
159 UNLOCK;
161 /* is it absolute path ? if yes, load immediately */
162 if (strchr(name, '/') != NULL)
163 return (load_msgcat(name, name, lang));
165 /* sanity checking */
166 if ((plang = cptr1 = strdup(lang)) == NULL)
167 return (NLERR);
168 if ((cptr = strchr(cptr1, '@')) != NULL)
169 *cptr = '\0';
170 pter = pcode = "";
171 if ((cptr = strchr(cptr1, '_')) != NULL) {
172 *cptr++ = '\0';
173 pter = cptr1 = cptr;
175 if ((cptr = strchr(cptr1, '.')) != NULL) {
176 *cptr++ = '\0';
177 pcode = cptr;
180 if ((nlspath = getenv("NLSPATH")) == NULL || issetugid())
181 nlspath = _DEFAULT_NLS_PATH;
183 if ((base = cptr = strdup(nlspath)) == NULL) {
184 saverr = errno;
185 free(plang);
186 errno = saverr;
187 return (NLERR);
190 while ((nlspath = strsep(&cptr, ":")) != NULL) {
191 pathP = path;
192 if (*nlspath) {
193 for (; *nlspath; ++nlspath) {
194 if (*nlspath == '%') {
195 switch (*(nlspath + 1)) {
196 case 'l':
197 tmpptr = plang;
198 break;
199 case 't':
200 tmpptr = pter;
201 break;
202 case 'c':
203 tmpptr = pcode;
204 break;
205 case 'L':
206 tmpptr = lang;
207 break;
208 case 'N':
209 tmpptr = (char *)name;
210 break;
211 case '%':
212 ++nlspath;
213 /* FALLTHROUGH */
214 default:
215 if (pathP - path >=
216 sizeof(path) - 1)
217 goto too_long;
218 *(pathP++) = *nlspath;
219 continue;
221 ++nlspath;
222 put_tmpptr:
223 spcleft = sizeof(path) -
224 (pathP - path) - 1;
225 if (strlcpy(pathP, tmpptr, spcleft) >=
226 spcleft) {
227 too_long:
228 free(plang);
229 free(base);
230 SAVEFAIL(name, lang, ENAMETOOLONG);
231 NLRETERR(ENAMETOOLONG);
233 pathP += strlen(tmpptr);
234 } else {
235 if (pathP - path >= sizeof(path) - 1)
236 goto too_long;
237 *(pathP++) = *nlspath;
240 *pathP = '\0';
241 if (stat(path, &sbuf) == 0) {
242 free(plang);
243 free(base);
244 return (load_msgcat(path, name, lang));
246 } else {
247 tmpptr = (char *)name;
248 --nlspath;
249 goto put_tmpptr;
252 free(plang);
253 free(base);
254 SAVEFAIL(name, lang, ENOENT);
255 NLRETERR(ENOENT);
258 char *
259 catgets(nl_catd catd, int set_id, int msg_id, const char *s)
261 struct _nls_cat_hdr *cat_hdr;
262 struct _nls_msg_hdr *msg_hdr;
263 struct _nls_set_hdr *set_hdr;
264 int i, l, r, u;
266 if (catd == NULL || catd == NLERR) {
267 errno = EBADF;
268 /* LINTED interface problem */
269 return ((char *)s);
272 cat_hdr = (struct _nls_cat_hdr *)catd->__data;
273 set_hdr = (struct _nls_set_hdr *)(void *)((char *)catd->__data +
274 sizeof(struct _nls_cat_hdr));
276 /* binary search, see knuth algorithm b */
277 l = 0;
278 u = ntohl((u_int32_t)cat_hdr->__nsets) - 1;
279 while (l <= u) {
280 i = (l + u) / 2;
281 r = set_id - ntohl((u_int32_t)set_hdr[i].__setno);
283 if (r == 0) {
284 msg_hdr = (struct _nls_msg_hdr *)
285 (void *)((char *)catd->__data +
286 sizeof(struct _nls_cat_hdr) +
287 ntohl((u_int32_t)cat_hdr->__msg_hdr_offset));
289 l = ntohl((u_int32_t)set_hdr[i].__index);
290 u = l + ntohl((u_int32_t)set_hdr[i].__nmsgs) - 1;
291 while (l <= u) {
292 i = (l + u) / 2;
293 r = msg_id -
294 ntohl((u_int32_t)msg_hdr[i].__msgno);
295 if (r == 0) {
296 return ((char *) catd->__data +
297 sizeof(struct _nls_cat_hdr) +
298 ntohl((u_int32_t)
299 cat_hdr->__msg_txt_offset) +
300 ntohl((u_int32_t)
301 msg_hdr[i].__offset));
302 } else if (r < 0) {
303 u = i - 1;
304 } else {
305 l = i + 1;
309 /* not found */
310 goto notfound;
312 } else if (r < 0) {
313 u = i - 1;
314 } else {
315 l = i + 1;
319 notfound:
320 /* not found */
321 errno = ENOMSG;
322 /* LINTED interface problem */
323 return ((char *)s);
326 static void
327 catfree(struct catentry *np)
330 if (np->catd != NULL && np->catd != NLERR) {
331 munmap(np->catd->__data, (size_t)np->catd->__size);
332 free(np->catd);
334 SLIST_REMOVE(&cache, np, catentry, list);
335 free(np->name);
336 free(np->path);
337 free(np->lang);
338 free(np);
342 catclose(nl_catd catd)
344 struct catentry *np;
346 /* sanity checking */
347 if (catd == NULL || catd == NLERR) {
348 errno = EBADF;
349 return (-1);
352 /* Remove from cache if not referenced any more */
353 WLOCK(-1);
354 SLIST_FOREACH(np, &cache, list) {
355 if (catd == np->catd) {
356 np->refcount--;
357 if (np->refcount == 0)
358 catfree(np);
359 break;
362 UNLOCK;
363 return (0);
367 * Internal support functions
370 static nl_catd
371 load_msgcat(const char *path, const char *name, const char *lang)
373 struct stat st;
374 nl_catd catd;
375 struct catentry *np;
376 void *data;
377 int fd;
379 /* path/name will never be NULL here */
382 * One more try in cache; if it was not found by name,
383 * it might still be found by absolute path.
385 RLOCK(NLERR);
386 SLIST_FOREACH(np, &cache, list) {
387 if ((np->path != NULL) && (strcmp(np->path, path) == 0)) {
388 np->refcount++;
389 UNLOCK;
390 return (np->catd);
393 UNLOCK;
395 if ((fd = _open(path, O_RDONLY | O_CLOEXEC)) == -1) {
396 SAVEFAIL(name, lang, errno);
397 NLRETERR(errno);
400 if (_fstat(fd, &st) != 0) {
401 _close(fd);
402 SAVEFAIL(name, lang, EFTYPE);
403 NLRETERR(EFTYPE);
407 * If the file size cannot be held in size_t we cannot mmap()
408 * it to the memory. Probably, this will not be a problem given
409 * that catalog files are usually small.
411 if (st.st_size > SIZE_T_MAX) {
412 _close(fd);
413 SAVEFAIL(name, lang, EFBIG);
414 NLRETERR(EFBIG);
417 if ((data = mmap(0, (size_t)st.st_size, PROT_READ,
418 MAP_FILE|MAP_SHARED, fd, (off_t)0)) == MAP_FAILED) {
419 int saved_errno = errno;
420 _close(fd);
421 SAVEFAIL(name, lang, saved_errno);
422 NLRETERR(saved_errno);
424 _close(fd);
426 if (ntohl((u_int32_t)((struct _nls_cat_hdr *)data)->__magic) !=
427 _NLS_MAGIC) {
428 munmap(data, (size_t)st.st_size);
429 SAVEFAIL(name, lang, EFTYPE);
430 NLRETERR(EFTYPE);
433 if ((catd = malloc(sizeof (*catd))) == NULL) {
434 munmap(data, (size_t)st.st_size);
435 SAVEFAIL(name, lang, ENOMEM);
436 NLRETERR(ENOMEM);
439 catd->__data = data;
440 catd->__size = (int)st.st_size;
442 /* Caching opened catalog */
443 WLOCK(NLERR);
444 if ((np = malloc(sizeof(struct catentry))) != NULL) {
445 np->name = strdup(name);
446 np->path = strdup(path);
447 np->catd = catd;
448 np->lang = (lang == NULL) ? NULL : strdup(lang);
449 np->refcount = 1;
450 np->caterrno = 0;
451 SLIST_INSERT_HEAD(&cache, np, list);
453 UNLOCK;
454 return (catd);