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