drm: Add linux/ktime.h
[dragonfly.git] / lib / libc / nls / msgcat.c
blob2625fa87a061343a6235159f38734f81b3654922
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 244358 2012-12-17 12:57:36Z eadler $
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 <locale.h>
49 #include <nl_types.h>
50 #include <pthread.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <unistd.h>
55 #include "un-namespace.h"
57 #include "../locale/setlocale.h" /* for ENCODING_LEN */
59 #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"
61 #define RLOCK(fail) { int ret; \
62 if (__isthreaded && \
63 ((ret = _pthread_rwlock_rdlock(&rwlock)) != 0)) { \
64 errno = ret; \
65 return (fail); \
67 #define WLOCK(fail) { int ret; \
68 if (__isthreaded && \
69 ((ret = _pthread_rwlock_wrlock(&rwlock)) != 0)) { \
70 errno = ret; \
71 return (fail); \
73 #define UNLOCK { if (__isthreaded) \
74 _pthread_rwlock_unlock(&rwlock); }
76 #define NLERR ((nl_catd) -1)
77 #define NLRETERR(errc) { errno = errc; return (NLERR); }
78 #define SAVEFAIL(n, l, e) { WLOCK(NLERR); \
79 np = malloc(sizeof(struct catentry)); \
80 if (np != NULL) { \
81 np->name = strdup(n); \
82 np->path = NULL; \
83 np->catd = NLERR; \
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, *lang, *nlspath, *pathP, *pcode;
116 char *plang, *pter, *tmpptr;
117 int saverr, spcleft;
118 char path[PATH_MAX];
120 /* sanity checking */
121 if (name == NULL || *name == '\0')
122 NLRETERR(EINVAL);
124 if (strchr(name, '/') != NULL)
125 /* have a pathname */
126 lang = NULL;
127 else {
128 if (type == NL_CAT_LOCALE)
129 lang = setlocale(LC_MESSAGES, NULL);
130 else
131 lang = getenv("LANG");
133 if (lang == NULL || *lang == '\0' || strlen(lang) > ENCODING_LEN ||
134 (lang[0] == '.' &&
135 (lang[1] == '\0' || (lang[1] == '.' && lang[2] == '\0'))) ||
136 strchr(lang, '/') != NULL)
137 lang = "C";
140 /* Try to get it from the cache first */
141 RLOCK(NLERR);
142 SLIST_FOREACH(np, &cache, list) {
143 if ((strcmp(np->name, name) == 0) &&
144 ((lang != NULL && np->lang != NULL &&
145 strcmp(np->lang, lang) == 0) || (np->lang == lang))) {
146 if (np->caterrno != 0) {
147 /* Found cached failing entry */
148 UNLOCK;
149 NLRETERR(np->caterrno);
150 } else {
151 /* Found cached successful entry */
152 np->refcount++;
153 UNLOCK;
154 return (np->catd);
158 UNLOCK;
160 /* is it absolute path ? if yes, load immediately */
161 if (strchr(name, '/') != NULL)
162 return (load_msgcat(name, name, lang));
164 /* sanity checking */
165 if ((plang = cptr1 = strdup(lang)) == NULL)
166 return (NLERR);
167 if ((cptr = strchr(cptr1, '@')) != NULL)
168 *cptr = '\0';
169 pter = pcode = "";
170 if ((cptr = strchr(cptr1, '_')) != NULL) {
171 *cptr++ = '\0';
172 pter = cptr1 = cptr;
174 if ((cptr = strchr(cptr1, '.')) != NULL) {
175 *cptr++ = '\0';
176 pcode = cptr;
179 if ((nlspath = getenv("NLSPATH")) == NULL || issetugid())
180 nlspath = _DEFAULT_NLS_PATH;
182 if ((base = cptr = strdup(nlspath)) == NULL) {
183 saverr = errno;
184 free(plang);
185 errno = saverr;
186 return (NLERR);
189 while ((nlspath = strsep(&cptr, ":")) != NULL) {
190 pathP = path;
191 if (*nlspath) {
192 for (; *nlspath; ++nlspath) {
193 if (*nlspath == '%') {
194 switch (*(nlspath + 1)) {
195 case 'l':
196 tmpptr = plang;
197 break;
198 case 't':
199 tmpptr = pter;
200 break;
201 case 'c':
202 tmpptr = pcode;
203 break;
204 case 'L':
205 tmpptr = lang;
206 break;
207 case 'N':
208 tmpptr = (char *)name;
209 break;
210 case '%':
211 ++nlspath;
212 /* FALLTHROUGH */
213 default:
214 if (pathP - path >=
215 sizeof(path) - 1)
216 goto too_long;
217 *(pathP++) = *nlspath;
218 continue;
220 ++nlspath;
221 put_tmpptr:
222 spcleft = sizeof(path) -
223 (pathP - path) - 1;
224 if (strlcpy(pathP, tmpptr, spcleft) >=
225 spcleft) {
226 too_long:
227 free(plang);
228 free(base);
229 SAVEFAIL(name, lang, ENAMETOOLONG);
230 NLRETERR(ENAMETOOLONG);
232 pathP += strlen(tmpptr);
233 } else {
234 if (pathP - path >= sizeof(path) - 1)
235 goto too_long;
236 *(pathP++) = *nlspath;
239 *pathP = '\0';
240 if (stat(path, &sbuf) == 0) {
241 free(plang);
242 free(base);
243 return (load_msgcat(path, name, lang));
245 } else {
246 tmpptr = (char *)name;
247 --nlspath;
248 goto put_tmpptr;
251 free(plang);
252 free(base);
253 SAVEFAIL(name, lang, ENOENT);
254 NLRETERR(ENOENT);
257 char *
258 catgets(nl_catd catd, int set_id, int msg_id, const char *s)
260 struct _nls_cat_hdr *cat_hdr;
261 struct _nls_msg_hdr *msg_hdr;
262 struct _nls_set_hdr *set_hdr;
263 int i, l, r, u;
265 if (catd == NULL || catd == NLERR) {
266 errno = EBADF;
267 /* LINTED interface problem */
268 return ((char *)s);
271 cat_hdr = (struct _nls_cat_hdr *)catd->__data;
272 set_hdr = (struct _nls_set_hdr *)(void *)((char *)catd->__data +
273 sizeof(struct _nls_cat_hdr));
275 /* binary search, see knuth algorithm b */
276 l = 0;
277 u = ntohl((u_int32_t)cat_hdr->__nsets) - 1;
278 while (l <= u) {
279 i = (l + u) / 2;
280 r = set_id - ntohl((u_int32_t)set_hdr[i].__setno);
282 if (r == 0) {
283 msg_hdr = (struct _nls_msg_hdr *)
284 (void *)((char *)catd->__data +
285 sizeof(struct _nls_cat_hdr) +
286 ntohl((u_int32_t)cat_hdr->__msg_hdr_offset));
288 l = ntohl((u_int32_t)set_hdr[i].__index);
289 u = l + ntohl((u_int32_t)set_hdr[i].__nmsgs) - 1;
290 while (l <= u) {
291 i = (l + u) / 2;
292 r = msg_id -
293 ntohl((u_int32_t)msg_hdr[i].__msgno);
294 if (r == 0) {
295 return ((char *) catd->__data +
296 sizeof(struct _nls_cat_hdr) +
297 ntohl((u_int32_t)
298 cat_hdr->__msg_txt_offset) +
299 ntohl((u_int32_t)
300 msg_hdr[i].__offset));
301 } else if (r < 0) {
302 u = i - 1;
303 } else {
304 l = i + 1;
308 /* not found */
309 goto notfound;
311 } else if (r < 0) {
312 u = i - 1;
313 } else {
314 l = i + 1;
318 notfound:
319 /* not found */
320 errno = ENOMSG;
321 /* LINTED interface problem */
322 return ((char *)s);
326 catclose(nl_catd catd)
328 struct catentry *np;
330 /* sanity checking */
331 if (catd == NULL || catd == NLERR) {
332 errno = EBADF;
333 return (-1);
336 /* Remove from cache if not referenced any more */
337 WLOCK(-1);
338 SLIST_FOREACH(np, &cache, list) {
339 if (catd == np->catd) {
340 np->refcount--;
341 if (np->refcount == 0) {
342 munmap(catd->__data, (size_t)catd->__size);
343 free(catd);
344 SLIST_REMOVE(&cache, np, catentry, list);
345 free(np->name);
346 free(np->path);
347 free(np->lang);
348 free(np);
350 break;
353 UNLOCK;
354 return (0);
358 * Internal support functions
361 static nl_catd
362 load_msgcat(const char *path, const char *name, const char *lang)
364 struct stat st;
365 nl_catd catd;
366 struct catentry *np;
367 void *data;
368 int fd;
370 /* path/name will never be NULL here */
373 * One more try in cache; if it was not found by name,
374 * it might still be found by absolute path.
376 RLOCK(NLERR);
377 SLIST_FOREACH(np, &cache, list) {
378 if ((np->path != NULL) && (strcmp(np->path, path) == 0)) {
379 np->refcount++;
380 UNLOCK;
381 return (np->catd);
384 UNLOCK;
386 if ((fd = _open(path, O_RDONLY | O_CLOEXEC)) == -1) {
387 SAVEFAIL(name, lang, errno);
388 NLRETERR(errno);
391 if (_fstat(fd, &st) != 0) {
392 _close(fd);
393 SAVEFAIL(name, lang, EFTYPE);
394 NLRETERR(EFTYPE);
398 * If the file size cannot be held in size_t we cannot mmap()
399 * it to the memory. Probably, this will not be a problem given
400 * that catalog files are usually small.
402 if (st.st_size > SIZE_T_MAX) {
403 _close(fd);
404 SAVEFAIL(name, lang, EFBIG);
405 NLRETERR(EFBIG);
408 if ((data = mmap(0, (size_t)st.st_size, PROT_READ,
409 MAP_FILE|MAP_SHARED, fd, (off_t)0)) == MAP_FAILED) {
410 int saved_errno = errno;
411 _close(fd);
412 SAVEFAIL(name, lang, saved_errno);
413 NLRETERR(saved_errno);
415 _close(fd);
417 if (ntohl((u_int32_t)((struct _nls_cat_hdr *)data)->__magic) !=
418 _NLS_MAGIC) {
419 munmap(data, (size_t)st.st_size);
420 SAVEFAIL(name, lang, EFTYPE);
421 NLRETERR(EFTYPE);
424 if ((catd = malloc(sizeof (*catd))) == NULL) {
425 munmap(data, (size_t)st.st_size);
426 SAVEFAIL(name, lang, ENOMEM);
427 NLRETERR(ENOMEM);
430 catd->__data = data;
431 catd->__size = (int)st.st_size;
433 /* Caching opened catalog */
434 WLOCK(NLERR);
435 if ((np = malloc(sizeof(struct catentry))) != NULL) {
436 np->name = strdup(name);
437 np->path = strdup(path);
438 np->catd = catd;
439 np->lang = (lang == NULL) ? NULL : strdup(lang);
440 np->refcount = 1;
441 np->caterrno = 0;
442 SLIST_INSERT_HEAD(&cache, np, list);
444 UNLOCK;
445 return (catd);