]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libc/nls/msgcat.c
Merge lldb trunk r366426, resolve conflicts, and update FREEBSD-Xlist.
[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
47 #include <errno.h>
48 #include <fcntl.h>
49 #include <limits.h>
50 #include <nl_types.h>
51 #include <pthread.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <unistd.h>
56 #include "un-namespace.h"
57
58 #include "../locale/xlocale_private.h"
59
60 #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"
61
62 #define RLOCK(fail)     { int ret;                                              \
63                           if (__isthreaded &&                                   \
64                               ((ret = _pthread_rwlock_rdlock(&rwlock)) != 0)) { \
65                                   errno = ret;                                  \
66                                   return (fail);                                \
67                           }}
68 #define WLOCK(fail)     { int ret;                                              \
69                           if (__isthreaded &&                                   \
70                               ((ret = _pthread_rwlock_wrlock(&rwlock)) != 0)) { \
71                                   errno = ret;                                  \
72                                   return (fail);                                \
73                           }}
74 #define UNLOCK          { if (__isthreaded)                                     \
75                               _pthread_rwlock_unlock(&rwlock); }
76
77 #define NLERR           ((nl_catd) -1)
78 #define NLRETERR(errc)  { errno = errc; return (NLERR); }
79 #define SAVEFAIL(n, l, e)       { WLOCK(NLERR);                                 \
80                                   np = malloc(sizeof(struct catentry));         \
81                                   if (np != NULL) {                             \
82                                         np->name = strdup(n);                   \
83                                         np->path = NULL;                        \
84                                         np->catd = NLERR;                       \
85                                         np->refcount = 0;                       \
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, *nlspath, *pathP, *pcode;
118         char *plang, *pter;
119         int saverr, spcleft;
120         const char *lang, *tmpptr;
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 = querylocale(LC_MESSAGES_MASK, __get_locale());
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 static void
329 catfree(struct catentry *np)
330 {
331
332         if (np->catd != NULL && np->catd != NLERR) {
333                 munmap(np->catd->__data, (size_t)np->catd->__size);
334                 free(np->catd);
335         }
336         SLIST_REMOVE(&cache, np, catentry, list);
337         free(np->name);
338         free(np->path);
339         free(np->lang);
340         free(np);
341 }
342
343 int
344 catclose(nl_catd catd)
345 {
346         struct catentry *np;
347
348         /* sanity checking */
349         if (catd == NULL || catd == NLERR) {
350                 errno = EBADF;
351                 return (-1);
352         }
353
354         /* Remove from cache if not referenced any more */
355         WLOCK(-1);
356         SLIST_FOREACH(np, &cache, list) {
357                 if (catd == np->catd) {
358                         np->refcount--;
359                         if (np->refcount == 0)
360                                 catfree(np);
361                         break;
362                 }
363         }
364         UNLOCK;
365         return (0);
366 }
367
368 /*
369  * Internal support functions
370  */
371
372 static nl_catd
373 load_msgcat(const char *path, const char *name, const char *lang)
374 {
375         struct stat st;
376         nl_catd catd;
377         struct catentry *np;
378         void *data;
379         int fd;
380
381         /* path/name will never be NULL here */
382
383         /*
384          * One more try in cache; if it was not found by name,
385          * it might still be found by absolute path.
386          */
387         RLOCK(NLERR);
388         SLIST_FOREACH(np, &cache, list) {
389                 if ((np->path != NULL) && (strcmp(np->path, path) == 0)) {
390                         np->refcount++;
391                         UNLOCK;
392                         return (np->catd);
393                 }
394         }
395         UNLOCK;
396
397         if ((fd = _open(path, O_RDONLY | O_CLOEXEC)) == -1) {
398                 SAVEFAIL(name, lang, errno);
399                 NLRETERR(errno);
400         }
401
402         if (_fstat(fd, &st) != 0) {
403                 _close(fd);
404                 SAVEFAIL(name, lang, EFTYPE);
405                 NLRETERR(EFTYPE);
406         }
407
408         /*
409          * If the file size cannot be held in size_t we cannot mmap()
410          * it to the memory.  Probably, this will not be a problem given
411          * that catalog files are usually small.
412          */
413         if (st.st_size > SIZE_T_MAX) {
414                 _close(fd);
415                 SAVEFAIL(name, lang, EFBIG);
416                 NLRETERR(EFBIG);
417         }
418
419         if ((data = mmap(0, (size_t)st.st_size, PROT_READ,
420             MAP_FILE|MAP_SHARED, fd, (off_t)0)) == MAP_FAILED) {
421                 int saved_errno = errno;
422                 _close(fd);
423                 SAVEFAIL(name, lang, saved_errno);
424                 NLRETERR(saved_errno);
425         }
426         _close(fd);
427
428         if (ntohl((u_int32_t)((struct _nls_cat_hdr *)data)->__magic) !=
429             _NLS_MAGIC) {
430                 munmap(data, (size_t)st.st_size);
431                 SAVEFAIL(name, lang, EFTYPE);
432                 NLRETERR(EFTYPE);
433         }
434
435         if ((catd = malloc(sizeof (*catd))) == NULL) {
436                 munmap(data, (size_t)st.st_size);
437                 SAVEFAIL(name, lang, ENOMEM);
438                 NLRETERR(ENOMEM);
439         }
440
441         catd->__data = data;
442         catd->__size = (int)st.st_size;
443
444         /* Caching opened catalog */
445         WLOCK(NLERR);
446         if ((np = malloc(sizeof(struct catentry))) != NULL) {
447                 np->name = strdup(name);
448                 np->path = strdup(path);
449                 np->catd = catd;
450                 np->lang = (lang == NULL) ? NULL : strdup(lang);
451                 np->refcount = 1;
452                 np->caterrno = 0;
453                 SLIST_INSERT_HEAD(&cache, np, list);
454         }
455         UNLOCK;
456         return (catd);
457 }