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 } 463