filter.c revision 296373
178189Sbrian/* filter - postprocessing of flex output through filters */ 278189Sbrian 378189Sbrian/* This file is part of flex. */ 478189Sbrian 578189Sbrian/* Redistribution and use in source and binary forms, with or without */ 66059Samurai/* modification, are permitted provided that the following conditions */ 778189Sbrian/* are met: */ 878189Sbrian 978189Sbrian/* 1. Redistributions of source code must retain the above copyright */ 1078189Sbrian/* notice, this list of conditions and the following disclaimer. */ 1178189Sbrian/* 2. Redistributions in binary form must reproduce the above copyright */ 1278189Sbrian/* notice, this list of conditions and the following disclaimer in the */ 1378189Sbrian/* documentation and/or other materials provided with the distribution. */ 1478189Sbrian 156059Samurai/* Neither the name of the University nor the names of its contributors */ 1678189Sbrian/* may be used to endorse or promote products derived from this software */ 1778189Sbrian/* without specific prior written permission. */ 1878189Sbrian 1978189Sbrian/* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR */ 2078189Sbrian/* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED */ 2178189Sbrian/* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR */ 2278189Sbrian/* PURPOSE. */ 2378189Sbrian 2478189Sbrian#include "flexdef.h" 2578189Sbrianstatic const char * check_4_gnu_m4 = 2678189Sbrian "m4_dnl ifdef(`__gnu__', ," 276059Samurai "`errprint(Flex requires GNU M4. Set the PATH or set the M4 environment variable to its path name.)" 2850479Speter " m4exit(2)')\n"; 296059Samurai 3078189Sbrian 3143313Sbrian/** global chain. */ 3230715Sbrianstruct filter *output_chain = NULL; 3336285Sbrian 3436285Sbrian/* Allocate and initialize an external filter. 3581634Sbrian * @param chain the current chain or NULL for new chain 3636285Sbrian * @param cmd the command to execute. 3730715Sbrian * @param ... a NULL terminated list of (const char*) arguments to command, 3836285Sbrian * not including argv[0]. 3930715Sbrian * @return newest filter in chain 40121702Sru */ 4130715Sbrianstruct filter *filter_create_ext (struct filter *chain, const char *cmd, 4243525Sbrian ...) 4330715Sbrian{ 4430715Sbrian struct filter *f; 45121702Sru int max_args; 46121702Sru const char *s; 47174870Sdes va_list ap; 48121702Sru 49121702Sru /* allocate and initialize new filter */ 50121702Sru f = (struct filter *) flex_alloc (sizeof (struct filter)); 51121702Sru if (!f) 5246686Sbrian flexerror (_("flex_alloc failed (f) in filter_create_ext")); 5330715Sbrian memset (f, 0, sizeof (*f)); 5430715Sbrian f->filter_func = NULL; 5543525Sbrian f->extra = NULL; 5630715Sbrian f->next = NULL; 576059Samurai f->argc = 0; 5836285Sbrian 5936285Sbrian if (chain != NULL) { 6036285Sbrian /* append f to end of chain */ 6138557Sbrian while (chain->next) 6238557Sbrian chain = chain->next; 6381634Sbrian chain->next = f; 646059Samurai } 656735Samurai 6636285Sbrian 6736285Sbrian /* allocate argv, and populate it with the argument list. */ 6836285Sbrian max_args = 8; 6936285Sbrian f->argv = 7036285Sbrian (const char **) flex_alloc (sizeof (char *) * 7113389Sphk (max_args + 1)); 7246686Sbrian if (!f->argv) 7336285Sbrian flexerror (_("flex_alloc failed (f->argv) in filter_create_ext")); 7436285Sbrian f->argv[f->argc++] = cmd; 7543313Sbrian 7643313Sbrian va_start (ap, cmd); 7743313Sbrian while ((s = va_arg (ap, const char *)) != NULL) { 7843525Sbrian if (f->argc >= max_args) { 7943525Sbrian max_args += 8; 8043525Sbrian f->argv = 8143525Sbrian (const char **) flex_realloc (f->argv, 8243525Sbrian sizeof (char 8381634Sbrian *) * 8481634Sbrian (max_args + 8536285Sbrian 1)); 866059Samurai } 8736285Sbrian f->argv[f->argc++] = s; 8844106Sbrian } 8913389Sphk f->argv[f->argc] = NULL; 9044106Sbrian 9144106Sbrian va_end (ap); 9236285Sbrian return f; 9336285Sbrian} 9436285Sbrian 9536285Sbrian/* Allocate and initialize an internal filter. 9644106Sbrian * @param chain the current chain or NULL for new chain 9744106Sbrian * @param filter_func The function that will perform the filtering. 9836285Sbrian * filter_func should return 0 if successful, and -1 9936285Sbrian * if an error occurs -- or it can simply exit(). 10036285Sbrian * @param extra optional user-defined data to pass to the filter. 10136285Sbrian * @return newest filter in chain 1026735Samurai */ 1036735Samuraistruct filter *filter_create_int (struct filter *chain, 104174870Sdes int (*filter_func) (struct filter *), 10536285Sbrian void *extra) 106121702Sru{ 107121702Sru struct filter *f; 108121702Sru 109121702Sru /* allocate and initialize new filter */ 110121702Sru f = (struct filter *) flex_alloc (sizeof (struct filter)); 111121702Sru if (!f) 112121702Sru flexerror (_("flex_alloc failed in filter_create_int")); 113121702Sru memset (f, 0, sizeof (*f)); 114121702Sru f->next = NULL; 115121702Sru f->argc = 0; 116121702Sru f->argv = NULL; 117121702Sru 118121702Sru f->filter_func = filter_func; 119174870Sdes f->extra = extra; 120121702Sru 121121702Sru if (chain != NULL) { 12236285Sbrian /* append f to end of chain */ 12328679Sbrian while (chain->next) 12436285Sbrian chain = chain->next; 125121702Sru chain->next = f; 12636285Sbrian } 12736285Sbrian 12836285Sbrian return f; 129231994Skevlo} 13036285Sbrian 131231994Skevlo/** Fork and exec entire filter chain. 13236285Sbrian * @param chain The head of the chain. 133231994Skevlo * @return true on success. 13436285Sbrian */ 13536285Sbrianbool filter_apply_chain (struct filter * chain) 136121702Sru{ 137121702Sru int pid, pipes[2]; 138121702Sru 139121702Sru 140121702Sru /* Tricky recursion, since we want to begin the chain 141121702Sru * at the END. Why? Because we need all the forked processes 142174870Sdes * to be children of the main flex process. 143121702Sru */ 144121702Sru if (chain) 145174870Sdes filter_apply_chain (chain->next); 146121702Sru else 147121702Sru return true; 148121702Sru 149121702Sru /* Now we are the right-most unprocessed link in the chain. 150121702Sru */ 151174870Sdes 152121702Sru fflush (stdout); 153121702Sru fflush (stderr); 154121702Sru 155121702Sru 156121702Sru if (pipe (pipes) == -1) 157121702Sru flexerror (_("pipe failed")); 15836285Sbrian 15936285Sbrian if ((pid = fork ()) == -1) 16036285Sbrian flexerror (_("fork failed")); 16136285Sbrian 16236285Sbrian if (pid == 0) { 16336285Sbrian /* child */ 16438174Sbrian 16536285Sbrian /* We need stdin (the FILE* stdin) to connect to this new pipe. 1666735Samurai * There is no portable way to set stdin to a new file descriptor, 16754914Sbrian * as stdin is not an lvalue on some systems (BSD). 16889072Sbrian * So we dup the new pipe onto the stdin descriptor and use a no-op fseek 16989072Sbrian * to sync the stream. This is a Hail Mary situation. It seems to work. 17038174Sbrian */ 17138174Sbrian close (pipes[1]); 17238174Sbrianclearerr(stdin); 17389072Sbrian if (dup2 (pipes[0], fileno (stdin)) == -1) 17489072Sbrian flexfatal (_("dup2(pipes[0],0)")); 17538174Sbrian close (pipes[0]); 17654914Sbrian fseek (stdin, 0, SEEK_CUR); 17738174Sbrian 17838174Sbrian /* run as a filter, either internally or by exec */ 17938174Sbrian if (chain->filter_func) { 18038174Sbrian int r; 18155145Sbrian 18254914Sbrian if ((r = chain->filter_func (chain)) == -1) 18338174Sbrian flexfatal (_("filter_func failed")); 18438174Sbrian exit (0); 18538174Sbrian } 18643313Sbrian else { 18743313Sbrian execvp (chain->argv[0], 18838174Sbrian (char **const) (chain->argv)); 18938174Sbrian lerrsf_fatal ( _("exec of %s failed"), 19038174Sbrian chain->argv[0]); 19143313Sbrian } 19238174Sbrian 19338174Sbrian exit (1); 19489072Sbrian } 19589072Sbrian 19689072Sbrian /* Parent */ 19789072Sbrian close (pipes[0]); 19889072Sbrian if (dup2 (pipes[1], fileno (stdout)) == -1) 19989072Sbrian flexfatal (_("dup2(pipes[1],1)")); 20089072Sbrian close (pipes[1]); 20189072Sbrian fseek (stdout, 0, SEEK_CUR); 20238174Sbrian 20338174Sbrian return true; 20438174Sbrian} 20538174Sbrian 20638174Sbrian/** Truncate the chain to max_len number of filters. 20738174Sbrian * @param chain the current chain. 20838174Sbrian * @param max_len the maximum length of the chain. 20938174Sbrian * @return the resulting length of the chain. 21038174Sbrian */ 21138174Sbrianint filter_truncate (struct filter *chain, int max_len) 21254914Sbrian{ 21389072Sbrian int len = 1; 21489072Sbrian 2156735Samurai if (!chain) 21636285Sbrian return 0; 21743313Sbrian 21836285Sbrian while (chain->next && len < max_len) { 21936285Sbrian chain = chain->next; 22036285Sbrian ++len; 22143313Sbrian } 22293767Shosokawa 22393767Shosokawa chain->next = NULL; 22443313Sbrian return len; 22543313Sbrian} 22643313Sbrian 22743313Sbrian/** Splits the chain in order to write to a header file. 22843313Sbrian * Similar in spirit to the 'tee' program. 22943313Sbrian * The header file name is in extra. 23043313Sbrian * @return 0 (zero) on success, and -1 on failure. 23143313Sbrian */ 23236285Sbrianint filter_tee_header (struct filter *chain) 23336285Sbrian{ 23489072Sbrian /* This function reads from stdin and writes to both the C file and the 23589072Sbrian * header file at the same time. 23636285Sbrian */ 23754914Sbrian 23836285Sbrian const int readsz = 512; 23936285Sbrian char *buf; 24037763Sbrian int to_cfd = -1; 24136285Sbrian FILE *to_c = NULL, *to_h = NULL; 24255145Sbrian bool write_header; 24354914Sbrian 24436285Sbrian write_header = (chain->extra != NULL); 24536285Sbrian 24637763Sbrian /* Store a copy of the stdout pipe, which is already piped to C file 24743313Sbrian * through the running chain. Then create a new pipe to the H file as 24843313Sbrian * stdout, and fork the rest of the chain again. 24943313Sbrian */ 25043313Sbrian 25143313Sbrian if ((to_cfd = dup (1)) == -1) 25243313Sbrian flexfatal (_("dup(1) failed")); 25343313Sbrian to_c = fdopen (to_cfd, "w"); 25443313Sbrian 25543313Sbrian if (write_header) { 25643313Sbrian if (freopen ((char *) chain->extra, "w", stdout) == NULL) 25743313Sbrian flexfatal (_("freopen(headerfilename) failed")); 25843313Sbrian 25943313Sbrian filter_apply_chain (chain->next); 26043313Sbrian to_h = stdout; 26137763Sbrian } 2626735Samurai 26389072Sbrian /* Now to_c is a pipe to the C branch, and to_h is a pipe to the H branch. 26489072Sbrian */ 26589072Sbrian 26689072Sbrian if (write_header) { 26789072Sbrian fputs (check_4_gnu_m4, to_h); 26889072Sbrian fputs ("m4_changecom`'m4_dnl\n", to_h); 26989072Sbrian fputs ("m4_changequote`'m4_dnl\n", to_h); 27089072Sbrian fputs ("m4_changequote([[,]])[[]]m4_dnl\n", to_h); 27136285Sbrian fputs ("m4_define([[M4_YY_NOOP]])[[]]m4_dnl\n", to_h); 2726735Samurai fputs ("m4_define( [[M4_YY_IN_HEADER]],[[]])m4_dnl\n", 27336285Sbrian to_h); 27436285Sbrian fprintf (to_h, "#ifndef %sHEADER_H\n", prefix); 27536285Sbrian fprintf (to_h, "#define %sHEADER_H 1\n", prefix); 27643313Sbrian fprintf (to_h, "#define %sIN_HEADER 1\n\n", prefix); 27736285Sbrian fprintf (to_h, 27836285Sbrian "m4_define( [[M4_YY_OUTFILE_NAME]],[[%s]])m4_dnl\n", 27943313Sbrian headerfilename ? headerfilename : "<stdout>"); 28043313Sbrian 28143313Sbrian } 28243313Sbrian 28343313Sbrian fputs (check_4_gnu_m4, to_c); 28443313Sbrian fputs ("m4_changecom`'m4_dnl\n", to_c); 28536285Sbrian fputs ("m4_changequote`'m4_dnl\n", to_c); 28636285Sbrian fputs ("m4_changequote([[,]])[[]]m4_dnl\n", to_c); 2876735Samurai fputs ("m4_define([[M4_YY_NOOP]])[[]]m4_dnl\n", to_c); 2886735Samurai fprintf (to_c, "m4_define( [[M4_YY_OUTFILE_NAME]],[[%s]])m4_dnl\n", 2896059Samurai outfilename ? outfilename : "<stdout>"); 290134789Sbrian 2916059Samurai buf = (char *) flex_alloc (readsz); 29236285Sbrian if (!buf) 29336285Sbrian flexerror (_("flex_alloc failed in filter_tee_header")); 2946059Samurai while (fgets (buf, readsz, stdin)) { 29554914Sbrian fputs (buf, to_c); 29689072Sbrian if (write_header) 29789072Sbrian fputs (buf, to_h); 2986059Samurai } 29936285Sbrian 30089072Sbrian if (write_header) { 30154914Sbrian fprintf (to_h, "\n"); 30236285Sbrian 30336285Sbrian /* write a fake line number. It will get fixed by the linedir filter. */ 30454914Sbrian fprintf (to_h, "#line 4000 \"M4_YY_OUTFILE_NAME\"\n"); 30536285Sbrian 30636285Sbrian fprintf (to_h, "#undef %sIN_HEADER\n", prefix); 30736285Sbrian fprintf (to_h, "#endif /* %sHEADER_H */\n", prefix); 30836285Sbrian fputs ("m4_undefine( [[M4_YY_IN_HEADER]])m4_dnl\n", to_h); 30955145Sbrian 31054914Sbrian fflush (to_h); 31136285Sbrian if (ferror (to_h)) 31236285Sbrian lerrsf (_("error writing output file %s"), 31343313Sbrian (char *) chain->extra); 31443313Sbrian 31543313Sbrian else if (fclose (to_h)) 3166059Samurai lerrsf (_("error closing output file %s"), 3176059Samurai (char *) chain->extra); 3186059Samurai } 31936285Sbrian 32089072Sbrian fflush (to_c); 32189072Sbrian if (ferror (to_c)) 32289072Sbrian lerrsf (_("error writing output file %s"), 32389072Sbrian outfilename ? outfilename : "<stdout>"); 32489072Sbrian 32589072Sbrian else if (fclose (to_c)) 32689072Sbrian lerrsf (_("error closing output file %s"), 32789072Sbrian outfilename ? outfilename : "<stdout>"); 32889072Sbrian 32989072Sbrian while (wait (0) > 0) ; 33089072Sbrian 33189072Sbrian exit (0); 33236285Sbrian return 0; 33336285Sbrian} 33443313Sbrian 33536285Sbrian/** Adjust the line numbers in the #line directives of the generated scanner. 33636285Sbrian * After the m4 expansion, the line numbers are incorrect since the m4 macros 33736285Sbrian * can add or remove lines. This only adjusts line numbers for generated code, 3386059Samurai * not user code. This also happens to be a good place to squeeze multiple 3396059Samurai * blank lines into a single blank line. 3406059Samurai */ 341134789Sbrianint filter_fix_linedirs (struct filter *chain) 3426059Samurai{ 34336285Sbrian char *buf; 34436285Sbrian const int readsz = 512; 3456059Samurai int lineno = 1; 34654914Sbrian bool in_gen = true; /* in generated code */ 34732267Sbrian bool last_was_blank = false; 34889072Sbrian 34947843Sbrian if (!chain) 3506059Samurai return 0; 35136285Sbrian 3526059Samurai buf = (char *) flex_alloc (readsz); 35328679Sbrian if (!buf) 35436285Sbrian flexerror (_("flex_alloc failed in filter_fix_linedirs")); 35589072Sbrian 35654914Sbrian while (fgets (buf, readsz, stdin)) { 35731962Sbrian 35854914Sbrian regmatch_t m[10]; 3596059Samurai 3606059Samurai /* Check for #line directive. */ 36147843Sbrian if (buf[0] == '#' 36247843Sbrian && regexec (®ex_linedir, buf, 3, m, 0) == 0) { 36347843Sbrian 36431962Sbrian int num; 36555145Sbrian char *fname; 36654914Sbrian 3676059Samurai /* extract the line number and filename */ 3686059Samurai num = regmatch_strtol (&m[1], buf, NULL, 0); 36943313Sbrian fname = regmatch_dup (&m[2], buf); 37036285Sbrian 37136285Sbrian if (strcmp (fname, 3726059Samurai outfilename ? outfilename : "<stdout>") 3736059Samurai == 0 37489072Sbrian || strcmp (fname, 37589072Sbrian headerfilename ? headerfilename : "<stdout>") 37689072Sbrian == 0) { 37789072Sbrian 37889072Sbrian char *s1, *s2; 37989072Sbrian char filename[MAXLINE]; 38089072Sbrian 38189072Sbrian s1 = fname; 38289072Sbrian s2 = filename; 3836059Samurai 38428679Sbrian while ((s2 - filename) < (MAXLINE - 1) && *s1) { 3856059Samurai /* Escape the backslash */ 3866735Samurai if (*s1 == '\\') 3876735Samurai *s2++ = '\\'; 38831343Sbrian /* Escape the double quote */ 3896735Samurai if (*s1 == '\"') 39031343Sbrian *s2++ = '\\'; 3916735Samurai /* Copy the character as usual */ 39236285Sbrian *s2++ = *s1++; 3936735Samurai } 39443693Sbrian 39543693Sbrian *s2 = '\0'; 39636285Sbrian 39743525Sbrian /* Adjust the line directives. */ 39843525Sbrian in_gen = true; 39943525Sbrian snprintf (buf, readsz, "#line %d \"%s\"\n", 40043525Sbrian lineno + 1, filename); 4016735Samurai } 4026735Samurai else { 4036735Samurai /* it's a #line directive for code we didn't write */ 40443693Sbrian in_gen = false; 40543693Sbrian } 4066735Samurai 40743693Sbrian free (fname); 40844305Sbrian last_was_blank = false; 40944305Sbrian } 41044305Sbrian 41143693Sbrian /* squeeze blank lines from generated code */ 41243693Sbrian else if (in_gen 41343693Sbrian && regexec (®ex_blank_line, buf, 0, NULL, 41443693Sbrian 0) == 0) { 41536285Sbrian if (last_was_blank) 4166735Samurai continue; 41736285Sbrian else 41843693Sbrian last_was_blank = true; 41936285Sbrian } 42036285Sbrian 42136285Sbrian else { 42236285Sbrian /* it's a line of normal, non-empty code. */ 42344305Sbrian last_was_blank = false; 42443693Sbrian } 42544305Sbrian 4266735Samurai fputs (buf, stdout); 42743693Sbrian lineno++; 42836285Sbrian } 4296735Samurai fflush (stdout); 4306735Samurai if (ferror (stdout)) 4316735Samurai lerrsf (_("error writing output file %s"), 43236285Sbrian outfilename ? outfilename : "<stdout>"); 4336735Samurai 43436285Sbrian else if (fclose (stdout)) 4356735Samurai lerrsf (_("error closing output file %s"), 43643693Sbrian outfilename ? outfilename : "<stdout>"); 43743693Sbrian 43843693Sbrian return 0; 43943693Sbrian} 440134789Sbrian 44143693Sbrian/* vim:set expandtab cindent tabstop=4 softtabstop=4 shiftwidth=4 textwidth=0: */ 44254912Sbrian