Merge branch 'vendor/GCC50'
[dragonfly.git] / usr.bin / gencat / gencat.c
1 /* ex:ts=4
2  */
3
4 /*      $NetBSD: gencat.c,v 1.18 2003/10/27 00:12:43 lukem Exp $        */
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.
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.
33  *
34  * $FreeBSD: head/usr.bin/gencat/gencat.c 241737 2012-10-19 14:49:42Z ed $
35  */
36
37 /***********************************************************
38 Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts.
39
40                         All Rights Reserved
41
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.
49
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.
57
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
66
67 ******************************************************************/
68
69
70 #define _NLS_PRIVATE
71
72 #include <sys/types.h>
73 #include <sys/queue.h>
74
75 #include <arpa/inet.h>          /* for htonl() */
76
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>
86
87 struct _msgT {
88         long    msgId;
89         char   *str;
90         LIST_ENTRY(_msgT) entries;
91 };
92
93 struct _setT {
94         long    setId;
95         LIST_HEAD(msghead, _msgT) msghead;
96         LIST_ENTRY(_setT) entries;
97 };
98
99 static LIST_HEAD(sethead, _setT) sethead;
100 static struct _setT *curSet;
101
102 static char *curline = NULL;
103 static long lineno = 0;
104
105 static  char   *cskip(char *);
106 static  void    error(const char *);
107 static  char   *getline(int);
108 static  char   *getmsg(int, char *, char);
109 static  void    warning(const char *, const char *);
110 static  char   *wskip(char *);
111 static  char   *xstrdup(const char *);
112 static  void   *xmalloc(size_t);
113 static  void   *xrealloc(void *, size_t);
114
115 void    MCParse(int);
116 void    MCReadCat(int);
117 void    MCWriteCat(int);
118 void    MCDelMsg(int);
119 void    MCAddMsg(int, const char *);
120 void    MCAddSet(int);
121 void    MCDelSet(int);
122 void    usage(void);
123
124 void
125 usage(void)
126 {
127         fprintf(stderr, "usage: %s catfile msgfile ...\n", getprogname());
128         exit(1);
129 }
130
131 int
132 main(int argc, char **argv)
133 {
134         int     ofd, ifd;
135         char    *catfile = NULL;
136         int     c;
137
138 #define DEPRECATEDMSG   1
139
140 #ifdef DEPRECATEDMSG
141         while ((c = getopt(argc, argv, "new")) != -1) {
142 #else
143         while ((c = getopt(argc, argv, "")) != -1) {
144 #endif
145                 switch (c) {
146 #ifdef DEPRECATEDMSG
147                 case 'n':
148                         fprintf(stderr, "WARNING: Usage of \"-new\" argument is deprecated.\n");
149                 case 'e':
150                 case 'w':
151                         break;
152 #endif
153                 case '?':
154                 default:
155                         usage();
156                         /* NOTREACHED */
157                 }
158         }
159         argc -= optind;
160         argv += optind;
161
162         if (argc < 2) {
163                 usage();
164                 /* NOTREACHED */
165         }
166         catfile = *argv++;
167
168         for (; *argv; argv++) {
169                 if ((ifd = open(*argv, O_RDONLY)) < 0)
170                         err(1, "Unable to read %s", *argv);
171                 MCParse(ifd);
172                 close(ifd);
173         }
174
175         if ((ofd = open(catfile, O_WRONLY | O_TRUNC | O_CREAT, 0666)) < 0)
176                 err(1, "Unable to create a new %s", catfile);
177         MCWriteCat(ofd);
178         exit(0);
179 }
180
181 static void
182 warning(const char *cptr, const char *msg)
183 {
184         fprintf(stderr, "%s: %s on line %ld\n", getprogname(), msg, lineno);
185         fprintf(stderr, "%s\n", curline);
186         if (cptr) {
187                 char   *tptr;
188                 for (tptr = curline; tptr < cptr; ++tptr)
189                         putc(' ', stderr);
190                 fprintf(stderr, "^\n");
191         }
192 }
193
194 #define CORRUPT()       { error("corrupt message catalog"); }
195 #define NOMEM()         { error("out of memory"); }
196
197 static void
198 error(const char *msg)
199 {
200         warning(NULL, msg);
201         exit(1);
202 }
203
204 static void *
205 xmalloc(size_t len)
206 {
207         void   *p;
208
209         if ((p = malloc(len)) == NULL)
210                 NOMEM();
211         return (p);
212 }
213
214 static void *
215 xrealloc(void *ptr, size_t size)
216 {
217         if ((ptr = realloc(ptr, size)) == NULL)
218                 NOMEM();
219         return (ptr);
220 }
221
222 static char *
223 xstrdup(const char *str)
224 {
225         char *nstr;
226
227         if ((nstr = strdup(str)) == NULL)
228                 NOMEM();
229         return (nstr);
230 }
231
232 static char *
233 getline(int fd)
234 {
235         static long curlen = BUFSIZ;
236         static char buf[BUFSIZ], *bptr = buf, *bend = buf;
237         char   *cptr, *cend;
238         long    buflen;
239
240         if (!curline) {
241                 curline = xmalloc(curlen);
242         }
243         ++lineno;
244
245         cptr = curline;
246         cend = curline + curlen;
247         for (;;) {
248                 for (; bptr < bend && cptr < cend; ++cptr, ++bptr) {
249                         if (*bptr == '\n') {
250                                 *cptr = '\0';
251                                 ++bptr;
252                                 return (curline);
253                         } else
254                                 *cptr = *bptr;
255                 }
256                 if (cptr == cend) {
257                         cptr = curline = xrealloc(curline, curlen *= 2);
258                         cend = curline + curlen;
259                 }
260                 if (bptr == bend) {
261                         buflen = read(fd, buf, BUFSIZ);
262                         if (buflen <= 0) {
263                                 if (cptr > curline) {
264                                         *cptr = '\0';
265                                         return (curline);
266                                 }
267                                 return (NULL);
268                         }
269                         bend = buf + buflen;
270                         bptr = buf;
271                 }
272         }
273 }
274
275 static char *
276 wskip(char *cptr)
277 {
278         if (!*cptr || !isspace((unsigned char) *cptr)) {
279                 warning(cptr, "expected a space");
280                 return (cptr);
281         }
282         while (*cptr && isspace((unsigned char) *cptr))
283                 ++cptr;
284         return (cptr);
285 }
286
287 static char *
288 cskip(char *cptr)
289 {
290         if (!*cptr || isspace((unsigned char) *cptr)) {
291                 warning(cptr, "wasn't expecting a space");
292                 return (cptr);
293         }
294         while (*cptr && !isspace((unsigned char) *cptr))
295                 ++cptr;
296         return (cptr);
297 }
298
299 static char *
300 getmsg(int fd, char *cptr, char quote)
301 {
302         static char *msg = NULL;
303         static long msglen = 0;
304         long    clen, i;
305         char   *tptr;
306
307         if (quote && *cptr == quote) {
308                 ++cptr;
309         } 
310
311         clen = strlen(cptr) + 1;
312         if (clen > msglen) {
313                 if (msglen)
314                         msg = xrealloc(msg, clen);
315                 else
316                         msg = xmalloc(clen);
317                 msglen = clen;
318         }
319         tptr = msg;
320
321         while (*cptr) {
322                 if (quote && *cptr == quote) {
323                         char   *tmp;
324                         tmp = cptr + 1;
325                         if (*tmp && (!isspace((unsigned char) *tmp) || *wskip(tmp))) {
326                                 warning(cptr, "unexpected quote character, ignoring");
327                                 *tptr++ = *cptr++;
328                         } else {
329                                 *cptr = '\0';
330                         }
331                 } else
332                         if (*cptr == '\\') {
333                                 ++cptr;
334                                 switch (*cptr) {
335                                 case '\0':
336                                         cptr = getline(fd);
337                                         if (!cptr)
338                                                 error("premature end of file");
339                                         msglen += strlen(cptr);
340                                         i = tptr - msg;
341                                         msg = xrealloc(msg, msglen);
342                                         tptr = msg + i;
343                                         break;
344
345                 #define CASEOF(CS, CH)          \
346                         case CS:                \
347                                 *tptr++ = CH;   \
348                                 ++cptr;         \
349                                 break;          \
350
351                                 CASEOF('n', '\n');
352                                 CASEOF('t', '\t');
353                                 CASEOF('v', '\v');
354                                 CASEOF('b', '\b');
355                                 CASEOF('r', '\r');
356                                 CASEOF('f', '\f');
357                                 CASEOF('"', '"');
358                                 CASEOF('\\', '\\');
359
360                                 default:
361                                         if (quote && *cptr == quote) {
362                                                 *tptr++ = *cptr++;
363                                         } else if (isdigit((unsigned char) *cptr)) {
364                                                 *tptr = 0;
365                                                 for (i = 0; i < 3; ++i) {
366                                                         if (!isdigit((unsigned char) *cptr))
367                                                                 break;
368                                                         if (*cptr > '7')
369                                                                 warning(cptr, "octal number greater than 7?!");
370                                                         *tptr *= 8;
371                                                         *tptr += (*cptr - '0');
372                                                         ++cptr;
373                                                 }
374                                         } else {
375                                                 warning(cptr, "unrecognized escape sequence");
376                                         }
377                                         break;
378                                 }
379                         } else {
380                                 *tptr++ = *cptr++;
381                         }
382         }
383         *tptr = '\0';
384         return (msg);
385 }
386
387 void
388 MCParse(int fd)
389 {
390         char   *cptr, *str;
391         int     setid, msgid = 0;
392         char    quote = 0;
393
394         /* XXX: init sethead? */
395
396         while ((cptr = getline(fd))) {
397                 if (*cptr == '$') {
398                         ++cptr;
399                         if (strncmp(cptr, "set", 3) == 0) {
400                                 cptr += 3;
401                                 cptr = wskip(cptr);
402                                 setid = atoi(cptr);
403                                 MCAddSet(setid);
404                                 msgid = 0;
405                         } else if (strncmp(cptr, "delset", 6) == 0) {
406                                 cptr += 6;
407                                 cptr = wskip(cptr);
408                                 setid = atoi(cptr);
409                                 MCDelSet(setid);
410                         } else if (strncmp(cptr, "quote", 5) == 0) {
411                                 cptr += 5;
412                                 if (!*cptr)
413                                         quote = 0;
414                                 else {
415                                         cptr = wskip(cptr);
416                                         if (!*cptr)
417                                                 quote = 0;
418                                         else
419                                                 quote = *cptr;
420                                 }
421                         } else if (isspace((unsigned char) *cptr)) {
422                                 ;
423                         } else {
424                                 if (*cptr) {
425                                         cptr = wskip(cptr);
426                                         if (*cptr)
427                                                 warning(cptr, "unrecognized line");
428                                 }
429                         }
430                 } else {
431                         /*
432                          * First check for (and eat) empty lines....
433                          */
434                         if (!*cptr)
435                                 continue;
436                         /*
437                          * We have a digit? Start of a message. Else,
438                          * syntax error.
439                          */
440                         if (isdigit((unsigned char) *cptr)) {
441                                 msgid = atoi(cptr);
442                                 cptr = cskip(cptr);
443                                 cptr = wskip(cptr);
444                                 /* if (*cptr) ++cptr; */
445                         } else {
446                                 warning(cptr, "neither blank line nor start of a message id");
447                                 continue;
448                         }
449                         /*
450                          * If we have a message ID, but no message,
451                          * then this means "delete this message id
452                          * from the catalog".
453                          */
454                         if (!*cptr) {
455                                 MCDelMsg(msgid);
456                         } else {
457                                 str = getmsg(fd, cptr, quote);
458                                 MCAddMsg(msgid, str);
459                         }
460                 }
461         }
462 }
463
464 /*
465  * Write message catalog.
466  *
467  * The message catalog is first converted from its internal to its
468  * external representation in a chunk of memory allocated for this
469  * purpose.  Then the completed catalog is written.  This approach
470  * avoids additional housekeeping variables and/or a lot of seeks
471  * that would otherwise be required.
472  */
473 void
474 MCWriteCat(int fd)
475 {
476         int     nsets;          /* number of sets */
477         int     nmsgs;          /* number of msgs */
478         int     string_size;    /* total size of string pool */
479         int     msgcat_size;    /* total size of message catalog */
480         void   *msgcat;         /* message catalog data */
481         struct _nls_cat_hdr *cat_hdr;
482         struct _nls_set_hdr *set_hdr;
483         struct _nls_msg_hdr *msg_hdr;
484         char   *strings;
485         struct _setT *set;
486         struct _msgT *msg;
487         int     msg_index;
488         int     msg_offset;
489
490         /* determine number of sets, number of messages, and size of the
491          * string pool */
492         nsets = 0;
493         nmsgs = 0;
494         string_size = 0;
495
496         for (set = sethead.lh_first; set != NULL;
497             set = set->entries.le_next) {
498                 nsets++;
499
500                 for (msg = set->msghead.lh_first; msg != NULL;
501                     msg = msg->entries.le_next) {
502                         nmsgs++;
503                         string_size += strlen(msg->str) + 1;
504                 }
505         }
506
507 #ifdef DEBUG
508         printf("number of sets: %d\n", nsets);
509         printf("number of msgs: %d\n", nmsgs);
510         printf("string pool size: %d\n", string_size);
511 #endif
512
513         /* determine size and then allocate buffer for constructing external
514          * message catalog representation */
515         msgcat_size = sizeof(struct _nls_cat_hdr)
516             + (nsets * sizeof(struct _nls_set_hdr))
517             + (nmsgs * sizeof(struct _nls_msg_hdr))
518             + string_size;
519
520         msgcat = xmalloc(msgcat_size);
521         memset(msgcat, '\0', msgcat_size);
522
523         /* fill in msg catalog header */
524         cat_hdr = (struct _nls_cat_hdr *) msgcat;
525         cat_hdr->__magic = htonl(_NLS_MAGIC);
526         cat_hdr->__nsets = htonl(nsets);
527         cat_hdr->__mem = htonl(msgcat_size - sizeof(struct _nls_cat_hdr));
528         cat_hdr->__msg_hdr_offset =
529             htonl(nsets * sizeof(struct _nls_set_hdr));
530         cat_hdr->__msg_txt_offset =
531             htonl(nsets * sizeof(struct _nls_set_hdr) +
532             nmsgs * sizeof(struct _nls_msg_hdr));
533
534         /* compute offsets for set & msg header tables and string pool */
535         set_hdr = (struct _nls_set_hdr *)(void *)((char *)msgcat +
536             sizeof(struct _nls_cat_hdr));
537         msg_hdr = (struct _nls_msg_hdr *)(void *)((char *)msgcat +
538             sizeof(struct _nls_cat_hdr) +
539             nsets * sizeof(struct _nls_set_hdr));
540         strings = (char *) msgcat +
541             sizeof(struct _nls_cat_hdr) +
542             nsets * sizeof(struct _nls_set_hdr) +
543             nmsgs * sizeof(struct _nls_msg_hdr);
544
545         msg_index = 0;
546         msg_offset = 0;
547         for (set = sethead.lh_first; set != NULL;
548             set = set->entries.le_next) {
549
550                 nmsgs = 0;
551                 for (msg = set->msghead.lh_first; msg != NULL;
552                     msg = msg->entries.le_next) {
553                         int     msg_len = strlen(msg->str) + 1;
554
555                         msg_hdr->__msgno = htonl(msg->msgId);
556                         msg_hdr->__msglen = htonl(msg_len);
557                         msg_hdr->__offset = htonl(msg_offset);
558
559                         memcpy(strings, msg->str, msg_len);
560                         strings += msg_len;
561                         msg_offset += msg_len;
562
563                         nmsgs++;
564                         msg_hdr++;
565                 }
566
567                 set_hdr->__setno = htonl(set->setId);
568                 set_hdr->__nmsgs = htonl(nmsgs);
569                 set_hdr->__index = htonl(msg_index);
570                 msg_index += nmsgs;
571                 set_hdr++;
572         }
573
574         /* write out catalog.  XXX: should this be done in small chunks? */
575         write(fd, msgcat, msgcat_size);
576 }
577
578 void
579 MCAddSet(int setId)
580 {
581         struct _setT *p, *q;
582
583         if (setId <= 0) {
584                 error("setId's must be greater than zero");
585                 /* NOTREACHED */
586         }
587         if (setId > NL_SETMAX) {
588                 error("setId exceeds limit");
589                 /* NOTREACHED */
590         }
591
592         p = sethead.lh_first;
593         q = NULL;
594         for (; p != NULL && p->setId < setId; q = p, p = p->entries.le_next);
595
596         if (p && p->setId == setId) {
597                 ;
598         } else {
599                 p = xmalloc(sizeof(struct _setT));
600                 memset(p, '\0', sizeof(struct _setT));
601                 LIST_INIT(&p->msghead);
602
603                 p->setId = setId;
604
605                 if (q == NULL) {
606                         LIST_INSERT_HEAD(&sethead, p, entries);
607                 } else {
608                         LIST_INSERT_AFTER(q, p, entries);
609                 }
610         }
611
612         curSet = p;
613 }
614
615 void
616 MCAddMsg(int msgId, const char *str)
617 {
618         struct _msgT *p, *q;
619
620         if (!curSet)
621                 error("can't specify a message when no set exists");
622
623         if (msgId <= 0) {
624                 error("msgId's must be greater than zero");
625                 /* NOTREACHED */
626         }
627         if (msgId > NL_MSGMAX) {
628                 error("msgID exceeds limit");
629                 /* NOTREACHED */
630         }
631
632         p = curSet->msghead.lh_first;
633         q = NULL;
634         for (; p != NULL && p->msgId < msgId; q = p, p = p->entries.le_next);
635
636         if (p && p->msgId == msgId) {
637                 free(p->str);
638         } else {
639                 p = xmalloc(sizeof(struct _msgT));
640                 memset(p, '\0', sizeof(struct _msgT));
641
642                 if (q == NULL) {
643                         LIST_INSERT_HEAD(&curSet->msghead, p, entries);
644                 } else {
645                         LIST_INSERT_AFTER(q, p, entries);
646                 }
647         }
648
649         p->msgId = msgId;
650         p->str = xstrdup(str);
651 }
652
653 void
654 MCDelSet(int setId)
655 {
656         struct _setT *set;
657         struct _msgT *msg;
658
659         set = sethead.lh_first;
660         for (; set != NULL && set->setId < setId; set = set->entries.le_next);
661
662         if (set && set->setId == setId) {
663
664                 msg = set->msghead.lh_first;
665                 while (msg) {
666                         free(msg->str);
667                         LIST_REMOVE(msg, entries);
668                 }
669
670                 LIST_REMOVE(set, entries);
671                 return;
672         }
673         warning(NULL, "specified set doesn't exist");
674 }
675
676 void
677 MCDelMsg(int msgId)
678 {
679         struct _msgT *msg;
680
681         if (!curSet)
682                 error("you can't delete a message before defining the set");
683
684         msg = curSet->msghead.lh_first;
685         for (; msg != NULL && msg->msgId < msgId; msg = msg->entries.le_next);
686
687         if (msg && msg->msgId == msgId) {
688                 free(msg->str);
689                 LIST_REMOVE(msg, entries);
690                 return;
691         }
692         warning(NULL, "specified msg doesn't exist");
693 }