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