xref: /freebsd/usr.bin/xstr/xstr.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 
33 #include <sys/types.h>
34 
35 #include <ctype.h>
36 #include <err.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <signal.h>
40 #include <string.h>
41 #include <unistd.h>
42 
43 #include "pathnames.h"
44 
45 /*
46  * xstr - extract and hash strings in a C program
47  *
48  * Bill Joy UCB
49  * November, 1978
50  */
51 
52 #define	ignore(a)	((void) a)
53 
54 static off_t	tellpt;
55 
56 static off_t	mesgpt;
57 static char	cstrings[] =	"strings";
58 static char	*strings =	cstrings;
59 
60 static int	cflg;
61 static int	vflg;
62 static int	readstd;
63 
64 static char lastchr(char *);
65 
66 static int fgetNUL(char *, int, FILE *);
67 static int istail(char *, char *);
68 static int octdigit(char);
69 static int xgetc(FILE *);
70 
71 static off_t hashit(char *, int);
72 static off_t yankstr(char **);
73 
74 static void usage(void) __dead2;
75 
76 static void flushsh(void);
77 static void found(int, off_t, char *);
78 static void inithash(void);
79 static void onintr(int);
80 static void process(const char *);
81 static void prstr(char *);
82 static void xsdotc(void);
83 
84 int
85 main(int argc, char *argv[])
86 {
87 	int c;
88 	int fdesc;
89 
90 	while ((c = getopt(argc, argv, "-cv")) != -1)
91 		switch (c) {
92 		case '-':
93 			readstd++;
94 			break;
95 		case 'c':
96 			cflg++;
97 			break;
98 		case 'v':
99 			vflg++;
100 			break;
101 		default:
102 			usage();
103 		}
104 	argc -= optind;
105 	argv += optind;
106 
107 	if (signal(SIGINT, SIG_IGN) == SIG_DFL)
108 		signal(SIGINT, onintr);
109 	if (cflg || (argc == 0 && !readstd))
110 		inithash();
111 	else {
112 		strings = strdup(_PATH_TMP);
113 		if (strings == NULL)
114 			err(1, "strdup() failed");
115 		fdesc = mkstemp(strings);
116 		if (fdesc == -1)
117 			err(1, "Unable to create temporary file");
118 		close(fdesc);
119 	}
120 
121 	while (readstd || argc > 0) {
122 		if (freopen("x.c", "w", stdout) == NULL)
123 			err(1, "x.c");
124 		if (!readstd && freopen(argv[0], "r", stdin) == NULL)
125 			err(2, "%s", argv[0]);
126 		process("x.c");
127 		if (readstd == 0)
128 			argc--, argv++;
129 		else
130 			readstd = 0;
131 	}
132 	flushsh();
133 	if (cflg == 0)
134 		xsdotc();
135 	if (strings[0] == '/')
136 		ignore(unlink(strings));
137 	exit(0);
138 }
139 
140 static void
141 usage(void)
142 {
143 	fprintf(stderr, "usage: xstr [-cv] [-] [file ...]\n");
144 	exit (1);
145 }
146 
147 static char linebuf[BUFSIZ];
148 
149 static void
150 process(const char *name)
151 {
152 	char *cp;
153 	int c;
154 	int incomm = 0;
155 	int ret;
156 
157 	printf("extern char\txstr[];\n");
158 	for (;;) {
159 		if (fgets(linebuf, sizeof linebuf, stdin) == NULL) {
160 			if (ferror(stdin))
161 				err(3, "%s", name);
162 			break;
163 		}
164 		if (linebuf[0] == '#') {
165 			if (linebuf[1] == ' ' && isdigit(linebuf[2]))
166 				printf("#line%s", &linebuf[1]);
167 			else
168 				printf("%s", linebuf);
169 			continue;
170 		}
171 		for (cp = linebuf; (c = *cp++);) switch (c) {
172 
173 		case '"':
174 			if (incomm)
175 				goto def;
176 			if ((ret = (int) yankstr(&cp)) == -1)
177 				goto out;
178 			printf("(&xstr[%d])", ret);
179 			break;
180 
181 		case '\'':
182 			if (incomm)
183 				goto def;
184 			putchar(c);
185 			if (*cp)
186 				putchar(*cp++);
187 			break;
188 
189 		case '/':
190 			if (incomm || *cp != '*')
191 				goto def;
192 			incomm = 1;
193 			cp++;
194 			printf("/*");
195 			continue;
196 
197 		case '*':
198 			if (incomm && *cp == '/') {
199 				incomm = 0;
200 				cp++;
201 				printf("*/");
202 				continue;
203 			}
204 			goto def;
205 
206 def:
207 		default:
208 			putchar(c);
209 			break;
210 		}
211 	}
212 out:
213 	if (ferror(stdout))
214 		warn("x.c"), onintr(0);
215 }
216 
217 static off_t
218 yankstr(char **cpp)
219 {
220 	char *cp = *cpp;
221 	int c, ch;
222 	char dbuf[BUFSIZ];
223 	char *dp = dbuf;
224 	char *tp;
225 	static char tmp[] = "b\bt\tr\rn\nf\f\\\\\"\"";
226 
227 	while ((c = *cp++)) {
228 		if (dp == dbuf + sizeof(dbuf) - 3)
229 			errx(1, "message too long");
230 		switch (c) {
231 
232 		case '"':
233 			cp++;
234 			goto out;
235 
236 		case '\\':
237 			c = *cp++;
238 			if (c == 0)
239 				break;
240 			if (c == '\n') {
241 				if (fgets(linebuf, sizeof linebuf, stdin)
242 				    == NULL) {
243 					if (ferror(stdin))
244 						err(3, "x.c");
245 					return(-1);
246 				}
247 				cp = linebuf;
248 				continue;
249 			}
250 			for (tp = tmp; (ch = *tp++); tp++)
251 				if (c == ch) {
252 					c = *tp;
253 					goto gotc;
254 				}
255 			if (!octdigit(c)) {
256 				*dp++ = '\\';
257 				break;
258 			}
259 			c -= '0';
260 			if (!octdigit(*cp))
261 				break;
262 			c <<= 3, c += *cp++ - '0';
263 			if (!octdigit(*cp))
264 				break;
265 			c <<= 3, c += *cp++ - '0';
266 			break;
267 		}
268 gotc:
269 		*dp++ = c;
270 	}
271 out:
272 	*cpp = --cp;
273 	*dp = 0;
274 	return (hashit(dbuf, 1));
275 }
276 
277 static int
278 octdigit(char c)
279 {
280 	return (isdigit(c) && c != '8' && c != '9');
281 }
282 
283 static void
284 inithash(void)
285 {
286 	char buf[BUFSIZ];
287 	FILE *mesgread = fopen(strings, "r");
288 
289 	if (mesgread == NULL)
290 		return;
291 	for (;;) {
292 		mesgpt = tellpt;
293 		if (fgetNUL(buf, sizeof buf, mesgread) == 0)
294 			break;
295 		ignore(hashit(buf, 0));
296 	}
297 	ignore(fclose(mesgread));
298 }
299 
300 static int
301 fgetNUL(char *obuf, int rmdr, FILE *file)
302 {
303 	int c;
304 	char *buf = obuf;
305 
306 	while (--rmdr > 0 && (c = xgetc(file)) != 0 && c != EOF)
307 		*buf++ = c;
308 	*buf++ = 0;
309 	return ((feof(file) || ferror(file)) ? 0 : 1);
310 }
311 
312 static int
313 xgetc(FILE *file)
314 {
315 
316 	tellpt++;
317 	return (getc(file));
318 }
319 
320 #define	BUCKETS	128
321 
322 static struct hash {
323 	off_t	hpt;
324 	char	*hstr;
325 	struct	hash *hnext;
326 	short	hnew;
327 } bucket[BUCKETS];
328 
329 static off_t
330 hashit(char *str, int new)
331 {
332 	int i;
333 	struct hash *hp, *hp0;
334 
335 	hp = hp0 = &bucket[lastchr(str) & 0177];
336 	while (hp->hnext) {
337 		hp = hp->hnext;
338 		i = istail(str, hp->hstr);
339 		if (i >= 0)
340 			return (hp->hpt + i);
341 	}
342 	if ((hp = (struct hash *) calloc(1, sizeof (*hp))) == NULL)
343 		errx(8, "calloc");
344 	hp->hpt = mesgpt;
345 	if (!(hp->hstr = strdup(str)))
346 		err(1, NULL);
347 	mesgpt += strlen(hp->hstr) + 1;
348 	hp->hnext = hp0->hnext;
349 	hp->hnew = new;
350 	hp0->hnext = hp;
351 	return (hp->hpt);
352 }
353 
354 static void
355 flushsh(void)
356 {
357 	int i;
358 	struct hash *hp;
359 	FILE *mesgwrit;
360 	int old = 0, new = 0;
361 
362 	for (i = 0; i < BUCKETS; i++)
363 		for (hp = bucket[i].hnext; hp != NULL; hp = hp->hnext)
364 			if (hp->hnew)
365 				new++;
366 			else
367 				old++;
368 	if (new == 0 && old != 0)
369 		return;
370 	mesgwrit = fopen(strings, old ? "r+" : "w");
371 	if (mesgwrit == NULL)
372 		err(4, "%s", strings);
373 	for (i = 0; i < BUCKETS; i++)
374 		for (hp = bucket[i].hnext; hp != NULL; hp = hp->hnext) {
375 			found(hp->hnew, hp->hpt, hp->hstr);
376 			if (hp->hnew) {
377 				fseek(mesgwrit, hp->hpt, 0);
378 				ignore(fwrite(hp->hstr, strlen(hp->hstr) + 1, 1, mesgwrit));
379 				if (ferror(mesgwrit))
380 					err(4, "%s", strings);
381 			}
382 		}
383 	if (fclose(mesgwrit) == EOF)
384 		err(4, "%s", strings);
385 }
386 
387 static void
388 found(int new, off_t off, char *str)
389 {
390 	if (vflg == 0)
391 		return;
392 	if (!new)
393 		fprintf(stderr, "found at %d:", (int) off);
394 	else
395 		fprintf(stderr, "new at %d:", (int) off);
396 	prstr(str);
397 	fprintf(stderr, "\n");
398 }
399 
400 static void
401 prstr(char *cp)
402 {
403 	int c;
404 
405 	while ((c = (*cp++ & 0377)))
406 		if (c < ' ')
407 			fprintf(stderr, "^%c", c + '`');
408 		else if (c == 0177)
409 			fprintf(stderr, "^?");
410 		else if (c > 0200)
411 			fprintf(stderr, "\\%03o", c);
412 		else
413 			fprintf(stderr, "%c", c);
414 }
415 
416 static void
417 xsdotc(void)
418 {
419 	FILE *strf = fopen(strings, "r");
420 	FILE *xdotcf;
421 
422 	if (strf == NULL)
423 		err(5, "%s", strings);
424 	xdotcf = fopen("xs.c", "w");
425 	if (xdotcf == NULL)
426 		err(6, "xs.c");
427 	fprintf(xdotcf, "char\txstr[] = {\n");
428 	for (;;) {
429 		int i, c;
430 
431 		for (i = 0; i < 8; i++) {
432 			c = getc(strf);
433 			if (ferror(strf)) {
434 				warn("%s", strings);
435 				onintr(0);
436 			}
437 			if (feof(strf)) {
438 				fprintf(xdotcf, "\n");
439 				goto out;
440 			}
441 			fprintf(xdotcf, "0x%02x,", c);
442 		}
443 		fprintf(xdotcf, "\n");
444 	}
445 out:
446 	fprintf(xdotcf, "};\n");
447 	ignore(fclose(xdotcf));
448 	ignore(fclose(strf));
449 }
450 
451 static char
452 lastchr(char *cp)
453 {
454 
455 	while (cp[0] && cp[1])
456 		cp++;
457 	return (*cp);
458 }
459 
460 static int
461 istail(char *str, char *of)
462 {
463 	int d = strlen(of) - strlen(str);
464 
465 	if (d < 0 || strcmp(&of[d], str) != 0)
466 		return (-1);
467 	return (d);
468 }
469 
470 static void
471 onintr(int dummy __unused)
472 {
473 
474 	ignore(signal(SIGINT, SIG_IGN));
475 	if (strings[0] == '/')
476 		ignore(unlink(strings));
477 	ignore(unlink("x.c"));
478 	ignore(unlink("xs.c"));
479 	exit(7);
480 }
481