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