1/***********************************************************
2Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts.
3
4                        All Rights Reserved
5
6Permission to use, copy, modify, and distribute this software and its
7documentation for any purpose and without fee is hereby granted,
8provided that the above copyright notice appear in all copies and that
9both that copyright notice and this permission notice appear in
10supporting documentation, and that Alfalfa's name not be used in
11advertising or publicity pertaining to distribution of the software
12without specific, written prior permission.
13
14ALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
15ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
16ALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
17ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
18WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
19ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
20SOFTWARE.
21
22If you make any modifications, bugfixes or other changes to this software
23we'd appreciate it if you could send a copy to us so we can keep things
24up-to-date.  Many thanks.
25				Kee Hinckley
26				Alfalfa Software, Inc.
27				267 Allston St., #3
28				Cambridge, MA 02139  USA
29				nazgul@alfalfa.com
30
31******************************************************************/
32
33#include <sys/cdefs.h>
34__FBSDID("$FreeBSD: src/usr.bin/gencat/gencat.c,v 1.9 2002/05/29 14:23:10 tjr Exp $");
35
36#include <sys/file.h>
37#include <sys/stat.h>
38#include <err.h>
39#include <paths.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.h>
43#include <unistd.h>
44#include "gencat.h"
45
46/*
47 * The spec says the syntax is "gencat catfile msgfile...".
48 * We extend it to:
49 * 	gencat [-lang C|C++|ANSIC] catfile msgfile [-h <header-file>]...
50 * Flags are order dependent, we'll take whatever lang was most recently chosen
51 * and use it to generate the next header file.  The header files are generated
52 * at the point in the command line they are listed.  Thus the sequence:
53 *	gencat -lang C foo.cat foo.mcs -h foo.h -lang C++ bar.mcs -h bar.H
54 * will put constants from foo.mcs into foo.h and constants from bar.mcs into
55 * bar.h.  Constants are not saved in the catalog file, so nothing will come
56 * from that, even if things have been defined before.  The constants in foo.h
57 * will be in C syntax, in bar.H in C++ syntax.
58 */
59
60static void writeIfChanged(char *, int, int);
61
62static void
63usage(void)
64{
65    fprintf(stderr, "usage: gencat [-new] [-or] [-lang C|C++|ANSIC]\n"
66                    "              catfile msgfile [-h <header-file>]...\n");
67    exit(1);
68}
69
70int
71main(int argc, char *argv[])
72{
73    int		ofd = -1, ifd, i;
74    char	*catfile = NULL;
75    char	*input = NULL;
76    int		lang = MCLangC;
77    int		new = FALSE;
78    int		orConsts = FALSE;
79
80    for (i = 1; i < argc; ++i) {
81	if (argv[i][0] == '-') {
82	    if (strcmp(argv[i], "-lang") == 0) {
83		++i;
84		if (strcmp(argv[i], "C") == 0) lang = MCLangC;
85		else if (strcmp(argv[i], "C++") == 0) lang = MCLangCPlusPlus;
86		else if (strcmp(argv[i], "ANSIC") == 0) lang = MCLangANSIC;
87		else {
88		    errx(1, "unrecognized language: %s", argv[i]);
89		}
90	    } else if (strcmp(argv[i], "-h") == 0) {
91		if (!input)
92		    errx(1, "can't write to a header before reading something");
93		++i;
94		writeIfChanged(argv[i], lang, orConsts);
95	    } else if (strcmp(argv[i], "-new") == 0) {
96		if (catfile)
97		    errx(1, "you must specify -new before the catalog file name");
98		new = TRUE;
99	    } else if (strcmp(argv[i], "-or") == 0) {
100		orConsts = ~orConsts;
101	    } else {
102		usage();
103	    }
104        } else {
105	    if (!catfile) {
106		catfile = argv[i];
107		if (new) {
108		    if ((ofd = open(catfile, O_WRONLY|O_TRUNC|O_CREAT, 0666)) < 0)
109				errx(1, "unable to create a new %s", catfile);
110		} else if ((ofd = open(catfile, O_RDONLY)) < 0) {
111		    if ((ofd = open(catfile, O_WRONLY|O_CREAT, 0666)) < 0)
112				errx(1, "unable to create %s", catfile);
113		} else {
114		    MCReadCat(ofd);
115		    close(ofd);
116		    if ((ofd = open(catfile, O_WRONLY|O_TRUNC)) < 0)
117				errx(1, "unable to truncate %s", catfile);
118		}
119	    } else {
120		input = argv[i];
121		if ((ifd = open(input, O_RDONLY)) < 0)
122		    errx(1, "unable to read %s", input);
123		MCParse(ifd);
124		close(ifd);
125	    }
126	}
127    }
128    if (catfile) {
129	MCWriteCat(ofd);
130	exit(0);
131    } else {
132	usage();
133    }
134    return 0;
135}
136
137static void
138writeIfChanged(char *fname, int lang, int orConsts)
139{
140    char	tmpname[] = _PATH_TMP"/gencat.XXXXXX";
141    char	buf[BUFSIZ], tbuf[BUFSIZ], *cptr, *tptr;
142    int		fd, tfd;
143    int		diff = FALSE;
144    int		len, tlen;
145    struct stat	sbuf;
146
147    /* If it doesn't exist, just create it */
148    if (stat(fname, &sbuf)) {
149	if ((fd = open(fname, O_WRONLY|O_CREAT, 0666)) < 0)
150	    errx(1, "unable to create header file %s", fname);
151	MCWriteConst(fd, lang, orConsts);
152	close(fd);
153	return;
154    }
155
156    /* If it does exist, create a temp file for now */
157    if ((tfd = mkstemp(tmpname)) < 0)
158		err(1, "mkstemp");
159    unlink(tmpname);
160
161    /* Write to the temp file and rewind */
162    MCWriteConst(tfd, lang, orConsts);
163
164    /* Open the real header file */
165    if ((fd = open(fname, O_RDONLY)) < 0)
166		errx(1, "unable to read header file: %s", fname);
167
168    /* Backup to the start of the temp file */
169    if (lseek(tfd, (off_t)0, L_SET) < 0)
170		errx(1, "unable to seek in tempfile: %s", tmpname);
171
172    /* Now compare them */
173    while ((tlen = read(tfd, tbuf, BUFSIZ)) > 0) {
174	if ((len = read(fd, buf, BUFSIZ)) != tlen) {
175	    diff = TRUE;
176	    goto done;
177	}
178	for (cptr = buf, tptr = tbuf; cptr < buf+len; ++cptr, ++tptr) {
179	    if (*tptr != *cptr) {
180		diff = TRUE;
181		goto done;
182	    }
183	}
184    }
185done:
186    if (diff) {
187	if (lseek(tfd, (off_t)0, L_SET) < 0)
188	    errx(1, "unable to seek in tempfile: %s", tmpname);
189	close(fd);
190	if ((fd = open(fname, O_WRONLY|O_TRUNC)) < 0)
191	    errx(1, "unable to truncate header file: %s", fname);
192	while ((len = read(tfd, buf, BUFSIZ)) > 0) {
193	    if (write(fd, buf, (size_t)len) != len)
194		warnx("error writing to header file: %s", fname);
195	}
196    }
197    close(fd);
198    close(tfd);
199}
200