From 2ea243b80a5a443c533e5f621b0d5a52790a97dc Mon Sep 17 00:00:00 2001 From: Will Estes Date: Tue, 25 Mar 2003 16:39:08 +0000 Subject: merge millaway's m4 branch work --- filter.c | 380 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 380 insertions(+) create mode 100644 filter.c (limited to 'filter.c') diff --git a/filter.c b/filter.c new file mode 100644 index 0000000..40c8cf2 --- /dev/null +++ b/filter.c @@ -0,0 +1,380 @@ +/* filter - postprocessing of flex output through filters */ + +/* This file is part of flex. */ + +/* Redistribution and use in source and binary forms, with or without */ +/* modification, are permitted provided that the following conditions */ +/* are met: */ + +/* 1. Redistributions of source code must retain the above copyright */ +/* notice, this list of conditions and the following disclaimer. */ +/* 2. Redistributions in binary form must reproduce the above copyright */ +/* notice, this list of conditions and the following disclaimer in the */ +/* documentation and/or other materials provided with the distribution. */ + +/* Neither the name of the University nor the names of its contributors */ +/* may be used to endorse or promote products derived from this software */ +/* without specific prior written permission. */ + +/* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR */ +/* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED */ +/* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR */ +/* PURPOSE. */ + +#include "flexdef.h" + +/** global chain. */ +struct filter *output_chain = NULL; + +/* Allocate and initialize an external filter. + * @param chain the current chain or NULL for new chain + * @param cmd the command to execute. + * @param ... a NULL terminated list of (const char*) arguments to command, + * not including argv[0]. + * @return newest filter in chain + */ +struct filter *filter_create_ext (struct filter *chain, const char *cmd, + ...) +{ + struct filter *f; + int max_args; + const char *s; + va_list ap; + + /* allocate and initialize new filter */ + f = (struct filter *) flex_alloc (sizeof (struct filter)); + memset (f, 0, sizeof (*f)); + f->filter_func = NULL; + f->extra = NULL; + f->next = NULL; + f->argc = 0; + + if (chain != NULL) { + /* append f to end of chain */ + while (chain->next) + chain = chain->next; + chain->next = f; + } + + + /* allocate argv, and populate it with the argument list. */ + max_args = 8; + f->argv = + (const char **) flex_alloc (sizeof (char *) * + (max_args + 1)); + f->argv[f->argc++] = cmd; + + va_start (ap, cmd); + while ((s = va_arg (ap, const char *)) != NULL) { + if (f->argc >= max_args) { + max_args += 8; + f->argv = + (const char **) flex_realloc (f->argv, + sizeof (char + *) * + (max_args + + 1)); + } + f->argv[f->argc++] = s; + } + f->argv[f->argc] = NULL; + + va_end (ap); + return f; +} + +/* Allocate and initialize an internal filter. + * @param chain the current chain or NULL for new chain + * @param filter_func The function that will perform the filtering. + * filter_func should return 0 if successful, and -1 + * if an error occurs -- or it can simply exit(). + * @param extra optional user-defined data to pass to the filter. + * @return newest filter in chain + */ +struct filter *filter_create_int (struct filter *chain, + int (*filter_func) (struct filter *), + void *extra) +{ + struct filter *f; + + /* allocate and initialize new filter */ + f = (struct filter *) flex_alloc (sizeof (struct filter)); + memset (f, 0, sizeof (*f)); + f->next = NULL; + f->argc = 0; + f->argv = NULL; + + f->filter_func = filter_func; + f->extra = extra; + + if (chain != NULL) { + /* append f to end of chain */ + while (chain->next) + chain = chain->next; + chain->next = f; + } + + return f; +} + +/** Fork and exec entire filter chain. + * @param chain The head of the chain. + * @return true on success. + */ +bool filter_apply_chain (struct filter * chain) +{ + int pid, pipes[2]; + + /* Tricky recursion, since we want to begin the chain + * at the END. Why? Because we need all the forked processes + * to be children of the main flex process. + */ + if (chain) + filter_apply_chain (chain->next); + else + return true; + + /* Now we are the right-most unprocessed link in the chain. + */ + + fflush (stdout); + fflush (stderr); + + if (pipe (pipes) == -1) + flexerror (_("pipe failed")); + + if ((pid = fork ()) == -1) + flexerror (_("fork failed")); + + if (pid == 0) { + /* child */ + close (pipes[1]); + if (dup2 (pipes[0], 0) == -1) + flexfatal (_("dup2(pipes[0],0)")); + close (pipes[0]); + + /* run as a filter, either internally or by exec */ + if (chain->filter_func) { + int r; + + /* setup streams again */ + if ((stdin = fdopen (0, "r")) == NULL) + flexfatal (_("fdopen(0) failed")); + if ((stdout = fdopen (1, "w")) == NULL) + flexfatal (_("fdopen(1) failed")); + + if ((r = chain->filter_func (chain)) == -1) + flexfatal (_("filter_func failed")); + exit (0); + } + else { + execvp (chain->argv[0], + (char **const) (chain->argv)); + flexfatal (_("exec failed")); + } + + exit (1); + } + + /* Parent */ + close (pipes[0]); + if (dup2 (pipes[1], 1) == -1) + flexfatal (_("dup2(pipes[1],1)")); + close (pipes[1]); + if ((stdout = fdopen (1, "w")) == NULL) + flexfatal (_("fdopen(1) failed")); + + return true; +} + +/** Truncate the chain to max_len number of filters. + * @param chain the current chain. + * @param max_len the maximum length of the chain. + * @return the resulting length of the chain. + */ +int filter_truncate (struct filter *chain, int max_len) +{ + int len = 1; + + if (!chain) + return 0; + + while (chain->next && len < max_len) { + chain = chain->next; + ++len; + } + + chain->next = NULL; + return len; +} + +/** Splits the chain in order to write to a header file. + * Similar in spirit to the 'tee' program. + * The header file name is in extra. + * @return 0 (zero) on success, and -1 on failure. + */ +int filter_tee_header (struct filter *chain) +{ + /* This function reads from stdin and writes to both the C file and the + * header file at the same time. + */ + + const int readsz = 512; + char *buf; + int to_cfd = -1; + FILE *to_c = NULL, *to_h = NULL; + bool write_header; + + write_header = (chain->extra != NULL); + + /* Store a copy of the stdout pipe, which is already piped to C file + * through the running chain. Then create a new pipe to the H file as + * stdout, and fork the rest of the chain again. + */ + + if ((to_cfd = dup (1)) == -1) + flexfatal (_("dup(1) failed")); + to_c = fdopen (to_cfd, "w"); + + if (write_header) { + if (freopen ((char *) chain->extra, "w", stdout) == NULL) + flexfatal (_("freopen(headerfilename) failed")); + + filter_apply_chain (chain->next); + to_h = stdout; + } + + /* Now to_c is a pipe to the C branch, and to_h is a pipe to the H branch. + */ + + if (write_header) { + fputs ("m4_changecom`'m4_dnl\n", to_h); + fputs ("m4_changequote`'m4_dnl\n", to_h); + fputs ("m4_changequote([[,]])[[]]m4_dnl\n", to_h); + fputs ("m4_define( [[M4_YY_IN_HEADER]],[[]])m4_dnl\n", + to_h); + fprintf (to_h, "#ifndef %sHEADER_H\n", prefix); + fprintf (to_h, "#define %sHEADER_H 1\n", prefix); + fprintf (to_h, "#define %sIN_HEADER 1\n\n", prefix); + fprintf (to_h, + "m4_define( [[M4_YY_OUTFILE_NAME]],[[%s]])m4_dnl\n", + headerfilename ? headerfilename : ""); + + } + + fputs ("m4_changecom`'m4_dnl\n", to_c); + fputs ("m4_changequote`'m4_dnl\n", to_c); + fputs ("m4_changequote([[,]])[[]]m4_dnl\n", to_c); + fprintf (to_c, "m4_define( [[M4_YY_OUTFILE_NAME]],[[%s]])m4_dnl\n", + outfilename ? outfilename : ""); + + buf = (char *) flex_alloc (readsz); + while (fgets (buf, readsz, stdin)) { + fputs (buf, to_c); + if (write_header) + fputs (buf, to_h); + } + + if (write_header) { + fprintf (to_h, "\n"); + + /* write a fake line number. It will get fixed by the linedir filter. */ + fprintf (to_h, "#line 4000 \"M4_YY_OUTFILE_NAME\"\n"); + + fprintf (to_h, "#undef %sIN_HEADER\n", prefix); + fprintf (to_h, "#endif /* %sHEADER_H */\n", prefix); + fputs ("m4_undefine( [[M4_YY_IN_HEADER]])m4_dnl\n", to_h); + + fflush (to_h); + fclose (to_h); + } + + fflush (to_c); + fclose (to_c); + + + while (wait (0) > 0) ; + + exit (0); + return 0; +} + +/** Adjust the line numbers in the #line directives of the generated scanner. + * After the m4 expansion, the line numbers are incorrect since the m4 macros + * can add or remove lines. This only adjusts line numbers for generated code, + * not user code. This also happens to be a good place to squeeze multiple + * blank lines into a single blank line. + */ +int filter_fix_linedirs (struct filter *chain) +{ + char *buf; + const int readsz = 512; + int lineno = 1; + bool in_gen = true; /* in generated code */ + bool last_was_blank = false; + + if (!chain) + return 0; + + buf = (char *) flex_alloc (readsz); + + while (fgets (buf, readsz, stdin)) { + + regmatch_t m[10]; + + /* Check for #line directive. */ + if (buf[0] == '#' + && regexec (®ex_linedir, buf, 3, m, 0) == 0) { + + int num; + char *fname; + + /* extract the line number and filename */ + num = regmatch_strtol (&m[1], buf, NULL, 0); + fname = regmatch_dup (&m[2], buf); + + if (strcmp + (fname, outfilename ? outfilename : "") + == 0 + || strcmp (fname, + headerfilename ? headerfilename : + "") == 0) { + /* Adjust the line directives. */ + in_gen = true; + sprintf (buf, "#line %d \"%s\"\n", + lineno + 1, fname); + free (fname); + + } + else { + /* it's a #line directive for code we didn't write */ + in_gen = false; + } + + last_was_blank = false; + } + + /* squeeze blank lines from generated code */ + else if (in_gen + && regexec (®ex_blank_line, buf, 0, NULL, + 0) == 0) { + if (last_was_blank) + continue; + else + last_was_blank = true; + } + + else { + /* it's a line of normal, non-empty code. */ + last_was_blank = false; + } + + fputs (buf, stdout); + lineno++; + } + fflush (stdout); + + return 0; +} + +/* vim:set expandtab cindent tabstop=4 softtabstop=4 shiftwidth=4 textwidth=0: */ -- cgit v1.2.3