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