1 1.36 apb /* $NetBSD: gencat.c,v 1.36 2013/11/27 17:38:11 apb Exp $ */ 2 1.5 veego 3 1.5 veego /* 4 1.3 jtc * Copyright (c) 1996 The NetBSD Foundation, Inc. 5 1.3 jtc * All rights reserved. 6 1.3 jtc * 7 1.3 jtc * This code is derived from software contributed to The NetBSD Foundation 8 1.3 jtc * by J.T. Conklin. 9 1.3 jtc * 10 1.3 jtc * Redistribution and use in source and binary forms, with or without 11 1.3 jtc * modification, are permitted provided that the following conditions 12 1.3 jtc * are met: 13 1.3 jtc * 1. Redistributions of source code must retain the above copyright 14 1.3 jtc * notice, this list of conditions and the following disclaimer. 15 1.3 jtc * 2. Redistributions in binary form must reproduce the above copyright 16 1.3 jtc * notice, this list of conditions and the following disclaimer in the 17 1.3 jtc * documentation and/or other materials provided with the distribution. 18 1.3 jtc * 19 1.3 jtc * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.3 jtc * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.3 jtc * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.6 jtc * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.6 jtc * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.3 jtc * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.3 jtc * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.3 jtc * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.3 jtc * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.3 jtc * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.3 jtc * POSSIBILITY OF SUCH DAMAGE. 30 1.3 jtc */ 31 1.1 jtc 32 1.7 lukem #include <sys/cdefs.h> 33 1.14 tv #if defined(__RCSID) && !defined(lint) 34 1.36 apb __RCSID("$NetBSD: gencat.c,v 1.36 2013/11/27 17:38:11 apb Exp $"); 35 1.7 lukem #endif 36 1.7 lukem 37 1.1 jtc /*********************************************************** 38 1.1 jtc Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts. 39 1.1 jtc 40 1.1 jtc All Rights Reserved 41 1.1 jtc 42 1.1 jtc Permission to use, copy, modify, and distribute this software and its 43 1.1 jtc documentation for any purpose and without fee is hereby granted, 44 1.1 jtc provided that the above copyright notice appear in all copies and that 45 1.1 jtc both that copyright notice and this permission notice appear in 46 1.1 jtc supporting documentation, and that Alfalfa's name not be used in 47 1.1 jtc advertising or publicity pertaining to distribution of the software 48 1.1 jtc without specific, written prior permission. 49 1.1 jtc 50 1.1 jtc ALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 51 1.1 jtc ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL 52 1.1 jtc ALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 53 1.1 jtc ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 54 1.1 jtc WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 55 1.1 jtc ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 56 1.1 jtc SOFTWARE. 57 1.1 jtc 58 1.1 jtc If you make any modifications, bugfixes or other changes to this software 59 1.1 jtc we'd appreciate it if you could send a copy to us so we can keep things 60 1.1 jtc up-to-date. Many thanks. 61 1.1 jtc Kee Hinckley 62 1.1 jtc Alfalfa Software, Inc. 63 1.1 jtc 267 Allston St., #3 64 1.1 jtc Cambridge, MA 02139 USA 65 1.1 jtc nazgul (at) alfalfa.com 66 1.3 jtc 67 1.1 jtc ******************************************************************/ 68 1.1 jtc 69 1.18 lukem #if HAVE_NBTOOL_CONFIG_H 70 1.18 lukem #include "nbtool_config.h" 71 1.13 tv #endif 72 1.13 tv 73 1.3 jtc #define _NLS_PRIVATE 74 1.1 jtc 75 1.17 bjh21 #include <sys/types.h> 76 1.3 jtc #include <sys/queue.h> 77 1.15 bjh21 78 1.16 bjh21 #include <netinet/in.h> /* Needed by arpa/inet.h on NetBSD */ 79 1.16 bjh21 #include <arpa/inet.h> /* Needed for htonl() on POSIX systems */ 80 1.15 bjh21 81 1.3 jtc #include <ctype.h> 82 1.14 tv #include <err.h> 83 1.24 ginsbach #include <errno.h> 84 1.7 lukem #include <fcntl.h> 85 1.12 yamt #include <limits.h> 86 1.7 lukem #include <nl_types.h> 87 1.1 jtc #include <stdio.h> 88 1.3 jtc #include <stdlib.h> 89 1.3 jtc #include <string.h> 90 1.3 jtc #include <unistd.h> 91 1.13 tv 92 1.14 tv #ifndef NL_SETMAX 93 1.14 tv #define NL_SETMAX 255 94 1.14 tv #endif 95 1.14 tv #ifndef NL_MSGMAX 96 1.14 tv #define NL_MSGMAX 2048 97 1.13 tv #endif 98 1.3 jtc 99 1.3 jtc struct _msgT { 100 1.3 jtc long msgId; 101 1.3 jtc char *str; 102 1.3 jtc LIST_ENTRY(_msgT) entries; 103 1.3 jtc }; 104 1.3 jtc 105 1.3 jtc struct _setT { 106 1.3 jtc long setId; 107 1.3 jtc LIST_HEAD(msghead, _msgT) msghead; 108 1.3 jtc LIST_ENTRY(_setT) entries; 109 1.3 jtc }; 110 1.1 jtc 111 1.36 apb static LIST_HEAD(sethead, _setT) sethead = LIST_HEAD_INITIALIZER(sethead); 112 1.3 jtc static struct _setT *curSet; 113 1.1 jtc 114 1.24 ginsbach static const char *curfile; 115 1.3 jtc static char *curline = NULL; 116 1.3 jtc static long lineno = 0; 117 1.1 jtc 118 1.23 ginsbach static char *cskip(char *); 119 1.31 joerg __dead static void error(const char *); 120 1.30 roy static char *get_line(int); 121 1.23 ginsbach static char *getmsg(int, char *, char); 122 1.23 ginsbach static void warning(const char *, const char *); 123 1.23 ginsbach static char *wskip(char *); 124 1.23 ginsbach static char *xstrdup(const char *); 125 1.23 ginsbach static void *xmalloc(size_t); 126 1.23 ginsbach static void *xrealloc(void *, size_t); 127 1.23 ginsbach 128 1.31 joerg static void MCParse(int fd); 129 1.31 joerg static void MCReadCat(int fd); 130 1.31 joerg static void MCWriteCat(int fd); 131 1.31 joerg static void MCDelMsg(int msgId); 132 1.31 joerg static void MCAddMsg(int msgId, const char *msg); 133 1.31 joerg static void MCAddSet(int setId); 134 1.31 joerg static void MCDelSet(int setId); 135 1.31 joerg __dead static void usage(void); 136 1.7 lukem 137 1.24 ginsbach #define CORRUPT "corrupt message catalog" 138 1.24 ginsbach #define NOMEMORY "out of memory" 139 1.7 lukem 140 1.31 joerg static void 141 1.23 ginsbach usage(void) 142 1.3 jtc { 143 1.34 wiz fprintf(stderr, "usage: %s catfile [msgfile|- ...]\n", getprogname()); 144 1.3 jtc exit(1); 145 1.3 jtc } 146 1.1 jtc 147 1.3 jtc int 148 1.23 ginsbach main(int argc, char *argv[]) 149 1.3 jtc { 150 1.3 jtc int ofd, ifd; 151 1.3 jtc char *catfile = NULL; 152 1.3 jtc int c; 153 1.24 ginsbach int updatecat = 0; 154 1.3 jtc 155 1.3 jtc while ((c = getopt(argc, argv, "")) != -1) { 156 1.3 jtc switch (c) { 157 1.3 jtc case '?': 158 1.3 jtc default: 159 1.3 jtc usage(); 160 1.3 jtc /* NOTREACHED */ 161 1.3 jtc } 162 1.3 jtc } 163 1.3 jtc argc -= optind; 164 1.3 jtc argv += optind; 165 1.1 jtc 166 1.33 christos if (argc < 1) { 167 1.1 jtc usage(); 168 1.3 jtc /* NOTREACHED */ 169 1.3 jtc } 170 1.3 jtc catfile = *argv++; 171 1.3 jtc 172 1.24 ginsbach if ((catfile[0] == '-') && (catfile[1] == '\0')) { 173 1.24 ginsbach ofd = STDOUT_FILENO; 174 1.24 ginsbach } else { 175 1.24 ginsbach ofd = open(catfile, O_WRONLY | O_CREAT | O_EXCL, 0666); 176 1.24 ginsbach if (ofd < 0) { 177 1.24 ginsbach if (errno == EEXIST) { 178 1.24 ginsbach if ((ofd = open(catfile, O_RDWR)) < 0) { 179 1.24 ginsbach err(1, "Unable to open %s", catfile); 180 1.24 ginsbach /* NOTREACHED */ 181 1.24 ginsbach } 182 1.24 ginsbach } else { 183 1.24 ginsbach err(1, "Unable to create new %s", catfile); 184 1.24 ginsbach /* NOTREACHED */ 185 1.24 ginsbach } 186 1.24 ginsbach curfile = catfile; 187 1.24 ginsbach updatecat = 1; 188 1.24 ginsbach MCReadCat(ofd); 189 1.32 christos if (lseek(ofd, (off_t)0, SEEK_SET) == (off_t)-1) { 190 1.24 ginsbach err(1, "Unable to seek on %s", catfile); 191 1.24 ginsbach /* NOTREACHED */ 192 1.24 ginsbach } 193 1.24 ginsbach } 194 1.24 ginsbach } 195 1.24 ginsbach 196 1.33 christos if (argc < 2 || (((*argv)[0] == '-') && ((*argv)[1] == '\0'))) { 197 1.33 christos if (argc > 2) 198 1.24 ginsbach usage(); 199 1.24 ginsbach /* NOTREACHED */ 200 1.24 ginsbach MCParse(STDIN_FILENO); 201 1.24 ginsbach } else { 202 1.24 ginsbach for (; *argv; argv++) { 203 1.24 ginsbach if ((ifd = open(*argv, O_RDONLY)) < 0) 204 1.24 ginsbach err(1, "Unable to read %s", *argv); 205 1.24 ginsbach curfile = *argv; 206 1.24 ginsbach lineno = 0; 207 1.24 ginsbach MCParse(ifd); 208 1.24 ginsbach close(ifd); 209 1.24 ginsbach } 210 1.24 ginsbach } 211 1.24 ginsbach 212 1.24 ginsbach if (updatecat) { 213 1.24 ginsbach if (ftruncate(ofd, 0) != 0) { 214 1.24 ginsbach err(1, "Unable to truncate %s", catfile); 215 1.24 ginsbach /* NOTREACHED */ 216 1.24 ginsbach } 217 1.1 jtc } 218 1.3 jtc 219 1.1 jtc MCWriteCat(ofd); 220 1.1 jtc exit(0); 221 1.3 jtc } 222 1.3 jtc 223 1.3 jtc static void 224 1.23 ginsbach warning(const char *cptr, const char *msg) 225 1.3 jtc { 226 1.24 ginsbach if (lineno) { 227 1.24 ginsbach fprintf(stderr, "%s: %s on line %ld, %s\n", 228 1.24 ginsbach getprogname(), msg, lineno, curfile); 229 1.24 ginsbach fprintf(stderr, "%s\n", curline); 230 1.24 ginsbach if (cptr) { 231 1.24 ginsbach char *tptr; 232 1.24 ginsbach for (tptr = curline; tptr < cptr; ++tptr) 233 1.24 ginsbach putc(' ', stderr); 234 1.24 ginsbach fprintf(stderr, "^\n"); 235 1.24 ginsbach } 236 1.24 ginsbach } else { 237 1.24 ginsbach fprintf(stderr, "%s: %s, %s\n", getprogname(), msg, curfile); 238 1.3 jtc } 239 1.3 jtc } 240 1.3 jtc 241 1.3 jtc static void 242 1.23 ginsbach error(const char *msg) 243 1.3 jtc { 244 1.22 ginsbach warning(NULL, msg); 245 1.1 jtc exit(1); 246 1.1 jtc } 247 1.1 jtc 248 1.3 jtc static void * 249 1.23 ginsbach xmalloc(size_t len) 250 1.3 jtc { 251 1.3 jtc void *p; 252 1.3 jtc 253 1.3 jtc if ((p = malloc(len)) == NULL) 254 1.24 ginsbach errx(1, NOMEMORY); 255 1.3 jtc return (p); 256 1.3 jtc } 257 1.3 jtc 258 1.3 jtc static void * 259 1.23 ginsbach xrealloc(void *ptr, size_t size) 260 1.3 jtc { 261 1.3 jtc if ((ptr = realloc(ptr, size)) == NULL) 262 1.24 ginsbach errx(1, NOMEMORY); 263 1.3 jtc return (ptr); 264 1.3 jtc } 265 1.3 jtc 266 1.3 jtc static char * 267 1.23 ginsbach xstrdup(const char *str) 268 1.3 jtc { 269 1.7 lukem char *nstr; 270 1.7 lukem 271 1.7 lukem if ((nstr = strdup(str)) == NULL) 272 1.24 ginsbach errx(1, NOMEMORY); 273 1.7 lukem return (nstr); 274 1.3 jtc } 275 1.3 jtc 276 1.3 jtc static char * 277 1.30 roy get_line(int fd) 278 1.3 jtc { 279 1.3 jtc static long curlen = BUFSIZ; 280 1.3 jtc static char buf[BUFSIZ], *bptr = buf, *bend = buf; 281 1.3 jtc char *cptr, *cend; 282 1.3 jtc long buflen; 283 1.3 jtc 284 1.3 jtc if (!curline) { 285 1.3 jtc curline = xmalloc(curlen); 286 1.3 jtc } 287 1.3 jtc ++lineno; 288 1.3 jtc 289 1.3 jtc cptr = curline; 290 1.3 jtc cend = curline + curlen; 291 1.3 jtc for (;;) { 292 1.3 jtc for (; bptr < bend && cptr < cend; ++cptr, ++bptr) { 293 1.3 jtc if (*bptr == '\n') { 294 1.3 jtc *cptr = '\0'; 295 1.3 jtc ++bptr; 296 1.3 jtc return (curline); 297 1.3 jtc } else 298 1.3 jtc *cptr = *bptr; 299 1.3 jtc } 300 1.11 yamt if (cptr == cend) { 301 1.11 yamt cptr = curline = xrealloc(curline, curlen *= 2); 302 1.11 yamt cend = curline + curlen; 303 1.11 yamt } 304 1.3 jtc if (bptr == bend) { 305 1.3 jtc buflen = read(fd, buf, BUFSIZ); 306 1.3 jtc if (buflen <= 0) { 307 1.3 jtc if (cptr > curline) { 308 1.3 jtc *cptr = '\0'; 309 1.3 jtc return (curline); 310 1.3 jtc } 311 1.3 jtc return (NULL); 312 1.3 jtc } 313 1.3 jtc bend = buf + buflen; 314 1.3 jtc bptr = buf; 315 1.3 jtc } 316 1.3 jtc } 317 1.3 jtc } 318 1.3 jtc 319 1.3 jtc static char * 320 1.23 ginsbach wskip(char *cptr) 321 1.3 jtc { 322 1.9 itohy if (!*cptr || !isspace((unsigned char) *cptr)) { 323 1.3 jtc warning(cptr, "expected a space"); 324 1.3 jtc return (cptr); 325 1.3 jtc } 326 1.9 itohy while (*cptr && isspace((unsigned char) *cptr)) 327 1.3 jtc ++cptr; 328 1.3 jtc return (cptr); 329 1.3 jtc } 330 1.3 jtc 331 1.3 jtc static char * 332 1.23 ginsbach cskip(char *cptr) 333 1.3 jtc { 334 1.9 itohy if (!*cptr || isspace((unsigned char) *cptr)) { 335 1.3 jtc warning(cptr, "wasn't expecting a space"); 336 1.3 jtc return (cptr); 337 1.3 jtc } 338 1.9 itohy while (*cptr && !isspace((unsigned char) *cptr)) 339 1.3 jtc ++cptr; 340 1.3 jtc return (cptr); 341 1.3 jtc } 342 1.3 jtc 343 1.3 jtc static char * 344 1.23 ginsbach getmsg(int fd, char *cptr, char quote) 345 1.3 jtc { 346 1.3 jtc static char *msg = NULL; 347 1.28 lukem static size_t msglen = 0; 348 1.35 ginsbach size_t clen, i; 349 1.35 ginsbach int in_quote = 0; 350 1.3 jtc char *tptr; 351 1.3 jtc 352 1.3 jtc if (quote && *cptr == quote) { 353 1.3 jtc ++cptr; 354 1.35 ginsbach in_quote = 1; 355 1.3 jtc } 356 1.3 jtc 357 1.3 jtc clen = strlen(cptr) + 1; 358 1.3 jtc if (clen > msglen) { 359 1.3 jtc if (msglen) 360 1.3 jtc msg = xrealloc(msg, clen); 361 1.3 jtc else 362 1.3 jtc msg = xmalloc(clen); 363 1.3 jtc msglen = clen; 364 1.3 jtc } 365 1.3 jtc tptr = msg; 366 1.3 jtc 367 1.3 jtc while (*cptr) { 368 1.3 jtc if (quote && *cptr == quote) { 369 1.3 jtc char *tmp; 370 1.3 jtc tmp = cptr + 1; 371 1.35 ginsbach if (!in_quote) { 372 1.35 ginsbach /* XXX hard error? */ 373 1.8 mjacob warning(cptr, "unexpected quote character, ignoring"); 374 1.3 jtc *tptr++ = *cptr++; 375 1.3 jtc } else { 376 1.35 ginsbach cptr++; 377 1.35 ginsbach /* don't use wskip() */ 378 1.35 ginsbach while (*cptr && isspace((unsigned char) *cptr)) 379 1.35 ginsbach #ifndef _BACKWARDS_COMPAT 380 1.35 ginsbach cptr++; 381 1.35 ginsbach #else 382 1.35 ginsbach *tptr++ = *cptr++; 383 1.35 ginsbach #endif 384 1.35 ginsbach /* XXX hard error? */ 385 1.35 ginsbach if (*cptr) 386 1.35 ginsbach warning(tmp, "unexpected extra characters, ignoring"); 387 1.35 ginsbach in_quote = 0; 388 1.35 ginsbach #ifndef _BACKWARDS_COMPAT 389 1.35 ginsbach break; 390 1.35 ginsbach #endif 391 1.3 jtc } 392 1.26 ginsbach } else { 393 1.3 jtc if (*cptr == '\\') { 394 1.3 jtc ++cptr; 395 1.3 jtc switch (*cptr) { 396 1.3 jtc case '\0': 397 1.30 roy cptr = get_line(fd); 398 1.3 jtc if (!cptr) 399 1.22 ginsbach error("premature end of file"); 400 1.3 jtc msglen += strlen(cptr); 401 1.3 jtc i = tptr - msg; 402 1.3 jtc msg = xrealloc(msg, msglen); 403 1.3 jtc tptr = msg + i; 404 1.3 jtc break; 405 1.3 jtc case 'n': 406 1.3 jtc *tptr++ = '\n'; 407 1.3 jtc ++cptr; 408 1.3 jtc break; 409 1.3 jtc case 't': 410 1.3 jtc *tptr++ = '\t'; 411 1.3 jtc ++cptr; 412 1.3 jtc break; 413 1.3 jtc case 'v': 414 1.3 jtc *tptr++ = '\v'; 415 1.3 jtc ++cptr; 416 1.3 jtc break; 417 1.3 jtc case 'b': 418 1.3 jtc *tptr++ = '\b'; 419 1.3 jtc ++cptr; 420 1.3 jtc break; 421 1.3 jtc case 'r': 422 1.3 jtc *tptr++ = '\r'; 423 1.3 jtc ++cptr; 424 1.3 jtc break; 425 1.3 jtc case 'f': 426 1.3 jtc *tptr++ = '\f'; 427 1.3 jtc ++cptr; 428 1.3 jtc break; 429 1.3 jtc case '\\': 430 1.3 jtc *tptr++ = '\\'; 431 1.3 jtc ++cptr; 432 1.3 jtc break; 433 1.3 jtc default: 434 1.8 mjacob if (quote && *cptr == quote) { 435 1.8 mjacob *tptr++ = *cptr++; 436 1.9 itohy } else if (isdigit((unsigned char) *cptr)) { 437 1.3 jtc *tptr = 0; 438 1.3 jtc for (i = 0; i < 3; ++i) { 439 1.9 itohy if (!isdigit((unsigned char) *cptr)) 440 1.3 jtc break; 441 1.3 jtc if (*cptr > '7') 442 1.3 jtc warning(cptr, "octal number greater than 7?!"); 443 1.3 jtc *tptr *= 8; 444 1.3 jtc *tptr += (*cptr - '0'); 445 1.3 jtc ++cptr; 446 1.3 jtc } 447 1.3 jtc } else { 448 1.3 jtc warning(cptr, "unrecognized escape sequence"); 449 1.3 jtc } 450 1.8 mjacob break; 451 1.3 jtc } 452 1.3 jtc } else { 453 1.3 jtc *tptr++ = *cptr++; 454 1.3 jtc } 455 1.26 ginsbach } 456 1.3 jtc } 457 1.35 ginsbach 458 1.35 ginsbach if (in_quote) 459 1.35 ginsbach warning(cptr, "unterminated quoted message, ignoring"); 460 1.35 ginsbach 461 1.3 jtc *tptr = '\0'; 462 1.3 jtc return (msg); 463 1.3 jtc } 464 1.3 jtc 465 1.31 joerg static void 466 1.23 ginsbach MCParse(int fd) 467 1.3 jtc { 468 1.3 jtc char *cptr, *str; 469 1.21 ginsbach int msgid = 0; 470 1.21 ginsbach int setid = 0; 471 1.3 jtc char quote = 0; 472 1.3 jtc 473 1.30 roy while ((cptr = get_line(fd))) { 474 1.3 jtc if (*cptr == '$') { 475 1.3 jtc ++cptr; 476 1.3 jtc if (strncmp(cptr, "set", 3) == 0) { 477 1.3 jtc cptr += 3; 478 1.3 jtc cptr = wskip(cptr); 479 1.3 jtc setid = atoi(cptr); 480 1.3 jtc MCAddSet(setid); 481 1.3 jtc msgid = 0; 482 1.3 jtc } else if (strncmp(cptr, "delset", 6) == 0) { 483 1.3 jtc cptr += 6; 484 1.3 jtc cptr = wskip(cptr); 485 1.3 jtc setid = atoi(cptr); 486 1.3 jtc MCDelSet(setid); 487 1.3 jtc } else if (strncmp(cptr, "quote", 5) == 0) { 488 1.3 jtc cptr += 5; 489 1.3 jtc if (!*cptr) 490 1.3 jtc quote = 0; 491 1.3 jtc else { 492 1.3 jtc cptr = wskip(cptr); 493 1.3 jtc if (!*cptr) 494 1.3 jtc quote = 0; 495 1.3 jtc else 496 1.3 jtc quote = *cptr; 497 1.3 jtc } 498 1.9 itohy } else if (isspace((unsigned char) *cptr)) { 499 1.3 jtc ; 500 1.3 jtc } else { 501 1.3 jtc if (*cptr) { 502 1.3 jtc cptr = wskip(cptr); 503 1.3 jtc if (*cptr) 504 1.3 jtc warning(cptr, "unrecognized line"); 505 1.3 jtc } 506 1.3 jtc } 507 1.3 jtc } else { 508 1.8 mjacob /* 509 1.8 mjacob * First check for (and eat) empty lines.... 510 1.8 mjacob */ 511 1.8 mjacob if (!*cptr) 512 1.8 mjacob continue; 513 1.8 mjacob /* 514 1.8 mjacob * We have a digit? Start of a message. Else, 515 1.8 mjacob * syntax error. 516 1.8 mjacob */ 517 1.9 itohy if (isdigit((unsigned char) *cptr)) { 518 1.3 jtc msgid = atoi(cptr); 519 1.3 jtc cptr = cskip(cptr); 520 1.26 ginsbach if (*cptr) { 521 1.24 ginsbach cptr = wskip(cptr); 522 1.26 ginsbach if (!*cptr) { 523 1.26 ginsbach MCAddMsg(msgid, ""); 524 1.26 ginsbach continue; 525 1.26 ginsbach } 526 1.26 ginsbach } 527 1.8 mjacob } else { 528 1.8 mjacob warning(cptr, "neither blank line nor start of a message id"); 529 1.8 mjacob continue; 530 1.3 jtc } 531 1.8 mjacob /* 532 1.21 ginsbach * If no set directive specified, all messages 533 1.21 ginsbach * shall be in default message set NL_SETD. 534 1.21 ginsbach */ 535 1.21 ginsbach if (setid == 0) { 536 1.21 ginsbach setid = NL_SETD; 537 1.21 ginsbach MCAddSet(setid); 538 1.21 ginsbach } 539 1.21 ginsbach /* 540 1.8 mjacob * If we have a message ID, but no message, 541 1.8 mjacob * then this means "delete this message id 542 1.8 mjacob * from the catalog". 543 1.8 mjacob */ 544 1.8 mjacob if (!*cptr) { 545 1.3 jtc MCDelMsg(msgid); 546 1.8 mjacob } else { 547 1.3 jtc str = getmsg(fd, cptr, quote); 548 1.3 jtc MCAddMsg(msgid, str); 549 1.3 jtc } 550 1.3 jtc } 551 1.3 jtc } 552 1.3 jtc } 553 1.3 jtc 554 1.31 joerg static void 555 1.23 ginsbach MCReadCat(int fd) 556 1.3 jtc { 557 1.24 ginsbach void *msgcat; /* message catalog data */ 558 1.24 ginsbach struct _nls_cat_hdr cat_hdr; 559 1.24 ginsbach struct _nls_set_hdr *set_hdr; 560 1.24 ginsbach struct _nls_msg_hdr *msg_hdr; 561 1.24 ginsbach char *strings; 562 1.28 lukem ssize_t n; 563 1.28 lukem int m, s; 564 1.24 ginsbach int msgno, setno; 565 1.3 jtc 566 1.24 ginsbach n = read(fd, &cat_hdr, sizeof(cat_hdr)); 567 1.28 lukem if (n < (ssize_t)sizeof(cat_hdr)) { 568 1.24 ginsbach if (n == 0) 569 1.24 ginsbach return; /* empty file */ 570 1.24 ginsbach else if (n == -1) 571 1.24 ginsbach err(1, "header read"); 572 1.24 ginsbach else 573 1.24 ginsbach errx(1, CORRUPT); 574 1.24 ginsbach } 575 1.29 tron if (ntohl((uint32_t)cat_hdr.__magic) != _NLS_MAGIC) 576 1.24 ginsbach errx(1, "%s: bad magic number (%#x)", CORRUPT, cat_hdr.__magic); 577 1.3 jtc 578 1.24 ginsbach cat_hdr.__mem = ntohl(cat_hdr.__mem); 579 1.3 jtc 580 1.24 ginsbach cat_hdr.__nsets = ntohl(cat_hdr.__nsets); 581 1.24 ginsbach cat_hdr.__msg_hdr_offset = ntohl(cat_hdr.__msg_hdr_offset); 582 1.24 ginsbach cat_hdr.__msg_txt_offset = ntohl(cat_hdr.__msg_txt_offset); 583 1.24 ginsbach if ((cat_hdr.__mem < 0) || 584 1.24 ginsbach (cat_hdr.__msg_hdr_offset < 0) || 585 1.24 ginsbach (cat_hdr.__msg_txt_offset < 0) || 586 1.28 lukem (cat_hdr.__mem < (int32_t)(cat_hdr.__nsets * sizeof(struct _nls_set_hdr))) || 587 1.24 ginsbach (cat_hdr.__mem < cat_hdr.__msg_hdr_offset) || 588 1.24 ginsbach (cat_hdr.__mem < cat_hdr.__msg_txt_offset)) 589 1.24 ginsbach errx(1, "%s: catalog header", CORRUPT); 590 1.24 ginsbach 591 1.27 christos msgcat = xmalloc(cat_hdr.__mem); 592 1.27 christos 593 1.24 ginsbach n = read(fd, msgcat, cat_hdr.__mem); 594 1.24 ginsbach if (n < cat_hdr.__mem) { 595 1.24 ginsbach if (n == -1) 596 1.24 ginsbach err(1, "data read"); 597 1.24 ginsbach else 598 1.24 ginsbach errx(1, CORRUPT); 599 1.24 ginsbach } 600 1.3 jtc 601 1.24 ginsbach set_hdr = (struct _nls_set_hdr *)msgcat; 602 1.24 ginsbach msg_hdr = (struct _nls_msg_hdr *)((char *)msgcat + 603 1.24 ginsbach cat_hdr.__msg_hdr_offset); 604 1.24 ginsbach strings = (char *)msgcat + cat_hdr.__msg_txt_offset; 605 1.24 ginsbach 606 1.24 ginsbach setno = 0; 607 1.24 ginsbach for (s = 0; s < cat_hdr.__nsets; s++, set_hdr++) { 608 1.24 ginsbach set_hdr->__setno = ntohl(set_hdr->__setno); 609 1.24 ginsbach if (set_hdr->__setno < setno) 610 1.24 ginsbach errx(1, "%s: bad set number (%d)", 611 1.24 ginsbach CORRUPT, set_hdr->__setno); 612 1.24 ginsbach setno = set_hdr->__setno; 613 1.24 ginsbach 614 1.24 ginsbach MCAddSet(setno); 615 1.24 ginsbach 616 1.24 ginsbach set_hdr->__nmsgs = ntohl(set_hdr->__nmsgs); 617 1.24 ginsbach set_hdr->__index = ntohl(set_hdr->__index); 618 1.24 ginsbach if (set_hdr->__nmsgs < 0 || set_hdr->__index < 0) 619 1.24 ginsbach errx(1, "%s: set header", CORRUPT); 620 1.3 jtc 621 1.3 jtc /* Get the data */ 622 1.24 ginsbach msgno = 0; 623 1.24 ginsbach for (m = 0; m < set_hdr->__nmsgs; m++, msg_hdr++) { 624 1.24 ginsbach msg_hdr->__msgno = ntohl(msg_hdr->__msgno); 625 1.24 ginsbach msg_hdr->__offset = ntohl(msg_hdr->__offset); 626 1.24 ginsbach if (msg_hdr->__msgno < msgno) 627 1.24 ginsbach errx(1, "%s: bad message number (%d)", 628 1.24 ginsbach CORRUPT, msg_hdr->__msgno); 629 1.24 ginsbach if ((msg_hdr->__offset < 0) || 630 1.24 ginsbach ((strings + msg_hdr->__offset) > 631 1.24 ginsbach ((char *)msgcat + cat_hdr.__mem))) 632 1.24 ginsbach errx(1, "%s: message header", CORRUPT); 633 1.3 jtc 634 1.24 ginsbach msgno = msg_hdr->__msgno; 635 1.24 ginsbach MCAddMsg(msgno, strings + msg_hdr->__offset); 636 1.3 jtc } 637 1.3 jtc } 638 1.24 ginsbach free(msgcat); 639 1.3 jtc } 640 1.3 jtc 641 1.3 jtc /* 642 1.3 jtc * Write message catalog. 643 1.3 jtc * 644 1.3 jtc * The message catalog is first converted from its internal to its 645 1.3 jtc * external representation in a chunk of memory allocated for this 646 1.3 jtc * purpose. Then the completed catalog is written. This approach 647 1.3 jtc * avoids additional housekeeping variables and/or a lot of seeks 648 1.3 jtc * that would otherwise be required. 649 1.3 jtc */ 650 1.31 joerg static void 651 1.23 ginsbach MCWriteCat(int fd) 652 1.1 jtc { 653 1.3 jtc int nsets; /* number of sets */ 654 1.3 jtc int nmsgs; /* number of msgs */ 655 1.3 jtc int string_size; /* total size of string pool */ 656 1.3 jtc int msgcat_size; /* total size of message catalog */ 657 1.3 jtc void *msgcat; /* message catalog data */ 658 1.3 jtc struct _nls_cat_hdr *cat_hdr; 659 1.3 jtc struct _nls_set_hdr *set_hdr; 660 1.3 jtc struct _nls_msg_hdr *msg_hdr; 661 1.3 jtc char *strings; 662 1.3 jtc struct _setT *set; 663 1.3 jtc struct _msgT *msg; 664 1.3 jtc int msg_index; 665 1.3 jtc int msg_offset; 666 1.3 jtc 667 1.3 jtc /* determine number of sets, number of messages, and size of the 668 1.3 jtc * string pool */ 669 1.3 jtc nsets = 0; 670 1.3 jtc nmsgs = 0; 671 1.3 jtc string_size = 0; 672 1.3 jtc 673 1.36 apb LIST_FOREACH(set, &sethead, entries) { 674 1.3 jtc nsets++; 675 1.3 jtc 676 1.36 apb LIST_FOREACH(msg, &set->msghead, entries) { 677 1.3 jtc nmsgs++; 678 1.3 jtc string_size += strlen(msg->str) + 1; 679 1.3 jtc } 680 1.3 jtc } 681 1.3 jtc 682 1.3 jtc #ifdef DEBUG 683 1.3 jtc printf("number of sets: %d\n", nsets); 684 1.3 jtc printf("number of msgs: %d\n", nmsgs); 685 1.3 jtc printf("string pool size: %d\n", string_size); 686 1.3 jtc #endif 687 1.3 jtc 688 1.3 jtc /* determine size and then allocate buffer for constructing external 689 1.3 jtc * message catalog representation */ 690 1.3 jtc msgcat_size = sizeof(struct _nls_cat_hdr) 691 1.3 jtc + (nsets * sizeof(struct _nls_set_hdr)) 692 1.3 jtc + (nmsgs * sizeof(struct _nls_msg_hdr)) 693 1.3 jtc + string_size; 694 1.3 jtc 695 1.3 jtc msgcat = xmalloc(msgcat_size); 696 1.3 jtc memset(msgcat, '\0', msgcat_size); 697 1.3 jtc 698 1.3 jtc /* fill in msg catalog header */ 699 1.3 jtc cat_hdr = (struct _nls_cat_hdr *) msgcat; 700 1.3 jtc cat_hdr->__magic = htonl(_NLS_MAGIC); 701 1.3 jtc cat_hdr->__nsets = htonl(nsets); 702 1.3 jtc cat_hdr->__mem = htonl(msgcat_size - sizeof(struct _nls_cat_hdr)); 703 1.3 jtc cat_hdr->__msg_hdr_offset = 704 1.3 jtc htonl(nsets * sizeof(struct _nls_set_hdr)); 705 1.3 jtc cat_hdr->__msg_txt_offset = 706 1.3 jtc htonl(nsets * sizeof(struct _nls_set_hdr) + 707 1.3 jtc nmsgs * sizeof(struct _nls_msg_hdr)); 708 1.3 jtc 709 1.3 jtc /* compute offsets for set & msg header tables and string pool */ 710 1.3 jtc set_hdr = (struct _nls_set_hdr *) ((char *) msgcat + 711 1.3 jtc sizeof(struct _nls_cat_hdr)); 712 1.3 jtc msg_hdr = (struct _nls_msg_hdr *) ((char *) msgcat + 713 1.3 jtc sizeof(struct _nls_cat_hdr) + 714 1.3 jtc nsets * sizeof(struct _nls_set_hdr)); 715 1.3 jtc strings = (char *) msgcat + 716 1.3 jtc sizeof(struct _nls_cat_hdr) + 717 1.3 jtc nsets * sizeof(struct _nls_set_hdr) + 718 1.3 jtc nmsgs * sizeof(struct _nls_msg_hdr); 719 1.3 jtc 720 1.3 jtc msg_index = 0; 721 1.3 jtc msg_offset = 0; 722 1.36 apb LIST_FOREACH(set, &sethead, entries) { 723 1.3 jtc 724 1.3 jtc nmsgs = 0; 725 1.36 apb LIST_FOREACH(msg, &set->msghead, entries) { 726 1.28 lukem int32_t msg_len = strlen(msg->str) + 1; 727 1.3 jtc 728 1.3 jtc msg_hdr->__msgno = htonl(msg->msgId); 729 1.3 jtc msg_hdr->__msglen = htonl(msg_len); 730 1.3 jtc msg_hdr->__offset = htonl(msg_offset); 731 1.3 jtc 732 1.3 jtc memcpy(strings, msg->str, msg_len); 733 1.3 jtc strings += msg_len; 734 1.3 jtc msg_offset += msg_len; 735 1.3 jtc 736 1.3 jtc nmsgs++; 737 1.3 jtc msg_hdr++; 738 1.3 jtc } 739 1.3 jtc 740 1.3 jtc set_hdr->__setno = htonl(set->setId); 741 1.3 jtc set_hdr->__nmsgs = htonl(nmsgs); 742 1.3 jtc set_hdr->__index = htonl(msg_index); 743 1.3 jtc msg_index += nmsgs; 744 1.3 jtc set_hdr++; 745 1.3 jtc } 746 1.3 jtc 747 1.3 jtc /* write out catalog. XXX: should this be done in small chunks? */ 748 1.3 jtc write(fd, msgcat, msgcat_size); 749 1.3 jtc } 750 1.3 jtc 751 1.31 joerg static void 752 1.23 ginsbach MCAddSet(int setId) 753 1.3 jtc { 754 1.3 jtc struct _setT *p, *q; 755 1.3 jtc 756 1.3 jtc if (setId <= 0) { 757 1.22 ginsbach error("setId's must be greater than zero"); 758 1.3 jtc /* NOTREACHED */ 759 1.3 jtc } 760 1.3 jtc if (setId > NL_SETMAX) { 761 1.22 ginsbach error("setId exceeds limit"); 762 1.3 jtc /* NOTREACHED */ 763 1.3 jtc } 764 1.3 jtc 765 1.36 apb p = LIST_FIRST(&sethead); 766 1.3 jtc q = NULL; 767 1.36 apb for (; p != NULL && p->setId < setId; q = p, p = LIST_NEXT(p, entries)) 768 1.36 apb continue; 769 1.3 jtc 770 1.3 jtc if (p && p->setId == setId) { 771 1.3 jtc ; 772 1.3 jtc } else { 773 1.3 jtc p = xmalloc(sizeof(struct _setT)); 774 1.3 jtc memset(p, '\0', sizeof(struct _setT)); 775 1.3 jtc LIST_INIT(&p->msghead); 776 1.3 jtc 777 1.3 jtc p->setId = setId; 778 1.3 jtc 779 1.3 jtc if (q == NULL) { 780 1.3 jtc LIST_INSERT_HEAD(&sethead, p, entries); 781 1.3 jtc } else { 782 1.3 jtc LIST_INSERT_AFTER(q, p, entries); 783 1.3 jtc } 784 1.3 jtc } 785 1.3 jtc 786 1.3 jtc curSet = p; 787 1.3 jtc } 788 1.3 jtc 789 1.31 joerg static void 790 1.23 ginsbach MCAddMsg(int msgId, const char *str) 791 1.3 jtc { 792 1.3 jtc struct _msgT *p, *q; 793 1.3 jtc 794 1.3 jtc if (!curSet) 795 1.22 ginsbach error("can't specify a message when no set exists"); 796 1.3 jtc 797 1.3 jtc if (msgId <= 0) { 798 1.22 ginsbach error("msgId's must be greater than zero"); 799 1.3 jtc /* NOTREACHED */ 800 1.3 jtc } 801 1.12 yamt if (msgId > NL_MSGMAX) { 802 1.22 ginsbach error("msgID exceeds limit"); 803 1.3 jtc /* NOTREACHED */ 804 1.3 jtc } 805 1.3 jtc 806 1.36 apb p = LIST_FIRST(&curSet->msghead); 807 1.3 jtc q = NULL; 808 1.36 apb for (; p != NULL && p->msgId < msgId; q = p, p = LIST_NEXT(p, entries)) 809 1.36 apb continue; 810 1.3 jtc 811 1.3 jtc if (p && p->msgId == msgId) { 812 1.3 jtc free(p->str); 813 1.3 jtc } else { 814 1.3 jtc p = xmalloc(sizeof(struct _msgT)); 815 1.3 jtc memset(p, '\0', sizeof(struct _msgT)); 816 1.3 jtc 817 1.3 jtc if (q == NULL) { 818 1.3 jtc LIST_INSERT_HEAD(&curSet->msghead, p, entries); 819 1.3 jtc } else { 820 1.3 jtc LIST_INSERT_AFTER(q, p, entries); 821 1.3 jtc } 822 1.3 jtc } 823 1.3 jtc 824 1.3 jtc p->msgId = msgId; 825 1.3 jtc p->str = xstrdup(str); 826 1.3 jtc } 827 1.3 jtc 828 1.31 joerg static void 829 1.23 ginsbach MCDelSet(int setId) 830 1.3 jtc { 831 1.3 jtc struct _setT *set; 832 1.3 jtc struct _msgT *msg; 833 1.3 jtc 834 1.21 ginsbach if (setId <= 0) { 835 1.22 ginsbach error("setId's must be greater than zero"); 836 1.21 ginsbach /* NOTREACHED */ 837 1.21 ginsbach } 838 1.21 ginsbach if (setId > NL_SETMAX) { 839 1.22 ginsbach error("setId exceeds limit"); 840 1.21 ginsbach /* NOTREACHED */ 841 1.21 ginsbach } 842 1.21 ginsbach 843 1.36 apb set = LIST_FIRST(&sethead); 844 1.36 apb for (; set != NULL && set->setId < setId; set = LIST_NEXT(set, entries)) 845 1.36 apb continue; 846 1.3 jtc 847 1.3 jtc if (set && set->setId == setId) { 848 1.20 dsl LIST_REMOVE(set, entries); 849 1.36 apb while ((msg = LIST_FIRST(&set->msghead)) != NULL) { 850 1.20 dsl LIST_REMOVE(msg, entries); 851 1.3 jtc free(msg->str); 852 1.20 dsl free(msg); 853 1.3 jtc } 854 1.20 dsl free(set); 855 1.3 jtc return; 856 1.3 jtc } 857 1.3 jtc warning(NULL, "specified set doesn't exist"); 858 1.3 jtc } 859 1.1 jtc 860 1.31 joerg static void 861 1.23 ginsbach MCDelMsg(int msgId) 862 1.3 jtc { 863 1.3 jtc struct _msgT *msg; 864 1.1 jtc 865 1.3 jtc if (!curSet) 866 1.22 ginsbach error("you can't delete a message before defining the set"); 867 1.1 jtc 868 1.36 apb msg = LIST_FIRST(&curSet->msghead); 869 1.36 apb for (; msg != NULL && msg->msgId < msgId; msg = LIST_NEXT(msg, entries)) 870 1.36 apb continue; 871 1.1 jtc 872 1.3 jtc if (msg && msg->msgId == msgId) { 873 1.20 dsl LIST_REMOVE(msg, entries); 874 1.3 jtc free(msg->str); 875 1.20 dsl free(msg); 876 1.3 jtc return; 877 1.3 jtc } 878 1.3 jtc warning(NULL, "specified msg doesn't exist"); 879 1.1 jtc } 880