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