1 /* $Id: msgcat.c,v 1.8 1997/03/24 06:15:07 imp 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, *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) 103 lang = "C"; 104 /* XXX Should really be issetguid(), but we don't have that */ 105 if ((nlspath = (char *) getenv("NLSPATH")) == NULL || 106 getuid() != geteuid() || getgid() != getegid()) { 107 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"; 108 } 109 110 len = strlen(nlspath); 111 base = cptr = (char *) malloc(len + 2); 112 if (!base) return(NLERR); 113 strcpy(cptr, nlspath); 114 cptr[len] = ':'; 115 cptr[len+1] = '\0'; 116 117 for (nlspath = cptr; *cptr; ++cptr) { 118 if (*cptr == ':') { 119 *cptr = '\0'; 120 for (pathP = path; *nlspath; ++nlspath) { 121 if (*nlspath == '%') { 122 if (*(nlspath + 1) == 'L') { 123 ++nlspath; 124 strcpy(pathP, lang); 125 pathP += strlen(lang); 126 } else if (*(nlspath + 1) == 'N') { 127 ++nlspath; 128 strcpy(pathP, name); 129 pathP += strlen(name); 130 } else *(pathP++) = *nlspath; 131 } else *(pathP++) = *nlspath; 132 } 133 *pathP = '\0'; 134 if (stat(path, &sbuf) == 0) { 135 catpath = path; 136 break; 137 } 138 nlspath = cptr+1; 139 } 140 } 141 free(base); 142 if (tmppath) free(tmppath); 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); return(0);} 291 #define NOSPACE() {fprintf(stderr, "%s: no more memory.\n", ERRNAME); 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; 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 return(0); 309 } 310 311 fcntl(cat->fd, F_SETFD, FD_CLOEXEC); 312 313 if (read(cat->fd, &header, sizeof(header)) != sizeof(header)) CORRUPT(); 314 315 if (strncmp(header.magic, MCMagic, MCMagicLen) != 0) CORRUPT(); 316 317 if (header.majorVer != MCMajorVer) { 318 fprintf(stderr, "%s: %s is version %ld, we need %ld.\n", ERRNAME, 319 catpath, header.majorVer, MCMajorVer); 320 return(0); 321 } 322 323 if (header.numSets <= 0) { 324 fprintf(stderr, "%s: %s has %ld sets!\n", ERRNAME, catpath, 325 header.numSets); 326 return(0); 327 } 328 329 cat->numSets = header.numSets; 330 cat->sets = (MCSetT *) malloc(sizeof(MCSetT) * header.numSets); 331 if (!cat->sets) NOSPACE(); 332 333 nextSet = header.firstSet; 334 for (i = 0; i < cat->numSets; ++i) { 335 if (lseek(cat->fd, nextSet, 0) == -1) CORRUPT(); 336 337 /* read in the set header */ 338 set = cat->sets + i; 339 if (read(cat->fd, set, sizeof(*set)) != sizeof(*set)) CORRUPT(); 340 341 /* if it's invalid, skip over it (and backup 'i') */ 342 343 if (set->invalid) { 344 --i; 345 nextSet = set->nextSet; 346 continue; 347 } 348 349 if (cat->loadType == MCLoadAll) { 350 nl_catd res; 351 if ((res = loadSet(cat, set)) <= 0) { 352 if (res == -1) NOSPACE(); 353 CORRUPT(); 354 } 355 } else set->invalid = True; 356 nextSet = set->nextSet; 357 } 358 if (cat->loadType == MCLoadAll) { 359 close(cat->fd); 360 cat->fd = -1; 361 } 362 return((nl_catd) cat); 363 } 364 365 static nl_catd loadSet( cat, set) 366 MCCatT *cat; 367 MCSetT *set; 368 { 369 MCMsgT *msg; 370 int i; 371 372 /* Get the data */ 373 if (lseek(cat->fd, set->data.off, 0) == -1) return(0); 374 if ((set->data.str = (char *) malloc(set->dataLen)) == NULL) return(-1); 375 if (read(cat->fd, set->data.str, set->dataLen) != set->dataLen) return(0); 376 377 /* Get the messages */ 378 if (lseek(cat->fd, set->u.firstMsg, 0) == -1) return(0); 379 if ((set->u.msgs = (MCMsgT *) malloc(sizeof(MCMsgT) * set->numMsgs)) == NULL) return(-1); 380 381 for (i = 0; i < set->numMsgs; ++i) { 382 msg = set->u.msgs + i; 383 if (read(cat->fd, msg, sizeof(*msg)) != sizeof(*msg)) return(0); 384 if (msg->invalid) { 385 --i; 386 continue; 387 } 388 msg->msg.str = (char *) (set->data.str + msg->msg.off); 389 } 390 set->invalid = False; 391 return(1); 392 } 393 394 395 396 397 398