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