17494Sjkh/*********************************************************** 27494SjkhCopyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts. 3202992SgaborCopyright 2010, Gabor Kovesdan <gabor@FreeBSD.org> 47494Sjkh 57494Sjkh All Rights Reserved 67494Sjkh 77494SjkhPermission to use, copy, modify, and distribute this software and its 87494Sjkhdocumentation for any purpose and without fee is hereby granted, 97494Sjkhprovided that the above copyright notice appear in all copies and that 107494Sjkhboth that copyright notice and this permission notice appear in 117494Sjkhsupporting documentation, and that Alfalfa's name not be used in 127494Sjkhadvertising or publicity pertaining to distribution of the software 137494Sjkhwithout specific, written prior permission. 147494Sjkh 157494SjkhALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 167494SjkhALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL 177494SjkhALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 187494SjkhANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 197494SjkhWHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 207494SjkhARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 217494SjkhSOFTWARE. 227494Sjkh 237494SjkhIf you make any modifications, bugfixes or other changes to this software 247494Sjkhwe'd appreciate it if you could send a copy to us so we can keep things 257494Sjkhup-to-date. Many thanks. 267494Sjkh Kee Hinckley 277494Sjkh Alfalfa Software, Inc. 287494Sjkh 267 Allston St., #3 297494Sjkh Cambridge, MA 02139 USA 307494Sjkh nazgul@alfalfa.com 318870Srgrimes 327494Sjkh******************************************************************/ 337494Sjkh 3492986Sobrien#include <sys/cdefs.h> 3592986Sobrien__FBSDID("$FreeBSD$"); 367494Sjkh 37142664Sphantom#define _NLS_PRIVATE 387494Sjkh 3971579Sdeischen#include "namespace.h" 407494Sjkh#include <sys/types.h> 417494Sjkh#include <sys/stat.h> 42142664Sphantom#include <sys/mman.h> 43202992Sgabor#include <sys/queue.h> 44106053Swollman 45142664Sphantom#include <arpa/inet.h> /* for ntohl() */ 46142664Sphantom 4737643Sache#include <errno.h> 487494Sjkh#include <fcntl.h> 49106053Swollman#include <limits.h> 5035548Sache#include <locale.h> 5165326Sphantom#include <nl_types.h> 52202992Sgabor#include <pthread.h> 537494Sjkh#include <stdio.h> 547494Sjkh#include <stdlib.h> 557494Sjkh#include <string.h> 567494Sjkh#include <unistd.h> 5771579Sdeischen#include "un-namespace.h" 587494Sjkh 59101316Sache#include "../locale/setlocale.h" /* for ENCODING_LEN */ 6065326Sphantom 6165436Sphantom#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" 627494Sjkh 63204110Sgabor#define RLOCK(fail) { int ret; \ 64204110Sgabor if (__isthreaded && \ 65204110Sgabor ((ret = _pthread_rwlock_rdlock(&rwlock)) != 0)) { \ 66204110Sgabor errno = ret; \ 67204110Sgabor return (fail); \ 68202992Sgabor }} 69204110Sgabor#define WLOCK(fail) { int ret; \ 70204110Sgabor if (__isthreaded && \ 71204110Sgabor ((ret = _pthread_rwlock_wrlock(&rwlock)) != 0)) { \ 72204110Sgabor errno = ret; \ 73204110Sgabor return (fail); \ 74202992Sgabor }} 75204110Sgabor#define UNLOCK { if (__isthreaded) \ 76202992Sgabor _pthread_rwlock_unlock(&rwlock); } 77202992Sgabor 7865436Sphantom#define NLERR ((nl_catd) -1) 79101316Sache#define NLRETERR(errc) { errno = errc; return (NLERR); } 80204110Sgabor#define SAVEFAIL(n, l, e) { WLOCK(NLERR); \ 81204110Sgabor np = malloc(sizeof(struct catentry)); \ 82204110Sgabor if (np != NULL) { \ 83204110Sgabor np->name = strdup(n); \ 84204110Sgabor np->path = NULL; \ 85244358Seadler np->catd = NLERR; \ 86204110Sgabor np->lang = (l == NULL) ? NULL : \ 87204110Sgabor strdup(l); \ 88204110Sgabor np->caterrno = e; \ 89204110Sgabor SLIST_INSERT_HEAD(&cache, np, list); \ 90204110Sgabor } \ 91204110Sgabor UNLOCK; \ 92204110Sgabor errno = e; \ 93203719Sgabor } 9465436Sphantom 95202992Sgaborstatic nl_catd load_msgcat(const char *, const char *, const char *); 967494Sjkh 97203719Sgaborstatic pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; 98202992Sgabor 99202992Sgaborstruct catentry { 100202992Sgabor SLIST_ENTRY(catentry) list; 101202992Sgabor char *name; 102202992Sgabor char *path; 103202992Sgabor int caterrno; 104202992Sgabor nl_catd catd; 105202992Sgabor char *lang; 106202992Sgabor int refcount; 107202992Sgabor}; 108202992Sgabor 109202992SgaborSLIST_HEAD(listhead, catentry) cache = 110202992Sgabor SLIST_HEAD_INITIALIZER(cache); 111202992Sgabor 11265436Sphantomnl_catd 113142664Sphantomcatopen(const char *name, int type) 1147494Sjkh{ 115203174Sgabor struct stat sbuf; 116203174Sgabor struct catentry *np; 117203174Sgabor char *base, *cptr, *cptr1, *lang, *nlspath, *pathP, *pcode; 118203174Sgabor char *plang, *pter, *tmpptr; 119203174Sgabor int saverr, spcleft; 120203174Sgabor char path[PATH_MAX]; 1218870Srgrimes 122203719Sgabor /* sanity checking */ 123101316Sache if (name == NULL || *name == '\0') 124101316Sache NLRETERR(EINVAL); 1257494Sjkh 126101316Sache if (strchr(name, '/') != NULL) 127203719Sgabor /* have a pathname */ 128202992Sgabor lang = NULL; 129202992Sgabor else { 130202993Sgabor if (type == NL_CAT_LOCALE) 131202993Sgabor lang = setlocale(LC_MESSAGES, NULL); 132202993Sgabor else 133202993Sgabor lang = getenv("LANG"); 13465476Sache 135202993Sgabor if (lang == NULL || *lang == '\0' || strlen(lang) > ENCODING_LEN || 136202993Sgabor (lang[0] == '.' && 137202993Sgabor (lang[1] == '\0' || (lang[1] == '.' && lang[2] == '\0'))) || 138202993Sgabor strchr(lang, '/') != NULL) 139202993Sgabor lang = "C"; 140202992Sgabor } 14165476Sache 142202992Sgabor /* Try to get it from the cache first */ 143202992Sgabor RLOCK(NLERR); 144202992Sgabor SLIST_FOREACH(np, &cache, list) { 145203719Sgabor if ((strcmp(np->name, name) == 0) && 146203719Sgabor ((lang != NULL && np->lang != NULL && 147203719Sgabor strcmp(np->lang, lang) == 0) || (np->lang == lang))) { 148202992Sgabor if (np->caterrno != 0) { 149202992Sgabor /* Found cached failing entry */ 150202992Sgabor UNLOCK; 151202992Sgabor NLRETERR(np->caterrno); 152203719Sgabor } else { 153202992Sgabor /* Found cached successful entry */ 154202992Sgabor np->refcount++; 155202992Sgabor UNLOCK; 156202992Sgabor return (np->catd); 157202992Sgabor } 158202992Sgabor } 159202992Sgabor } 160202992Sgabor UNLOCK; 161202992Sgabor 162202992Sgabor /* is it absolute path ? if yes, load immediately */ 163202992Sgabor if (strchr(name, '/') != NULL) 164202992Sgabor return (load_msgcat(name, name, lang)); 165202992Sgabor 166203719Sgabor /* sanity checking */ 167101727Sache if ((plang = cptr1 = strdup(lang)) == NULL) 168101316Sache return (NLERR); 169101316Sache if ((cptr = strchr(cptr1, '@')) != NULL) 170101316Sache *cptr = '\0'; 171101316Sache pter = pcode = ""; 172101316Sache if ((cptr = strchr(cptr1, '_')) != NULL) { 173101316Sache *cptr++ = '\0'; 174101316Sache pter = cptr1 = cptr; 175101316Sache } 176101316Sache if ((cptr = strchr(cptr1, '.')) != NULL) { 177101316Sache *cptr++ = '\0'; 178101316Sache pcode = cptr; 179101316Sache } 180101316Sache 181121667Stjr if ((nlspath = getenv("NLSPATH")) == NULL || issetugid()) 182101316Sache nlspath = _DEFAULT_NLS_PATH; 1838870Srgrimes 184101316Sache if ((base = cptr = strdup(nlspath)) == NULL) { 185101727Sache saverr = errno; 186101316Sache free(plang); 187101727Sache errno = saverr; 188101316Sache return (NLERR); 189101316Sache } 1908870Srgrimes 191101316Sache while ((nlspath = strsep(&cptr, ":")) != NULL) { 192101316Sache pathP = path; 193101316Sache if (*nlspath) { 194101316Sache for (; *nlspath; ++nlspath) { 195101316Sache if (*nlspath == '%') { 196101316Sache switch (*(nlspath + 1)) { 197101316Sache case 'l': 198101316Sache tmpptr = plang; 199101316Sache break; 200101316Sache case 't': 201101316Sache tmpptr = pter; 202101316Sache break; 203101316Sache case 'c': 204101316Sache tmpptr = pcode; 205101316Sache break; 206101316Sache case 'L': 207101316Sache tmpptr = lang; 208101316Sache break; 209101316Sache case 'N': 210101316Sache tmpptr = (char *)name; 211101316Sache break; 212101316Sache case '%': 213101316Sache ++nlspath; 214204110Sgabor /* FALLTHROUGH */ 215101316Sache default: 216101316Sache if (pathP - path >= 217101316Sache sizeof(path) - 1) 218101316Sache goto too_long; 219101316Sache *(pathP++) = *nlspath; 220101316Sache continue; 221101316Sache } 222101316Sache ++nlspath; 223101316Sache put_tmpptr: 224101316Sache spcleft = sizeof(path) - 225101316Sache (pathP - path) - 1; 226114443Snectar if (strlcpy(pathP, tmpptr, spcleft) >= 227101316Sache spcleft) { 228141118Sphantom too_long: 229101316Sache free(plang); 230101316Sache free(base); 231203719Sgabor SAVEFAIL(name, lang, ENAMETOOLONG); 232101316Sache NLRETERR(ENAMETOOLONG); 233101316Sache } 234101316Sache pathP += strlen(tmpptr); 235101316Sache } else { 236101316Sache if (pathP - path >= sizeof(path) - 1) 237101316Sache goto too_long; 238101316Sache *(pathP++) = *nlspath; 239101316Sache } 24065436Sphantom } 241101316Sache *pathP = '\0'; 242101316Sache if (stat(path, &sbuf) == 0) { 24365476Sache free(plang); 24465323Sphantom free(base); 245202992Sgabor return (load_msgcat(path, name, lang)); 24665436Sphantom } 247101316Sache } else { 248101316Sache tmpptr = (char *)name; 249101316Sache --nlspath; 250101316Sache goto put_tmpptr; 2517494Sjkh } 2527494Sjkh } 253101316Sache free(plang); 254101316Sache free(base); 255203719Sgabor SAVEFAIL(name, lang, ENOENT); 256101316Sache NLRETERR(ENOENT); 2577494Sjkh} 2587494Sjkh 259142664Sphantomchar * 260142664Sphantomcatgets(nl_catd catd, int set_id, int msg_id, const char *s) 261142664Sphantom{ 262203174Sgabor struct _nls_cat_hdr *cat_hdr; 263203174Sgabor struct _nls_msg_hdr *msg_hdr; 264203174Sgabor struct _nls_set_hdr *set_hdr; 265203174Sgabor int i, l, r, u; 26665436Sphantom 267142664Sphantom if (catd == NULL || catd == NLERR) { 268142664Sphantom errno = EBADF; 269142664Sphantom /* LINTED interface problem */ 270202992Sgabor return ((char *)s); 271202992Sgabor } 27265436Sphantom 273202992Sgabor cat_hdr = (struct _nls_cat_hdr *)catd->__data; 274202992Sgabor set_hdr = (struct _nls_set_hdr *)(void *)((char *)catd->__data + 275202992Sgabor sizeof(struct _nls_cat_hdr)); 2767494Sjkh 277142664Sphantom /* binary search, see knuth algorithm b */ 278142664Sphantom l = 0; 279142664Sphantom u = ntohl((u_int32_t)cat_hdr->__nsets) - 1; 280142664Sphantom while (l <= u) { 281142664Sphantom i = (l + u) / 2; 282142664Sphantom r = set_id - ntohl((u_int32_t)set_hdr[i].__setno); 2837494Sjkh 284142664Sphantom if (r == 0) { 285142664Sphantom msg_hdr = (struct _nls_msg_hdr *) 286142664Sphantom (void *)((char *)catd->__data + 287142664Sphantom sizeof(struct _nls_cat_hdr) + 288142664Sphantom ntohl((u_int32_t)cat_hdr->__msg_hdr_offset)); 2898870Srgrimes 290142664Sphantom l = ntohl((u_int32_t)set_hdr[i].__index); 291142664Sphantom u = l + ntohl((u_int32_t)set_hdr[i].__nmsgs) - 1; 292142664Sphantom while (l <= u) { 293142664Sphantom i = (l + u) / 2; 294142664Sphantom r = msg_id - 295142664Sphantom ntohl((u_int32_t)msg_hdr[i].__msgno); 296142664Sphantom if (r == 0) { 297142664Sphantom return ((char *) catd->__data + 298142664Sphantom sizeof(struct _nls_cat_hdr) + 299142664Sphantom ntohl((u_int32_t) 300142664Sphantom cat_hdr->__msg_txt_offset) + 301142664Sphantom ntohl((u_int32_t) 302142664Sphantom msg_hdr[i].__offset)); 303142664Sphantom } else if (r < 0) { 304142664Sphantom u = i - 1; 305142664Sphantom } else { 306142664Sphantom l = i + 1; 307142664Sphantom } 308202992Sgabor } 3097494Sjkh 310142664Sphantom /* not found */ 311142664Sphantom goto notfound; 3127494Sjkh 313142664Sphantom } else if (r < 0) { 314142664Sphantom u = i - 1; 315142664Sphantom } else { 316142664Sphantom l = i + 1; 317142664Sphantom } 318202992Sgabor } 3197494Sjkh 320142664Sphantomnotfound: 321142664Sphantom /* not found */ 322142664Sphantom errno = ENOMSG; 323142664Sphantom /* LINTED interface problem */ 324202992Sgabor return ((char *)s); 325142664Sphantom} 326142664Sphantom 32765436Sphantomint 328141118Sphantomcatclose(nl_catd catd) 3297494Sjkh{ 330203174Sgabor struct catentry *np; 331202992Sgabor 332203719Sgabor /* sanity checking */ 333101316Sache if (catd == NULL || catd == NLERR) { 334101316Sache errno = EBADF; 335101316Sache return (-1); 336101316Sache } 337141118Sphantom 338202992Sgabor /* Remove from cache if not referenced any more */ 339202992Sgabor WLOCK(-1); 340202992Sgabor SLIST_FOREACH(np, &cache, list) { 341203719Sgabor if (catd == np->catd) { 342202992Sgabor np->refcount--; 343202992Sgabor if (np->refcount == 0) { 344202993Sgabor munmap(catd->__data, (size_t)catd->__size); 345202993Sgabor free(catd); 346202992Sgabor SLIST_REMOVE(&cache, np, catentry, list); 347203719Sgabor free(np->name); 348203719Sgabor free(np->path); 349203719Sgabor free(np->lang); 350202992Sgabor free(np); 351202992Sgabor } 352202992Sgabor break; 353202992Sgabor } 354202992Sgabor } 355202992Sgabor UNLOCK; 356101316Sache return (0); 3577494Sjkh} 3587494Sjkh 3597494Sjkh/* 360142664Sphantom * Internal support functions 3617494Sjkh */ 3627494Sjkh 363142664Sphantomstatic nl_catd 364202992Sgaborload_msgcat(const char *path, const char *name, const char *lang) 3657494Sjkh{ 366203174Sgabor struct stat st; 367203174Sgabor nl_catd catd; 368203174Sgabor struct catentry *np; 369203174Sgabor void *data; 370203174Sgabor int fd; 37165436Sphantom 372202992Sgabor /* path/name will never be NULL here */ 37365436Sphantom 374204110Sgabor /* 375204110Sgabor * One more try in cache; if it was not found by name, 376203719Sgabor * it might still be found by absolute path. 377203719Sgabor */ 378202992Sgabor RLOCK(NLERR); 379202992Sgabor SLIST_FOREACH(np, &cache, list) { 380203719Sgabor if ((np->path != NULL) && (strcmp(np->path, path) == 0)) { 381202992Sgabor np->refcount++; 382202992Sgabor UNLOCK; 383202992Sgabor return (np->catd); 384202992Sgabor } 385202992Sgabor } 386202992Sgabor UNLOCK; 387202992Sgabor 388241046Sjilles if ((fd = _open(path, O_RDONLY | O_CLOEXEC)) == -1) { 389203719Sgabor SAVEFAIL(name, lang, errno); 390203719Sgabor NLRETERR(errno); 391202992Sgabor } 3927494Sjkh 393142664Sphantom if (_fstat(fd, &st) != 0) { 394142664Sphantom _close(fd); 395203719Sgabor SAVEFAIL(name, lang, EFTYPE); 396203719Sgabor NLRETERR(EFTYPE); 397101316Sache } 3987494Sjkh 399204110Sgabor /* 400204110Sgabor * If the file size cannot be held in size_t we cannot mmap() 401204110Sgabor * it to the memory. Probably, this will not be a problem given 402203719Sgabor * that catalog files are usually small. 403203719Sgabor */ 404203719Sgabor if (st.st_size > SIZE_T_MAX) { 405203719Sgabor _close(fd); 406203719Sgabor SAVEFAIL(name, lang, EFBIG); 407203719Sgabor NLRETERR(EFBIG); 408203719Sgabor } 4097494Sjkh 410203719Sgabor if ((data = mmap(0, (size_t)st.st_size, PROT_READ, 411203719Sgabor MAP_FILE|MAP_SHARED, fd, (off_t)0)) == MAP_FAILED) { 412203719Sgabor int saved_errno = errno; 413203719Sgabor _close(fd); 414203719Sgabor SAVEFAIL(name, lang, saved_errno); 415203719Sgabor NLRETERR(saved_errno); 416202992Sgabor } 417203719Sgabor _close(fd); 418142664Sphantom 419142664Sphantom if (ntohl((u_int32_t)((struct _nls_cat_hdr *)data)->__magic) != 420142664Sphantom _NLS_MAGIC) { 421142664Sphantom munmap(data, (size_t)st.st_size); 422203719Sgabor SAVEFAIL(name, lang, EFTYPE); 423203719Sgabor NLRETERR(EFTYPE); 424101316Sache } 4257494Sjkh 426142664Sphantom if ((catd = malloc(sizeof (*catd))) == NULL) { 427142664Sphantom munmap(data, (size_t)st.st_size); 428203719Sgabor SAVEFAIL(name, lang, ENOMEM); 429203719Sgabor NLRETERR(ENOMEM); 43025642Sache } 431141118Sphantom 432142664Sphantom catd->__data = data; 433142664Sphantom catd->__size = (int)st.st_size; 434202992Sgabor 435202992Sgabor /* Caching opened catalog */ 436202992Sgabor WLOCK(NLERR); 437202992Sgabor if ((np = malloc(sizeof(struct catentry))) != NULL) { 438202992Sgabor np->name = strdup(name); 439202992Sgabor np->path = strdup(path); 440202992Sgabor np->catd = catd; 441202992Sgabor np->lang = (lang == NULL) ? NULL : strdup(lang); 442202992Sgabor np->refcount = 1; 443202992Sgabor np->caterrno = 0; 444202992Sgabor SLIST_INSERT_HEAD(&cache, np, list); 445202992Sgabor } 446202992Sgabor UNLOCK; 447142664Sphantom return (catd); 4487494Sjkh} 449