msgcat.c revision 71579
1/* $FreeBSD: head/lib/libc/nls/msgcat.c 71579 2001-01-24 13:01:12Z deischen $ */ 2 3/*********************************************************** 4Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts. 5 6 All Rights Reserved 7 8Permission to use, copy, modify, and distribute this software and its 9documentation for any purpose and without fee is hereby granted, 10provided that the above copyright notice appear in all copies and that 11both that copyright notice and this permission notice appear in 12supporting documentation, and that Alfalfa's name not be used in 13advertising or publicity pertaining to distribution of the software 14without specific, written prior permission. 15 16ALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 17ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL 18ALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 19ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 20WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 21ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 22SOFTWARE. 23 24If you make any modifications, bugfixes or other changes to this software 25we'd appreciate it if you could send a copy to us so we can keep things 26up-to-date. Many thanks. 27 Kee Hinckley 28 Alfalfa Software, Inc. 29 267 Allston St., #3 30 Cambridge, MA 02139 USA 31 nazgul@alfalfa.com 32 33******************************************************************/ 34 35#if defined(LIBC_SCCS) && !defined(lint) 36static char *rcsid = "$FreeBSD: head/lib/libc/nls/msgcat.c 71579 2001-01-24 13:01:12Z deischen $"; 37#endif /* LIBC_SCCS and not lint */ 38 39/* 40 * We need a better way of handling errors than printing text. I need 41 * to add an error handling routine. 42 */ 43 44#include "namespace.h" 45#include <sys/types.h> 46#include <sys/stat.h> 47#include <sys/syslimits.h> 48#include <errno.h> 49#include <fcntl.h> 50#include <locale.h> 51#include <nl_types.h> 52#include <stdio.h> 53#include <stdlib.h> 54#include <string.h> 55#include <unistd.h> 56#include "un-namespace.h" 57 58#include "msgcat.h" 59 60#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" 61 62#define TRUE 1 63#define FALSE 0 64 65#define NLERR ((nl_catd) -1) 66#define NLRETERR(errc) errno = errc; return(NLERR); 67 68static nl_catd loadCat(); 69static int loadSet(); 70static void __nls_free_resources(); 71 72nl_catd 73catopen( name, type) 74 __const char *name; 75 int type; 76{ 77 int spcleft; 78 char path[PATH_MAX]; 79 char *nlspath, *lang, *base, *cptr, *pathP, *tmpptr; 80 char *cptr1, *plang, *pter, *pcode; 81 struct stat sbuf; 82 83 if (name == NULL || *name == '\0') { 84 NLRETERR(ENOENT); 85 } 86 87 /* is it absolute path ? if yes, load immidiately */ 88 if (strchr(name, '/')) 89 return loadCat(name); 90 91 if (type == NL_CAT_LOCALE) 92 lang = setlocale(LC_MESSAGES, NULL); 93 else 94 lang = getenv("LANG"); 95 if (lang == NULL || *lang == '\0' || strchr(lang, '/') != NULL) 96 lang = "C"; 97 98 if ((plang = cptr1 = strdup(lang)) == NULL) 99 return (NLERR); 100 if ((cptr = strchr(cptr1, '@')) != NULL) 101 *cptr = '\0'; 102 pter = pcode = ""; 103 if ((cptr = strchr(cptr1, '_')) != NULL) { 104 *cptr++ = '\0'; 105 pter = cptr1 = cptr; 106 } 107 if ((cptr = strchr(cptr1, '.')) != NULL) { 108 *cptr++ = '\0'; 109 pcode = cptr; 110 } 111 112 if ((nlspath = getenv("NLSPATH")) == NULL 113#ifndef __NETBSD_SYSCALLS 114 || issetugid() 115#endif 116 ) 117 nlspath = _DEFAULT_NLS_PATH; 118 119 if ((base = cptr = strdup(nlspath)) == NULL) { 120 free(plang); 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 >= sizeof(path) - 1) 150 goto too_long; 151 *(pathP++) = *nlspath; 152 continue; 153 } 154 ++nlspath; 155 put_tmpptr: 156 spcleft = sizeof(path) - (pathP - path) - 1; 157 if (strlcpy(pathP, tmpptr, spcleft) >= spcleft) { 158 too_long: 159 free(plang); 160 free(base); 161 NLRETERR(ENAMETOOLONG); 162 } 163 pathP += strlen(tmpptr); 164 } else { 165 if (pathP - path >= sizeof(path) - 1) 166 goto too_long; 167 *(pathP++) = *nlspath; 168 } 169 } 170 *pathP = '\0'; 171 if (stat(path, &sbuf) == 0) { 172 free(plang); 173 free(base); 174 return loadCat(path); 175 } 176 } else { 177 tmpptr = (char*)name; 178 --nlspath; 179 goto put_tmpptr; 180 } 181 } 182 free(plang); 183 free(base); 184 NLRETERR(ENOENT); 185} 186 187/* 188 * We've got an odd situation here. The odds are real good that the 189 * number we are looking for is almost the same as the index. We could 190 * use the index, check the difference and do something intelligent, but 191 * I haven't quite figured out what's intelligent. 192 * 193 * Here's a start. 194 * Take an id N. If there are > N items in the list, then N cannot 195 * be more than N items from the start, since otherwise there would 196 * have to be duplicate items. So we can safely set the top to N+1 197 * (after taking into account that ids start at 1, and arrays at 0) 198 * 199 * Let's say we are at position P, and we are looking for N, but have 200 * V. If N > V, then the furthest away that N could be is 201 * P + (N-V). So we can safely set hi to P+(N-V)+1. For example: 202 * We are looking for 10, but have 8 203 * 8 ? ? ? ? 204 * >=9 >=10 >=11 205 * 206 */ 207 208#define LOOKUP(PARENT, CHILD, ID, NUM, SET) { \ 209 lo = 0; \ 210 if (ID - 1 < PARENT->NUM) { \ 211 cur = ID - 1; hi = ID; \ 212 } else { \ 213 hi = PARENT->NUM; cur = (hi - lo) / 2; \ 214 } \ 215 while (TRUE) { \ 216 CHILD = PARENT->SET + cur; \ 217 if (CHILD->ID == ID) break; \ 218 if (CHILD->ID < ID) { \ 219 lo = cur+1; \ 220 if (hi > cur+(ID-CHILD->ID)+1) \ 221 hi = cur+(ID-CHILD->ID)+1; \ 222 dir = 1; \ 223 } else { \ 224 hi = cur; dir = -1; \ 225 } \ 226 if (lo >= hi) return(NULL); \ 227 if (hi - lo == 1) cur += dir; \ 228 else cur += ((hi - lo) / 2) * dir; \ 229 } \ 230 } 231 232static MCSetT* 233MCGetSet( cat, setId) 234 MCCatT *cat; 235 int setId; 236{ 237 MCSetT *set; 238 long lo, hi, cur, dir; 239 240 if (cat == NULL || setId <= 0) return(NULL); 241 LOOKUP(cat, set, setId, numSets, sets); 242 if (set->invalid && loadSet(cat, set) <= 0) 243 return(NULL); 244 return(set); 245} 246 247 248static MCMsgT* 249MCGetMsg( set, msgId) 250 MCSetT *set; 251 int msgId; 252{ 253 MCMsgT *msg; 254 long lo, hi, cur, dir; 255 256 if (set == NULL || set->invalid || msgId <= 0) return(NULL); 257 LOOKUP(set, msg, msgId, numMsgs, u.msgs); 258 return(msg); 259} 260 261char* 262catgets( catd, setId, msgId, dflt) 263 nl_catd catd; 264 int setId; 265 int msgId; 266 __const char *dflt; 267{ 268 MCMsgT *msg; 269 MCCatT *cat = (MCCatT *) catd; 270 __const char *cptr; 271 272 if (catd == NULL || catd == NLERR) 273 return((char *)dflt); 274 msg = MCGetMsg(MCGetSet(cat, setId), msgId); 275 if (msg != NULL) cptr = msg->msg.str; 276 else cptr = dflt; 277 return((char *)cptr); 278} 279 280 281int 282catclose( catd) 283 nl_catd catd; 284{ 285 MCCatT *cat = (MCCatT *) catd; 286 287 if (catd == NULL || catd == NLERR) { 288 errno = EBADF; return(-1); 289 } 290 291#if 0 292 if (cat->loadType != MCLoadAll) 293#endif 294 (void) fclose(cat->fp); 295 __nls_free_resources(cat, cat->numSets); 296 free(cat); 297 298 return(0); 299} 300 301/* 302 * Internal routines 303 */ 304 305/* Note that only malloc failures are allowed to return an error */ 306static char* _errowner = "Message Catalog System";; 307#define CORRUPT() { \ 308 fprintf(stderr, "%s: corrupt file.", _errowner); \ 309 free(cat); \ 310 NLRETERR(EINVAL); \ 311 } 312 313#define NOSPACE() { \ 314 fprintf(stderr, "%s: no more memory.", _errowner); \ 315 free(cat); \ 316 return(NLERR); \ 317 } 318 319static void 320__nls_free_resources(cat, i) 321 MCCatT *cat; 322 int i; 323{ 324 MCSetT *set; 325 int j; 326 327 for (j = 0; j < i; j++) { 328 set = cat->sets + j; 329 if (!set->invalid) { 330 free(set->data.str); 331 free(set->u.msgs); 332 } 333 } 334 free(cat->sets); 335} 336 337static nl_catd 338loadCat(catpath) 339 __const char *catpath; 340{ 341 MCHeaderT header; 342 MCCatT *cat; 343 MCSetT *set; 344 long i; 345 off_t nextSet; 346 347 cat = (MCCatT *) malloc(sizeof(MCCatT)); 348 if (cat == NULL) return(NLERR); 349 cat->loadType = MCLoadBySet; 350 351 if ((cat->fp = fopen(catpath, "r")) == NULL) { 352 free(cat); 353 return(NLERR); 354 } 355 356 (void) _fcntl(fileno(cat->fp), F_SETFD, FD_CLOEXEC); 357 358 if (fread(&header, sizeof(header), 1, cat->fp) != 1) 359 CORRUPT(); 360 361 if (strncmp(header.magic, MCMagic, MCMagicLen) != 0) CORRUPT(); 362 363 if (header.majorVer != MCMajorVer) { 364 free(cat); 365 fprintf(stderr, "%s: %s is version %ld, we need %ld.\n", _errowner, 366 catpath, header.majorVer, MCMajorVer); 367 NLRETERR(EINVAL); 368 } 369 370 if (header.numSets <= 0) { 371 free(cat); 372 fprintf(stderr, "%s: %s has %ld sets!\n", _errowner, catpath, 373 header.numSets); 374 NLRETERR(EINVAL); 375 } 376 377 cat->numSets = header.numSets; 378 cat->sets = (MCSetT *) malloc(sizeof(MCSetT) * header.numSets); 379 if (cat->sets == NULL) NOSPACE(); 380 381 nextSet = header.firstSet; 382 for (i = 0; i < cat->numSets; ++i) { 383 if (fseeko(cat->fp, nextSet, SEEK_SET) == -1) { 384 __nls_free_resources(cat, i); 385 CORRUPT(); 386 } 387 388 /* read in the set header */ 389 set = cat->sets + i; 390 if (fread(set, sizeof(*set), 1, cat->fp) != 1) { 391 __nls_free_resources(cat, i); 392 CORRUPT(); 393 } 394 395 /* if it's invalid, skip over it (and backup 'i') */ 396 if (set->invalid) { 397 --i; 398 nextSet = set->nextSet; 399 continue; 400 } 401 402#if 0 403 if (cat->loadType == MCLoadAll) { 404 int res; 405 406 if ((res = loadSet(cat, set)) <= 0) { 407 __nls_free_resources(cat, i); 408 if (res < 0) NOSPACE(); 409 CORRUPT(); 410 } 411 } else 412#endif 413 set->invalid = TRUE; 414 nextSet = set->nextSet; 415 } 416#if 0 417 if (cat->loadType == MCLoadAll) { 418 (void) fclose(cat->fp); 419 cat->fp = NULL; 420 } 421#endif 422 return((nl_catd) cat); 423} 424 425static int 426loadSet(cat, set) 427 MCCatT *cat; 428 MCSetT *set; 429{ 430 MCMsgT *msg; 431 int i; 432 433 /* Get the data */ 434 if (fseeko(cat->fp, set->data.off, SEEK_SET) == -1) return(0); 435 if ((set->data.str = malloc(set->dataLen)) == NULL) return(-1); 436 if (fread(set->data.str, set->dataLen, 1, cat->fp) != 1) { 437 free(set->data.str); return(0); 438 } 439 440 /* Get the messages */ 441 if (fseeko(cat->fp, set->u.firstMsg, SEEK_SET) == -1) { 442 free(set->data.str); return(0); 443 } 444 if ((set->u.msgs = (MCMsgT *) malloc(sizeof(MCMsgT) * set->numMsgs)) == NULL) { 445 free(set->data.str); return(-1); 446 } 447 448 for (i = 0; i < set->numMsgs; ++i) { 449 msg = set->u.msgs + i; 450 if (fread(msg, sizeof(*msg), 1, cat->fp) != 1) { 451 free(set->u.msgs); free(set->data.str); return(0); 452 } 453 if (msg->invalid) { 454 --i; 455 continue; 456 } 457 msg->msg.str = (char *) (set->data.str + msg->msg.off); 458 } 459 set->invalid = FALSE; 460 return(1); 461} 462