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