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