msgcat.c revision 304863
1/*********************************************************** 2Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts. 3Copyright 2010, Gabor Kovesdan <gabor@FreeBSD.org> 4 5 All Rights Reserved 6 7Permission to use, copy, modify, and distribute this software and its 8documentation for any purpose and without fee is hereby granted, 9provided that the above copyright notice appear in all copies and that 10both that copyright notice and this permission notice appear in 11supporting documentation, and that Alfalfa's name not be used in 12advertising or publicity pertaining to distribution of the software 13without specific, written prior permission. 14 15ALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 16ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL 17ALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 18ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 19WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 20ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 21SOFTWARE. 22 23If you make any modifications, bugfixes or other changes to this software 24we'd appreciate it if you could send a copy to us so we can keep things 25up-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: stable/11/lib/libc/nls/msgcat.c 304863 2016-08-26 21:23:38Z ache $"); 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 95static nl_catd load_msgcat(const char *, const char *, const char *); 96 97static pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; 98 99struct 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 109SLIST_HEAD(listhead, catentry) cache = 110 SLIST_HEAD_INITIALIZER(cache); 111 112nl_catd 113catopen(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 260char * 261catgets(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 321notfound: 322 /* not found */ 323 errno = ENOMSG; 324 /* LINTED interface problem */ 325 return ((char *)s); 326} 327 328static void 329catfree(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 343int 344catclose(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 372static nl_catd 373load_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} 458