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 = "$NetBSD: msgcat.c,v 1.11 1995/02/27 13:06:51 cgd Exp $"; 37 #endif /* LIBC_SCCS and not lint */ 38 39 /* Edit History 40 41 03/06/91 4 schulert remove working directory from nlspath 42 01/18/91 2 hamilton #if not rescanned 43 01/12/91 3 schulert conditionally use prototypes 44 11/03/90 1 hamilton Alphalpha->Alfalfa & OmegaMail->Poste 45 10/15/90 2 schulert > #include <unistd.h> if MIPS 46 08/13/90 1 schulert move from ua to omu 47 */ 48 49 /* 50 * We need a better way of handling errors than printing text. I need 51 * to add an error handling routine. 52 */ 53 54 #include "nl_types.h" 55 #include "msgcat.h" 56 57 #include <sys/types.h> 58 #include <sys/stat.h> 59 #include <errno.h> 60 #include <fcntl.h> 61 #include <locale.h> 62 #include <stdio.h> 63 #include <stdlib.h> 64 #include <string.h> 65 #include <unistd.h> 66 67 #ifndef True 68 # define True ~0 69 # define False 0 70 #endif 71 72 /* take care of sysv diffs */ 73 #ifndef MAXPATHLEN 74 #define MAXPATHLEN 1024 75 #endif 76 77 #ifndef FD_CLOEXEC 78 #define FD_CLOEXEC 1 79 #endif 80 81 #define NLERR ((nl_catd) -1) 82 83 static nl_catd loadCat(); 84 static int loadSet(); 85 86 nl_catd _catopen( name, type) 87 __const char *name; 88 int type; 89 { 90 char path[MAXPATHLEN]; 91 __const char *catpath = NULL; 92 char *nlspath; 93 char *lang; 94 char *base, *cptr, *pathP; 95 int spcleft; 96 long len; 97 struct stat sbuf; 98 99 if (!name || !*name) { 100 errno = EINVAL; 101 return(NLERR); 102 } 103 104 if (strchr(name, '/')) { 105 catpath = name; 106 if (stat(catpath, &sbuf)) return(NLERR); 107 } else { 108 if (type == NL_CAT_LOCALE) 109 lang = setlocale(LC_MESSAGES, NULL); 110 else { 111 if ((lang = (char *) getenv("LANG")) == NULL) 112 lang = "C"; 113 } 114 if ((nlspath = (char *) getenv("NLSPATH")) == NULL 115 #ifndef __NETBSD_SYSCALLS 116 || issetugid() 117 #endif 118 ) 119 nlspath = "/usr/share/nls/%L/%N.cat:/usr/share/nls/%N/%L:/usr/local/share/nls/%L/%N.cat:/usr/local/share/nls/%N/%L"; 120 121 len = strlen(nlspath); 122 base = cptr = malloc(len + 2); 123 if (!base) return(NLERR); 124 strcpy(cptr, nlspath); 125 cptr[len] = ':'; 126 cptr[len+1] = '\0'; 127 128 for (nlspath = cptr; *cptr; ++cptr) { 129 if (*cptr == ':') { 130 *cptr = '\0'; 131 for (pathP = path; *nlspath; ++nlspath) { 132 if (*nlspath == '%') { 133 spcleft = sizeof(path) - (pathP - path); 134 if (*(nlspath + 1) == 'L') { 135 ++nlspath; 136 if (strlcpy(pathP, lang, spcleft) >= spcleft) { 137 errno = ENAMETOOLONG; 138 return(NLERR); 139 } 140 pathP += strlen(lang); 141 } else if (*(nlspath + 1) == 'N') { 142 ++nlspath; 143 if (strlcpy(pathP, name, spcleft) >= spcleft) { 144 errno = ENAMETOOLONG; 145 return(NLERR); 146 } 147 pathP += strlen(name); 148 } else *(pathP++) = *nlspath; 149 } else *(pathP++) = *nlspath; 150 } 151 *pathP = '\0'; 152 if (stat(path, &sbuf) == 0) { 153 catpath = path; 154 break; 155 } 156 nlspath = cptr+1; 157 } 158 } 159 free(base); 160 161 if (!catpath) { 162 errno = ENOENT; 163 return(NLERR); 164 } 165 } 166 167 return(loadCat(catpath)); 168 } 169 170 /* 171 * We've got an odd situation here. The odds are real good that the 172 * number we are looking for is almost the same as the index. We could 173 * use the index, check the difference and do something intelligent, but 174 * I haven't quite figured out what's intelligent. 175 * 176 * Here's a start. 177 * Take an id N. If there are > N items in the list, then N cannot 178 * be more than N items from the start, since otherwise there would 179 * have to be duplicate items. So we can safely set the top to N+1 180 * (after taking into account that ids start at 1, and arrays at 0) 181 * 182 * Let's say we are at position P, and we are looking for N, but have 183 * V. If N > V, then the furthest away that N could be is 184 * P + (N-V). So we can safely set hi to P+(N-V)+1. For example: 185 * We are looking for 10, but have 8 186 * 8 ? ? ? ? 187 * >=9 >=10 >=11 188 * 189 */ 190 static MCSetT *MCGetSet( cat, setId) 191 MCCatT *cat; 192 int setId; 193 { 194 MCSetT *set; 195 long lo, hi, cur, dir; 196 197 if (!cat || setId <= 0) return(NULL); 198 199 lo = 0; 200 if (setId - 1 < cat->numSets) { 201 cur = setId - 1; 202 hi = setId; 203 } else { 204 hi = cat->numSets; 205 cur = (hi - lo) / 2; 206 } 207 208 while (True) { 209 set = cat->sets + cur; 210 if (set->setId == setId) break; 211 if (set->setId < setId) { 212 lo = cur+1; 213 if (hi > cur + (setId - set->setId) + 1) hi = cur+(setId-set->setId)+1; 214 dir = 1; 215 } else { 216 hi = cur; 217 dir = -1; 218 } 219 if (lo >= hi) return(NULL); 220 if (hi - lo == 1) cur += dir; 221 else cur += ((hi - lo) / 2) * dir; 222 } 223 if (set->invalid) 224 (void) loadSet(cat, set); 225 return(set); 226 } 227 228 229 static MCMsgT *MCGetMsg( set, msgId) 230 MCSetT *set; 231 int msgId; 232 { 233 MCMsgT *msg; 234 long lo, hi, cur, dir; 235 236 if (!set || set->invalid || msgId <= 0) return(NULL); 237 238 lo = 0; 239 if (msgId - 1 < set->numMsgs) { 240 cur = msgId - 1; 241 hi = msgId; 242 } else { 243 hi = set->numMsgs; 244 cur = (hi - lo) / 2; 245 } 246 247 while (True) { 248 msg = set->u.msgs + cur; 249 if (msg->msgId == msgId) break; 250 if (msg->msgId < msgId) { 251 lo = cur+1; 252 if (hi > cur + (msgId - msg->msgId) + 1) hi = cur+(msgId-msg->msgId)+1; 253 dir = 1; 254 } else { 255 hi = cur; 256 dir = -1; 257 } 258 if (lo >= hi) return(NULL); 259 if (hi - lo == 1) cur += dir; 260 else cur += ((hi - lo) / 2) * dir; 261 } 262 return(msg); 263 } 264 265 char *_catgets( catd, setId, msgId, dflt) 266 nl_catd catd; 267 int setId; 268 int msgId; 269 __const char *dflt; 270 { 271 MCMsgT *msg; 272 MCCatT *cat = (MCCatT *) catd; 273 __const char *cptr; 274 275 if (catd == NULL || catd == NLERR) 276 return((char *)dflt); 277 msg = MCGetMsg(MCGetSet(cat, setId), msgId); 278 if (msg) cptr = msg->msg.str; 279 else cptr = dflt; 280 return((char *)cptr); 281 } 282 283 284 int _catclose( catd) 285 nl_catd catd; 286 { 287 MCCatT *cat = (MCCatT *) catd; 288 MCSetT *set; 289 int i; 290 291 if (catd == NULL || catd == NLERR) { 292 errno = EBADF; 293 return -1; 294 } 295 296 if (cat->loadType != MCLoadAll) _close(cat->fd); 297 for (i = 0; i < cat->numSets; ++i) { 298 set = cat->sets + i; 299 if (!set->invalid) { 300 free(set->data.str); 301 free(set->u.msgs); 302 } 303 } 304 free(cat->sets); 305 free(cat); 306 307 return 0; 308 } 309 310 /* 311 * Internal routines 312 */ 313 314 /* Note that only malloc failures are allowed to return an error */ 315 #define ERRNAME "Message Catalog System" 316 #define CORRUPT() {fprintf(stderr, "%s: corrupt file.\n", ERRNAME); free(cat); errno = EINVAL; return(NLERR);} 317 #define NOSPACE() {fprintf(stderr, "%s: no more memory.\n", ERRNAME); free(cat); return(NLERR);} 318 319 static nl_catd loadCat(catpath) 320 __const char *catpath; 321 { 322 MCHeaderT header; 323 MCCatT *cat; 324 MCSetT *set; 325 long i, j; 326 off_t nextSet; 327 328 cat = (MCCatT *) malloc(sizeof(MCCatT)); 329 if (!cat) return(NLERR); 330 cat->loadType = MCLoadBySet; 331 332 if ((cat->fd = _open(catpath, O_RDONLY)) < 0) { 333 free(cat); 334 return(NLERR); 335 } 336 337 (void)_fcntl(cat->fd, F_SETFD, FD_CLOEXEC); 338 339 if (_read(cat->fd, &header, sizeof(header)) != sizeof(header)) 340 CORRUPT(); 341 342 if (strncmp(header.magic, MCMagic, MCMagicLen) != 0) CORRUPT(); 343 344 if (header.majorVer != MCMajorVer) { 345 free(cat); 346 fprintf(stderr, "%s: %s is version %ld, we need %ld.\n", ERRNAME, 347 catpath, header.majorVer, MCMajorVer); 348 errno = EINVAL; 349 return(NLERR); 350 } 351 352 if (header.numSets <= 0) { 353 free(cat); 354 fprintf(stderr, "%s: %s has %ld sets!\n", ERRNAME, catpath, 355 header.numSets); 356 errno = EINVAL; 357 return(NLERR); 358 } 359 360 cat->numSets = header.numSets; 361 cat->sets = (MCSetT *) malloc(sizeof(MCSetT) * header.numSets); 362 if (!cat->sets) NOSPACE(); 363 364 nextSet = header.firstSet; 365 for (i = 0; i < cat->numSets; ++i) { 366 if (lseek(cat->fd, nextSet, 0) == -1) { 367 for (j = 0; j < i; j++) { 368 set = cat->sets + j; 369 if (!set->invalid) { 370 free(set->data.str); 371 free(set->u.msgs); 372 } 373 } 374 free(cat->sets); 375 CORRUPT(); 376 } 377 378 /* read in the set header */ 379 set = cat->sets + i; 380 if (_read(cat->fd, set, sizeof(*set)) != sizeof(*set)) { 381 for (j = 0; j < i; j++) { 382 set = cat->sets + j; 383 if (!set->invalid) { 384 free(set->data.str); 385 free(set->u.msgs); 386 } 387 } 388 free(cat->sets); 389 CORRUPT(); 390 } 391 392 /* if it's invalid, skip over it (and backup 'i') */ 393 394 if (set->invalid) { 395 --i; 396 nextSet = set->nextSet; 397 continue; 398 } 399 400 if (cat->loadType == MCLoadAll) { 401 int res; 402 403 if ((res = loadSet(cat, set)) <= 0) { 404 for (j = 0; j < i; j++) { 405 set = cat->sets + j; 406 if (!set->invalid) { 407 free(set->data.str); 408 free(set->u.msgs); 409 } 410 } 411 free(cat->sets); 412 if (res < 0) NOSPACE(); 413 CORRUPT(); 414 } 415 } else set->invalid = True; 416 nextSet = set->nextSet; 417 } 418 if (cat->loadType == MCLoadAll) { 419 _close(cat->fd); 420 cat->fd = -1; 421 } 422 return((nl_catd) cat); 423 } 424 425 static int loadSet(cat, set) 426 MCCatT *cat; 427 MCSetT *set; 428 { 429 MCMsgT *msg; 430 int i; 431 432 /* Get the data */ 433 if (lseek(cat->fd, set->data.off, 0) == -1) return(0); 434 if ((set->data.str = malloc(set->dataLen)) == NULL) return(-1); 435 if (_read(cat->fd, set->data.str, set->dataLen) != set->dataLen) { 436 free(set->data.str); return(0); 437 } 438 439 /* Get the messages */ 440 if (lseek(cat->fd, set->u.firstMsg, 0) == -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 (_read(cat->fd, msg, sizeof(*msg)) != sizeof(*msg)) { 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