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