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