]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libc/nls/msgcat.c
acpica: Create merge commit against vendor branch
[FreeBSD/FreeBSD.git] / lib / libc / nls / msgcat.c
1 /***********************************************************
2 Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts.
3 Copyright 2010, Gabor Kovesdan <gabor@FreeBSD.org>
4
5                         All Rights Reserved
6
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.
14
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.
22
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
31
32 ******************************************************************/
33
34 #define _NLS_PRIVATE
35
36 #include "namespace.h"
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <sys/mman.h>
40 #include <sys/queue.h>
41
42 #include <arpa/inet.h>          /* for ntohl() */
43 #include <machine/atomic.h>
44
45 #include <errno.h>
46 #include <fcntl.h>
47 #include <limits.h>
48 #include <nl_types.h>
49 #include <paths.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"
56
57 #include "../locale/xlocale_private.h"
58 #include "libc_private.h"
59
60 #define _DEFAULT_NLS_PATH "/usr/share/nls/%L/%N.cat:/usr/share/nls/%N/%L:"      \
61                                 _PATH_LOCALBASE "/share/nls/%L/%N.cat:"         \
62                                 _PATH_LOCALBASE "/share/nls/%N/%L"
63
64 #define RLOCK(fail)     { int ret;                                              \
65                           if (__isthreaded &&                                   \
66                               ((ret = _pthread_rwlock_rdlock(&rwlock)) != 0)) { \
67                                   errno = ret;                                  \
68                                   return (fail);                                \
69                           }}
70 #define WLOCK(fail)     { int ret;                                              \
71                           if (__isthreaded &&                                   \
72                               ((ret = _pthread_rwlock_wrlock(&rwlock)) != 0)) { \
73                                   errno = ret;                                  \
74                                   return (fail);                                \
75                           }}
76 #define UNLOCK          { if (__isthreaded)                                     \
77                               _pthread_rwlock_unlock(&rwlock); }
78
79 #define NLERR           ((nl_catd) -1)
80 #define NLRETERR(errc)  { errno = errc; return (NLERR); }
81 #define SAVEFAIL(n, l, e)       { np = calloc(1, sizeof(struct catentry));      \
82                                   if (np != NULL) {                             \
83                                         np->name = strdup(n);                   \
84                                         np->catd = NLERR;                       \
85                                         np->lang = (l == NULL) ? NULL :         \
86                                             strdup(l);                          \
87                                         np->caterrno = e;                       \
88                                         if (np->name == NULL ||                 \
89                                             (l != NULL && np->lang == NULL)) {  \
90                                                 free(np->name);                 \
91                                                 free(np->lang);                 \
92                                                 free(np);                       \
93                                         } else {                                \
94                                                 WLOCK(NLERR);                   \
95                                                 SLIST_INSERT_HEAD(&cache, np,   \
96                                                     list);                      \
97                                                 UNLOCK;                         \
98                                         }                                       \
99                                   }                                             \
100                                   errno = e;                                    \
101                                 }
102
103 static nl_catd load_msgcat(const char *, const char *, const char *);
104
105 static pthread_rwlock_t          rwlock = PTHREAD_RWLOCK_INITIALIZER;
106
107 struct catentry {
108         SLIST_ENTRY(catentry)    list;
109         char                    *name;
110         char                    *path;
111         int                      caterrno;
112         nl_catd                  catd;
113         char                    *lang;
114         int                      refcount;
115 };
116
117 SLIST_HEAD(listhead, catentry) cache =
118     SLIST_HEAD_INITIALIZER(cache);
119
120 nl_catd
121 catopen(const char *name, int type)
122 {
123         return (__catopen_l(name, type, __get_locale()));
124 }
125
126 nl_catd
127 __catopen_l(const char *name, int type, locale_t locale)
128 {
129         struct stat sbuf;
130         struct catentry *np;
131         char *base, *cptr, *cptr1, *nlspath, *pathP, *pcode;
132         char *plang, *pter;
133         int saverr, spcleft;
134         const char *lang, *tmpptr;
135         char path[PATH_MAX];
136
137         /* sanity checking */
138         if (name == NULL || *name == '\0')
139                 NLRETERR(EINVAL);
140
141         if (strchr(name, '/') != NULL)
142                 /* have a pathname */
143                 lang = NULL;
144         else {
145                 if (type == NL_CAT_LOCALE)
146                         lang = querylocale(LC_MESSAGES_MASK, locale);
147                 else
148                         lang = getenv("LANG");
149
150                 if (lang == NULL || *lang == '\0' || strlen(lang) > ENCODING_LEN ||
151                     (lang[0] == '.' &&
152                     (lang[1] == '\0' || (lang[1] == '.' && lang[2] == '\0'))) ||
153                     strchr(lang, '/') != NULL)
154                         lang = "C";
155         }
156
157         /* Try to get it from the cache first */
158         RLOCK(NLERR);
159         SLIST_FOREACH(np, &cache, list) {
160                 if ((strcmp(np->name, name) == 0) &&
161                     ((lang != NULL && np->lang != NULL &&
162                     strcmp(np->lang, lang) == 0) || (np->lang == lang))) {
163                         if (np->caterrno != 0) {
164                                 /* Found cached failing entry */
165                                 UNLOCK;
166                                 NLRETERR(np->caterrno);
167                         } else {
168                                 /* Found cached successful entry */
169                                 atomic_add_int(&np->refcount, 1);
170                                 UNLOCK;
171                                 return (np->catd);
172                         }
173                 }
174         }
175         UNLOCK;
176
177         /* is it absolute path ? if yes, load immediately */
178         if (strchr(name, '/') != NULL)
179                 return (load_msgcat(name, name, lang));
180
181         /* sanity checking */
182         if ((plang = cptr1 = strdup(lang)) == NULL)
183                 return (NLERR);
184         if ((cptr = strchr(cptr1, '@')) != NULL)
185                 *cptr = '\0';
186         pter = pcode = "";
187         if ((cptr = strchr(cptr1, '_')) != NULL) {
188                 *cptr++ = '\0';
189                 pter = cptr1 = cptr;
190         }
191         if ((cptr = strchr(cptr1, '.')) != NULL) {
192                 *cptr++ = '\0';
193                 pcode = cptr;
194         }
195
196         if ((nlspath = secure_getenv("NLSPATH")) == NULL)
197                 nlspath = _DEFAULT_NLS_PATH;
198
199         if ((base = cptr = strdup(nlspath)) == NULL) {
200                 saverr = errno;
201                 free(plang);
202                 errno = saverr;
203                 return (NLERR);
204         }
205
206         while ((nlspath = strsep(&cptr, ":")) != NULL) {
207                 pathP = path;
208                 if (*nlspath) {
209                         for (; *nlspath; ++nlspath) {
210                                 if (*nlspath == '%') {
211                                         switch (*(nlspath + 1)) {
212                                         case 'l':
213                                                 tmpptr = plang;
214                                                 break;
215                                         case 't':
216                                                 tmpptr = pter;
217                                                 break;
218                                         case 'c':
219                                                 tmpptr = pcode;
220                                                 break;
221                                         case 'L':
222                                                 tmpptr = lang;
223                                                 break;
224                                         case 'N':
225                                                 tmpptr = (char *)name;
226                                                 break;
227                                         case '%':
228                                                 ++nlspath;
229                                                 /* FALLTHROUGH */
230                                         default:
231                                                 if (pathP - path >=
232                                                     sizeof(path) - 1)
233                                                         goto too_long;
234                                                 *(pathP++) = *nlspath;
235                                                 continue;
236                                         }
237                                         ++nlspath;
238                         put_tmpptr:
239                                         spcleft = sizeof(path) -
240                                                   (pathP - path) - 1;
241                                         if (strlcpy(pathP, tmpptr, spcleft) >=
242                                             spcleft) {
243                         too_long:
244                                                 free(plang);
245                                                 free(base);
246                                                 SAVEFAIL(name, lang, ENAMETOOLONG);
247                                                 NLRETERR(ENAMETOOLONG);
248                                         }
249                                         pathP += strlen(tmpptr);
250                                 } else {
251                                         if (pathP - path >= sizeof(path) - 1)
252                                                 goto too_long;
253                                         *(pathP++) = *nlspath;
254                                 }
255                         }
256                         *pathP = '\0';
257                         if (stat(path, &sbuf) == 0) {
258                                 free(plang);
259                                 free(base);
260                                 return (load_msgcat(path, name, lang));
261                         }
262                 } else {
263                         tmpptr = (char *)name;
264                         --nlspath;
265                         goto put_tmpptr;
266                 }
267         }
268         free(plang);
269         free(base);
270         SAVEFAIL(name, lang, ENOENT);
271         NLRETERR(ENOENT);
272 }
273
274 char *
275 catgets(nl_catd catd, int set_id, int msg_id, const char *s)
276 {
277         struct _nls_cat_hdr *cat_hdr;
278         struct _nls_msg_hdr *msg_hdr;
279         struct _nls_set_hdr *set_hdr;
280         int i, l, r, u;
281
282         if (catd == NULL || catd == NLERR) {
283                 errno = EBADF;
284                 /* LINTED interface problem */
285                 return ((char *)s);
286         }
287
288         cat_hdr = (struct _nls_cat_hdr *)catd->__data;
289         set_hdr = (struct _nls_set_hdr *)(void *)((char *)catd->__data +
290             sizeof(struct _nls_cat_hdr));
291
292         /* binary search, see knuth algorithm b */
293         l = 0;
294         u = ntohl((u_int32_t)cat_hdr->__nsets) - 1;
295         while (l <= u) {
296                 i = (l + u) / 2;
297                 r = set_id - ntohl((u_int32_t)set_hdr[i].__setno);
298
299                 if (r == 0) {
300                         msg_hdr = (struct _nls_msg_hdr *)
301                             (void *)((char *)catd->__data +
302                             sizeof(struct _nls_cat_hdr) +
303                             ntohl((u_int32_t)cat_hdr->__msg_hdr_offset));
304
305                         l = ntohl((u_int32_t)set_hdr[i].__index);
306                         u = l + ntohl((u_int32_t)set_hdr[i].__nmsgs) - 1;
307                         while (l <= u) {
308                                 i = (l + u) / 2;
309                                 r = msg_id -
310                                     ntohl((u_int32_t)msg_hdr[i].__msgno);
311                                 if (r == 0) {
312                                         return ((char *) catd->__data +
313                                             sizeof(struct _nls_cat_hdr) +
314                                             ntohl((u_int32_t)
315                                             cat_hdr->__msg_txt_offset) +
316                                             ntohl((u_int32_t)
317                                             msg_hdr[i].__offset));
318                                 } else if (r < 0) {
319                                         u = i - 1;
320                                 } else {
321                                         l = i + 1;
322                                 }
323                         }
324
325                         /* not found */
326                         goto notfound;
327
328                 } else if (r < 0) {
329                         u = i - 1;
330                 } else {
331                         l = i + 1;
332                 }
333         }
334
335 notfound:
336         /* not found */
337         errno = ENOMSG;
338         /* LINTED interface problem */
339         return ((char *)s);
340 }
341
342 static void
343 catfree(struct catentry *np)
344 {
345
346         if (np->catd != NULL && np->catd != NLERR) {
347                 munmap(np->catd->__data, (size_t)np->catd->__size);
348                 free(np->catd);
349         }
350         SLIST_REMOVE(&cache, np, catentry, list);
351         free(np->name);
352         free(np->path);
353         free(np->lang);
354         free(np);
355 }
356
357 int
358 catclose(nl_catd catd)
359 {
360         struct catentry *np;
361
362         /* sanity checking */
363         if (catd == NULL || catd == NLERR) {
364                 errno = EBADF;
365                 return (-1);
366         }
367
368         /* Remove from cache if not referenced any more */
369         WLOCK(-1);
370         SLIST_FOREACH(np, &cache, list) {
371                 if (catd == np->catd) {
372                         if (atomic_fetchadd_int(&np->refcount, -1) == 1)
373                                 catfree(np);
374                         break;
375                 }
376         }
377         UNLOCK;
378         return (0);
379 }
380
381 /*
382  * Internal support functions
383  */
384
385 static nl_catd
386 load_msgcat(const char *path, const char *name, const char *lang)
387 {
388         struct stat st;
389         nl_catd catd;
390         struct catentry *np;
391         void *data;
392         char *copy_path, *copy_name, *copy_lang;
393         int fd;
394
395         /* path/name will never be NULL here */
396
397         /*
398          * One more try in cache; if it was not found by name,
399          * it might still be found by absolute path.
400          */
401         RLOCK(NLERR);
402         SLIST_FOREACH(np, &cache, list) {
403                 if ((np->path != NULL) && (strcmp(np->path, path) == 0)) {
404                         atomic_add_int(&np->refcount, 1);
405                         UNLOCK;
406                         return (np->catd);
407                 }
408         }
409         UNLOCK;
410
411         if ((fd = _open(path, O_RDONLY | O_CLOEXEC)) == -1) {
412                 SAVEFAIL(name, lang, errno);
413                 NLRETERR(errno);
414         }
415
416         if (_fstat(fd, &st) != 0) {
417                 _close(fd);
418                 SAVEFAIL(name, lang, EFTYPE);
419                 NLRETERR(EFTYPE);
420         }
421
422         /*
423          * If the file size cannot be held in size_t we cannot mmap()
424          * it to the memory.  Probably, this will not be a problem given
425          * that catalog files are usually small.
426          */
427         if (st.st_size > SIZE_T_MAX) {
428                 _close(fd);
429                 SAVEFAIL(name, lang, EFBIG);
430                 NLRETERR(EFBIG);
431         }
432
433         if ((data = mmap(0, (size_t)st.st_size, PROT_READ,
434             MAP_FILE|MAP_SHARED, fd, (off_t)0)) == MAP_FAILED) {
435                 int saved_errno = errno;
436                 _close(fd);
437                 SAVEFAIL(name, lang, saved_errno);
438                 NLRETERR(saved_errno);
439         }
440         _close(fd);
441
442         if (ntohl((u_int32_t)((struct _nls_cat_hdr *)data)->__magic) !=
443             _NLS_MAGIC) {
444                 munmap(data, (size_t)st.st_size);
445                 SAVEFAIL(name, lang, EFTYPE);
446                 NLRETERR(EFTYPE);
447         }
448
449         copy_name = strdup(name);
450         copy_path = strdup(path);
451         copy_lang = (lang == NULL) ? NULL : strdup(lang);
452         catd = malloc(sizeof (*catd));
453         np = calloc(1, sizeof(struct catentry));
454
455         if (copy_name == NULL || copy_path == NULL ||
456             (lang != NULL && copy_lang == NULL) ||
457             catd == NULL || np == NULL) {
458                 free(copy_name);
459                 free(copy_path);
460                 free(copy_lang);
461                 free(catd);
462                 free(np);
463                 munmap(data, (size_t)st.st_size);
464                 SAVEFAIL(name, lang, ENOMEM);
465                 NLRETERR(ENOMEM);
466         }
467
468         catd->__data = data;
469         catd->__size = (int)st.st_size;
470
471         /* Caching opened catalog */
472         np->name = copy_name;
473         np->path = copy_path;
474         np->catd = catd;
475         np->lang = copy_lang;
476         atomic_store_int(&np->refcount, 1);
477         WLOCK(NLERR);
478         SLIST_INSERT_HEAD(&cache, np, list);
479         UNLOCK;
480         return (catd);
481 }