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 #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 #include "un-namespace.h" 54 55 #include "msgcat.h" 56 #include "../locale/setlocale.h" /* for ENCODING_LEN */ 57 58 #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" 59 60 #define TRUE 1 61 #define FALSE 0 62 63 #define NLERR ((nl_catd) -1) 64 #define NLRETERR(errc) { errno = errc; return (NLERR); } 65 66 static nl_catd loadCat(); 67 static int loadSet(); 68 static void __nls_free_resources(); 69 70 nl_catd 71 catopen(name, type) 72 __const char *name; 73 int type; 74 { 75 int spcleft, saverr; 76 char path[PATH_MAX]; 77 char *nlspath, *lang, *base, *cptr, *pathP, *tmpptr; 78 char *cptr1, *plang, *pter, *pcode; 79 struct stat sbuf; 80 81 if (name == NULL || *name == '\0') 82 NLRETERR(EINVAL); 83 84 /* is it absolute path ? if yes, load immediately */ 85 if (strchr(name, '/') != NULL) 86 return (loadCat(name)); 87 88 if (type == NL_CAT_LOCALE) 89 lang = setlocale(LC_MESSAGES, NULL); 90 else 91 lang = getenv("LANG"); 92 93 if (lang == NULL || *lang == '\0' || strlen(lang) > ENCODING_LEN || 94 (lang[0] == '.' && 95 (lang[1] == '\0' || (lang[1] == '.' && lang[2] == '\0'))) || 96 strchr(lang, '/') != NULL) 97 lang = "C"; 98 99 if ((plang = cptr1 = strdup(lang)) == NULL) 100 return (NLERR); 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 saverr = errno; 122 free(plang); 123 errno = saverr; 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 saverr = errno; \ 331 (void)fclose(cat->fp); \ 332 (void)fprintf(stderr, "%s: no more memory.", _errowner); \ 333 free(cat); \ 334 errno = saverr; \ 335 return (NLERR); \ 336 } 337 338 static void 339 __nls_free_resources(cat, i) 340 MCCatT *cat; 341 int i; 342 { 343 MCSetT *set; 344 int j; 345 346 for (j = 0; j < i; j++) { 347 set = cat->sets + j; 348 if (!set->invalid) { 349 free(set->data.str); 350 free(set->u.msgs); 351 } 352 } 353 free(cat->sets); 354 } 355 356 static nl_catd 357 loadCat(catpath) 358 __const char *catpath; 359 { 360 MCHeaderT header; 361 MCCatT *cat; 362 MCSetT *set; 363 long i; 364 off_t nextSet; 365 int saverr; 366 367 if ((cat = (MCCatT *)malloc(sizeof(MCCatT))) == NULL) 368 return (NLERR); 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 saverr = errno; 429 __nls_free_resources(cat, i); 430 errno = saverr; 431 if (res < 0) 432 NOSPACE(); 433 CORRUPT(); 434 } 435 } else 436 #endif 437 set->invalid = TRUE; 438 nextSet = set->nextSet; 439 } 440 #if 0 441 if (cat->loadType == MCLoadAll) { 442 (void)fclose(cat->fp); 443 cat->fp = NULL; 444 } 445 #endif 446 return ((nl_catd) cat); 447 } 448 449 static int 450 loadSet(cat, set) 451 MCCatT *cat; 452 MCSetT *set; 453 { 454 MCMsgT *msg; 455 int i; 456 int saverr; 457 458 /* Get the data */ 459 if (fseeko(cat->fp, set->data.off, SEEK_SET) == -1) 460 return (0); 461 if ((set->data.str = malloc(set->dataLen)) == NULL) 462 return (-1); 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 saverr = errno; 480 free(set->data.str); 481 errno = saverr; 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