libc: Include <unistd.h> for ftell/ftruncate/truncate prototypes.
[dragonfly.git] / usr.bin / gencat / gencat.c
blobda5a209cb5197bd74bcd65fcc515bd629ac1b2a1
1 /* ex:ts=4
2 */
4 /* $NetBSD: gencat.c,v 1.18 2003/10/27 00:12:43 lukem Exp $ */
6 /*
7 * Copyright (c) 1996 The NetBSD Foundation, Inc.
8 * All rights reserved.
10 * This code is derived from software contributed to The NetBSD Foundation
11 * by J.T. Conklin.
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.
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.
34 * $FreeBSD: head/usr.bin/gencat/gencat.c 299356 2016-05-10 11:12:31Z bapt $
37 /***********************************************************
38 Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts.
40 All Rights Reserved
42 Permission to use, copy, modify, and distribute this software and its
43 documentation for any purpose and without fee is hereby granted,
44 provided that the above copyright notice appear in all copies and that
45 both that copyright notice and this permission notice appear in
46 supporting documentation, and that Alfalfa's name not be used in
47 advertising or publicity pertaining to distribution of the software
48 without specific, written prior permission.
50 ALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
51 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
52 ALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
53 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
54 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
55 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
56 SOFTWARE.
58 If you make any modifications, bugfixes or other changes to this software
59 we'd appreciate it if you could send a copy to us so we can keep things
60 up-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
67 ******************************************************************/
70 #define _NLS_PRIVATE
72 #include <sys/types.h>
73 #include <sys/queue.h>
75 #include <arpa/inet.h> /* for htonl() */
77 #include <ctype.h>
78 #include <err.h>
79 #include <fcntl.h>
80 #include <limits.h>
81 #include <nl_types.h>
82 #include <stdio.h>
83 #include <stdlib.h>
84 #include <string.h>
85 #include <unistd.h>
87 #ifdef BOOTSTRAPPING
88 #define getline get_line /* help bootstrap previous stdio.h */
89 #endif
91 struct _msgT {
92 long msgId;
93 char *str;
94 LIST_ENTRY(_msgT) entries;
97 struct _setT {
98 long setId;
99 LIST_HEAD(msghead, _msgT) msghead;
100 LIST_ENTRY(_setT) entries;
103 static LIST_HEAD(sethead, _setT) sethead;
104 static struct _setT *curSet;
106 static char *curline = NULL;
107 static long lineno = 0;
109 static char *cskip(char *);
110 static void error(const char *);
111 static char *get_line(int);
112 static char *getmsg(int, char *, char);
113 static void warning(const char *, const char *);
114 static char *wskip(char *);
115 static char *xstrdup(const char *);
116 static void *xmalloc(size_t);
117 static void *xrealloc(void *, size_t);
119 void MCParse(int);
120 void MCReadCat(int);
121 void MCWriteCat(int);
122 void MCDelMsg(int);
123 void MCAddMsg(int, const char *);
124 void MCAddSet(int);
125 void MCDelSet(int);
126 void usage(void);
128 void
129 usage(void)
131 fprintf(stderr, "usage: %s catfile msgfile ...\n", getprogname());
132 exit(1);
136 main(int argc, char **argv)
138 int ofd, ifd;
139 char *catfile = NULL;
140 int c;
142 #define DEPRECATEDMSG 1
144 #ifdef DEPRECATEDMSG
145 while ((c = getopt(argc, argv, "new")) != -1) {
146 #else
147 while ((c = getopt(argc, argv, "")) != -1) {
148 #endif
149 switch (c) {
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 '?':
158 default:
159 usage();
160 /* NOTREACHED */
163 argc -= optind;
164 argv += optind;
166 if (argc < 2) {
167 usage();
168 /* NOTREACHED */
170 catfile = *argv++;
172 for (; *argv; argv++) {
173 if ((ifd = open(*argv, O_RDONLY)) < 0)
174 err(1, "Unable to read %s", *argv);
175 MCParse(ifd);
176 close(ifd);
179 if ((ofd = open(catfile, O_WRONLY | O_TRUNC | O_CREAT, 0666)) < 0)
180 err(1, "Unable to create a new %s", catfile);
181 MCWriteCat(ofd);
182 exit(0);
185 static void
186 warning(const char *cptr, const char *msg)
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");
198 #define CORRUPT() { error("corrupt message catalog"); }
199 #define NOMEM() { error("out of memory"); }
201 static void
202 error(const char *msg)
204 warning(NULL, msg);
205 exit(1);
208 static void *
209 xmalloc(size_t len)
211 void *p;
213 if ((p = malloc(len)) == NULL)
214 NOMEM();
215 return (p);
218 static void *
219 xrealloc(void *ptr, size_t size)
221 if ((ptr = realloc(ptr, size)) == NULL)
222 NOMEM();
223 return (ptr);
226 static char *
227 xstrdup(const char *str)
229 char *nstr;
231 if ((nstr = strdup(str)) == NULL)
232 NOMEM();
233 return (nstr);
236 static char *
237 get_line(int fd)
239 static long curlen = BUFSIZ;
240 static char buf[BUFSIZ], *bptr = buf, *bend = buf;
241 char *cptr, *cend;
242 long buflen;
244 if (!curline) {
245 curline = xmalloc(curlen);
247 ++lineno;
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;
260 if (cptr == cend) {
261 cptr = curline = xrealloc(curline, curlen *= 2);
262 cend = curline + curlen;
264 if (bptr == bend) {
265 buflen = read(fd, buf, BUFSIZ);
266 if (buflen <= 0) {
267 if (cptr > curline) {
268 *cptr = '\0';
269 return (curline);
271 return (NULL);
273 bend = buf + buflen;
274 bptr = buf;
279 static char *
280 wskip(char *cptr)
282 if (!*cptr || !isspace((unsigned char) *cptr)) {
283 warning(cptr, "expected a space");
284 return (cptr);
286 while (*cptr && isspace((unsigned char) *cptr))
287 ++cptr;
288 return (cptr);
291 static char *
292 cskip(char *cptr)
294 if (!*cptr || isspace((unsigned char) *cptr)) {
295 warning(cptr, "wasn't expecting a space");
296 return (cptr);
298 while (*cptr && !isspace((unsigned char) *cptr))
299 ++cptr;
300 return (cptr);
303 static char *
304 getmsg(int fd, char *cptr, char quote)
306 static char *msg = NULL;
307 static long msglen = 0;
308 long clen, i;
309 char *tptr;
311 if (quote && *cptr == quote) {
312 ++cptr;
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;
323 tptr = msg;
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';
335 } else
336 if (*cptr == '\\') {
337 ++cptr;
338 switch (*cptr) {
339 case '\0':
340 cptr = get_line(fd);
341 if (!cptr)
342 error("premature end of file");
343 msglen += strlen(cptr);
344 i = tptr - msg;
345 msg = xrealloc(msg, msglen);
346 tptr = msg + i;
347 break;
349 #define CASEOF(CS, CH) \
350 case CS: \
351 *tptr++ = CH; \
352 ++cptr; \
353 break; \
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('\\', '\\');
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;
378 } else {
379 warning(cptr, "unrecognized escape sequence");
381 break;
383 } else {
384 *tptr++ = *cptr++;
387 *tptr = '\0';
388 return (msg);
391 void
392 MCParse(int fd)
394 char *cptr, *str;
395 int setid, msgid = 0;
396 char quote = 0;
398 /* XXX: init sethead? */
400 while ((cptr = get_line(fd))) {
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;
425 } else if (isspace((unsigned char) *cptr)) {
427 } else {
428 if (*cptr) {
429 cptr = wskip(cptr);
430 if (*cptr)
431 warning(cptr, "unrecognized line");
434 } else {
436 * First check for (and eat) empty lines....
438 if (!*cptr)
439 continue;
441 * We have a digit? Start of a message. Else,
442 * syntax error.
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;
454 * If we have a message ID, but no message,
455 * then this means "delete this message id
456 * from the catalog".
458 if (!*cptr) {
459 MCDelMsg(msgid);
460 } else {
461 str = getmsg(fd, cptr, quote);
462 MCAddMsg(msgid, str);
469 * Write message catalog.
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.
477 void
478 MCWriteCat(int fd)
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;
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;
500 for (set = sethead.lh_first; set != NULL;
501 set = set->entries.le_next) {
502 nsets++;
504 for (msg = set->msghead.lh_first; msg != NULL;
505 msg = msg->entries.le_next) {
506 nmsgs++;
507 string_size += strlen(msg->str) + 1;
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
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;
524 msgcat = xmalloc(msgcat_size);
525 memset(msgcat, '\0', msgcat_size);
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));
538 /* compute offsets for set & msg header tables and string pool */
539 set_hdr = (struct _nls_set_hdr *)(void *)((char *)msgcat +
540 sizeof(struct _nls_cat_hdr));
541 msg_hdr = (struct _nls_msg_hdr *)(void *)((char *)msgcat +
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);
549 msg_index = 0;
550 msg_offset = 0;
551 for (set = sethead.lh_first; set != NULL;
552 set = set->entries.le_next) {
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;
559 msg_hdr->__msgno = htonl(msg->msgId);
560 msg_hdr->__msglen = htonl(msg_len);
561 msg_hdr->__offset = htonl(msg_offset);
563 memcpy(strings, msg->str, msg_len);
564 strings += msg_len;
565 msg_offset += msg_len;
567 nmsgs++;
568 msg_hdr++;
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++;
578 /* write out catalog. XXX: should this be done in small chunks? */
579 write(fd, msgcat, msgcat_size);
582 void
583 MCAddSet(int setId)
585 struct _setT *p, *q;
587 if (setId <= 0) {
588 error("setId's must be greater than zero");
589 /* NOTREACHED */
591 if (setId > NL_SETMAX) {
592 error("setId exceeds limit");
593 /* NOTREACHED */
596 p = sethead.lh_first;
597 q = NULL;
598 for (; p != NULL && p->setId < setId; q = p, p = p->entries.le_next);
600 if (p && p->setId == setId) {
602 } else {
603 p = xmalloc(sizeof(struct _setT));
604 memset(p, '\0', sizeof(struct _setT));
605 LIST_INIT(&p->msghead);
607 p->setId = setId;
609 if (q == NULL) {
610 LIST_INSERT_HEAD(&sethead, p, entries);
611 } else {
612 LIST_INSERT_AFTER(q, p, entries);
616 curSet = p;
619 void
620 MCAddMsg(int msgId, const char *str)
622 struct _msgT *p, *q;
624 if (!curSet)
625 error("can't specify a message when no set exists");
627 if (msgId <= 0) {
628 error("msgId's must be greater than zero");
629 /* NOTREACHED */
631 if (msgId > NL_MSGMAX) {
632 error("msgID exceeds limit");
633 /* NOTREACHED */
636 p = curSet->msghead.lh_first;
637 q = NULL;
638 for (; p != NULL && p->msgId < msgId; q = p, p = p->entries.le_next);
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));
646 if (q == NULL) {
647 LIST_INSERT_HEAD(&curSet->msghead, p, entries);
648 } else {
649 LIST_INSERT_AFTER(q, p, entries);
653 p->msgId = msgId;
654 p->str = xstrdup(str);
657 void
658 MCDelSet(int setId)
660 struct _setT *set;
661 struct _msgT *msg;
663 set = sethead.lh_first;
664 for (; set != NULL && set->setId < setId; set = set->entries.le_next);
666 if (set && set->setId == setId) {
668 msg = set->msghead.lh_first;
669 while (msg) {
670 free(msg->str);
671 LIST_REMOVE(msg, entries);
674 LIST_REMOVE(set, entries);
675 return;
677 warning(NULL, "specified set doesn't exist");
680 void
681 MCDelMsg(int msgId)
683 struct _msgT *msg;
685 if (!curSet)
686 error("you can't delete a message before defining the set");
688 msg = curSet->msghead.lh_first;
689 for (; msg != NULL && msg->msgId < msgId; msg = msg->entries.le_next);
691 if (msg && msg->msgId == msgId) {
692 free(msg->str);
693 LIST_REMOVE(msg, entries);
694 return;
696 warning(NULL, "specified msg doesn't exist");