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