GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/gencat/gencat.c Lines: 0 274 0.0 %
Date: 2016-12-06 Branches: 0 226 0.0 %

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