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