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)) CORRUPT(); 332 333 if (strncmp(header.magic, MCMagic, MCMagicLen) != 0) CORRUPT(); 334 335 if (header.majorVer != MCMajorVer) { 336 free(cat); 337 fprintf(stderr, "%s: %s is version %ld, we need %ld.\n", ERRNAME, 338 catpath, header.majorVer, MCMajorVer); 339 errno = EINVAL; 340 return(NLERR); 341 } 342 343 if (header.numSets <= 0) { 344 free(cat); 345 fprintf(stderr, "%s: %s has %ld sets!\n", ERRNAME, catpath, 346 header.numSets); 347 errno = EINVAL; 348 return(NLERR); 349 } 350 351 cat->numSets = header.numSets; 352 cat->sets = (MCSetT *) malloc(sizeof(MCSetT) * header.numSets); 353 if (!cat->sets) NOSPACE(); 354 355 nextSet = header.firstSet; 356 for (i = 0; i < cat->numSets; ++i) { 357 if (lseek(cat->fd, nextSet, 0) == -1) { 358 for (j = 0; j < i; j++) { 359 set = cat->sets + j; 360 if (!set->invalid) { 361 free(set->data.str); 362 free(set->u.msgs); 363 } 364 } 365 free(cat->sets); 366 CORRUPT(); 367 } 368 369 /* read in the set header */ 370 set = cat->sets + i; 371 if (read(cat->fd, set, sizeof(*set)) != sizeof(*set)) { 372 for (j = 0; j < i; j++) { 373 set = cat->sets + j; 374 if (!set->invalid) { 375 free(set->data.str); 376 free(set->u.msgs); 377 } 378 } 379 free(cat->sets); 380 CORRUPT(); 381 } 382 383 /* if it's invalid, skip over it (and backup 'i') */ 384 385 if (set->invalid) { 386 --i; 387 nextSet = set->nextSet; 388 continue; 389 } 390 391 if (cat->loadType == MCLoadAll) { 392 int res; 393 394 if ((res = loadSet(cat, set)) <= 0) { 395 for (j = 0; j < i; j++) { 396 set = cat->sets + j; 397 if (!set->invalid) { 398 free(set->data.str); 399 free(set->u.msgs); 400 } 401 } 402 free(cat->sets); 403 if (res < 0) NOSPACE(); 404 CORRUPT(); 405 } 406 } else set->invalid = True; 407 nextSet = set->nextSet; 408 } 409 if (cat->loadType == MCLoadAll) { 410 close(cat->fd); 411 cat->fd = -1; 412 } 413 return((nl_catd) cat); 414 } 415 416 static int loadSet(cat, set) 417 MCCatT *cat; 418 MCSetT *set; 419 { 420 MCMsgT *msg; 421 int i; 422 423 /* Get the data */ 424 if (lseek(cat->fd, set->data.off, 0) == -1) return(0); 425 if ((set->data.str = malloc(set->dataLen)) == NULL) return(-1); 426 if (read(cat->fd, set->data.str, set->dataLen) != set->dataLen) { 427 free(set->data.str); return(0); 428 } 429 430 /* Get the messages */ 431 if (lseek(cat->fd, set->u.firstMsg, 0) == -1) { 432 free(set->data.str); return(0); 433 } 434 if ((set->u.msgs = (MCMsgT *) malloc(sizeof(MCMsgT) * set->numMsgs)) == NULL) { 435 free(set->data.str); return(-1); 436 } 437 438 for (i = 0; i < set->numMsgs; ++i) { 439 msg = set->u.msgs + i; 440 if (read(cat->fd, msg, sizeof(*msg)) != sizeof(*msg)) { 441 free(set->u.msgs); free(set->data.str); return(0); 442 } 443 if (msg->invalid) { 444 --i; 445 continue; 446 } 447 msg->msg.str = (char *) (set->data.str + msg->msg.off); 448 } 449 set->invalid = False; 450 return(1); 451 } 452