1213317Sgordon/* ex:ts=4 2213317Sgordon */ 3213317Sgordon 4213317Sgordon/* $NetBSD: gencat.c,v 1.18 2003/10/27 00:12:43 lukem Exp $ */ 5213317Sgordon 6213317Sgordon/*- 7213317Sgordon * SPDX-License-Identifier: BSD-2-Clause-NetBSD AND MIT 8213317Sgordon * 9213317Sgordon * Copyright (c) 1996 The NetBSD Foundation, Inc. 10213317Sgordon * All rights reserved. 11213317Sgordon * 12213317Sgordon * This code is derived from software contributed to The NetBSD Foundation 13213317Sgordon * by J.T. Conklin. 14213317Sgordon * 15213317Sgordon * Redistribution and use in source and binary forms, with or without 16213317Sgordon * modification, are permitted provided that the following conditions 17213317Sgordon * are met: 18213317Sgordon * 1. Redistributions of source code must retain the above copyright 19213317Sgordon * notice, this list of conditions and the following disclaimer. 20213317Sgordon * 2. Redistributions in binary form must reproduce the above copyright 21213317Sgordon * notice, this list of conditions and the following disclaimer in the 22213317Sgordon * documentation and/or other materials provided with the distribution. 23213317Sgordon * 24213317Sgordon * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 25213317Sgordon * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 26213317Sgordon * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 27213317Sgordon * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 28222650Sru * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 29233520Sjoel * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 30213317Sgordon * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 31213317Sgordon * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 32213317Sgordon * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33213317Sgordon * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 34213317Sgordon * POSSIBILITY OF SUCH DAMAGE. 35213317Sgordon */ 36213317Sgordon 37213317Sgordon/*********************************************************** 38213317SgordonCopyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts. 39213317Sgordon 40213317Sgordon All Rights Reserved 41213317Sgordon 42213317SgordonPermission to use, copy, modify, and distribute this software and its 43213317Sgordondocumentation for any purpose and without fee is hereby granted, 44213317Sgordonprovided that the above copyright notice appear in all copies and that 45213317Sgordonboth that copyright notice and this permission notice appear in 46213317Sgordonsupporting documentation, and that Alfalfa's name not be used in 47213317Sgordonadvertising or publicity pertaining to distribution of the software 48213317Sgordonwithout specific, written prior permission. 49213317Sgordon 50213317SgordonALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 51213317SgordonALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL 52213317SgordonALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 53213317SgordonANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 54213317SgordonWHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 55213317SgordonARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 56213317SgordonSOFTWARE. 57213317Sgordon 58213317SgordonIf you make any modifications, bugfixes or other changes to this software 59213317Sgordonwe'd appreciate it if you could send a copy to us so we can keep things 60236508Sjoelup-to-date. Many thanks. 61213317Sgordon Kee Hinckley 62213317Sgordon Alfalfa Software, Inc. 63213317Sgordon 267 Allston St., #3 64213317Sgordon Cambridge, MA 02139 USA 65213317Sgordon nazgul@alfalfa.com 66213317Sgordon 67213317Sgordon******************************************************************/ 68213317Sgordon 69213317Sgordon#include <sys/cdefs.h> 70213317Sgordon__FBSDID("$FreeBSD: stable/11/usr.bin/gencat/gencat.c 330449 2018-03-05 07:26:05Z eadler $"); 71213317Sgordon 72213317Sgordon#define _NLS_PRIVATE 73236508Sjoel 74236508Sjoel#include <sys/types.h> 75213317Sgordon#include <sys/queue.h> 76213317Sgordon 77213317Sgordon#include <arpa/inet.h> /* for htonl() */ 78213317Sgordon 79213317Sgordon#include <ctype.h> 80213317Sgordon#include <err.h> 81213317Sgordon#include <fcntl.h> 82213317Sgordon#include <limits.h> 83213317Sgordon#include <nl_types.h> 84213317Sgordon#include <stdio.h> 85213317Sgordon#include <stdlib.h> 86213317Sgordon#include <string.h> 87213317Sgordon#include <unistd.h> 88213317Sgordon 89213317Sgordonstruct _msgT { 90213317Sgordon long msgId; 91236508Sjoel char *str; 92213317Sgordon LIST_ENTRY(_msgT) entries; 93213317Sgordon}; 94213317Sgordon 95213317Sgordonstruct _setT { 96213317Sgordon long setId; 97213317Sgordon LIST_HEAD(msghead, _msgT) msghead; 98213317Sgordon LIST_ENTRY(_setT) entries; 99213317Sgordon}; 100213317Sgordon 101213317Sgordonstatic LIST_HEAD(sethead, _setT) sethead; 102213317Sgordonstatic struct _setT *curSet; 103213317Sgordon 104213317Sgordonstatic char *curline = NULL; 105213317Sgordonstatic long lineno = 0; 106213317Sgordon 107213317Sgordonstatic char *cskip(char *); 108213317Sgordonstatic void error(const char *); 109213317Sgordonstatic char *get_line(int); 110213317Sgordonstatic char *getmsg(int, char *, char); 111213317Sgordonstatic void warning(const char *, const char *); 112213317Sgordonstatic char *wskip(char *); 113213317Sgordonstatic char *xstrdup(const char *); 114213317Sgordonstatic void *xmalloc(size_t); 115222636Srustatic void *xrealloc(void *, size_t); 116213317Sgordon 117213317Sgordonvoid MCParse(int); 118213317Sgordonvoid MCReadCat(int); 119213317Sgordonvoid MCWriteCat(int); 120213317Sgordonvoid MCDelMsg(int); 121213317Sgordonvoid MCAddMsg(int, const char *); 122213317Sgordonvoid MCAddSet(int); 123213317Sgordonvoid MCDelSet(int); 124213317Sgordonvoid usage(void); 125213317Sgordonint main(int, char **); 126213317Sgordon 127213317Sgordonvoid 128213317Sgordonusage(void) 129213317Sgordon{ 130213317Sgordon fprintf(stderr, "usage: %s catfile msgfile ...\n", getprogname()); 131213317Sgordon exit(1); 132213317Sgordon} 133213317Sgordon 134213317Sgordonint 135213317Sgordonmain(int argc, char **argv) 136213317Sgordon{ 137213317Sgordon int ofd, ifd; 138213317Sgordon char *catfile = NULL; 139213317Sgordon int c; 140213317Sgordon 141213317Sgordon#define DEPRECATEDMSG 1 142213317Sgordon 143#ifdef DEPRECATEDMSG 144 while ((c = getopt(argc, argv, "new")) != -1) { 145#else 146 while ((c = getopt(argc, argv, "")) != -1) { 147#endif 148 switch (c) { 149#ifdef DEPRECATEDMSG 150 case 'n': 151 fprintf(stderr, "WARNING: Usage of \"-new\" argument is deprecated.\n"); 152 case 'e': 153 case 'w': 154 break; 155#endif 156 case '?': 157 default: 158 usage(); 159 /* NOTREACHED */ 160 } 161 } 162 argc -= optind; 163 argv += optind; 164 165 if (argc < 2) { 166 usage(); 167 /* NOTREACHED */ 168 } 169 catfile = *argv++; 170 171 for (; *argv; argv++) { 172 if ((ifd = open(*argv, O_RDONLY)) < 0) 173 err(1, "Unable to read %s", *argv); 174 MCParse(ifd); 175 close(ifd); 176 } 177 178 if ((ofd = open(catfile, O_WRONLY | O_TRUNC | O_CREAT, 0666)) < 0) 179 err(1, "Unable to create a new %s", catfile); 180 MCWriteCat(ofd); 181 exit(0); 182} 183 184static void 185warning(const char *cptr, const char *msg) 186{ 187 fprintf(stderr, "%s: %s on line %ld\n", getprogname(), msg, lineno); 188 fprintf(stderr, "%s\n", curline); 189 if (cptr) { 190 char *tptr; 191 for (tptr = curline; tptr < cptr; ++tptr) 192 putc(' ', stderr); 193 fprintf(stderr, "^\n"); 194 } 195} 196 197#define CORRUPT() { error("corrupt message catalog"); } 198#define NOMEM() { error("out of memory"); } 199 200static void 201error(const char *msg) 202{ 203 warning(NULL, msg); 204 exit(1); 205} 206 207static void * 208xmalloc(size_t len) 209{ 210 void *p; 211 212 if ((p = malloc(len)) == NULL) 213 NOMEM(); 214 return (p); 215} 216 217static void * 218xrealloc(void *ptr, size_t size) 219{ 220 if ((ptr = realloc(ptr, size)) == NULL) 221 NOMEM(); 222 return (ptr); 223} 224 225static char * 226xstrdup(const char *str) 227{ 228 char *nstr; 229 230 if ((nstr = strdup(str)) == NULL) 231 NOMEM(); 232 return (nstr); 233} 234 235static char * 236get_line(int fd) 237{ 238 static long curlen = BUFSIZ; 239 static char buf[BUFSIZ], *bptr = buf, *bend = buf; 240 char *cptr, *cend; 241 long buflen; 242 243 if (!curline) { 244 curline = xmalloc(curlen); 245 } 246 ++lineno; 247 248 cptr = curline; 249 cend = curline + curlen; 250 for (;;) { 251 for (; bptr < bend && cptr < cend; ++cptr, ++bptr) { 252 if (*bptr == '\n') { 253 *cptr = '\0'; 254 ++bptr; 255 return (curline); 256 } else 257 *cptr = *bptr; 258 } 259 if (cptr == cend) { 260 cptr = curline = xrealloc(curline, curlen *= 2); 261 cend = curline + curlen; 262 } 263 if (bptr == bend) { 264 buflen = read(fd, buf, BUFSIZ); 265 if (buflen <= 0) { 266 if (cptr > curline) { 267 *cptr = '\0'; 268 return (curline); 269 } 270 return (NULL); 271 } 272 bend = buf + buflen; 273 bptr = buf; 274 } 275 } 276} 277 278static char * 279wskip(char *cptr) 280{ 281 if (!*cptr || !isspace((unsigned char) *cptr)) { 282 warning(cptr, "expected a space"); 283 return (cptr); 284 } 285 while (*cptr && isspace((unsigned char) *cptr)) 286 ++cptr; 287 return (cptr); 288} 289 290static char * 291cskip(char *cptr) 292{ 293 if (!*cptr || isspace((unsigned char) *cptr)) { 294 warning(cptr, "wasn't expecting a space"); 295 return (cptr); 296 } 297 while (*cptr && !isspace((unsigned char) *cptr)) 298 ++cptr; 299 return (cptr); 300} 301 302static char * 303getmsg(int fd, char *cptr, char quote) 304{ 305 static char *msg = NULL; 306 static long msglen = 0; 307 long clen, i; 308 char *tptr; 309 310 if (quote && *cptr == quote) { 311 ++cptr; 312 } 313 314 clen = strlen(cptr) + 1; 315 if (clen > msglen) { 316 if (msglen) 317 msg = xrealloc(msg, clen); 318 else 319 msg = xmalloc(clen); 320 msglen = clen; 321 } 322 tptr = msg; 323 324 while (*cptr) { 325 if (quote && *cptr == quote) { 326 char *tmp; 327 tmp = cptr + 1; 328 if (*tmp && (!isspace((unsigned char) *tmp) || *wskip(tmp))) { 329 warning(cptr, "unexpected quote character, ignoring"); 330 *tptr++ = *cptr++; 331 } else { 332 *cptr = '\0'; 333 } 334 } else 335 if (*cptr == '\\') { 336 ++cptr; 337 switch (*cptr) { 338 case '\0': 339 cptr = get_line(fd); 340 if (!cptr) 341 error("premature end of file"); 342 msglen += strlen(cptr); 343 i = tptr - msg; 344 msg = xrealloc(msg, msglen); 345 tptr = msg + i; 346 break; 347 348 #define CASEOF(CS, CH) \ 349 case CS: \ 350 *tptr++ = CH; \ 351 ++cptr; \ 352 break; \ 353 354 CASEOF('n', '\n'); 355 CASEOF('t', '\t'); 356 CASEOF('v', '\v'); 357 CASEOF('b', '\b'); 358 CASEOF('r', '\r'); 359 CASEOF('f', '\f'); 360 CASEOF('"', '"'); 361 CASEOF('\\', '\\'); 362 363 default: 364 if (quote && *cptr == quote) { 365 *tptr++ = *cptr++; 366 } else if (isdigit((unsigned char) *cptr)) { 367 *tptr = 0; 368 for (i = 0; i < 3; ++i) { 369 if (!isdigit((unsigned char) *cptr)) 370 break; 371 if (*cptr > '7') 372 warning(cptr, "octal number greater than 7?!"); 373 *tptr *= 8; 374 *tptr += (*cptr - '0'); 375 ++cptr; 376 } 377 } else { 378 warning(cptr, "unrecognized escape sequence"); 379 } 380 break; 381 } 382 } else { 383 *tptr++ = *cptr++; 384 } 385 } 386 *tptr = '\0'; 387 return (msg); 388} 389 390void 391MCParse(int fd) 392{ 393 char *cptr, *str; 394 int setid, msgid = 0; 395 char quote = 0; 396 397 /* XXX: init sethead? */ 398 399 while ((cptr = get_line(fd))) { 400 if (*cptr == '$') { 401 ++cptr; 402 if (strncmp(cptr, "set", 3) == 0) { 403 cptr += 3; 404 cptr = wskip(cptr); 405 setid = atoi(cptr); 406 MCAddSet(setid); 407 msgid = 0; 408 } else if (strncmp(cptr, "delset", 6) == 0) { 409 cptr += 6; 410 cptr = wskip(cptr); 411 setid = atoi(cptr); 412 MCDelSet(setid); 413 } else if (strncmp(cptr, "quote", 5) == 0) { 414 cptr += 5; 415 if (!*cptr) 416 quote = 0; 417 else { 418 cptr = wskip(cptr); 419 if (!*cptr) 420 quote = 0; 421 else 422 quote = *cptr; 423 } 424 } else if (isspace((unsigned char) *cptr)) { 425 ; 426 } else { 427 if (*cptr) { 428 cptr = wskip(cptr); 429 if (*cptr) 430 warning(cptr, "unrecognized line"); 431 } 432 } 433 } else { 434 /* 435 * First check for (and eat) empty lines.... 436 */ 437 if (!*cptr) 438 continue; 439 /* 440 * We have a digit? Start of a message. Else, 441 * syntax error. 442 */ 443 if (isdigit((unsigned char) *cptr)) { 444 msgid = atoi(cptr); 445 cptr = cskip(cptr); 446 cptr = wskip(cptr); 447 /* if (*cptr) ++cptr; */ 448 } else { 449 warning(cptr, "neither blank line nor start of a message id"); 450 continue; 451 } 452 /* 453 * If we have a message ID, but no message, 454 * then this means "delete this message id 455 * from the catalog". 456 */ 457 if (!*cptr) { 458 MCDelMsg(msgid); 459 } else { 460 str = getmsg(fd, cptr, quote); 461 MCAddMsg(msgid, str); 462 } 463 } 464 } 465} 466 467/* 468 * Write message catalog. 469 * 470 * The message catalog is first converted from its internal to its 471 * external representation in a chunk of memory allocated for this 472 * purpose. Then the completed catalog is written. This approach 473 * avoids additional housekeeping variables and/or a lot of seeks 474 * that would otherwise be required. 475 */ 476void 477MCWriteCat(int fd) 478{ 479 int nsets; /* number of sets */ 480 int nmsgs; /* number of msgs */ 481 int string_size; /* total size of string pool */ 482 int msgcat_size; /* total size of message catalog */ 483 void *msgcat; /* message catalog data */ 484 struct _nls_cat_hdr *cat_hdr; 485 struct _nls_set_hdr *set_hdr; 486 struct _nls_msg_hdr *msg_hdr; 487 char *strings; 488 struct _setT *set; 489 struct _msgT *msg; 490 int msg_index; 491 int msg_offset; 492 493 /* determine number of sets, number of messages, and size of the 494 * string pool */ 495 nsets = 0; 496 nmsgs = 0; 497 string_size = 0; 498 499 for (set = sethead.lh_first; set != NULL; 500 set = set->entries.le_next) { 501 nsets++; 502 503 for (msg = set->msghead.lh_first; msg != NULL; 504 msg = msg->entries.le_next) { 505 nmsgs++; 506 string_size += strlen(msg->str) + 1; 507 } 508 } 509 510#ifdef DEBUG 511 printf("number of sets: %d\n", nsets); 512 printf("number of msgs: %d\n", nmsgs); 513 printf("string pool size: %d\n", string_size); 514#endif 515 516 /* determine size and then allocate buffer for constructing external 517 * message catalog representation */ 518 msgcat_size = sizeof(struct _nls_cat_hdr) 519 + (nsets * sizeof(struct _nls_set_hdr)) 520 + (nmsgs * sizeof(struct _nls_msg_hdr)) 521 + string_size; 522 523 msgcat = xmalloc(msgcat_size); 524 memset(msgcat, '\0', msgcat_size); 525 526 /* fill in msg catalog header */ 527 cat_hdr = (struct _nls_cat_hdr *) msgcat; 528 cat_hdr->__magic = htonl(_NLS_MAGIC); 529 cat_hdr->__nsets = htonl(nsets); 530 cat_hdr->__mem = htonl(msgcat_size - sizeof(struct _nls_cat_hdr)); 531 cat_hdr->__msg_hdr_offset = 532 htonl(nsets * sizeof(struct _nls_set_hdr)); 533 cat_hdr->__msg_txt_offset = 534 htonl(nsets * sizeof(struct _nls_set_hdr) + 535 nmsgs * sizeof(struct _nls_msg_hdr)); 536 537 /* compute offsets for set & msg header tables and string pool */ 538 set_hdr = (struct _nls_set_hdr *)(void *)((char *)msgcat + 539 sizeof(struct _nls_cat_hdr)); 540 msg_hdr = (struct _nls_msg_hdr *)(void *)((char *)msgcat + 541 sizeof(struct _nls_cat_hdr) + 542 nsets * sizeof(struct _nls_set_hdr)); 543 strings = (char *) msgcat + 544 sizeof(struct _nls_cat_hdr) + 545 nsets * sizeof(struct _nls_set_hdr) + 546 nmsgs * sizeof(struct _nls_msg_hdr); 547 548 msg_index = 0; 549 msg_offset = 0; 550 for (set = sethead.lh_first; set != NULL; 551 set = set->entries.le_next) { 552 553 nmsgs = 0; 554 for (msg = set->msghead.lh_first; msg != NULL; 555 msg = msg->entries.le_next) { 556 int msg_len = strlen(msg->str) + 1; 557 558 msg_hdr->__msgno = htonl(msg->msgId); 559 msg_hdr->__msglen = htonl(msg_len); 560 msg_hdr->__offset = htonl(msg_offset); 561 562 memcpy(strings, msg->str, msg_len); 563 strings += msg_len; 564 msg_offset += msg_len; 565 566 nmsgs++; 567 msg_hdr++; 568 } 569 570 set_hdr->__setno = htonl(set->setId); 571 set_hdr->__nmsgs = htonl(nmsgs); 572 set_hdr->__index = htonl(msg_index); 573 msg_index += nmsgs; 574 set_hdr++; 575 } 576 577 /* write out catalog. XXX: should this be done in small chunks? */ 578 write(fd, msgcat, msgcat_size); 579} 580 581void 582MCAddSet(int setId) 583{ 584 struct _setT *p, *q; 585 586 if (setId <= 0) { 587 error("setId's must be greater than zero"); 588 /* NOTREACHED */ 589 } 590 if (setId > NL_SETMAX) { 591 error("setId exceeds limit"); 592 /* NOTREACHED */ 593 } 594 595 p = sethead.lh_first; 596 q = NULL; 597 for (; p != NULL && p->setId < setId; q = p, p = p->entries.le_next); 598 599 if (p && p->setId == setId) { 600 ; 601 } else { 602 p = xmalloc(sizeof(struct _setT)); 603 memset(p, '\0', sizeof(struct _setT)); 604 LIST_INIT(&p->msghead); 605 606 p->setId = setId; 607 608 if (q == NULL) { 609 LIST_INSERT_HEAD(&sethead, p, entries); 610 } else { 611 LIST_INSERT_AFTER(q, p, entries); 612 } 613 } 614 615 curSet = p; 616} 617 618void 619MCAddMsg(int msgId, const char *str) 620{ 621 struct _msgT *p, *q; 622 623 if (!curSet) 624 error("can't specify a message when no set exists"); 625 626 if (msgId <= 0) { 627 error("msgId's must be greater than zero"); 628 /* NOTREACHED */ 629 } 630 if (msgId > NL_MSGMAX) { 631 error("msgID exceeds limit"); 632 /* NOTREACHED */ 633 } 634 635 p = curSet->msghead.lh_first; 636 q = NULL; 637 for (; p != NULL && p->msgId < msgId; q = p, p = p->entries.le_next); 638 639 if (p && p->msgId == msgId) { 640 free(p->str); 641 } else { 642 p = xmalloc(sizeof(struct _msgT)); 643 memset(p, '\0', sizeof(struct _msgT)); 644 645 if (q == NULL) { 646 LIST_INSERT_HEAD(&curSet->msghead, p, entries); 647 } else { 648 LIST_INSERT_AFTER(q, p, entries); 649 } 650 } 651 652 p->msgId = msgId; 653 p->str = xstrdup(str); 654} 655 656void 657MCDelSet(int setId) 658{ 659 struct _setT *set; 660 struct _msgT *msg; 661 662 set = sethead.lh_first; 663 for (; set != NULL && set->setId < setId; set = set->entries.le_next); 664 665 if (set && set->setId == setId) { 666 667 msg = set->msghead.lh_first; 668 while (msg) { 669 free(msg->str); 670 LIST_REMOVE(msg, entries); 671 } 672 673 LIST_REMOVE(set, entries); 674 return; 675 } 676 warning(NULL, "specified set doesn't exist"); 677} 678 679void 680MCDelMsg(int msgId) 681{ 682 struct _msgT *msg; 683 684 if (!curSet) 685 error("you can't delete a message before defining the set"); 686 687 msg = curSet->msghead.lh_first; 688 for (; msg != NULL && msg->msgId < msgId; msg = msg->entries.le_next); 689 690 if (msg && msg->msgId == msgId) { 691 free(msg->str); 692 LIST_REMOVE(msg, entries); 693 return; 694 } 695 warning(NULL, "specified msg doesn't exist"); 696} 697