Annotation of src/usr.bin/lex/filter.c, Revision 1.7
1.7 ! krw 1: /* $OpenBSD: filter.c,v 1.6 2015/11/19 23:28:03 tedu Exp $ */
1.1 tedu 2:
3: /* filter - postprocessing of flex output through filters */
4:
5: /* This file is part of flex. */
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:
11: /* 1. Redistributions of source code must retain the above copyright */
12: /* notice, this list of conditions and the following disclaimer. */
13: /* 2. Redistributions in binary form must reproduce the above copyright */
14: /* notice, this list of conditions and the following disclaimer in the */
15: /* documentation and/or other materials provided with the distribution. */
16:
17: /* Neither the name of the University nor the names of its contributors */
18: /* may be used to endorse or promote products derived from this software */
19: /* without specific prior written permission. */
20:
21: /* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR */
22: /* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED */
23: /* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR */
24: /* PURPOSE. */
25:
26: #include "flexdef.h"
1.4 tedu 27: static const char *check_4_gnu_m4 =
28: "m4_dnl ifdef(`__gnu__', ,"
29: "`errprint(Flex requires GNU M4. Set the PATH or set the M4 environment variable to its path name.)"
30: " m4exit(2)')\n";
1.1 tedu 31:
32:
33: /** global chain. */
34: struct filter *output_chain = NULL;
35:
36: /* Allocate and initialize an external filter.
37: * @param chain the current chain or NULL for new chain
38: * @param cmd the command to execute.
39: * @param ... a NULL terminated list of (const char*) arguments to command,
40: * not including argv[0].
41: * @return newest filter in chain
42: */
1.4 tedu 43: struct filter *
44: filter_create_ext(struct filter * chain, const char *cmd,
45: ...)
1.1 tedu 46: {
47: struct filter *f;
1.4 tedu 48: int max_args;
1.1 tedu 49: const char *s;
50: va_list ap;
51:
52: /* allocate and initialize new filter */
1.6 tedu 53: f = malloc(sizeof(struct filter));
1.1 tedu 54: if (!f)
1.5 tedu 55: flexerror(_("malloc failed (f) in filter_create_ext"));
1.4 tedu 56: memset(f, 0, sizeof(*f));
1.1 tedu 57: f->filter_func = NULL;
58: f->extra = NULL;
59: f->next = NULL;
60: f->argc = 0;
61:
62: if (chain != NULL) {
63: /* append f to end of chain */
64: while (chain->next)
65: chain = chain->next;
66: chain->next = f;
67: }
68: /* allocate argv, and populate it with the argument list. */
69: max_args = 8;
1.5 tedu 70: f->argv = malloc(sizeof(char *) * (max_args + 1));
1.1 tedu 71: if (!f->argv)
1.5 tedu 72: flexerror(_("malloc failed (f->argv) in filter_create_ext"));
1.1 tedu 73: f->argv[f->argc++] = cmd;
74:
1.4 tedu 75: va_start(ap, cmd);
76: while ((s = va_arg(ap, const char *)) != NULL) {
1.1 tedu 77: if (f->argc >= max_args) {
78: max_args += 8;
1.5 tedu 79: f->argv = realloc(f->argv,
1.4 tedu 80: sizeof(char *) * (max_args + 1));
1.1 tedu 81: }
82: f->argv[f->argc++] = s;
83: }
84: f->argv[f->argc] = NULL;
85:
1.4 tedu 86: va_end(ap);
1.1 tedu 87: return f;
88: }
89:
90: /* Allocate and initialize an internal filter.
91: * @param chain the current chain or NULL for new chain
92: * @param filter_func The function that will perform the filtering.
93: * filter_func should return 0 if successful, and -1
94: * if an error occurs -- or it can simply exit().
95: * @param extra optional user-defined data to pass to the filter.
96: * @return newest filter in chain
97: */
1.4 tedu 98: struct filter *
99: filter_create_int(struct filter * chain,
100: int (*filter_func) (struct filter *),
101: void *extra)
1.1 tedu 102: {
103: struct filter *f;
104:
105: /* allocate and initialize new filter */
1.6 tedu 106: f = malloc(sizeof(struct filter));
1.1 tedu 107: if (!f)
1.5 tedu 108: flexerror(_("malloc failed in filter_create_int"));
1.4 tedu 109: memset(f, 0, sizeof(*f));
1.1 tedu 110: f->next = NULL;
111: f->argc = 0;
112: f->argv = NULL;
113:
114: f->filter_func = filter_func;
115: f->extra = extra;
116:
117: if (chain != NULL) {
118: /* append f to end of chain */
119: while (chain->next)
120: chain = chain->next;
121: chain->next = f;
122: }
123: return f;
124: }
125:
126: /** Fork and exec entire filter chain.
127: * @param chain The head of the chain.
128: * @return true on success.
129: */
1.4 tedu 130: bool
131: filter_apply_chain(struct filter * chain)
1.1 tedu 132: {
1.4 tedu 133: int pid, pipes[2];
1.1 tedu 134:
1.4 tedu 135: /*
136: * Tricky recursion, since we want to begin the chain at the END.
137: * Why? Because we need all the forked processes to be children of
138: * the main flex process.
1.1 tedu 139: */
140: if (chain)
1.4 tedu 141: filter_apply_chain(chain->next);
1.1 tedu 142: else
143: return true;
144:
1.4 tedu 145: /*
146: * Now we are the right-most unprocessed link in the chain.
1.1 tedu 147: */
148:
1.4 tedu 149: fflush(stdout);
150: fflush(stderr);
1.1 tedu 151:
152:
1.4 tedu 153: if (pipe(pipes) == -1)
154: flexerror(_("pipe failed"));
1.1 tedu 155:
1.4 tedu 156: if ((pid = fork()) == -1)
157: flexerror(_("fork failed"));
1.1 tedu 158:
159: if (pid == 0) {
160: /* child */
161:
1.4 tedu 162: /*
163: * We need stdin (the FILE* stdin) to connect to this new
164: * pipe. There is no portable way to set stdin to a new file
165: * descriptor, as stdin is not an lvalue on some systems
166: * (BSD). So we dup the new pipe onto the stdin descriptor
167: * and use a no-op fseek to sync the stream. This is a Hail
168: * Mary situation. It seems to work.
169: */
170: close(pipes[1]);
1.3 tedu 171: clearerr(stdin);
1.4 tedu 172: if (dup2(pipes[0], fileno(stdin)) == -1)
173: flexfatal(_("dup2(pipes[0],0)"));
174: close(pipes[0]);
175: fseek(stdin, 0, SEEK_CUR);
1.1 tedu 176:
177: /* run as a filter, either internally or by exec */
178: if (chain->filter_func) {
1.4 tedu 179: int r;
1.1 tedu 180:
1.4 tedu 181: if ((r = chain->filter_func(chain)) == -1)
182: flexfatal(_("filter_func failed"));
183: exit(0);
184: } else {
185: execvp(chain->argv[0],
186: (char **const) (chain->argv));
187: lerrsf_fatal(_("exec of %s failed"),
1.3 tedu 188: chain->argv[0]);
1.1 tedu 189: }
190:
1.4 tedu 191: exit(1);
1.1 tedu 192: }
193: /* Parent */
1.4 tedu 194: close(pipes[0]);
195: if (dup2(pipes[1], fileno(stdout)) == -1)
196: flexfatal(_("dup2(pipes[1],1)"));
197: close(pipes[1]);
198: fseek(stdout, 0, SEEK_CUR);
1.1 tedu 199:
200: return true;
201: }
202:
203: /** Truncate the chain to max_len number of filters.
204: * @param chain the current chain.
205: * @param max_len the maximum length of the chain.
206: * @return the resulting length of the chain.
207: */
1.4 tedu 208: int
209: filter_truncate(struct filter * chain, int max_len)
1.1 tedu 210: {
1.4 tedu 211: int len = 1;
1.1 tedu 212:
213: if (!chain)
214: return 0;
215:
216: while (chain->next && len < max_len) {
217: chain = chain->next;
218: ++len;
219: }
220:
221: chain->next = NULL;
222: return len;
223: }
224:
225: /** Splits the chain in order to write to a header file.
226: * Similar in spirit to the 'tee' program.
227: * The header file name is in extra.
228: * @return 0 (zero) on success, and -1 on failure.
229: */
1.4 tedu 230: int
231: filter_tee_header(struct filter * chain)
1.1 tedu 232: {
1.4 tedu 233: /*
234: * This function reads from stdin and writes to both the C file and
235: * the header file at the same time.
1.1 tedu 236: */
237:
238: const int readsz = 512;
1.4 tedu 239: char *buf;
240: int to_cfd = -1;
241: FILE *to_c = NULL, *to_h = NULL;
242: bool write_header;
1.1 tedu 243:
244: write_header = (chain->extra != NULL);
245:
1.4 tedu 246: /*
247: * Store a copy of the stdout pipe, which is already piped to C file
1.1 tedu 248: * through the running chain. Then create a new pipe to the H file as
249: * stdout, and fork the rest of the chain again.
250: */
251:
1.4 tedu 252: if ((to_cfd = dup(1)) == -1)
253: flexfatal(_("dup(1) failed"));
254: to_c = fdopen(to_cfd, "w");
1.1 tedu 255:
256: if (write_header) {
1.4 tedu 257: if (freopen((char *) chain->extra, "w", stdout) == NULL)
258: flexfatal(_("freopen(headerfilename) failed"));
1.1 tedu 259:
1.4 tedu 260: filter_apply_chain(chain->next);
1.1 tedu 261: to_h = stdout;
262: }
1.4 tedu 263: /*
264: * Now to_c is a pipe to the C branch, and to_h is a pipe to the H
265: * branch.
1.1 tedu 266: */
267:
268: if (write_header) {
1.4 tedu 269: fputs(check_4_gnu_m4, to_h);
270: fputs("m4_changecom`'m4_dnl\n", to_h);
271: fputs("m4_changequote`'m4_dnl\n", to_h);
272: fputs("m4_changequote([[,]])[[]]m4_dnl\n", to_h);
273: fputs("m4_define([[M4_YY_NOOP]])[[]]m4_dnl\n", to_h);
274: fputs("m4_define( [[M4_YY_IN_HEADER]],[[]])m4_dnl\n",
275: to_h);
276: fprintf(to_h, "#ifndef %sHEADER_H\n", prefix);
277: fprintf(to_h, "#define %sHEADER_H 1\n", prefix);
278: fprintf(to_h, "#define %sIN_HEADER 1\n\n", prefix);
279: fprintf(to_h,
280: "m4_define( [[M4_YY_OUTFILE_NAME]],[[%s]])m4_dnl\n",
281: headerfilename ? headerfilename : "<stdout>");
1.1 tedu 282:
283: }
1.4 tedu 284: fputs(check_4_gnu_m4, to_c);
285: fputs("m4_changecom`'m4_dnl\n", to_c);
286: fputs("m4_changequote`'m4_dnl\n", to_c);
287: fputs("m4_changequote([[,]])[[]]m4_dnl\n", to_c);
288: fputs("m4_define([[M4_YY_NOOP]])[[]]m4_dnl\n", to_c);
289: fprintf(to_c, "m4_define( [[M4_YY_OUTFILE_NAME]],[[%s]])m4_dnl\n",
290: outfilename ? outfilename : "<stdout>");
1.1 tedu 291:
1.6 tedu 292: buf = malloc(readsz);
1.1 tedu 293: if (!buf)
1.5 tedu 294: flexerror(_("malloc failed in filter_tee_header"));
1.4 tedu 295: while (fgets(buf, readsz, stdin)) {
296: fputs(buf, to_c);
1.1 tedu 297: if (write_header)
1.4 tedu 298: fputs(buf, to_h);
1.1 tedu 299: }
300:
301: if (write_header) {
1.4 tedu 302: fprintf(to_h, "\n");
1.1 tedu 303:
1.4 tedu 304: /*
305: * write a fake line number. It will get fixed by the linedir
306: * filter.
307: */
308: fprintf(to_h, "#line 4000 \"M4_YY_OUTFILE_NAME\"\n");
309:
310: fprintf(to_h, "#undef %sIN_HEADER\n", prefix);
311: fprintf(to_h, "#endif /* %sHEADER_H */\n", prefix);
312: fputs("m4_undefine( [[M4_YY_IN_HEADER]])m4_dnl\n", to_h);
313:
314: fflush(to_h);
315: if (ferror(to_h))
316: lerrsf(_("error writing output file %s"),
317: (char *) chain->extra);
318:
319: else if (fclose(to_h))
320: lerrsf(_("error closing output file %s"),
321: (char *) chain->extra);
1.1 tedu 322: }
1.4 tedu 323: fflush(to_c);
324: if (ferror(to_c))
325: lerrsf(_("error writing output file %s"),
326: outfilename ? outfilename : "<stdout>");
327:
328: else if (fclose(to_c))
329: lerrsf(_("error closing output file %s"),
330: outfilename ? outfilename : "<stdout>");
1.1 tedu 331:
1.4 tedu 332: while (wait(0) > 0);
1.1 tedu 333:
1.4 tedu 334: exit(0);
1.1 tedu 335: return 0;
336: }
337:
338: /** Adjust the line numbers in the #line directives of the generated scanner.
339: * After the m4 expansion, the line numbers are incorrect since the m4 macros
340: * can add or remove lines. This only adjusts line numbers for generated code,
341: * not user code. This also happens to be a good place to squeeze multiple
342: * blank lines into a single blank line.
343: */
1.4 tedu 344: int
345: filter_fix_linedirs(struct filter * chain)
1.1 tedu 346: {
1.4 tedu 347: char *buf;
1.1 tedu 348: const int readsz = 512;
1.4 tedu 349: int lineno = 1;
350: bool in_gen = true; /* in generated code */
351: bool last_was_blank = false;
1.1 tedu 352:
353: if (!chain)
354: return 0;
355:
1.6 tedu 356: buf = malloc(readsz);
1.1 tedu 357: if (!buf)
1.5 tedu 358: flexerror(_("malloc failed in filter_fix_linedirs"));
1.1 tedu 359:
1.4 tedu 360: while (fgets(buf, readsz, stdin)) {
1.1 tedu 361:
362: regmatch_t m[10];
363:
364: /* Check for #line directive. */
365: if (buf[0] == '#'
1.4 tedu 366: && regexec(®ex_linedir, buf, 3, m, 0) == 0) {
1.1 tedu 367:
1.4 tedu 368: int num;
369: char *fname;
1.1 tedu 370:
371: /* extract the line number and filename */
1.4 tedu 372: num = regmatch_strtol(&m[1], buf, NULL, 0);
373: fname = regmatch_dup(&m[2], buf);
1.1 tedu 374:
1.4 tedu 375: if (strcmp(fname,
376: outfilename ? outfilename : "<stdout>") == 0 ||
377: strcmp(fname, headerfilename ? headerfilename :
378: "<stdout>") == 0) {
1.1 tedu 379:
1.4 tedu 380: char *s1, *s2;
381: char filename[MAXLINE];
1.1 tedu 382:
383: s1 = fname;
384: s2 = filename;
385:
386: while ((s2 - filename) < (MAXLINE - 1) && *s1) {
387: /* Escape the backslash */
388: if (*s1 == '\\')
389: *s2++ = '\\';
390: /* Escape the double quote */
391: if (*s1 == '\"')
392: *s2++ = '\\';
393: /* Copy the character as usual */
394: *s2++ = *s1++;
395: }
396:
397: *s2 = '\0';
398:
399: /* Adjust the line directives. */
400: in_gen = true;
1.4 tedu 401: snprintf(buf, readsz, "#line %d \"%s\"\n",
402: lineno + 1, filename);
403: } else {
404: /*
405: * it's a #line directive for code we didn't
406: * write
407: */
1.1 tedu 408: in_gen = false;
409: }
410:
1.4 tedu 411: free(fname);
1.1 tedu 412: last_was_blank = false;
413: }
414: /* squeeze blank lines from generated code */
1.3 tedu 415: else if (in_gen &&
1.4 tedu 416: regexec(®ex_blank_line, buf, 0, NULL, 0) == 0) {
1.1 tedu 417: if (last_was_blank)
418: continue;
419: else
420: last_was_blank = true;
1.4 tedu 421: } else {
1.1 tedu 422: /* it's a line of normal, non-empty code. */
423: last_was_blank = false;
424: }
425:
1.4 tedu 426: fputs(buf, stdout);
1.1 tedu 427: lineno++;
428: }
1.4 tedu 429: fflush(stdout);
430: if (ferror(stdout))
431: lerrsf(_("error writing output file %s"),
432: outfilename ? outfilename : "<stdout>");
433:
434: else if (fclose(stdout))
435: lerrsf(_("error closing output file %s"),
436: outfilename ? outfilename : "<stdout>");
1.1 tedu 437:
438: return 0;
439: }