msgcat.c revision 106053
1193326Sed/*********************************************************** 2193326SedCopyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts. 3193326Sed 4193326Sed All Rights Reserved 5193326Sed 6193326SedPermission to use, copy, modify, and distribute this software and its 7193326Seddocumentation for any purpose and without fee is hereby granted, 8193326Sedprovided that the above copyright notice appear in all copies and that 9239462Sdimboth that copyright notice and this permission notice appear in 10239462Sdimsupporting documentation, and that Alfalfa's name not be used in 11239462Sdimadvertising or publicity pertaining to distribution of the software 12239462Sdimwithout specific, written prior permission. 13239462Sdim 14193326SedALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 15193326SedALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL 16193326SedALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 17193326SedANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 18193326SedWHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 19249423SdimARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 20193326SedSOFTWARE. 21193326Sed 22263508SdimIf you make any modifications, bugfixes or other changes to this software 23193326Sedwe'd appreciate it if you could send a copy to us so we can keep things 24205219Srdivackyup-to-date. Many thanks. 25193326Sed Kee Hinckley 26218893Sdim Alfalfa Software, Inc. 27198092Srdivacky 267 Allston St., #3 28193326Sed Cambridge, MA 02139 USA 29193326Sed nazgul@alfalfa.com 30193326Sed 31193326Sed******************************************************************/ 32193326Sed 33193326Sed#include <sys/cdefs.h> 34193326Sed__FBSDID("$FreeBSD: head/lib/libc/nls/msgcat.c 106053 2002-10-27 17:44:33Z wollman $"); 35193326Sed 36193326Sed/* 37193326Sed * We need a better way of handling errors than printing text. I need 38193326Sed * to add an error handling routine. 39193326Sed */ 40193326Sed 41239462Sdim#include "namespace.h" 42193326Sed#include <sys/types.h> 43198092Srdivacky#include <sys/stat.h> 44198092Srdivacky 45239462Sdim#include <errno.h> 46239462Sdim#include <fcntl.h> 47193326Sed#include <limits.h> 48193326Sed#include <locale.h> 49198092Srdivacky#include <nl_types.h> 50193326Sed#include <stdio.h> 51226633Sdim#include <stdlib.h> 52193326Sed#include <string.h> 53193326Sed#include <unistd.h> 54193326Sed#include "un-namespace.h" 55218893Sdim 56193326Sed#include "msgcat.h" 57243830Sdim#include "../locale/setlocale.h" /* for ENCODING_LEN */ 58193326Sed 59226633Sdim#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" 60193326Sed 61193326Sed#define TRUE 1 62193326Sed#define FALSE 0 63234353Sdim 64234353Sdim#define NLERR ((nl_catd) -1) 65234353Sdim#define NLRETERR(errc) { errno = errc; return (NLERR); } 66234353Sdim 67212904Sdimstatic nl_catd loadCat(); 68212904Sdimstatic int loadSet(); 69234353Sdimstatic void __nls_free_resources(); 70234353Sdim 71234353Sdimnl_catd 72239462Sdimcatopen(name, type) 73234353Sdim __const char *name; 74243830Sdim int type; 75243830Sdim{ 76193326Sed int spcleft, saverr; 77193326Sed char path[PATH_MAX]; 78198092Srdivacky char *nlspath, *lang, *base, *cptr, *pathP, *tmpptr; 79243830Sdim char *cptr1, *plang, *pter, *pcode; 80243830Sdim struct stat sbuf; 81193326Sed 82198092Srdivacky if (name == NULL || *name == '\0') 83218893Sdim NLRETERR(EINVAL); 84193326Sed 85193326Sed /* is it absolute path ? if yes, load immediately */ 86193326Sed if (strchr(name, '/') != NULL) 87198092Srdivacky return (loadCat(name)); 88239462Sdim 89239462Sdim if (type == NL_CAT_LOCALE) 90193326Sed lang = setlocale(LC_MESSAGES, NULL); 91193326Sed else 92193326Sed lang = getenv("LANG"); 93198398Srdivacky 94193326Sed if (lang == NULL || *lang == '\0' || strlen(lang) > ENCODING_LEN || 95198092Srdivacky (lang[0] == '.' && 96239462Sdim (lang[1] == '\0' || (lang[1] == '.' && lang[2] == '\0'))) || 97239462Sdim strchr(lang, '/') != NULL) 98193326Sed lang = "C"; 99198398Srdivacky 100193326Sed if ((plang = cptr1 = strdup(lang)) == NULL) 101193326Sed return (NLERR); 102193326Sed if ((cptr = strchr(cptr1, '@')) != NULL) 103198092Srdivacky *cptr = '\0'; 104193326Sed pter = pcode = ""; 105193326Sed if ((cptr = strchr(cptr1, '_')) != NULL) { 106210299Sed *cptr++ = '\0'; 107210299Sed pter = cptr1 = cptr; 108193326Sed } 109198092Srdivacky if ((cptr = strchr(cptr1, '.')) != NULL) { 110239462Sdim *cptr++ = '\0'; 111193326Sed pcode = cptr; 112193326Sed } 113193326Sed 114193326Sed if ((nlspath = getenv("NLSPATH")) == NULL 115193326Sed#ifndef __NETBSD_SYSCALLS 116198092Srdivacky || issetugid() 117193326Sed#endif 118193326Sed ) 119210299Sed nlspath = _DEFAULT_NLS_PATH; 120210299Sed 121198398Srdivacky if ((base = cptr = strdup(nlspath)) == NULL) { 122193326Sed saverr = errno; 123198092Srdivacky free(plang); 124239462Sdim errno = saverr; 125226633Sdim return (NLERR); 126226633Sdim } 127198398Srdivacky 128198398Srdivacky while ((nlspath = strsep(&cptr, ":")) != NULL) { 129239462Sdim pathP = path; 130193326Sed if (*nlspath) { 131193326Sed for (; *nlspath; ++nlspath) { 132193326Sed if (*nlspath == '%') { 133193326Sed switch (*(nlspath + 1)) { 134193326Sed case 'l': 135198092Srdivacky tmpptr = plang; 136193326Sed break; 137243830Sdim case 't': 138193326Sed tmpptr = pter; 139243830Sdim break; 140243830Sdim case 'c': 141193326Sed tmpptr = pcode; 142243830Sdim break; 143193326Sed case 'L': 144243830Sdim tmpptr = lang; 145243830Sdim break; 146243830Sdim case 'N': 147243830Sdim tmpptr = (char *)name; 148243830Sdim break; 149243830Sdim case '%': 150198092Srdivacky ++nlspath; 151263508Sdim /* fallthrough */ 152193326Sed default: 153193326Sed if (pathP - path >= 154193326Sed sizeof(path) - 1) 155198092Srdivacky goto too_long; 156212904Sdim *(pathP++) = *nlspath; 157212904Sdim continue; 158212904Sdim } 159212904Sdim ++nlspath; 160212904Sdim put_tmpptr: 161212904Sdim spcleft = sizeof(path) - 162212904Sdim (pathP - path) - 1; 163212904Sdim if (strlcpy(pathP, tmpptr, spcleft) >= 164212904Sdim spcleft) { 165212904Sdim too_long: 166212904Sdim free(plang); 167212904Sdim free(base); 168212904Sdim NLRETERR(ENAMETOOLONG); 169212904Sdim } 170212904Sdim pathP += strlen(tmpptr); 171239462Sdim } else { 172239462Sdim if (pathP - path >= sizeof(path) - 1) 173193326Sed goto too_long; 174193326Sed *(pathP++) = *nlspath; 175198092Srdivacky } 176239462Sdim } 177239462Sdim *pathP = '\0'; 178239462Sdim if (stat(path, &sbuf) == 0) { 179193326Sed free(plang); 180198092Srdivacky free(base); 181193326Sed return (loadCat(path)); 182193326Sed } 183193326Sed } else { 184193326Sed tmpptr = (char *)name; 185193326Sed --nlspath; 186193326Sed goto put_tmpptr; 187263508Sdim } 188263508Sdim } 189263508Sdim free(plang); 190193326Sed free(base); 191198092Srdivacky NLRETERR(ENOENT); 192193326Sed} 193198092Srdivacky 194193326Sed/* 195193326Sed * We've got an odd situation here. The odds are real good that the 196193326Sed * number we are looking for is almost the same as the index. We could 197193326Sed * use the index, check the difference and do something intelligent, but 198193326Sed * I haven't quite figured out what's intelligent. 199198092Srdivacky * 200193326Sed * Here's a start. 201193326Sed * Take an id N. If there are > N items in the list, then N cannot 202193326Sed * be more than N items from the start, since otherwise there would 203193326Sed * have to be duplicate items. So we can safely set the top to N+1 204193326Sed * (after taking into account that ids start at 1, and arrays at 0) 205193326Sed * 206193326Sed * Let's say we are at position P, and we are looking for N, but have 207193326Sed * V. If N > V, then the furthest away that N could be is 208193326Sed * P + (N-V). So we can safely set hi to P+(N-V)+1. For example: 209193326Sed * We are looking for 10, but have 8 210193326Sed * 8 ? ? ? ? 211193326Sed * >=9 >=10 >=11 212193326Sed * 213193326Sed */ 214193326Sed 215193326Sed#define LOOKUP(PARENT, CHILD, ID, NUM, SET) { \ 216193326Sed lo = 0; \ 217198092Srdivacky if (ID - 1 < PARENT->NUM) { \ 218226633Sdim cur = ID - 1; \ 219226633Sdim hi = ID; \ 220226633Sdim } else { \ 221226633Sdim hi = PARENT->NUM; \ 222226633Sdim cur = (hi - lo) / 2; \ 223226633Sdim } \ 224226633Sdim while (TRUE) { \ 225226633Sdim CHILD = PARENT->SET + cur; \ 226226633Sdim if (CHILD->ID == ID) \ 227226633Sdim break; \ 228226633Sdim if (CHILD->ID < ID) { \ 229226633Sdim lo = cur + 1; \ 230226633Sdim if (hi > cur + (ID - CHILD->ID) + 1) \ 231193326Sed hi = cur + (ID - CHILD->ID) + 1; \ 232193326Sed dir = 1; \ 233193326Sed } else { \ 234193326Sed hi = cur; \ 235193326Sed dir = -1; \ 236193326Sed } \ 237193326Sed if (lo >= hi) \ 238193326Sed return (NULL); \ 239193326Sed if (hi - lo == 1) \ 240198092Srdivacky cur += dir; \ 241263508Sdim else \ 242193326Sed cur += ((hi - lo) / 2) * dir; \ 243198092Srdivacky } \ 244193326Sed} 245193326Sed 246193326Sedstatic MCSetT * 247193326SedMCGetSet(cat, setId) 248193326Sed MCCatT *cat; 249193326Sed int setId; 250193326Sed{ 251193326Sed MCSetT *set; 252193326Sed long lo, hi, cur, dir; 253193326Sed 254193326Sed if (cat == NULL || setId <= 0) 255193326Sed return (NULL); 256193326Sed LOOKUP(cat, set, setId, numSets, sets); 257193326Sed if (set->invalid && loadSet(cat, set) <= 0) 258193326Sed return (NULL); 259193326Sed return (set); 260193326Sed} 261263508Sdim 262263508Sdimstatic MCMsgT * 263263508SdimMCGetMsg(set, msgId) 264263508Sdim MCSetT *set; 265263508Sdim int msgId; 266193326Sed{ 267198092Srdivacky MCMsgT *msg; 268263508Sdim long lo, hi, cur, dir; 269212904Sdim 270212904Sdim if (set == NULL || set->invalid || msgId <= 0) 271212904Sdim return (NULL); 272234353Sdim LOOKUP(set, msg, msgId, numMsgs, u.msgs); 273212904Sdim return (msg); 274234353Sdim} 275234353Sdim 276234353Sdimchar * 277234353Sdimcatgets(catd, setId, msgId, dflt) 278234353Sdim nl_catd catd; 279234353Sdim int setId; 280234353Sdim int msgId; 281234353Sdim __const char *dflt; 282234353Sdim{ 283234353Sdim MCMsgT *msg; 284234353Sdim MCCatT *cat = (MCCatT *)catd; 285234353Sdim __const char *cptr; 286234353Sdim 287234353Sdim if (catd == NULL || catd == NLERR) 288234353Sdim return ((char *)dflt); 289234353Sdim msg = MCGetMsg(MCGetSet(cat, setId), msgId); 290234353Sdim if (msg != NULL) 291234353Sdim cptr = msg->msg.str; 292234353Sdim else 293234353Sdim cptr = dflt; 294234353Sdim return ((char *)cptr); 295234353Sdim} 296234353Sdim 297234353Sdimint 298234353Sdimcatclose(catd) 299234353Sdim nl_catd catd; 300263508Sdim{ 301234353Sdim MCCatT *cat = (MCCatT *)catd; 302234353Sdim 303263508Sdim if (catd == NULL || catd == NLERR) { 304234353Sdim errno = EBADF; 305234353Sdim return (-1); 306234353Sdim } 307234353Sdim#if 0 308234353Sdim if (cat->loadType != MCLoadAll) 309234353Sdim#endif 310234353Sdim (void)fclose(cat->fp); 311234353Sdim __nls_free_resources(cat, cat->numSets); 312193326Sed free(cat); 313263508Sdim return (0); 314263508Sdim} 315263508Sdim 316193326Sed/* 317193326Sed * Internal routines 318193326Sed */ 319193326Sed 320193326Sed/* Note that only malloc failures are allowed to return an error */ 321193326Sedstatic char *_errowner = "Message Catalog System"; 322234353Sdim 323234353Sdim#define CORRUPT() { \ 324193326Sed (void)fclose(cat->fp); \ 325193326Sed (void)fprintf(stderr, "%s: corrupt file.", _errowner); \ 326193326Sed free(cat); \ 327263508Sdim NLRETERR(EFTYPE); \ 328263508Sdim} 329263508Sdim 330263508Sdim#define NOSPACE() { \ 331221345Sdim saverr = errno; \ 332221345Sdim (void)fclose(cat->fp); \ 333221345Sdim (void)fprintf(stderr, "%s: no more memory.", _errowner); \ 334221345Sdim free(cat); \ 335221345Sdim errno = saverr; \ 336221345Sdim return (NLERR); \ 337221345Sdim} 338221345Sdim 339221345Sdimstatic void 340221345Sdim__nls_free_resources(cat, i) 341221345Sdim MCCatT *cat; 342221345Sdim int i; 343221345Sdim{ 344221345Sdim MCSetT *set; 345221345Sdim int j; 346221345Sdim 347218893Sdim for (j = 0; j < i; j++) { 348218893Sdim set = cat->sets + j; 349218893Sdim if (!set->invalid) { 350218893Sdim free(set->data.str); 351218893Sdim free(set->u.msgs); 352218893Sdim } 353218893Sdim } 354218893Sdim free(cat->sets); 355218893Sdim} 356218893Sdim 357218893Sdimstatic nl_catd 358218893SdimloadCat(catpath) 359243830Sdim __const char *catpath; 360243830Sdim{ 361218893Sdim MCHeaderT header; 362218893Sdim MCCatT *cat; 363218893Sdim MCSetT *set; 364218893Sdim long i; 365218893Sdim off_t nextSet; 366218893Sdim int saverr; 367218893Sdim 368218893Sdim if ((cat = (MCCatT *)malloc(sizeof(MCCatT))) == NULL) 369218893Sdim return (NLERR); 370218893Sdim cat->loadType = MCLoadBySet; 371218893Sdim 372226633Sdim if ((cat->fp = fopen(catpath, "r")) == NULL) { 373226633Sdim saverr = errno; 374218893Sdim free(cat); 375218893Sdim errno = saverr; 376263508Sdim return (NLERR); 377193326Sed } 378193326Sed (void)_fcntl(fileno(cat->fp), F_SETFD, FD_CLOEXEC); 379193326Sed 380198092Srdivacky if (fread(&header, sizeof(header), 1, cat->fp) != 1 || 381263508Sdim strncmp(header.magic, MCMagic, MCMagicLen) != 0) 382263508Sdim CORRUPT(); 383263508Sdim 384263508Sdim if (header.majorVer != MCMajorVer) { 385263508Sdim (void)fclose(cat->fp); 386226633Sdim free(cat); 387218893Sdim (void)fprintf(stderr, "%s: %s is version %ld, we need %ld.\n", 388218893Sdim _errowner, catpath, header.majorVer, MCMajorVer); 389218893Sdim NLRETERR(EFTYPE); 390218893Sdim } 391218893Sdim if (header.numSets <= 0) { 392218893Sdim (void)fclose(cat->fp); 393218893Sdim free(cat); 394218893Sdim (void)fprintf(stderr, "%s: %s has %ld sets!\n", 395218893Sdim _errowner, catpath, header.numSets); 396218893Sdim NLRETERR(EFTYPE); 397218893Sdim } 398251662Sdim 399198092Srdivacky cat->numSets = header.numSets; 400193326Sed if ((cat->sets = (MCSetT *)malloc(sizeof(MCSetT) * header.numSets)) == 401193326Sed NULL) 402193326Sed NOSPACE(); 403193326Sed 404193326Sed nextSet = header.firstSet; 405193326Sed for (i = 0; i < cat->numSets; ++i) { 406193326Sed if (fseeko(cat->fp, nextSet, SEEK_SET) == -1) { 407193326Sed __nls_free_resources(cat, i); 408193326Sed CORRUPT(); 409193326Sed } 410193326Sed 411193326Sed /* read in the set header */ 412193326Sed set = cat->sets + i; 413193326Sed if (fread(set, sizeof(*set), 1, cat->fp) != 1) { 414239462Sdim __nls_free_resources(cat, i); 415239462Sdim CORRUPT(); 416239462Sdim } 417239462Sdim 418239462Sdim /* if it's invalid, skip over it (and backup 'i') */ 419193326Sed if (set->invalid) { 420193326Sed --i; 421193326Sed nextSet = set->nextSet; 422193326Sed continue; 423193326Sed } 424198092Srdivacky#if 0 425193326Sed if (cat->loadType == MCLoadAll) { 426193326Sed int res; 427193326Sed 428239462Sdim if ((res = loadSet(cat, set)) <= 0) { 429239462Sdim saverr = errno; 430193326Sed __nls_free_resources(cat, i); 431193326Sed errno = saverr; 432198092Srdivacky if (res < 0) 433193326Sed NOSPACE(); 434193326Sed CORRUPT(); 435193326Sed } 436193326Sed } else 437193326Sed#endif 438218893Sdim set->invalid = TRUE; 439218893Sdim nextSet = set->nextSet; 440218893Sdim } 441218893Sdim#if 0 442218893Sdim if (cat->loadType == MCLoadAll) { 443193326Sed (void)fclose(cat->fp); 444193326Sed cat->fp = NULL; 445193326Sed } 446198092Srdivacky#endif 447239462Sdim return ((nl_catd) cat); 448239462Sdim} 449226633Sdim 450193326Sedstatic int 451205219SrdivackyloadSet(cat, set) 452198092Srdivacky MCCatT *cat; 453193326Sed MCSetT *set; 454193326Sed{ 455198092Srdivacky MCMsgT *msg; 456193326Sed int i; 457193326Sed int saverr; 458205219Srdivacky 459193326Sed /* Get the data */ 460193326Sed if (fseeko(cat->fp, set->data.off, SEEK_SET) == -1) 461193326Sed return (0); 462193326Sed if ((set->data.str = malloc(set->dataLen)) == NULL) 463193326Sed return (-1); 464193326Sed if (fread(set->data.str, set->dataLen, 1, cat->fp) != 1) { 465193326Sed saverr = errno; 466193326Sed free(set->data.str); 467193326Sed errno = saverr; 468193326Sed return (0); 469193326Sed } 470193326Sed 471193326Sed /* Get the messages */ 472193326Sed if (fseeko(cat->fp, set->u.firstMsg, SEEK_SET) == -1) { 473193326Sed saverr = errno; 474193326Sed free(set->data.str); 475193326Sed errno = saverr; 476193326Sed return (0); 477198092Srdivacky } 478226633Sdim if ((set->u.msgs = (MCMsgT *)malloc(sizeof(MCMsgT) * set->numMsgs)) == 479212904Sdim NULL) { 480212904Sdim saverr = errno; 481226633Sdim free(set->data.str); 482212904Sdim errno = saverr; 483212904Sdim return (-1); 484212904Sdim } 485212904Sdim 486212904Sdim for (i = 0; i < set->numMsgs; ++i) { 487193326Sed msg = set->u.msgs + i; 488212904Sdim if (fread(msg, sizeof(*msg), 1, cat->fp) != 1) { 489212904Sdim saverr = errno; 490212904Sdim free(set->u.msgs); 491226633Sdim free(set->data.str); 492193326Sed errno = saverr; 493224145Sdim return (0); 494198092Srdivacky } 495193326Sed if (msg->invalid) { 496212904Sdim --i; 497198092Srdivacky continue; 498212904Sdim } 499212904Sdim msg->msg.str = (char *)(set->data.str + msg->msg.off); 500212904Sdim } 501212904Sdim set->invalid = FALSE; 502193326Sed return (1); 503212904Sdim} 504212904Sdim