1 /*********************************************************** 2 Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts. 3 4 All Rights Reserved 5 6 Permission to use, copy, modify, and distribute this software and its 7 documentation for any purpose and without fee is hereby granted, 8 provided that the above copyright notice appear in all copies and that 9 both that copyright notice and this permission notice appear in 10 supporting documentation, and that Alfalfa's name not be used in 11 advertising or publicity pertaining to distribution of the software 12 without specific, written prior permission. 13 14 ALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 15 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL 16 ALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 17 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 18 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 19 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 20 SOFTWARE. 21 22 If you make any modifications, bugfixes or other changes to this software 23 we'd appreciate it if you could send a copy to us so we can keep things 24 up-to-date. Many thanks. 25 Kee Hinckley 26 Alfalfa Software, Inc. 27 267 Allston St., #3 28 Cambridge, MA 02139 USA 29 nazgul@alfalfa.com 30 31 ******************************************************************/ 32 33 #include <sys/cdefs.h> 34 __FBSDID("$FreeBSD: src/lib/libc/nls/msgcat.c,v 1.21.2.6 2002/08/12 11:23:54 ache Exp $"); 35 36 /* 37 * We need a better way of handling errors than printing text. I need 38 * to add an error handling routine. 39 */ 40 41 #include <sys/types.h> 42 #include <sys/stat.h> 43 #include <sys/syslimits.h> 44 #include <errno.h> 45 #include <fcntl.h> 46 #include <locale.h> 47 #include <nl_types.h> 48 #include <stdio.h> 49 #include <stdlib.h> 50 #include <string.h> 51 #include <unistd.h> 52 53 #include "msgcat.h" 54 #include "../locale/setlocale.h" /* for ENCODING_LEN */ 55 56 #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" 57 58 #define TRUE 1 59 #define FALSE 0 60 61 #define NLERR ((nl_catd) -1) 62 #define NLRETERR(errc) { errno = errc; return (NLERR); } 63 64 static nl_catd loadCat(); 65 static int loadSet(); 66 static void __nls_free_resources(); 67 68 nl_catd 69 catopen(name, type) 70 __const char *name; 71 int type; 72 { 73 int spcleft, saverr; 74 char path[PATH_MAX]; 75 char *nlspath, *lang, *base, *cptr, *pathP, *tmpptr; 76 char *cptr1, *plang, *pter, *pcode; 77 struct stat sbuf; 78 79 if (name == NULL || *name == '\0') 80 NLRETERR(EINVAL); 81 82 /* is it absolute path ? if yes, load immediately */ 83 if (strchr(name, '/') != NULL) 84 return (loadCat(name)); 85 86 if (type == NL_CAT_LOCALE) 87 lang = setlocale(LC_MESSAGES, NULL); 88 else 89 lang = getenv("LANG"); 90 91 if (lang == NULL || *lang == '\0' || strlen(lang) > ENCODING_LEN || 92 (lang[0] == '.' && 93 (lang[1] == '\0' || (lang[1] == '.' && lang[2] == '\0'))) || 94 strchr(lang, '/') != NULL) 95 lang = "C"; 96 97 if ((plang = cptr1 = strdup(lang)) == NULL) { 98 errno = ENOMEM; 99 return (NLERR); 100 } 101 if ((cptr = strchr(cptr1, '@')) != NULL) 102 *cptr = '\0'; 103 pter = pcode = ""; 104 if ((cptr = strchr(cptr1, '_')) != NULL) { 105 *cptr++ = '\0'; 106 pter = cptr1 = cptr; 107 } 108 if ((cptr = strchr(cptr1, '.')) != NULL) { 109 *cptr++ = '\0'; 110 pcode = cptr; 111 } 112 113 if ((nlspath = getenv("NLSPATH")) == NULL 114 #ifndef __NETBSD_SYSCALLS 115 || issetugid() 116 #endif 117 ) 118 nlspath = _DEFAULT_NLS_PATH; 119 120 if ((base = cptr = strdup(nlspath)) == NULL) { 121 free(plang); 122 errno = ENOMEM; 123 return (NLERR); 124 } 125 126 while ((nlspath = strsep(&cptr, ":")) != NULL) { 127 pathP = path; 128 if (*nlspath) { 129 for (; *nlspath; ++nlspath) { 130 if (*nlspath == '%') { 131 switch (*(nlspath + 1)) { 132 case 'l': 133 tmpptr = plang; 134 break; 135 case 't': 136 tmpptr = pter; 137 break; 138 case 'c': 139 tmpptr = pcode; 140 break; 141 case 'L': 142 tmpptr = lang; 143 break; 144 case 'N': 145 tmpptr = (char *)name; 146 break; 147 case '%': 148 ++nlspath; 149 /* fallthrough */ 150 default: 151 if (pathP - path >= 152 sizeof(path) - 1) 153 goto too_long; 154 *(pathP++) = *nlspath; 155 continue; 156 } 157 ++nlspath; 158 put_tmpptr: 159 spcleft = sizeof(path) - 160 (pathP - path) - 1; 161 if (strlcpy(pathP, tmpptr, spcleft) >= 162 spcleft) { 163 too_long: 164 free(plang); 165 free(base); 166 NLRETERR(ENAMETOOLONG); 167 } 168 pathP += strlen(tmpptr); 169 } else { 170 if (pathP - path >= sizeof(path) - 1) 171 goto too_long; 172 *(pathP++) = *nlspath; 173 } 174 } 175 *pathP = '\0'; 176 if (stat(path, &sbuf) == 0) { 177 free(plang); 178 free(base); 179 return (loadCat(path)); 180 } 181 } else { 182 tmpptr = (char *)name; 183 --nlspath; 184 goto put_tmpptr; 185 } 186 } 187 free(plang); 188 free(base); 189 NLRETERR(ENOENT); 190 } 191 192 /* 193 * We've got an odd situation here. The odds are real good that the 194 * number we are looking for is almost the same as the index. We could 195 * use the index, check the difference and do something intelligent, but 196 * I haven't quite figured out what's intelligent. 197 * 198 * Here's a start. 199 * Take an id N. If there are > N items in the list, then N cannot 200 * be more than N items from the start, since otherwise there would 201 * have to be duplicate items. So we can safely set the top to N+1 202 * (after taking into account that ids start at 1, and arrays at 0) 203 * 204 * Let's say we are at position P, and we are looking for N, but have 205 * V. If N > V, then the furthest away that N could be is 206 * P + (N-V). So we can safely set hi to P+(N-V)+1. For example: 207 * We are looking for 10, but have 8 208 * 8 ? ? ? ? 209 * >=9 >=10 >=11 210 * 211 */ 212 213 #define LOOKUP(PARENT, CHILD, ID, NUM, SET) { \ 214 lo = 0; \ 215 if (ID - 1 < PARENT->NUM) { \ 216 cur = ID - 1; \ 217 hi = ID; \ 218 } else { \ 219 hi = PARENT->NUM; \ 220 cur = (hi - lo) / 2; \ 221 } \ 222 while (TRUE) { \ 223 CHILD = PARENT->SET + cur; \ 224 if (CHILD->ID == ID) \ 225 break; \ 226 if (CHILD->ID < ID) { \ 227 lo = cur + 1; \ 228 if (hi > cur + (ID - CHILD->ID) + 1) \ 229 hi = cur + (ID - CHILD->ID) + 1; \ 230 dir = 1; \ 231 } else { \ 232 hi = cur; \ 233 dir = -1; \ 234 } \ 235 if (lo >= hi) \ 236 return (NULL); \ 237 if (hi - lo == 1) \ 238 cur += dir; \ 239 else \ 240 cur += ((hi - lo) / 2) * dir; \ 241 } \ 242 } 243 244 static MCSetT * 245 MCGetSet(cat, setId) 246 MCCatT *cat; 247 int setId; 248 { 249 MCSetT *set; 250 long lo, hi, cur, dir; 251 252 if (cat == NULL || setId <= 0) 253 return (NULL); 254 LOOKUP(cat, set, setId, numSets, sets); 255 if (set->invalid && loadSet(cat, set) <= 0) 256 return (NULL); 257 return (set); 258 } 259 260 static MCMsgT * 261 MCGetMsg(set, msgId) 262 MCSetT *set; 263 int msgId; 264 { 265 MCMsgT *msg; 266 long lo, hi, cur, dir; 267 268 if (set == NULL || set->invalid || msgId <= 0) 269 return (NULL); 270 LOOKUP(set, msg, msgId, numMsgs, u.msgs); 271 return (msg); 272 } 273 274 char * 275 catgets(catd, setId, msgId, dflt) 276 nl_catd catd; 277 int setId; 278 int msgId; 279 __const char *dflt; 280 { 281 MCMsgT *msg; 282 MCCatT *cat = (MCCatT *)catd; 283 __const char *cptr; 284 285 if (catd == NULL || catd == NLERR) 286 return ((char *)dflt); 287 msg = MCGetMsg(MCGetSet(cat, setId), msgId); 288 if (msg != NULL) 289 cptr = msg->msg.str; 290 else 291 cptr = dflt; 292 return ((char *)cptr); 293 } 294 295 int 296 catclose(catd) 297 nl_catd catd; 298 { 299 MCCatT *cat = (MCCatT *)catd; 300 301 if (catd == NULL || catd == NLERR) { 302 errno = EBADF; 303 return (-1); 304 } 305 #if 0 306 if (cat->loadType != MCLoadAll) 307 #endif 308 (void)fclose(cat->fp); 309 __nls_free_resources(cat, cat->numSets); 310 free(cat); 311 return (0); 312 } 313 314 /* 315 * Internal routines 316 */ 317 318 /* Note that only malloc failures are allowed to return an error */ 319 static char *_errowner = "Message Catalog System"; 320 321 #define CORRUPT() { \ 322 (void)fclose(cat->fp); \ 323 (void)fprintf(stderr, "%s: corrupt file.", _errowner); \ 324 free(cat); \ 325 NLRETERR(EFTYPE); \ 326 } 327 328 #define NOSPACE() { \ 329 (void)fclose(cat->fp); \ 330 (void)fprintf(stderr, "%s: no more memory.", _errowner); \ 331 free(cat); \ 332 errno = ENOMEM; \ 333 return (NLERR); \ 334 } 335 336 static void 337 __nls_free_resources(cat, i) 338 MCCatT *cat; 339 int i; 340 { 341 MCSetT *set; 342 int j; 343 344 for (j = 0; j < i; j++) { 345 set = cat->sets + j; 346 if (!set->invalid) { 347 free(set->data.str); 348 free(set->u.msgs); 349 } 350 } 351 free(cat->sets); 352 } 353 354 static nl_catd 355 loadCat(catpath) 356 __const char *catpath; 357 { 358 MCHeaderT header; 359 MCCatT *cat; 360 MCSetT *set; 361 long i; 362 off_t nextSet; 363 int saverr; 364 365 if ((cat = (MCCatT *)malloc(sizeof(MCCatT))) == NULL) { 366 errno = ENOMEM; 367 return (NLERR); 368 } 369 cat->loadType = MCLoadBySet; 370 371 if ((cat->fp = fopen(catpath, "r")) == NULL) { 372 saverr = errno; 373 free(cat); 374 errno = saverr; 375 return (NLERR); 376 } 377 (void)_fcntl(fileno(cat->fp), F_SETFD, FD_CLOEXEC); 378 379 if (fread(&header, sizeof(header), 1, cat->fp) != 1 || 380 strncmp(header.magic, MCMagic, MCMagicLen) != 0) 381 CORRUPT(); 382 383 if (header.majorVer != MCMajorVer) { 384 (void)fclose(cat->fp); 385 free(cat); 386 (void)fprintf(stderr, "%s: %s is version %ld, we need %ld.\n", 387 _errowner, catpath, header.majorVer, MCMajorVer); 388 NLRETERR(EFTYPE); 389 } 390 if (header.numSets <= 0) { 391 (void)fclose(cat->fp); 392 free(cat); 393 (void)fprintf(stderr, "%s: %s has %ld sets!\n", 394 _errowner, catpath, header.numSets); 395 NLRETERR(EFTYPE); 396 } 397 398 cat->numSets = header.numSets; 399 if ((cat->sets = (MCSetT *)malloc(sizeof(MCSetT) * header.numSets)) == 400 NULL) 401 NOSPACE(); 402 403 nextSet = header.firstSet; 404 for (i = 0; i < cat->numSets; ++i) { 405 if (fseeko(cat->fp, nextSet, SEEK_SET) == -1) { 406 __nls_free_resources(cat, i); 407 CORRUPT(); 408 } 409 410 /* read in the set header */ 411 set = cat->sets + i; 412 if (fread(set, sizeof(*set), 1, cat->fp) != 1) { 413 __nls_free_resources(cat, i); 414 CORRUPT(); 415 } 416 417 /* if it's invalid, skip over it (and backup 'i') */ 418 if (set->invalid) { 419 --i; 420 nextSet = set->nextSet; 421 continue; 422 } 423 #if 0 424 if (cat->loadType == MCLoadAll) { 425 int res; 426 427 if ((res = loadSet(cat, set)) <= 0) { 428 __nls_free_resources(cat, i); 429 if (res < 0) 430 NOSPACE(); 431 CORRUPT(); 432 } 433 } else 434 #endif 435 set->invalid = TRUE; 436 nextSet = set->nextSet; 437 } 438 #if 0 439 if (cat->loadType == MCLoadAll) { 440 (void)fclose(cat->fp); 441 cat->fp = NULL; 442 } 443 #endif 444 return ((nl_catd) cat); 445 } 446 447 static int 448 loadSet(cat, set) 449 MCCatT *cat; 450 MCSetT *set; 451 { 452 MCMsgT *msg; 453 int i; 454 int saverr; 455 456 /* Get the data */ 457 if (fseeko(cat->fp, set->data.off, SEEK_SET) == -1) 458 return (0); 459 if ((set->data.str = malloc(set->dataLen)) == NULL) { 460 errno = ENOMEM; 461 return (-1); 462 } 463 if (fread(set->data.str, set->dataLen, 1, cat->fp) != 1) { 464 saverr = errno; 465 free(set->data.str); 466 errno = saverr; 467 return (0); 468 } 469 470 /* Get the messages */ 471 if (fseeko(cat->fp, set->u.firstMsg, SEEK_SET) == -1) { 472 saverr = errno; 473 free(set->data.str); 474 errno = saverr; 475 return (0); 476 } 477 if ((set->u.msgs = (MCMsgT *)malloc(sizeof(MCMsgT) * set->numMsgs)) == 478 NULL) { 479 free(set->data.str); 480 errno = ENOMEM; 481 return (-1); 482 } 483 484 for (i = 0; i < set->numMsgs; ++i) { 485 msg = set->u.msgs + i; 486 if (fread(msg, sizeof(*msg), 1, cat->fp) != 1) { 487 saverr = errno; 488 free(set->u.msgs); 489 free(set->data.str); 490 errno = saverr; 491 return (0); 492 } 493 if (msg->invalid) { 494 --i; 495 continue; 496 } 497 msg->msg.str = (char *)(set->data.str + msg->msg.off); 498 } 499 set->invalid = FALSE; 500 return (1); 501 } 502