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$"); 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 "namespace.h" 42 #include <sys/types.h> 43 #include <sys/stat.h> 44 45 #include <errno.h> 46 #include <fcntl.h> 47 #include <limits.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 return (NLERR); 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 saverr = errno; 123 free(plang); 124 errno = saverr; 125 return (NLERR); 126 } 127 128 while ((nlspath = strsep(&cptr, ":")) != NULL) { 129 pathP = path; 130 if (*nlspath) { 131 for (; *nlspath; ++nlspath) { 132 if (*nlspath == '%') { 133 switch (*(nlspath + 1)) { 134 case 'l': 135 tmpptr = plang; 136 break; 137 case 't': 138 tmpptr = pter; 139 break; 140 case 'c': 141 tmpptr = pcode; 142 break; 143 case 'L': 144 tmpptr = lang; 145 break; 146 case 'N': 147 tmpptr = (char *)name; 148 break; 149 case '%': 150 ++nlspath; 151 /* fallthrough */ 152 default: 153 if (pathP - path >= 154 sizeof(path) - 1) 155 goto too_long; 156 *(pathP++) = *nlspath; 157 continue; 158 } 159 ++nlspath; 160 put_tmpptr: 161 spcleft = sizeof(path) - 162 (pathP - path) - 1; 163 if (strlcpy(pathP, tmpptr, spcleft) >= 164 spcleft) { 165 too_long: 166 free(plang); 167 free(base); 168 NLRETERR(ENAMETOOLONG); 169 } 170 pathP += strlen(tmpptr); 171 } else { 172 if (pathP - path >= sizeof(path) - 1) 173 goto too_long; 174 *(pathP++) = *nlspath; 175 } 176 } 177 *pathP = '\0'; 178 if (stat(path, &sbuf) == 0) { 179 free(plang); 180 free(base); 181 return (loadCat(path)); 182 } 183 } else { 184 tmpptr = (char *)name; 185 --nlspath; 186 goto put_tmpptr; 187 } 188 } 189 free(plang); 190 free(base); 191 NLRETERR(ENOENT); 192 } 193 194 /* 195 * We've got an odd situation here. The odds are real good that the 196 * number we are looking for is almost the same as the index. We could 197 * use the index, check the difference and do something intelligent, but 198 * I haven't quite figured out what's intelligent. 199 * 200 * Here's a start. 201 * Take an id N. If there are > N items in the list, then N cannot 202 * be more than N items from the start, since otherwise there would 203 * have to be duplicate items. So we can safely set the top to N+1 204 * (after taking into account that ids start at 1, and arrays at 0) 205 * 206 * Let's say we are at position P, and we are looking for N, but have 207 * V. If N > V, then the furthest away that N could be is 208 * P + (N-V). So we can safely set hi to P+(N-V)+1. For example: 209 * We are looking for 10, but have 8 210 * 8 ? ? ? ? 211 * >=9 >=10 >=11 212 * 213 */ 214 215 #define LOOKUP(PARENT, CHILD, ID, NUM, SET) { \ 216 lo = 0; \ 217 if (ID - 1 < PARENT->NUM) { \ 218 cur = ID - 1; \ 219 hi = ID; \ 220 } else { \ 221 hi = PARENT->NUM; \ 222 cur = (hi - lo) / 2; \ 223 } \ 224 while (TRUE) { \ 225 CHILD = PARENT->SET + cur; \ 226 if (CHILD->ID == ID) \ 227 break; \ 228 if (CHILD->ID < ID) { \ 229 lo = cur + 1; \ 230 if (hi > cur + (ID - CHILD->ID) + 1) \ 231 hi = cur + (ID - CHILD->ID) + 1; \ 232 dir = 1; \ 233 } else { \ 234 hi = cur; \ 235 dir = -1; \ 236 } \ 237 if (lo >= hi) \ 238 return (NULL); \ 239 if (hi - lo == 1) \ 240 cur += dir; \ 241 else \ 242 cur += ((hi - lo) / 2) * dir; \ 243 } \ 244 } 245 246 static MCSetT * 247 MCGetSet(cat, setId) 248 MCCatT *cat; 249 int setId; 250 { 251 MCSetT *set; 252 long lo, hi, cur, dir; 253 254 if (cat == NULL || setId <= 0) 255 return (NULL); 256 LOOKUP(cat, set, setId, numSets, sets); 257 if (set->invalid && loadSet(cat, set) <= 0) 258 return (NULL); 259 return (set); 260 } 261 262 static MCMsgT * 263 MCGetMsg(set, msgId) 264 MCSetT *set; 265 int msgId; 266 { 267 MCMsgT *msg; 268 long lo, hi, cur, dir; 269 270 if (set == NULL || set->invalid || msgId <= 0) 271 return (NULL); 272 LOOKUP(set, msg, msgId, numMsgs, u.msgs); 273 return (msg); 274 } 275 276 char * 277 catgets(catd, setId, msgId, dflt) 278 nl_catd catd; 279 int setId; 280 int msgId; 281 __const char *dflt; 282 { 283 MCMsgT *msg; 284 MCCatT *cat = (MCCatT *)catd; 285 __const char *cptr; 286 287 if (catd == NULL || catd == NLERR) 288 return ((char *)dflt); 289 msg = MCGetMsg(MCGetSet(cat, setId), msgId); 290 if (msg != NULL) 291 cptr = msg->msg.str; 292 else 293 cptr = dflt; 294 return ((char *)cptr); 295 } 296 297 int 298 catclose(catd) 299 nl_catd catd; 300 { 301 MCCatT *cat = (MCCatT *)catd; 302 303 if (catd == NULL || catd == NLERR) { 304 errno = EBADF; 305 return (-1); 306 } 307 #if 0 308 if (cat->loadType != MCLoadAll) 309 #endif 310 (void)fclose(cat->fp); 311 __nls_free_resources(cat, cat->numSets); 312 free(cat); 313 return (0); 314 } 315 316 /* 317 * Internal routines 318 */ 319 320 /* Note that only malloc failures are allowed to return an error */ 321 static char *_errowner = "Message Catalog System"; 322 323 #define CORRUPT() { \ 324 (void)fclose(cat->fp); \ 325 (void)fprintf(stderr, "%s: corrupt file.", _errowner); \ 326 free(cat); \ 327 NLRETERR(EFTYPE); \ 328 } 329 330 #define NOSPACE() { \ 331 saverr = errno; \ 332 (void)fclose(cat->fp); \ 333 (void)fprintf(stderr, "%s: no more memory.", _errowner); \ 334 free(cat); \ 335 errno = saverr; \ 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 return (NLERR); 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 saverr = errno; 430 __nls_free_resources(cat, i); 431 errno = saverr; 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 return (-1); 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 saverr = errno; 481 free(set->data.str); 482 errno = saverr; 483 return (-1); 484 } 485 486 for (i = 0; i < set->numMsgs; ++i) { 487 msg = set->u.msgs + i; 488 if (fread(msg, sizeof(*msg), 1, cat->fp) != 1) { 489 saverr = errno; 490 free(set->u.msgs); 491 free(set->data.str); 492 errno = saverr; 493 return (0); 494 } 495 if (msg->invalid) { 496 --i; 497 continue; 498 } 499 msg->msg.str = (char *)(set->data.str + msg->msg.off); 500 } 501 set->invalid = FALSE; 502 return (1); 503 } 504