version 1.3, 2015/11/19 22:25:53 |
version 1.4, 2015/11/19 22:52:40 |
|
|
/* PURPOSE. */ |
/* PURPOSE. */ |
|
|
#include "flexdef.h" |
#include "flexdef.h" |
static const char * check_4_gnu_m4 = |
static const char *check_4_gnu_m4 = |
"m4_dnl ifdef(`__gnu__', ," |
"m4_dnl ifdef(`__gnu__', ," |
"`errprint(Flex requires GNU M4. Set the PATH or set the M4 environment variable to its path name.)" |
"`errprint(Flex requires GNU M4. Set the PATH or set the M4 environment variable to its path name.)" |
" m4exit(2)')\n"; |
" m4exit(2)')\n"; |
|
|
|
|
/** global chain. */ |
/** global chain. */ |
|
|
* not including argv[0]. |
* not including argv[0]. |
* @return newest filter in chain |
* @return newest filter in chain |
*/ |
*/ |
struct filter *filter_create_ext (struct filter *chain, const char *cmd, |
struct filter * |
...) |
filter_create_ext(struct filter * chain, const char *cmd, |
|
...) |
{ |
{ |
struct filter *f; |
struct filter *f; |
int max_args; |
int max_args; |
const char *s; |
const char *s; |
va_list ap; |
va_list ap; |
|
|
/* allocate and initialize new filter */ |
/* allocate and initialize new filter */ |
f = (struct filter *) flex_alloc (sizeof (struct filter)); |
f = (struct filter *) flex_alloc(sizeof(struct filter)); |
if (!f) |
if (!f) |
flexerror (_("flex_alloc failed (f) in filter_create_ext")); |
flexerror(_("flex_alloc failed (f) in filter_create_ext")); |
memset (f, 0, sizeof (*f)); |
memset(f, 0, sizeof(*f)); |
f->filter_func = NULL; |
f->filter_func = NULL; |
f->extra = NULL; |
f->extra = NULL; |
f->next = NULL; |
f->next = NULL; |
|
|
chain = chain->next; |
chain = chain->next; |
chain->next = f; |
chain->next = f; |
} |
} |
|
|
|
|
/* allocate argv, and populate it with the argument list. */ |
/* allocate argv, and populate it with the argument list. */ |
max_args = 8; |
max_args = 8; |
f->argv = flex_alloc (sizeof (char *) * (max_args + 1)); |
f->argv = flex_alloc(sizeof(char *) * (max_args + 1)); |
if (!f->argv) |
if (!f->argv) |
flexerror (_("flex_alloc failed (f->argv) in filter_create_ext")); |
flexerror(_("flex_alloc failed (f->argv) in filter_create_ext")); |
f->argv[f->argc++] = cmd; |
f->argv[f->argc++] = cmd; |
|
|
va_start (ap, cmd); |
va_start(ap, cmd); |
while ((s = va_arg (ap, const char *)) != NULL) { |
while ((s = va_arg(ap, const char *)) != NULL) { |
if (f->argc >= max_args) { |
if (f->argc >= max_args) { |
max_args += 8; |
max_args += 8; |
f->argv = flex_realloc (f->argv, |
f->argv = flex_realloc(f->argv, |
sizeof (char *) * (max_args + 1)); |
sizeof(char *) * (max_args + 1)); |
} |
} |
f->argv[f->argc++] = s; |
f->argv[f->argc++] = s; |
} |
} |
f->argv[f->argc] = NULL; |
f->argv[f->argc] = NULL; |
|
|
va_end (ap); |
va_end(ap); |
return f; |
return f; |
} |
} |
|
|
|
|
* @param extra optional user-defined data to pass to the filter. |
* @param extra optional user-defined data to pass to the filter. |
* @return newest filter in chain |
* @return newest filter in chain |
*/ |
*/ |
struct filter *filter_create_int (struct filter *chain, |
struct filter * |
int (*filter_func) (struct filter *), |
filter_create_int(struct filter * chain, |
void *extra) |
int (*filter_func) (struct filter *), |
|
void *extra) |
{ |
{ |
struct filter *f; |
struct filter *f; |
|
|
/* allocate and initialize new filter */ |
/* allocate and initialize new filter */ |
f = (struct filter *) flex_alloc (sizeof (struct filter)); |
f = (struct filter *) flex_alloc(sizeof(struct filter)); |
if (!f) |
if (!f) |
flexerror (_("flex_alloc failed in filter_create_int")); |
flexerror(_("flex_alloc failed in filter_create_int")); |
memset (f, 0, sizeof (*f)); |
memset(f, 0, sizeof(*f)); |
f->next = NULL; |
f->next = NULL; |
f->argc = 0; |
f->argc = 0; |
f->argv = NULL; |
f->argv = NULL; |
|
|
chain = chain->next; |
chain = chain->next; |
chain->next = f; |
chain->next = f; |
} |
} |
|
|
return f; |
return f; |
} |
} |
|
|
|
|
* @param chain The head of the chain. |
* @param chain The head of the chain. |
* @return true on success. |
* @return true on success. |
*/ |
*/ |
bool filter_apply_chain (struct filter * chain) |
bool |
|
filter_apply_chain(struct filter * chain) |
{ |
{ |
int pid, pipes[2]; |
int pid, pipes[2]; |
int r; |
int r; |
const int readsz = 512; |
const int readsz = 512; |
char *buf; |
char *buf; |
|
|
|
|
/* Tricky recursion, since we want to begin the chain |
/* |
* at the END. Why? Because we need all the forked processes |
* Tricky recursion, since we want to begin the chain at the END. |
* to be children of the main flex process. |
* Why? Because we need all the forked processes to be children of |
|
* the main flex process. |
*/ |
*/ |
if (chain) |
if (chain) |
filter_apply_chain (chain->next); |
filter_apply_chain(chain->next); |
else |
else |
return true; |
return true; |
|
|
/* Now we are the right-most unprocessed link in the chain. |
/* |
|
* Now we are the right-most unprocessed link in the chain. |
*/ |
*/ |
|
|
fflush (stdout); |
fflush(stdout); |
fflush (stderr); |
fflush(stderr); |
|
|
|
|
if (pipe (pipes) == -1) |
if (pipe(pipes) == -1) |
flexerror (_("pipe failed")); |
flexerror(_("pipe failed")); |
|
|
if ((pid = fork ()) == -1) |
if ((pid = fork()) == -1) |
flexerror (_("fork failed")); |
flexerror(_("fork failed")); |
|
|
if (pid == 0) { |
if (pid == 0) { |
/* child */ |
/* child */ |
|
|
/* We need stdin (the FILE* stdin) to connect to this new pipe. |
/* |
* There is no portable way to set stdin to a new file descriptor, |
* We need stdin (the FILE* stdin) to connect to this new |
* as stdin is not an lvalue on some systems (BSD). |
* pipe. There is no portable way to set stdin to a new file |
* So we dup the new pipe onto the stdin descriptor and use a no-op fseek |
* descriptor, as stdin is not an lvalue on some systems |
* to sync the stream. This is a Hail Mary situation. It seems to work. |
* (BSD). So we dup the new pipe onto the stdin descriptor |
*/ |
* and use a no-op fseek to sync the stream. This is a Hail |
close (pipes[1]); |
* Mary situation. It seems to work. |
|
*/ |
|
close(pipes[1]); |
clearerr(stdin); |
clearerr(stdin); |
if (dup2 (pipes[0], fileno (stdin)) == -1) |
if (dup2(pipes[0], fileno(stdin)) == -1) |
flexfatal (_("dup2(pipes[0],0)")); |
flexfatal(_("dup2(pipes[0],0)")); |
close (pipes[0]); |
close(pipes[0]); |
fseek (stdin, 0, SEEK_CUR); |
fseek(stdin, 0, SEEK_CUR); |
|
|
/* run as a filter, either internally or by exec */ |
/* run as a filter, either internally or by exec */ |
if (chain->filter_func) { |
if (chain->filter_func) { |
int r; |
int r; |
|
|
if ((r = chain->filter_func (chain)) == -1) |
if ((r = chain->filter_func(chain)) == -1) |
flexfatal (_("filter_func failed")); |
flexfatal(_("filter_func failed")); |
exit (0); |
exit(0); |
} |
} else { |
else { |
execvp(chain->argv[0], |
execvp (chain->argv[0], |
(char **const) (chain->argv)); |
(char **const) (chain->argv)); |
lerrsf_fatal(_("exec of %s failed"), |
lerrsf_fatal ( _("exec of %s failed"), |
|
chain->argv[0]); |
chain->argv[0]); |
} |
} |
|
|
exit (1); |
exit(1); |
} |
} |
|
|
/* Parent */ |
/* Parent */ |
close (pipes[0]); |
close(pipes[0]); |
if (dup2 (pipes[1], fileno (stdout)) == -1) |
if (dup2(pipes[1], fileno(stdout)) == -1) |
flexfatal (_("dup2(pipes[1],1)")); |
flexfatal(_("dup2(pipes[1],1)")); |
close (pipes[1]); |
close(pipes[1]); |
fseek (stdout, 0, SEEK_CUR); |
fseek(stdout, 0, SEEK_CUR); |
|
|
return true; |
return true; |
} |
} |
|
|
* @param max_len the maximum length of the chain. |
* @param max_len the maximum length of the chain. |
* @return the resulting length of the chain. |
* @return the resulting length of the chain. |
*/ |
*/ |
int filter_truncate (struct filter *chain, int max_len) |
int |
|
filter_truncate(struct filter * chain, int max_len) |
{ |
{ |
int len = 1; |
int len = 1; |
|
|
if (!chain) |
if (!chain) |
return 0; |
return 0; |
|
|
* The header file name is in extra. |
* The header file name is in extra. |
* @return 0 (zero) on success, and -1 on failure. |
* @return 0 (zero) on success, and -1 on failure. |
*/ |
*/ |
int filter_tee_header (struct filter *chain) |
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. |
* This function reads from stdin and writes to both the C file and |
|
* the header file at the same time. |
*/ |
*/ |
|
|
const int readsz = 512; |
const int readsz = 512; |
char *buf; |
char *buf; |
int to_cfd = -1; |
int to_cfd = -1; |
FILE *to_c = NULL, *to_h = NULL; |
FILE *to_c = NULL, *to_h = NULL; |
bool write_header; |
bool write_header; |
|
|
write_header = (chain->extra != NULL); |
write_header = (chain->extra != NULL); |
|
|
/* Store a copy of the stdout pipe, which is already piped to C file |
/* |
|
* 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 |
* through the running chain. Then create a new pipe to the H file as |
* stdout, and fork the rest of the chain again. |
* stdout, and fork the rest of the chain again. |
*/ |
*/ |
|
|
if ((to_cfd = dup (1)) == -1) |
if ((to_cfd = dup(1)) == -1) |
flexfatal (_("dup(1) failed")); |
flexfatal(_("dup(1) failed")); |
to_c = fdopen (to_cfd, "w"); |
to_c = fdopen(to_cfd, "w"); |
|
|
if (write_header) { |
if (write_header) { |
if (freopen ((char *) chain->extra, "w", stdout) == NULL) |
if (freopen((char *) chain->extra, "w", stdout) == NULL) |
flexfatal (_("freopen(headerfilename) failed")); |
flexfatal(_("freopen(headerfilename) failed")); |
|
|
filter_apply_chain (chain->next); |
filter_apply_chain(chain->next); |
to_h = stdout; |
to_h = stdout; |
} |
} |
|
/* |
/* Now to_c is a pipe to the C branch, and to_h is a pipe to the H branch. |
* Now to_c is a pipe to the C branch, and to_h is a pipe to the H |
|
* branch. |
*/ |
*/ |
|
|
if (write_header) { |
if (write_header) { |
fputs (check_4_gnu_m4, to_h); |
fputs(check_4_gnu_m4, to_h); |
fputs ("m4_changecom`'m4_dnl\n", to_h); |
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_changequote([[,]])[[]]m4_dnl\n", to_h); |
fputs("m4_changequote([[,]])[[]]m4_dnl\n", to_h); |
fputs ("m4_define([[M4_YY_NOOP]])[[]]m4_dnl\n", to_h); |
fputs("m4_define([[M4_YY_NOOP]])[[]]m4_dnl\n", to_h); |
fputs ("m4_define( [[M4_YY_IN_HEADER]],[[]])m4_dnl\n", |
fputs("m4_define( [[M4_YY_IN_HEADER]],[[]])m4_dnl\n", |
to_h); |
to_h); |
fprintf (to_h, "#ifndef %sHEADER_H\n", prefix); |
fprintf(to_h, "#ifndef %sHEADER_H\n", prefix); |
fprintf (to_h, "#define %sHEADER_H 1\n", prefix); |
fprintf(to_h, "#define %sHEADER_H 1\n", prefix); |
fprintf (to_h, "#define %sIN_HEADER 1\n\n", prefix); |
fprintf(to_h, "#define %sIN_HEADER 1\n\n", prefix); |
fprintf (to_h, |
fprintf(to_h, |
"m4_define( [[M4_YY_OUTFILE_NAME]],[[%s]])m4_dnl\n", |
"m4_define( [[M4_YY_OUTFILE_NAME]],[[%s]])m4_dnl\n", |
headerfilename ? headerfilename : "<stdout>"); |
headerfilename ? headerfilename : "<stdout>"); |
|
|
} |
} |
|
fputs(check_4_gnu_m4, to_c); |
|
fputs("m4_changecom`'m4_dnl\n", to_c); |
|
fputs("m4_changequote`'m4_dnl\n", to_c); |
|
fputs("m4_changequote([[,]])[[]]m4_dnl\n", to_c); |
|
fputs("m4_define([[M4_YY_NOOP]])[[]]m4_dnl\n", to_c); |
|
fprintf(to_c, "m4_define( [[M4_YY_OUTFILE_NAME]],[[%s]])m4_dnl\n", |
|
outfilename ? outfilename : "<stdout>"); |
|
|
fputs (check_4_gnu_m4, to_c); |
buf = (char *) flex_alloc(readsz); |
fputs ("m4_changecom`'m4_dnl\n", to_c); |
|
fputs ("m4_changequote`'m4_dnl\n", to_c); |
|
fputs ("m4_changequote([[,]])[[]]m4_dnl\n", to_c); |
|
fputs ("m4_define([[M4_YY_NOOP]])[[]]m4_dnl\n", to_c); |
|
fprintf (to_c, "m4_define( [[M4_YY_OUTFILE_NAME]],[[%s]])m4_dnl\n", |
|
outfilename ? outfilename : "<stdout>"); |
|
|
|
buf = (char *) flex_alloc (readsz); |
|
if (!buf) |
if (!buf) |
flexerror (_("flex_alloc failed in filter_tee_header")); |
flexerror(_("flex_alloc failed in filter_tee_header")); |
while (fgets (buf, readsz, stdin)) { |
while (fgets(buf, readsz, stdin)) { |
fputs (buf, to_c); |
fputs(buf, to_c); |
if (write_header) |
if (write_header) |
fputs (buf, to_h); |
fputs(buf, to_h); |
} |
} |
|
|
if (write_header) { |
if (write_header) { |
fprintf (to_h, "\n"); |
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"); |
* 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, "#undef %sIN_HEADER\n", prefix); |
fprintf (to_h, "#endif /* %sHEADER_H */\n", prefix); |
fprintf(to_h, "#endif /* %sHEADER_H */\n", prefix); |
fputs ("m4_undefine( [[M4_YY_IN_HEADER]])m4_dnl\n", to_h); |
fputs("m4_undefine( [[M4_YY_IN_HEADER]])m4_dnl\n", to_h); |
|
|
fflush (to_h); |
fflush(to_h); |
if (ferror (to_h)) |
if (ferror(to_h)) |
lerrsf (_("error writing output file %s"), |
lerrsf(_("error writing output file %s"), |
(char *) chain->extra); |
(char *) chain->extra); |
|
|
else if (fclose (to_h)) |
else if (fclose(to_h)) |
lerrsf (_("error closing output file %s"), |
lerrsf(_("error closing output file %s"), |
(char *) chain->extra); |
(char *) chain->extra); |
} |
} |
|
fflush(to_c); |
|
if (ferror(to_c)) |
|
lerrsf(_("error writing output file %s"), |
|
outfilename ? outfilename : "<stdout>"); |
|
|
fflush (to_c); |
else if (fclose(to_c)) |
if (ferror (to_c)) |
lerrsf(_("error closing output file %s"), |
lerrsf (_("error writing output file %s"), |
outfilename ? outfilename : "<stdout>"); |
outfilename ? outfilename : "<stdout>"); |
|
|
|
else if (fclose (to_c)) |
while (wait(0) > 0); |
lerrsf (_("error closing output file %s"), |
|
outfilename ? outfilename : "<stdout>"); |
|
|
|
while (wait (0) > 0) ; |
exit(0); |
|
|
exit (0); |
|
return 0; |
return 0; |
} |
} |
|
|
|
|
* not user code. This also happens to be a good place to squeeze multiple |
* not user code. This also happens to be a good place to squeeze multiple |
* blank lines into a single blank line. |
* blank lines into a single blank line. |
*/ |
*/ |
int filter_fix_linedirs (struct filter *chain) |
int |
|
filter_fix_linedirs(struct filter * chain) |
{ |
{ |
char *buf; |
char *buf; |
const int readsz = 512; |
const int readsz = 512; |
int lineno = 1; |
int lineno = 1; |
bool in_gen = true; /* in generated code */ |
bool in_gen = true; /* in generated code */ |
bool last_was_blank = false; |
bool last_was_blank = false; |
|
|
if (!chain) |
if (!chain) |
return 0; |
return 0; |
|
|
buf = (char *) flex_alloc (readsz); |
buf = (char *) flex_alloc(readsz); |
if (!buf) |
if (!buf) |
flexerror (_("flex_alloc failed in filter_fix_linedirs")); |
flexerror(_("flex_alloc failed in filter_fix_linedirs")); |
|
|
while (fgets (buf, readsz, stdin)) { |
while (fgets(buf, readsz, stdin)) { |
|
|
regmatch_t m[10]; |
regmatch_t m[10]; |
|
|
/* Check for #line directive. */ |
/* Check for #line directive. */ |
if (buf[0] == '#' |
if (buf[0] == '#' |
&& regexec (®ex_linedir, buf, 3, m, 0) == 0) { |
&& regexec(®ex_linedir, buf, 3, m, 0) == 0) { |
|
|
int num; |
int num; |
char *fname; |
char *fname; |
|
|
/* extract the line number and filename */ |
/* extract the line number and filename */ |
num = regmatch_strtol (&m[1], buf, NULL, 0); |
num = regmatch_strtol(&m[1], buf, NULL, 0); |
fname = regmatch_dup (&m[2], buf); |
fname = regmatch_dup(&m[2], buf); |
|
|
if (strcmp (fname, |
if (strcmp(fname, |
outfilename ? outfilename : "<stdout>") == 0 || |
outfilename ? outfilename : "<stdout>") == 0 || |
strcmp (fname, headerfilename ? headerfilename : |
strcmp(fname, headerfilename ? headerfilename : |
"<stdout>") == 0) { |
"<stdout>") == 0) { |
|
|
char *s1, *s2; |
char *s1, *s2; |
char filename[MAXLINE]; |
char filename[MAXLINE]; |
|
|
s1 = fname; |
s1 = fname; |
s2 = filename; |
s2 = filename; |
|
|
|
|
/* Adjust the line directives. */ |
/* Adjust the line directives. */ |
in_gen = true; |
in_gen = true; |
snprintf (buf, readsz, "#line %d \"%s\"\n", |
snprintf(buf, readsz, "#line %d \"%s\"\n", |
lineno + 1, filename); |
lineno + 1, filename); |
} |
} else { |
else { |
/* |
/* it's a #line directive for code we didn't write */ |
* it's a #line directive for code we didn't |
|
* write |
|
*/ |
in_gen = false; |
in_gen = false; |
} |
} |
|
|
free (fname); |
free(fname); |
last_was_blank = false; |
last_was_blank = false; |
} |
} |
|
|
/* squeeze blank lines from generated code */ |
/* squeeze blank lines from generated code */ |
else if (in_gen && |
else if (in_gen && |
regexec (®ex_blank_line, buf, 0, NULL, 0) == 0) { |
regexec(®ex_blank_line, buf, 0, NULL, 0) == 0) { |
if (last_was_blank) |
if (last_was_blank) |
continue; |
continue; |
else |
else |
last_was_blank = true; |
last_was_blank = true; |
} |
} else { |
|
|
else { |
|
/* it's a line of normal, non-empty code. */ |
/* it's a line of normal, non-empty code. */ |
last_was_blank = false; |
last_was_blank = false; |
} |
} |
|
|
fputs (buf, stdout); |
fputs(buf, stdout); |
lineno++; |
lineno++; |
} |
} |
fflush (stdout); |
fflush(stdout); |
if (ferror (stdout)) |
if (ferror(stdout)) |
lerrsf (_("error writing output file %s"), |
lerrsf(_("error writing output file %s"), |
outfilename ? outfilename : "<stdout>"); |
outfilename ? outfilename : "<stdout>"); |
|
|
else if (fclose (stdout)) |
else if (fclose(stdout)) |
lerrsf (_("error closing output file %s"), |
lerrsf(_("error closing output file %s"), |
outfilename ? outfilename : "<stdout>"); |
outfilename ? outfilename : "<stdout>"); |
|
|
return 0; |
return 0; |
} |
} |