Rename getline with get_line to avoid collision with getline(3).
[dragonfly.git] / usr.bin / gencat / gencat.c
CommitLineData
0d5acd74
JM
1/* ex:ts=4
2 */
3
4/* $NetBSD: gencat.c,v 1.18 2003/10/27 00:12:43 lukem Exp $ */
32a7b49a
JS
5
6/*
7 * Copyright (c) 1996 The NetBSD Foundation, Inc.
8 * All rights reserved.
9 *
10 * This code is derived from software contributed to The NetBSD Foundation
11 * by J.T. Conklin.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
32a7b49a
JS
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
26 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
0d5acd74 33 *
cae2835b 34 * $FreeBSD: head/usr.bin/gencat/gencat.c 299356 2016-05-10 11:12:31Z bapt $
32a7b49a 35 */
984263bc
MD
36
37/***********************************************************
38Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts.
39
40 All Rights Reserved
41
42Permission to use, copy, modify, and distribute this software and its
43documentation for any purpose and without fee is hereby granted,
44provided that the above copyright notice appear in all copies and that
45both that copyright notice and this permission notice appear in
46supporting documentation, and that Alfalfa's name not be used in
47advertising or publicity pertaining to distribution of the software
48without specific, written prior permission.
49
50ALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
51ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
52ALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
53ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
54WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
55ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
56SOFTWARE.
57
58If you make any modifications, bugfixes or other changes to this software
59we'd appreciate it if you could send a copy to us so we can keep things
60up-to-date. Many thanks.
61 Kee Hinckley
62 Alfalfa Software, Inc.
63 267 Allston St., #3
64 Cambridge, MA 02139 USA
65 nazgul@alfalfa.com
66
67******************************************************************/
68
0d5acd74
JM
69
70#define _NLS_PRIVATE
71
984263bc 72#include <sys/types.h>
32a7b49a
JS
73#include <sys/queue.h>
74
0d5acd74 75#include <arpa/inet.h> /* for htonl() */
32a7b49a
JS
76
77#include <ctype.h>
984263bc 78#include <err.h>
32a7b49a
JS
79#include <fcntl.h>
80#include <limits.h>
0d5acd74 81#include <nl_types.h>
984263bc
MD
82#include <stdio.h>
83#include <stdlib.h>
84#include <string.h>
85#include <unistd.h>
984263bc 86
00dca917 87#ifdef BOOTSTRAPPING
88#define getline get_line /* help bootstrap previous stdio.h */
89#endif
90
32a7b49a
JS
91struct _msgT {
92 long msgId;
93 char *str;
0d5acd74 94 LIST_ENTRY(_msgT) entries;
32a7b49a
JS
95};
96
97struct _setT {
98 long setId;
0d5acd74
JM
99 LIST_HEAD(msghead, _msgT) msghead;
100 LIST_ENTRY(_setT) entries;
32a7b49a
JS
101};
102
0d5acd74 103static LIST_HEAD(sethead, _setT) sethead;
32a7b49a
JS
104static struct _setT *curSet;
105
106static char *curline = NULL;
107static long lineno = 0;
108
0d5acd74
JM
109static char *cskip(char *);
110static void error(const char *);
cae2835b 111static char *get_line(int);
0d5acd74
JM
112static char *getmsg(int, char *, char);
113static void warning(const char *, const char *);
114static char *wskip(char *);
115static char *xstrdup(const char *);
116static void *xmalloc(size_t);
117static void *xrealloc(void *, size_t);
118
119void MCParse(int);
120void MCReadCat(int);
121void MCWriteCat(int);
122void MCDelMsg(int);
123void MCAddMsg(int, const char *);
124void MCAddSet(int);
125void MCDelSet(int);
815943a9 126void usage(void);
32a7b49a
JS
127
128void
815943a9 129usage(void)
984263bc 130{
32a7b49a
JS
131 fprintf(stderr, "usage: %s catfile msgfile ...\n", getprogname());
132 exit(1);
984263bc
MD
133}
134
135int
0d5acd74 136main(int argc, char **argv)
32a7b49a
JS
137{
138 int ofd, ifd;
0d5acd74 139 char *catfile = NULL;
32a7b49a
JS
140 int c;
141
0d5acd74
JM
142#define DEPRECATEDMSG 1
143
144#ifdef DEPRECATEDMSG
145 while ((c = getopt(argc, argv, "new")) != -1) {
146#else
32a7b49a 147 while ((c = getopt(argc, argv, "")) != -1) {
0d5acd74 148#endif
32a7b49a 149 switch (c) {
0d5acd74
JM
150#ifdef DEPRECATEDMSG
151 case 'n':
152 fprintf(stderr, "WARNING: Usage of \"-new\" argument is deprecated.\n");
153 case 'e':
154 case 'w':
155 break;
156#endif
157 case '?':
32a7b49a
JS
158 default:
159 usage();
160 /* NOTREACHED */
984263bc 161 }
32a7b49a
JS
162 }
163 argc -= optind;
164 argv += optind;
165
166 if (argc < 2) {
984263bc 167 usage();
32a7b49a
JS
168 /* NOTREACHED */
169 }
170 catfile = *argv++;
171
172 for (; *argv; argv++) {
173 if ((ifd = open(*argv, O_RDONLY)) < 0)
174 err(1, "Unable to read %s", *argv);
984263bc
MD
175 MCParse(ifd);
176 close(ifd);
984263bc 177 }
32a7b49a
JS
178
179 if ((ofd = open(catfile, O_WRONLY | O_TRUNC | O_CREAT, 0666)) < 0)
180 err(1, "Unable to create a new %s", catfile);
984263bc
MD
181 MCWriteCat(ofd);
182 exit(0);
984263bc
MD
183}
184
185static void
0d5acd74 186warning(const char *cptr, const char *msg)
32a7b49a
JS
187{
188 fprintf(stderr, "%s: %s on line %ld\n", getprogname(), msg, lineno);
189 fprintf(stderr, "%s\n", curline);
190 if (cptr) {
191 char *tptr;
192 for (tptr = curline; tptr < cptr; ++tptr)
193 putc(' ', stderr);
194 fprintf(stderr, "^\n");
195 }
196}
197
0d5acd74
JM
198#define CORRUPT() { error("corrupt message catalog"); }
199#define NOMEM() { error("out of memory"); }
32a7b49a
JS
200
201static void
0d5acd74 202error(const char *msg)
32a7b49a 203{
0d5acd74
JM
204 warning(NULL, msg);
205 exit(1);
32a7b49a
JS
206}
207
208static void *
815943a9 209xmalloc(size_t len)
32a7b49a
JS
210{
211 void *p;
212
213 if ((p = malloc(len)) == NULL)
0d5acd74 214 NOMEM();
32a7b49a
JS
215 return (p);
216}
217
218static void *
815943a9 219xrealloc(void *ptr, size_t size)
32a7b49a
JS
220{
221 if ((ptr = realloc(ptr, size)) == NULL)
0d5acd74 222 NOMEM();
32a7b49a
JS
223 return (ptr);
224}
225
226static char *
815943a9 227xstrdup(const char *str)
32a7b49a
JS
228{
229 char *nstr;
230
231 if ((nstr = strdup(str)) == NULL)
0d5acd74 232 NOMEM();
32a7b49a
JS
233 return (nstr);
234}
235
236static char *
cae2835b 237get_line(int fd)
32a7b49a
JS
238{
239 static long curlen = BUFSIZ;
240 static char buf[BUFSIZ], *bptr = buf, *bend = buf;
241 char *cptr, *cend;
242 long buflen;
243
244 if (!curline) {
245 curline = xmalloc(curlen);
246 }
247 ++lineno;
248
249 cptr = curline;
250 cend = curline + curlen;
251 for (;;) {
252 for (; bptr < bend && cptr < cend; ++cptr, ++bptr) {
253 if (*bptr == '\n') {
254 *cptr = '\0';
255 ++bptr;
256 return (curline);
257 } else
258 *cptr = *bptr;
259 }
260 if (cptr == cend) {
261 cptr = curline = xrealloc(curline, curlen *= 2);
262 cend = curline + curlen;
263 }
264 if (bptr == bend) {
265 buflen = read(fd, buf, BUFSIZ);
266 if (buflen <= 0) {
267 if (cptr > curline) {
268 *cptr = '\0';
269 return (curline);
270 }
271 return (NULL);
272 }
273 bend = buf + buflen;
274 bptr = buf;
275 }
276 }
277}
278
279static char *
815943a9 280wskip(char *cptr)
32a7b49a
JS
281{
282 if (!*cptr || !isspace((unsigned char) *cptr)) {
283 warning(cptr, "expected a space");
284 return (cptr);
285 }
286 while (*cptr && isspace((unsigned char) *cptr))
287 ++cptr;
288 return (cptr);
289}
290
291static char *
815943a9 292cskip(char *cptr)
32a7b49a
JS
293{
294 if (!*cptr || isspace((unsigned char) *cptr)) {
295 warning(cptr, "wasn't expecting a space");
296 return (cptr);
297 }
298 while (*cptr && !isspace((unsigned char) *cptr))
299 ++cptr;
300 return (cptr);
301}
302
303static char *
815943a9 304getmsg(int fd, char *cptr, char quote)
32a7b49a
JS
305{
306 static char *msg = NULL;
307 static long msglen = 0;
308 long clen, i;
309 char *tptr;
310
311 if (quote && *cptr == quote) {
312 ++cptr;
313 }
314
315 clen = strlen(cptr) + 1;
316 if (clen > msglen) {
317 if (msglen)
318 msg = xrealloc(msg, clen);
319 else
320 msg = xmalloc(clen);
321 msglen = clen;
322 }
323 tptr = msg;
324
325 while (*cptr) {
326 if (quote && *cptr == quote) {
327 char *tmp;
328 tmp = cptr + 1;
329 if (*tmp && (!isspace((unsigned char) *tmp) || *wskip(tmp))) {
330 warning(cptr, "unexpected quote character, ignoring");
331 *tptr++ = *cptr++;
332 } else {
333 *cptr = '\0';
334 }
335 } else
336 if (*cptr == '\\') {
337 ++cptr;
338 switch (*cptr) {
339 case '\0':
cae2835b 340 cptr = get_line(fd);
32a7b49a 341 if (!cptr)
0d5acd74 342 error("premature end of file");
32a7b49a
JS
343 msglen += strlen(cptr);
344 i = tptr - msg;
345 msg = xrealloc(msg, msglen);
346 tptr = msg + i;
347 break;
0d5acd74
JM
348
349 #define CASEOF(CS, CH) \
350 case CS: \
351 *tptr++ = CH; \
352 ++cptr; \
353 break; \
354
355 CASEOF('n', '\n');
356 CASEOF('t', '\t');
357 CASEOF('v', '\v');
358 CASEOF('b', '\b');
359 CASEOF('r', '\r');
360 CASEOF('f', '\f');
361 CASEOF('"', '"');
362 CASEOF('\\', '\\');
363
32a7b49a
JS
364 default:
365 if (quote && *cptr == quote) {
366 *tptr++ = *cptr++;
367 } else if (isdigit((unsigned char) *cptr)) {
368 *tptr = 0;
369 for (i = 0; i < 3; ++i) {
370 if (!isdigit((unsigned char) *cptr))
371 break;
372 if (*cptr > '7')
373 warning(cptr, "octal number greater than 7?!");
374 *tptr *= 8;
375 *tptr += (*cptr - '0');
376 ++cptr;
377 }
378 } else {
379 warning(cptr, "unrecognized escape sequence");
380 }
381 break;
382 }
383 } else {
384 *tptr++ = *cptr++;
385 }
386 }
387 *tptr = '\0';
388 return (msg);
389}
390
391void
815943a9 392MCParse(int fd)
32a7b49a
JS
393{
394 char *cptr, *str;
395 int setid, msgid = 0;
396 char quote = 0;
397
398 /* XXX: init sethead? */
399
cae2835b 400 while ((cptr = get_line(fd))) {
32a7b49a
JS
401 if (*cptr == '$') {
402 ++cptr;
403 if (strncmp(cptr, "set", 3) == 0) {
404 cptr += 3;
405 cptr = wskip(cptr);
406 setid = atoi(cptr);
407 MCAddSet(setid);
408 msgid = 0;
409 } else if (strncmp(cptr, "delset", 6) == 0) {
410 cptr += 6;
411 cptr = wskip(cptr);
412 setid = atoi(cptr);
413 MCDelSet(setid);
414 } else if (strncmp(cptr, "quote", 5) == 0) {
415 cptr += 5;
416 if (!*cptr)
417 quote = 0;
418 else {
419 cptr = wskip(cptr);
420 if (!*cptr)
421 quote = 0;
422 else
423 quote = *cptr;
424 }
425 } else if (isspace((unsigned char) *cptr)) {
426 ;
427 } else {
428 if (*cptr) {
429 cptr = wskip(cptr);
430 if (*cptr)
431 warning(cptr, "unrecognized line");
432 }
433 }
434 } else {
435 /*
436 * First check for (and eat) empty lines....
437 */
438 if (!*cptr)
439 continue;
440 /*
441 * We have a digit? Start of a message. Else,
442 * syntax error.
443 */
444 if (isdigit((unsigned char) *cptr)) {
445 msgid = atoi(cptr);
446 cptr = cskip(cptr);
447 cptr = wskip(cptr);
448 /* if (*cptr) ++cptr; */
449 } else {
450 warning(cptr, "neither blank line nor start of a message id");
451 continue;
452 }
453 /*
454 * If we have a message ID, but no message,
455 * then this means "delete this message id
456 * from the catalog".
457 */
458 if (!*cptr) {
459 MCDelMsg(msgid);
460 } else {
461 str = getmsg(fd, cptr, quote);
462 MCAddMsg(msgid, str);
463 }
464 }
465 }
466}
467
32a7b49a
JS
468/*
469 * Write message catalog.
470 *
471 * The message catalog is first converted from its internal to its
472 * external representation in a chunk of memory allocated for this
473 * purpose. Then the completed catalog is written. This approach
474 * avoids additional housekeeping variables and/or a lot of seeks
475 * that would otherwise be required.
476 */
477void
815943a9 478MCWriteCat(int fd)
32a7b49a
JS
479{
480 int nsets; /* number of sets */
481 int nmsgs; /* number of msgs */
482 int string_size; /* total size of string pool */
483 int msgcat_size; /* total size of message catalog */
484 void *msgcat; /* message catalog data */
485 struct _nls_cat_hdr *cat_hdr;
486 struct _nls_set_hdr *set_hdr;
487 struct _nls_msg_hdr *msg_hdr;
488 char *strings;
489 struct _setT *set;
490 struct _msgT *msg;
491 int msg_index;
492 int msg_offset;
493
494 /* determine number of sets, number of messages, and size of the
495 * string pool */
496 nsets = 0;
497 nmsgs = 0;
498 string_size = 0;
499
500 for (set = sethead.lh_first; set != NULL;
501 set = set->entries.le_next) {
502 nsets++;
503
504 for (msg = set->msghead.lh_first; msg != NULL;
505 msg = msg->entries.le_next) {
506 nmsgs++;
507 string_size += strlen(msg->str) + 1;
508 }
509 }
510
511#ifdef DEBUG
512 printf("number of sets: %d\n", nsets);
513 printf("number of msgs: %d\n", nmsgs);
514 printf("string pool size: %d\n", string_size);
515#endif
516
517 /* determine size and then allocate buffer for constructing external
518 * message catalog representation */
519 msgcat_size = sizeof(struct _nls_cat_hdr)
520 + (nsets * sizeof(struct _nls_set_hdr))
521 + (nmsgs * sizeof(struct _nls_msg_hdr))
522 + string_size;
523
524 msgcat = xmalloc(msgcat_size);
525 memset(msgcat, '\0', msgcat_size);
526
527 /* fill in msg catalog header */
528 cat_hdr = (struct _nls_cat_hdr *) msgcat;
529 cat_hdr->__magic = htonl(_NLS_MAGIC);
530 cat_hdr->__nsets = htonl(nsets);
531 cat_hdr->__mem = htonl(msgcat_size - sizeof(struct _nls_cat_hdr));
532 cat_hdr->__msg_hdr_offset =
533 htonl(nsets * sizeof(struct _nls_set_hdr));
534 cat_hdr->__msg_txt_offset =
535 htonl(nsets * sizeof(struct _nls_set_hdr) +
536 nmsgs * sizeof(struct _nls_msg_hdr));
537
538 /* compute offsets for set & msg header tables and string pool */
0d5acd74 539 set_hdr = (struct _nls_set_hdr *)(void *)((char *)msgcat +
32a7b49a 540 sizeof(struct _nls_cat_hdr));
0d5acd74 541 msg_hdr = (struct _nls_msg_hdr *)(void *)((char *)msgcat +
32a7b49a
JS
542 sizeof(struct _nls_cat_hdr) +
543 nsets * sizeof(struct _nls_set_hdr));
544 strings = (char *) msgcat +
545 sizeof(struct _nls_cat_hdr) +
546 nsets * sizeof(struct _nls_set_hdr) +
547 nmsgs * sizeof(struct _nls_msg_hdr);
548
549 msg_index = 0;
550 msg_offset = 0;
551 for (set = sethead.lh_first; set != NULL;
552 set = set->entries.le_next) {
553
554 nmsgs = 0;
555 for (msg = set->msghead.lh_first; msg != NULL;
556 msg = msg->entries.le_next) {
557 int msg_len = strlen(msg->str) + 1;
558
559 msg_hdr->__msgno = htonl(msg->msgId);
560 msg_hdr->__msglen = htonl(msg_len);
561 msg_hdr->__offset = htonl(msg_offset);
562
563 memcpy(strings, msg->str, msg_len);
564 strings += msg_len;
565 msg_offset += msg_len;
566
567 nmsgs++;
568 msg_hdr++;
569 }
570
571 set_hdr->__setno = htonl(set->setId);
572 set_hdr->__nmsgs = htonl(nmsgs);
573 set_hdr->__index = htonl(msg_index);
574 msg_index += nmsgs;
575 set_hdr++;
576 }
577
578 /* write out catalog. XXX: should this be done in small chunks? */
579 write(fd, msgcat, msgcat_size);
580}
581
582void
815943a9 583MCAddSet(int setId)
32a7b49a
JS
584{
585 struct _setT *p, *q;
586
587 if (setId <= 0) {
0d5acd74 588 error("setId's must be greater than zero");
32a7b49a
JS
589 /* NOTREACHED */
590 }
591 if (setId > NL_SETMAX) {
0d5acd74 592 error("setId exceeds limit");
32a7b49a
JS
593 /* NOTREACHED */
594 }
595
596 p = sethead.lh_first;
597 q = NULL;
598 for (; p != NULL && p->setId < setId; q = p, p = p->entries.le_next);
599
600 if (p && p->setId == setId) {
601 ;
602 } else {
603 p = xmalloc(sizeof(struct _setT));
604 memset(p, '\0', sizeof(struct _setT));
605 LIST_INIT(&p->msghead);
606
607 p->setId = setId;
608
609 if (q == NULL) {
610 LIST_INSERT_HEAD(&sethead, p, entries);
611 } else {
612 LIST_INSERT_AFTER(q, p, entries);
613 }
614 }
615
616 curSet = p;
617}
618
619void
815943a9 620MCAddMsg(int msgId, const char *str)
32a7b49a
JS
621{
622 struct _msgT *p, *q;
623
624 if (!curSet)
0d5acd74 625 error("can't specify a message when no set exists");
32a7b49a
JS
626
627 if (msgId <= 0) {
0d5acd74 628 error("msgId's must be greater than zero");
32a7b49a
JS
629 /* NOTREACHED */
630 }
631 if (msgId > NL_MSGMAX) {
0d5acd74 632 error("msgID exceeds limit");
32a7b49a
JS
633 /* NOTREACHED */
634 }
635
636 p = curSet->msghead.lh_first;
637 q = NULL;
638 for (; p != NULL && p->msgId < msgId; q = p, p = p->entries.le_next);
639
640 if (p && p->msgId == msgId) {
641 free(p->str);
642 } else {
643 p = xmalloc(sizeof(struct _msgT));
644 memset(p, '\0', sizeof(struct _msgT));
645
646 if (q == NULL) {
647 LIST_INSERT_HEAD(&curSet->msghead, p, entries);
648 } else {
649 LIST_INSERT_AFTER(q, p, entries);
650 }
651 }
652
653 p->msgId = msgId;
654 p->str = xstrdup(str);
655}
656
657void
815943a9 658MCDelSet(int setId)
32a7b49a
JS
659{
660 struct _setT *set;
661 struct _msgT *msg;
662
663 set = sethead.lh_first;
664 for (; set != NULL && set->setId < setId; set = set->entries.le_next);
665
666 if (set && set->setId == setId) {
667
668 msg = set->msghead.lh_first;
669 while (msg) {
670 free(msg->str);
671 LIST_REMOVE(msg, entries);
672 }
673
674 LIST_REMOVE(set, entries);
675 return;
676 }
677 warning(NULL, "specified set doesn't exist");
678}
679
680void
815943a9 681MCDelMsg(int msgId)
32a7b49a
JS
682{
683 struct _msgT *msg;
684
685 if (!curSet)
0d5acd74 686 error("you can't delete a message before defining the set");
32a7b49a
JS
687
688 msg = curSet->msghead.lh_first;
689 for (; msg != NULL && msg->msgId < msgId; msg = msg->entries.le_next);
690
691 if (msg && msg->msgId == msgId) {
692 free(msg->str);
693 LIST_REMOVE(msg, entries);
694 return;
695 }
696 warning(NULL, "specified msg doesn't exist");
984263bc 697}