xref: /freebsd/usr.bin/mkstr/mkstr.c (revision 8aac90f18aef7c9eea906c3ff9a001ca7b94f375)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1980, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <err.h>
33 #include <errno.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 
38 #define	ungetchar(c)	ungetc(c, stdin)
39 
40 /*
41  * mkstr - create a string error message file by massaging C source
42  *
43  * Bill Joy UCB August 1977
44  *
45  * Modified March 1978 to hash old messages to be able to recompile
46  * without addding messages to the message file (usually)
47  *
48  * Based on an earlier program conceived by Bill Joy and Chuck Haley
49  *
50  * Program to create a string error message file
51  * from a group of C programs.  Arguments are the name
52  * of the file where the strings are to be placed, the
53  * prefix of the new files where the processed source text
54  * is to be placed, and the files to be processed.
55  *
56  * The program looks for 'error("' in the source stream.
57  * Whenever it finds this, the following characters from the '"'
58  * to a '"' are replaced by 'seekpt' where seekpt is a
59  * pointer into the error message file.
60  * If the '(' is not immediately followed by a '"' no change occurs.
61  *
62  * The optional '-' causes strings to be added at the end of the
63  * existing error message file for recompilation of single routines.
64  */
65 
66 static FILE	*mesgread, *mesgwrite;
67 static char	name[100], *np;
68 
69 void copystr(void);
70 int fgetNUL(char *, int, FILE *);
71 unsigned hashit(char *, int, unsigned);
72 void inithash(void);
73 int match(const char *);
74 int octdigit(char);
75 void process(void);
76 void usage(void);
77 
78 int
79 main(int argc, char *argv[])
80 {
81 	char addon = 0;
82 	size_t namelen;
83 
84 	argc--, argv++;
85 	if (argc > 1 && argv[0][0] == '-')
86 		addon++, argc--, argv++;
87 	if (argc < 3)
88 		usage();
89 	mesgwrite = fopen(argv[0], addon ? "a" : "w");
90 	if (mesgwrite == NULL)
91 		err(1, "%s", argv[0]);
92 	mesgread = fopen(argv[0], "r");
93 	if (mesgread == NULL)
94 		err(1, "%s", argv[0]);
95 	inithash();
96 	argc--, argv++;
97 	namelen = strlcpy(name, argv[0], sizeof(name));
98 	if (namelen >= sizeof(name)) {
99 		errno = ENAMETOOLONG;
100 		err(1, "%s", argv[0]);
101 	}
102 	np = name + namelen;
103 	argc--, argv++;
104 	do {
105 		if (strlcpy(np, argv[0], sizeof(name) - namelen) >=
106 		    sizeof(name) - namelen) {
107 			errno = ENAMETOOLONG;
108 			err(1, "%s%s", name, argv[0]);
109 		}
110 		if (freopen(name, "w", stdout) == NULL)
111 			err(1, "%s", name);
112 		if (freopen(argv[0], "r", stdin) == NULL)
113 			err(1, "%s", argv[0]);
114 		process();
115 		argc--, argv++;
116 	} while (argc > 0);
117 	exit(0);
118 }
119 
120 void
121 usage(void)
122 {
123 	fprintf(stderr, "usage: mkstr [-] mesgfile prefix file ...\n");
124 	exit(1);
125 }
126 
127 void
128 process(void)
129 {
130 	int c;
131 
132 	for (;;) {
133 		c = getchar();
134 		if (c == EOF)
135 			return;
136 		if (c != 'e') {
137 			putchar(c);
138 			continue;
139 		}
140 		if (match("error(")) {
141 			printf("error(");
142 			c = getchar();
143 			if (c != '"')
144 				putchar(c);
145 			else
146 				copystr();
147 		}
148 	}
149 }
150 
151 int
152 match(const char *ocp)
153 {
154 	const char *cp;
155 	int c;
156 
157 	for (cp = ocp + 1; *cp; cp++) {
158 		c = getchar();
159 		if (c != *cp) {
160 			while (ocp < cp)
161 				putchar(*ocp++);
162 			ungetchar(c);
163 			return (0);
164 		}
165 	}
166 	return (1);
167 }
168 
169 void
170 copystr(void)
171 {
172 	int c, ch;
173 	char buf[512];
174 	char *cp = buf;
175 
176 	for (;;) {
177 		if (cp == buf + sizeof(buf) - 2)
178 			errx(1, "message too long");
179 		c = getchar();
180 		if (c == EOF)
181 			break;
182 		switch (c) {
183 
184 		case '"':
185 			*cp++ = 0;
186 			goto out;
187 		case '\\':
188 			c = getchar();
189 			switch (c) {
190 
191 			case 'b':
192 				c = '\b';
193 				break;
194 			case 't':
195 				c = '\t';
196 				break;
197 			case 'r':
198 				c = '\r';
199 				break;
200 			case 'n':
201 				c = '\n';
202 				break;
203 			case '\n':
204 				continue;
205 			case 'f':
206 				c = '\f';
207 				break;
208 			case '0':
209 				c = 0;
210 				break;
211 			case '\\':
212 				break;
213 			default:
214 				if (!octdigit(c))
215 					break;
216 				c -= '0';
217 				ch = getchar();
218 				if (!octdigit(ch))
219 					break;
220 				c <<= 7, c += ch - '0';
221 				ch = getchar();
222 				if (!octdigit(ch))
223 					break;
224 				c <<= 3, c+= ch - '0', ch = -1;
225 				break;
226 			}
227 		}
228 		*cp++ = c;
229 	}
230 out:
231 	*cp = 0;
232 	printf("%d", hashit(buf, 1, 0));
233 }
234 
235 int
236 octdigit(char c)
237 {
238 
239 	return (c >= '0' && c <= '7');
240 }
241 
242 void
243 inithash(void)
244 {
245 	char buf[512];
246 	int mesgpt = 0;
247 
248 	rewind(mesgread);
249 	while (fgetNUL(buf, sizeof buf, mesgread) != 0) {
250 		hashit(buf, 0, mesgpt);
251 		mesgpt += strlen(buf) + 2;
252 	}
253 }
254 
255 #define	NBUCKETS	511
256 
257 static struct	hash {
258 	long	hval;
259 	unsigned hpt;
260 	struct	hash *hnext;
261 } *bucket[NBUCKETS];
262 
263 unsigned
264 hashit(char *str, int really, unsigned fakept)
265 {
266 	int i;
267 	struct hash *hp;
268 	char buf[512];
269 	long hashval = 0;
270 	char *cp;
271 
272 	if (really)
273 		fflush(mesgwrite);
274 	for (cp = str; *cp;)
275 		hashval = (hashval << 1) + *cp++;
276 	i = hashval % NBUCKETS;
277 	if (i < 0)
278 		i += NBUCKETS;
279 	if (really != 0)
280 		for (hp = bucket[i]; hp != 0; hp = hp->hnext)
281 		if (hp->hval == hashval) {
282 			fseek(mesgread, (long) hp->hpt, 0);
283 			fgetNUL(buf, sizeof buf, mesgread);
284 /*
285 			fprintf(stderr, "Got (from %d) %s\n", hp->hpt, buf);
286 */
287 			if (strcmp(buf, str) == 0)
288 				break;
289 		}
290 	if (!really || hp == 0) {
291 		hp = (struct hash *) calloc(1, sizeof *hp);
292 		if (hp == NULL)
293 			err(1, NULL);
294 		hp->hnext = bucket[i];
295 		hp->hval = hashval;
296 		hp->hpt = really ? ftell(mesgwrite) : fakept;
297 		if (really) {
298 			fwrite(str, sizeof (char), strlen(str) + 1, mesgwrite);
299 			fwrite("\n", sizeof (char), 1, mesgwrite);
300 		}
301 		bucket[i] = hp;
302 	}
303 /*
304 	fprintf(stderr, "%s hashed to %ld at %d\n", str, hp->hval, hp->hpt);
305 */
306 	return (hp->hpt);
307 }
308 
309 int
310 fgetNUL(char *obuf, int rmdr, FILE *file)
311 {
312 	int c;
313 	char *buf = obuf;
314 
315 	while (--rmdr > 0 && (c = getc(file)) != 0 && c != EOF)
316 		*buf++ = c;
317 	*buf++ = 0;
318 	getc(file);
319 	return ((feof(file) || ferror(file)) ? 0 : 1);
320 }
321