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