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