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