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