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