xref: /dragonfly/lib/libc/nls/msgcat.c (revision 8ad5bb66841bad6639afa5a7a92e2ce2bb173f17)
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
catopen(const char * name,int type)112 catopen(const char *name, int type)
113 {
114           return (__catopen_l(name, type, __get_locale()));
115 }
116 
117 nl_catd
__catopen_l(const char * name,int type,locale_t locale)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 *
catgets(nl_catd catd,int set_id,int msg_id,const char * s)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
catfree(struct catentry * np)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
catclose(nl_catd catd)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
load_msgcat(const char * path,const char * name,const char * lang)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 }
463