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