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