]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/gencat/gencat.c
ident(1): Normalizing date format
[FreeBSD/FreeBSD.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  * SPDX-License-Identifier: (BSD-2-Clause-NetBSD AND ISC)
8  *
9  * Copyright (c) 1996 The NetBSD Foundation, Inc.
10  * All rights reserved.
11  *
12  * This code is derived from software contributed to The NetBSD Foundation
13  * by J.T. Conklin.
14  *
15  * Redistribution and use in source and binary forms, with or without
16  * modification, are permitted provided that the following conditions
17  * are met:
18  * 1. Redistributions of source code must retain the above copyright
19  *    notice, this list of conditions and the following disclaimer.
20  * 2. Redistributions in binary form must reproduce the above copyright
21  *    notice, this list of conditions and the following disclaimer in the
22  *    documentation and/or other materials provided with the distribution.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
25  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
26  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
27  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 
28  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34  * POSSIBILITY OF SUCH DAMAGE.
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 #include <sys/cdefs.h>
70 __FBSDID("$FreeBSD$");
71
72 #define _NLS_PRIVATE
73
74 #include <sys/types.h>
75 #include <sys/queue.h>
76
77 #include <arpa/inet.h>          /* for htonl() */
78
79 #include <ctype.h>
80 #include <err.h>
81 #include <fcntl.h>
82 #include <limits.h>
83 #include <nl_types.h>
84 #include <stdio.h>
85 #include <stdlib.h>
86 #include <string.h>
87 #include <unistd.h>
88
89 struct _msgT {
90         long    msgId;
91         char   *str;
92         LIST_ENTRY(_msgT) entries;
93 };
94
95 struct _setT {
96         long    setId;
97         LIST_HEAD(msghead, _msgT) msghead;
98         LIST_ENTRY(_setT) entries;
99 };
100
101 static LIST_HEAD(sethead, _setT) sethead;
102 static struct _setT *curSet;
103
104 static char *curline = NULL;
105 static long lineno = 0;
106
107 static  char   *cskip(char *);
108 static  void    error(const char *);
109 static  char   *get_line(int);
110 static  char   *getmsg(int, char *, char);
111 static  void    warning(const char *, const char *);
112 static  char   *wskip(char *);
113 static  char   *xstrdup(const char *);
114 static  void   *xmalloc(size_t);
115 static  void   *xrealloc(void *, size_t);
116
117 void    MCParse(int);
118 void    MCReadCat(int);
119 void    MCWriteCat(int);
120 void    MCDelMsg(int);
121 void    MCAddMsg(int, const char *);
122 void    MCAddSet(int);
123 void    MCDelSet(int);
124 void    usage(void);
125 int     main(int, char **);
126
127 void
128 usage(void)
129 {
130         fprintf(stderr, "usage: %s catfile msgfile ...\n", getprogname());
131         exit(1);
132 }
133
134 int
135 main(int argc, char **argv)
136 {
137         int     ofd, ifd;
138         char    *catfile = NULL;
139         int     c;
140
141 #define DEPRECATEDMSG   1
142
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
184 static void
185 warning(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
200 static void
201 error(const char *msg)
202 {
203         warning(NULL, msg);
204         exit(1);
205 }
206
207 static void *
208 xmalloc(size_t len)
209 {
210         void   *p;
211
212         if ((p = malloc(len)) == NULL)
213                 NOMEM();
214         return (p);
215 }
216
217 static void *
218 xrealloc(void *ptr, size_t size)
219 {
220         if ((ptr = realloc(ptr, size)) == NULL)
221                 NOMEM();
222         return (ptr);
223 }
224
225 static char *
226 xstrdup(const char *str)
227 {
228         char *nstr;
229
230         if ((nstr = strdup(str)) == NULL)
231                 NOMEM();
232         return (nstr);
233 }
234
235 static char *
236 get_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
278 static char *
279 wskip(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
290 static char *
291 cskip(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
302 static char *
303 getmsg(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
390 void
391 MCParse(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  */
476 void
477 MCWriteCat(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
581 void
582 MCAddSet(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
618 void
619 MCAddMsg(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
656 void
657 MCDelSet(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
679 void
680 MCDelMsg(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 }