1/*********************************************************** 2Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts. 3 4 All Rights Reserved 5 6Permission to use, copy, modify, and distribute this software and its 7documentation for any purpose and without fee is hereby granted, 8provided that the above copyright notice appear in all copies and that 9both that copyright notice and this permission notice appear in 10supporting documentation, and that Alfalfa's name not be used in 11advertising or publicity pertaining to distribution of the software 12without specific, written prior permission. 13 14ALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 15ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL 16ALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 17ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 18WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 19ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 20SOFTWARE. 21 22If you make any modifications, bugfixes or other changes to this software 23we'd appreciate it if you could send a copy to us so we can keep things 24up-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: src/lib/libc/nls/msgcat.c,v 1.49 2005/02/01 16:04:55 phantom Exp $"); 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 <xlocale.h> 49#include <nl_types.h> 50#include <stdio.h> 51#include <stdlib.h> 52#include <string.h> 53#include <unistd.h> 54#include <machine/endian.h> 55#include <libkern/OSByteOrder.h> 56#include "un-namespace.h" 57 58#include "msgcat.h" 59#include "setlocale.h" /* for ENCODING_LEN */ 60 61#ifndef ntohll 62#define ntohll(x) OSSwapBigToHostInt64(x) 63#endif 64 65#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" 66 67#define TRUE 1 68#define FALSE 0 69 70#define NLERR ((nl_catd) -1) 71#define NLRETERR(errc) { errno = errc; return (NLERR); } 72 73static nl_catd loadCat(__const char *); 74static int loadSet(MCCatT *, MCSetT *); 75static void __nls_free_resources(MCCatT *, int); 76 77nl_catd 78catopen(__const char *name, int type) 79{ 80 int spcleft, saverr; 81 char path[PATH_MAX]; 82 char *nlspath, *lang, *base, *cptr, *pathP, *tmpptr; 83 char *cptr1, *plang, *pter, *pcode; 84 struct stat sbuf; 85 86 if (name == NULL || *name == '\0') 87 NLRETERR(EINVAL); 88 89 /* is it absolute path ? if yes, load immediately */ 90 if (strchr(name, '/') != NULL) 91 return (loadCat(name)); 92 93 if (type == NL_CAT_LOCALE) 94 lang = (char *)querylocale(LC_MESSAGES_MASK, NULL); 95 else 96 lang = getenv("LANG"); 97 98 if (lang == NULL || *lang == '\0' || strlen(lang) > ENCODING_LEN || 99 (lang[0] == '.' && 100 (lang[1] == '\0' || (lang[1] == '.' && lang[2] == '\0'))) || 101 strchr(lang, '/') != NULL) 102 lang = "C"; 103 104 if ((plang = cptr1 = strdup(lang)) == NULL) 105 return (NLERR); 106 if ((cptr = strchr(cptr1, '@')) != NULL) 107 *cptr = '\0'; 108 pter = pcode = ""; 109 if ((cptr = strchr(cptr1, '_')) != NULL) { 110 *cptr++ = '\0'; 111 pter = cptr1 = cptr; 112 } 113 if ((cptr = strchr(cptr1, '.')) != NULL) { 114 *cptr++ = '\0'; 115 pcode = cptr; 116 } 117 118 if ((nlspath = getenv("NLSPATH")) == NULL || issetugid()) 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 < NUM) { \ 218 cur = ID - 1; \ 219 hi = ID; \ 220 } else { \ 221 hi = NUM; \ 222 cur = (hi - lo) / 2; \ 223 } \ 224 while (TRUE) { \ 225 CHILD = PARENT->SET + cur; \ 226 if (ntohl(CHILD->ID) == ID) \ 227 break; \ 228 if (ntohl(CHILD->ID) < ID) { \ 229 lo = cur + 1; \ 230 if (hi > cur + (ID - ntohl(CHILD->ID)) + 1) \ 231 hi = cur + (ID - ntohl(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 246static MCSetT * 247MCGetSet(MCCatT *cat, int setId) 248{ 249 MCSetT *set; 250 int32_t lo, hi, cur, dir; 251 252 if (cat == NULL || setId <= 0) 253 return (NULL); 254 LOOKUP(cat, set, setId, cat->numSets, sets); 255 if (set->invalid && loadSet(cat, set) <= 0) 256 return (NULL); 257 return (set); 258} 259 260static MCMsgT * 261MCGetMsg(MCSetT *set, int msgId) 262{ 263 MCMsgT *msg; 264 int32_t lo, hi, cur, dir; 265 266 if (set == NULL || set->invalid || msgId <= 0) 267 return (NULL); 268 LOOKUP(set, msg, msgId, ntohl(set->numMsgs), u.msgs); 269 return (msg); 270} 271 272char * 273catgets(nl_catd catd, int setId, int msgId, __const char *dflt) 274{ 275 MCMsgT *msg; 276 MCCatT *cat = (MCCatT *)catd; 277 __const char *cptr; 278 279 if (catd == NULL || catd == NLERR) 280 return ((char *)dflt); 281 msg = MCGetMsg(MCGetSet(cat, setId), msgId); 282 if (msg != NULL) 283 cptr = msg->msg.str; 284 else 285 cptr = dflt; 286 return ((char *)cptr); 287} 288 289int 290catclose(nl_catd catd) 291{ 292 MCCatT *cat = (MCCatT *)catd; 293 294 if (catd == NULL || catd == NLERR) { 295 errno = EBADF; 296 return (-1); 297 } 298 299 (void)fclose(cat->fp); 300 __nls_free_resources(cat, cat->numSets); 301 free(cat); 302 return (0); 303} 304 305/* 306 * Internal routines 307 */ 308 309/* Note that only malloc failures are allowed to return an error */ 310static char *_errowner = "Message Catalog System"; 311 312#define CORRUPT() { \ 313 (void)fclose(cat->fp); \ 314 (void)fprintf(stderr, "%s: corrupt file.", _errowner); \ 315 free(cat); \ 316 NLRETERR(EFTYPE); \ 317} 318 319#define NOSPACE() { \ 320 saverr = errno; \ 321 (void)fclose(cat->fp); \ 322 (void)fprintf(stderr, "%s: no more memory.", _errowner); \ 323 free(cat); \ 324 errno = saverr; \ 325 return (NLERR); \ 326} 327 328static void 329__nls_free_resources(MCCatT *cat, int i) 330{ 331 MCSetT *set; 332 int j; 333 334 for (j = 0; j < i; j++) { 335 set = cat->sets + j; 336 if (!set->invalid) { 337 free(set->data.str); 338 free(set->u.msgs); 339 } 340 } 341 free(cat->sets); 342} 343 344static nl_catd 345loadCat(__const char *catpath) 346{ 347 MCHeaderT header; 348 MCCatT *cat; 349 MCSetT *set; 350 int32_t i; 351 off_t nextSet; 352 int saverr; 353 int fd; 354 355 if ((cat = (MCCatT *)malloc(sizeof(MCCatT))) == NULL) 356 return (NLERR); 357 358 if ((fd = open(catpath, O_RDONLY | O_CLOEXEC)) == -1) { 359 saverr = errno; 360 free(cat); 361 errno = saverr; 362 return (NLERR); 363 } 364 365 if ((cat->fp = fdopen(fd, "r")) == NULL) { 366 saverr = errno; 367 close(fd); 368 free(cat); 369 errno = saverr; 370 return (NLERR); 371 } 372 373 if (fread(&header, sizeof(header), 1, cat->fp) != 1 || 374 strncmp(header.magic, MCMagic, MCMagicLen) != 0) 375 CORRUPT(); 376 377 if (ntohl(header.majorVer) != MCMajorVer) { 378 (void)fclose(cat->fp); 379 free(cat); 380 if (OSSwapInt32(ntohl(header.majorVer)) == MCMajorVer) { 381 (void)fprintf(stderr, "%s: %s is the wrong byte ordering.\n", _errowner, catpath); 382 } else { 383 (void)fprintf(stderr, "%s: %s is version %d, we need %d.\n", _errowner, catpath, (int)ntohl(header.majorVer), MCMajorVer); 384 } 385 NLRETERR(EFTYPE); 386 } 387 if (ntohl(header.numSets) <= 0) { 388 (void)fclose(cat->fp); 389 free(cat); 390 (void)fprintf(stderr, "%s: %s has %d sets!\n", 391 _errowner, catpath, (int)ntohl(header.numSets)); 392 NLRETERR(EFTYPE); 393 } 394 395 cat->numSets = ntohl(header.numSets); 396 if ((cat->sets = (MCSetT *)malloc(sizeof(MCSetT) * cat->numSets)) == 397 NULL) 398 NOSPACE(); 399 400 nextSet = ntohll(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 = ntohll(set->nextSet); 418 continue; 419 } 420 set->invalid = TRUE; 421 nextSet = ntohll(set->nextSet); 422 } 423 424 return ((nl_catd) cat); 425} 426 427static int 428loadSet(MCCatT *cat, MCSetT *set) 429{ 430 MCMsgT *msg; 431 int i; 432 int saverr; 433 434 /* Get the data */ 435 if (fseeko(cat->fp, ntohll(set->data.off), SEEK_SET) == -1) 436 return (0); 437 if ((set->data.str = malloc(ntohl(set->dataLen))) == NULL) 438 return (-1); 439 if (fread(set->data.str, ntohl(set->dataLen), 1, cat->fp) != 1) { 440 saverr = errno; 441 free(set->data.str); 442 errno = saverr; 443 return (0); 444 } 445 446 /* Get the messages */ 447 if (fseeko(cat->fp, ntohll(set->u.firstMsg), SEEK_SET) == -1) { 448 saverr = errno; 449 free(set->data.str); 450 errno = saverr; 451 return (0); 452 } 453 if ((set->u.msgs = (MCMsgT *)malloc(sizeof(MCMsgT) * ntohl(set->numMsgs))) == 454 NULL) { 455 saverr = errno; 456 free(set->data.str); 457 errno = saverr; 458 return (-1); 459 } 460 461 for (i = 0; i < ntohl(set->numMsgs); ++i) { 462 msg = set->u.msgs + i; 463 if (fread(msg, sizeof(*msg), 1, cat->fp) != 1) { 464 saverr = errno; 465 free(set->u.msgs); 466 free(set->data.str); 467 errno = saverr; 468 return (0); 469 } 470 if (msg->invalid) { 471 --i; 472 continue; 473 } 474 msg->msg.str = (char *)(set->data.str + ntohll(msg->msg.off)); 475 } 476 set->invalid = FALSE; 477 return (1); 478} 479