diff --git a/bootstrap.conf b/bootstrap.conf index 1b1dd00c..2450ff07 100644 --- a/bootstrap.conf +++ b/bootstrap.conf @@ -38,6 +38,7 @@ gnulib_modules=' readme-release realloc-posix relocatable-prog relocatable-script + rename spawn-pipe stdbool stpcpy strdup-posix strerror strverscmp timevar unistd unistd-safer unlink unlocked-io diff --git a/lib/.gitignore b/lib/.gitignore index d2ac6986..919f23c4 100644 --- a/lib/.gitignore +++ b/lib/.gitignore @@ -315,3 +315,6 @@ /xstrndup.h /xtime.c /xtime.h +/rename.c +/rmdir.c +/same-inode.h diff --git a/src/files.h b/src/files.h index 2ec10c95..9c60dcd5 100644 --- a/src/files.h +++ b/src/files.h @@ -55,7 +55,7 @@ extern char *dir_prefix; and therefore GCC warns about a name clash. */ extern uniqstr grammar_file; -/* The current file name. Might change with %include, or with #line. */ +/* The current file name. Might change with #line. */ extern uniqstr current_file; /* The computed base for output file names. */ diff --git a/src/fixits.c b/src/fixits.c index d1001280..769b8508 100644 --- a/src/fixits.c +++ b/src/fixits.c @@ -23,10 +23,17 @@ #include "system.h" +#include "error.h" +#include "get-errno.h" #include "getargs.h" -#include "gl_xlist.h" #include "gl_array_list.h" +#include "gl_xlist.h" +#include "progname.h" +#include "quote.h" #include "quotearg.h" +#include "vasnprintf.h" + +#include "files.h" typedef struct { @@ -84,6 +91,94 @@ fixits_register (location const *loc, char const* fix) } +void +fixits_run (void) +{ + if (!fixits) + return; + + /* This is not unlike what is done in location_caret. */ + uniqstr input = ((fixit *) gl_list_get_at (fixits, 0))->location.start.file; + /* Backup the file. */ + char buf[256]; + size_t len = sizeof (buf); + char *backup = asnprintf (buf, &len, "%s~", input); + if (!backup) + xalloc_die (); + if (rename (input, backup)) + error (EXIT_FAILURE, get_errno (), + _("%s: cannot backup"), quotearg_colon (input)); + FILE *in = xfopen (backup, "r"); + FILE *out = xfopen (input, "w"); + size_t line = 1; + size_t offset = 1; + fixit const *f = NULL; + gl_list_iterator_t iter = gl_list_iterator (fixits); + while (gl_list_iterator_next (&iter, (const void**) &f, NULL)) + { + /* Look for the correct line. */ + while (line < f->location.start.line) + { + int c = getc (in); + if (c == EOF) + break; + if (c == '\n') + { + ++line; + offset = 1; + } + putc (c, out); + } + /* Look for the right offset. */ + while (offset < f->location.start.column) + { + int c = getc (in); + if (c == EOF) + break; + ++offset; + putc (c, out); + } + + /* Paste the fix instead. */ + fputs (f->fix, out); + + /* Skip the bad input. */ + while (line < f->location.end.line) + { + int c = getc (in); + if (c == EOF) + break; + if (c == '\n') + { + ++line; + offset = 1; + } + } + while (offset < f->location.end.column) + { + int c = getc (in); + if (c == EOF) + break; + ++offset; + } + } + /* Paste the rest of the file. */ + { + int c; + while ((c = getc (in)) != EOF) + putc (c, out); + } + + gl_list_iterator_free (&iter); + xfclose (out); + xfclose (in); + fprintf (stderr, "%s: file %s was updated (backup: %s)\n", + program_name, quote_n (0, input), quote_n (1, backup)); + if (backup != buf) + free (backup); +} + + /* Free the registered fixits. */ void fixits_free (void) { diff --git a/src/fixits.h b/src/fixits.h index 0c531ab4..5adaf685 100644 --- a/src/fixits.h +++ b/src/fixits.h @@ -23,6 +23,9 @@ /* Declare a fix to apply. */ void fixits_register (location const *loc, char const* update); +/* Apply the fixits: update the source file. */ +void fixits_run (void); + /* Free the registered fixits. */ void fixits_free (void); diff --git a/src/getargs.c b/src/getargs.c index 9874c429..83ebcb09 100644 --- a/src/getargs.c +++ b/src/getargs.c @@ -36,12 +36,13 @@ #include "quote.h" #include "uniqstr.h" -bool defines_flag; -bool graph_flag; -bool xml_flag; -bool no_lines_flag; -bool token_table_flag; -bool yacc_flag; /* for -y */ +bool defines_flag = false; +bool graph_flag = false; +bool xml_flag = false; +bool no_lines_flag = false; +bool token_table_flag = false; +bool yacc_flag = false; /* for -y */ +bool update_flag = false; /* for -u */ bool nondeterministic_parser = false; bool glr_parser = false; @@ -283,7 +284,10 @@ Operation modes:\n\ -h, --help display this help and exit\n\ -V, --version output version information and exit\n\ --print-localedir output directory containing locale-dependent data\n\ + and exit\n\ --print-datadir output directory containing skeletons and XSLT\n\ + and exit\n\ + -u, --update apply fixes to the source grammar file\n\ -y, --yacc emulate POSIX Yacc\n\ -W, --warnings[=CATEGORY] report the warnings falling in CATEGORY\n\ -f, --feature[=FEATURE] activate miscellaneous features\n\ @@ -478,6 +482,7 @@ static char const short_options[] = "p:" "r:" "t" + "u" /* --update */ "v" "x::" "y" @@ -499,6 +504,7 @@ static struct option const long_options[] = { "version", no_argument, 0, 'V' }, { "print-localedir", no_argument, 0, PRINT_LOCALEDIR_OPTION }, { "print-datadir", no_argument, 0, PRINT_DATADIR_OPTION }, + { "update", no_argument, 0, 'u' }, { "warnings", optional_argument, 0, 'W' }, /* Parser. */ @@ -684,6 +690,10 @@ getargs (int argc, char *argv[]) MUSCLE_PERCENT_DEFINE_D); break; + case 'u': + update_flag = true; + break; + case 'v': report_flag |= report_states; break; diff --git a/src/getargs.h b/src/getargs.h index 925ef58e..f44f2183 100644 --- a/src/getargs.h +++ b/src/getargs.h @@ -40,7 +40,7 @@ extern bool xml_flag; /* for -x */ extern bool no_lines_flag; /* for -l */ extern bool token_table_flag; /* for -k */ extern bool yacc_flag; /* for -y */ - +extern bool update_flag; /* for -u */ /* GLR_PARSER is true if the input file says to use the GLR (Generalized LR) parser, and to output some additional information diff --git a/src/main.c b/src/main.c index f728e727..94782573 100644 --- a/src/main.c +++ b/src/main.c @@ -103,6 +103,12 @@ main (int argc, char *argv[]) reader (); timevar_pop (tv_reader); + /* Fix input file now, even if there are errors: that's less + warnings in the following runs. */ + if (update_flag) + fixits_run (); + fixits_free (); + if (complaint_status == status_complaint) goto finish; @@ -206,7 +212,6 @@ main (int argc, char *argv[]) conflicts_free (); grammar_free (); output_file_names_free (); - fixits_free (); /* The scanner memory cannot be released right after parsing, as it contains things such as user actions, prologue, epilogue etc. */ diff --git a/tests/input.at b/tests/input.at index 522eda54..ea30991c 100644 --- a/tests/input.at +++ b/tests/input.at @@ -2508,28 +2508,7 @@ AT_DATA_GRAMMAR([[input.y]], exp : '0' ]]) -AT_BISON_CHECK([[input.y]], [[1]], [[]], -[[input.y:10.1-13: warning: deprecated directive: '%default_prec', use '%default-prec' [-Wdeprecated] -input.y:11.1-14: warning: deprecated directive: '%error_verbose', use '%define parse.error verbose' [-Wdeprecated] -input.y:12.1-10: warning: deprecated directive: '%expect_rr', use '%expect-rr' [-Wdeprecated] -input.y:13.1-14: warning: deprecated directive: '%file-prefix =', use '%file-prefix' [-Wdeprecated] -input.y:14.1-15.2: warning: deprecated directive: '%file-prefix\n =', use '%file-prefix' [-Wdeprecated] -input.y:17.1-19: warning: deprecated directive: '%fixed-output_files', use '%fixed-output-files' [-Wdeprecated] -input.y:18.1-19: warning: deprecated directive: '%fixed_output-files', use '%fixed-output-files' [-Wdeprecated] -input.y:20.1-19: warning: deprecated directive: '%name-prefix= "foo"', use '%define api.prefix {foo}' [-Wdeprecated] -input.y:21.1-16: warning: deprecated directive: '%no-default_prec', use '%no-default-prec' [-Wdeprecated] -input.y:22.1-16: warning: deprecated directive: '%no_default-prec', use '%no-default-prec' [-Wdeprecated] -input.y:23.1-9: warning: deprecated directive: '%no_lines', use '%no-lines' [-Wdeprecated] -input.y:24.1-9: warning: deprecated directive: '%output =', use '%output' [-Wdeprecated] -input.y:25.1-12: warning: deprecated directive: '%pure_parser', use '%pure-parser' [-Wdeprecated] -input.y:26.1-12: warning: deprecated directive: '%token_table', use '%token-table' [-Wdeprecated] -input.y:27.1-14: warning: deprecated directive: '%error-verbose', use '%define parse.error verbose' [-Wdeprecated] -input.y:27-14: error: %define variable 'parse.error' redefined -input.y:11-14: previous definition -input.y:29.1-18: warning: deprecated directive: '%name-prefix "bar"', use '%define api.prefix {bar}' [-Wdeprecated] -]]) - -AT_BISON_CHECK([[-ffixit input.y]], [[1]], [[]], +AT_DATA([errors-all], [[input.y:10.1-13: warning: deprecated directive: '%default_prec', use '%default-prec' [-Wdeprecated] fix-it:"input.y":{10:1-10:14}:"%default-prec" input.y:11.1-14: warning: deprecated directive: '%error_verbose', use '%define parse.error verbose' [-Wdeprecated] @@ -2566,6 +2545,72 @@ input.y:29.1-18: warning: deprecated directive: '%name-prefix "bar"', use '%defi fix-it:"input.y":{29:1-29:19}:"%define api.prefix {bar}" ]]) +AT_CHECK([[sed -e '/^fix-it:/d' errors-all >experr]]) +AT_BISON_CHECK([[input.y]], [[1]], [[]], [experr]) + +AT_CHECK([cp errors-all experr]) +AT_BISON_CHECK([[-ffixit input.y]], [[1]], [[]], [experr]) + +# Update the input file. +AT_CHECK([cp input.y input.y.orig]) +AT_CHECK([sed -e '/fix-it/d' experr]) +AT_CHECK([echo "bison: file 'input.y' was updated (backup: 'input.y~')" >>experr]) +AT_BISON_CHECK([[--update input.y]], [[1]], [[]], [experr]) + +# Check the backup. +AT_CHECK([diff input.y.orig input.y~]) + +# Check the update. +AT_CHECK([cat input.y], [], +[[%code top { +/* Load config.h, and adjust to the compiler. + We used to do it here, but each time we add a new line, + we have to adjust all the line numbers in error messages. + It's simpler to use a constant include to a varying file. */ +#include +} + + +%default-prec +%define parse.error verbose +%expect-rr 0 +%file-prefix "foo" +%file-prefix +"bar" +%fixed-output-files +%fixed-output-files +%fixed-output-files +%define api.prefix {foo} +%no-default-prec +%no-default-prec +%no-lines +%output "foo" +%pure-parser +%token-table +%define parse.error verbose +%glr-parser +%define api.prefix {bar} +%% +exp : '0' +]]) + +# Unfortunately so far we don't remove duplicate definitions, +# so there are still warnings. +AT_BISON_CHECK([[-fcaret input.y]], [[1]], [], +[[input.y:26.1-27: error: %define variable 'parse.error' redefined + %define parse.error verbose + ^~~~~~~~~~~~~~~~~~~~~~~~~~~ +input.y:11.1-27: previous definition + %define parse.error verbose + ^~~~~~~~~~~~~~~~~~~~~~~~~~~ +input.y:28.1-24: error: %define variable 'api.prefix' redefined + %define api.prefix {bar} + ^~~~~~~~~~~~~~~~~~~~~~~~ +input.y:19.1-24: previous definition + %define api.prefix {foo} + ^~~~~~~~~~~~~~~~~~~~~~~~ +]]) + AT_CLEANUP