1 /* $Id: msgcat.c,v 1.3 1995/06/17 03:02:21 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, *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:/usr/local/share/nls/%L/%N.cat:/usr/local/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 int i; 264 265 if (!cat) return -1; 266 267 if (cat->loadType != MCLoadAll) close(cat->fd); 268 for (i = 0; i < cat->numSets; ++i) { 269 set = cat->sets + i; 270 if (!set->invalid) { 271 free(set->data.str); 272 free(set->u.msgs); 273 } 274 } 275 free(cat->sets); 276 free(cat); 277 278 return 0; 279 } 280 281 /* 282 * Internal routines 283 */ 284 285 /* Note that only malloc failures are allowed to return an error */ 286 #define ERRNAME "Message Catalog System" 287 #define CORRUPT() {fprintf(stderr, "%s: corrupt file.\n", ERRNAME); return(0);} 288 #define NOSPACE() {fprintf(stderr, "%s: no more memory.\n", ERRNAME); return(NLERR);} 289 290 static nl_catd loadCat( catpath, type) 291 __const char *catpath; 292 int type; 293 { 294 MCHeaderT header; 295 MCCatT *cat; 296 MCSetT *set; 297 long i; 298 off_t nextSet; 299 300 cat = (MCCatT *) malloc(sizeof(MCCatT)); 301 if (!cat) return(NLERR); 302 cat->loadType = type; 303 304 if ((cat->fd = open(catpath, O_RDONLY)) < 0) { 305 return(0); 306 } 307 308 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 fprintf(stderr, "%s: %s is version %d, we need %d.\n", ERRNAME, 316 catpath, header.majorVer, MCMajorVer); 317 return(0); 318 } 319 320 if (header.numSets <= 0) { 321 fprintf(stderr, "%s: %s has %d sets!\n", ERRNAME, catpath, 322 header.numSets); 323 return(0); 324 } 325 326 cat->numSets = header.numSets; 327 cat->sets = (MCSetT *) malloc(sizeof(MCSetT) * header.numSets); 328 if (!cat->sets) NOSPACE(); 329 330 nextSet = header.firstSet; 331 for (i = 0; i < cat->numSets; ++i) { 332 if (lseek(cat->fd, nextSet, 0) == -1) CORRUPT(); 333 334 /* read in the set header */ 335 set = cat->sets + i; 336 if (read(cat->fd, set, sizeof(*set)) != sizeof(*set)) CORRUPT(); 337 338 /* if it's invalid, skip over it (and backup 'i') */ 339 340 if (set->invalid) { 341 --i; 342 nextSet = set->nextSet; 343 continue; 344 } 345 346 if (cat->loadType == MCLoadAll) { 347 nl_catd res; 348 if ((res = loadSet(cat, set)) <= 0) { 349 if (res == -1) NOSPACE(); 350 CORRUPT(); 351 } 352 } else set->invalid = True; 353 nextSet = set->nextSet; 354 } 355 if (cat->loadType == MCLoadAll) { 356 close(cat->fd); 357 cat->fd = -1; 358 } 359 return((nl_catd) cat); 360 } 361 362 static nl_catd loadSet( cat, set) 363 MCCatT *cat; 364 MCSetT *set; 365 { 366 MCMsgT *msg; 367 int i; 368 369 /* Get the data */ 370 if (lseek(cat->fd, set->data.off, 0) == -1) return(0); 371 if ((set->data.str = (char *) malloc(set->dataLen)) == NULL) return(-1); 372 if (read(cat->fd, set->data.str, set->dataLen) != set->dataLen) return(0); 373 374 /* Get the messages */ 375 if (lseek(cat->fd, set->u.firstMsg, 0) == -1) return(0); 376 if ((set->u.msgs = (MCMsgT *) malloc(sizeof(MCMsgT) * set->numMsgs)) == NULL) return(-1); 377 378 for (i = 0; i < set->numMsgs; ++i) { 379 msg = set->u.msgs + i; 380 if (read(cat->fd, msg, sizeof(*msg)) != sizeof(*msg)) return(0); 381 if (msg->invalid) { 382 --i; 383 continue; 384 } 385 msg->msg.str = (char *) (set->data.str + msg->msg.off); 386 } 387 set->invalid = False; 388 return(1); 389 } 390 391 392 393 394 395