xref: /dragonfly/usr.bin/gencat/gencat.c (revision 0db87cb7)
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 }
694