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