From cba9d8d05e3d37687537818d81e90f2b7abd872c Mon Sep 17 00:00:00 2001 From: Brand Huntsman Date: Mon, 13 May 2019 18:01:59 -0600 Subject: [PATCH] rcfile: fully parse a syntax file only when needed When parsing an included syntax file, stop reading when a command other than 'syntax', 'header' or 'magic' is encountered. The syntax file is fully parsed the first time that a file needs it. Each 'extendsyntax' command is stored for unloaded syntaxes and applied after the syntax is loaded. Closing a buffer does not unload the syntax, even if no longer used by another buffer. This addresses https://savannah.gnu.org/bugs/?54928. Signed-off-by: Brand Huntsman --- src/color.c | 58 +++++++++++++--------- src/nano.h | 15 ++++++ src/proto.h | 4 +- src/rcfile.c | 135 ++++++++++++++++++++++++++++++++++++++++----------- 4 files changed, 161 insertions(+), 51 deletions(-) diff --git a/src/color.c b/src/color.c index c879a392..94b41f08 100644 --- a/src/color.c +++ b/src/color.c @@ -39,11 +39,34 @@ #define A_BANDAID A_NORMAL #endif +/* Assign pair numbers for the colors in the given syntax, giving identical + * color pairs the same number. */ +void set_syntax_colorpairs(syntaxtype *sint) +{ + int new_number = NUMBER_OF_ELEMENTS + 1; + colortype *ink; + + for (ink = sint->color; ink != NULL; ink = ink->next) { + const colortype *beforenow = sint->color; + + while (beforenow != ink && (beforenow->fg != ink->fg || + beforenow->bg != ink->bg)) + beforenow = beforenow->next; + + if (beforenow != ink) + ink->pairnum = beforenow->pairnum; + else + ink->pairnum = new_number++; + + ink->attributes |= COLOR_PAIR(ink->pairnum) | A_BANDAID; + } +} + /* Initialize the colors for nano's interface, and assign pair numbers - * for the colors in each syntax. */ + * for the colors in each loaded syntax. */ void set_colorpairs(void) { - const syntaxtype *sint; + syntaxtype *sint; bool using_defaults = FALSE; size_t i; @@ -82,27 +105,10 @@ void set_colorpairs(void) free(color_combo[i]); } - /* For each syntax, go through its list of colors and assign each - * its pair number, giving identical color pairs the same number. */ - for (sint = syntaxes; sint != NULL; sint = sint->next) { - colortype *ink; - int new_number = NUMBER_OF_ELEMENTS + 1; - - for (ink = sint->color; ink != NULL; ink = ink->next) { - const colortype *beforenow = sint->color; - - while (beforenow != ink && (beforenow->fg != ink->fg || - beforenow->bg != ink->bg)) - beforenow = beforenow->next; - - if (beforenow != ink) - ink->pairnum = beforenow->pairnum; - else - ink->pairnum = new_number++; - - ink->attributes |= COLOR_PAIR(ink->pairnum) | A_BANDAID; - } - } + /* For each loaded syntax, assign pair numbers to color combinations. */ + for (sint = syntaxes; sint != NULL; sint = sint->next) + if (sint->filename == NULL) + set_syntax_colorpairs(sint); } /* Initialize the color information. */ @@ -267,6 +273,12 @@ void color_update(void) } } + /* When the syntax isn't loaded yet, parse it and initialize its colors. */ + if (sint->filename != NULL) { + parse_one_include(sint->filename, sint); + set_syntax_colorpairs(sint); + } + openfile->syntax = sint; openfile->colorstrings = (sint == NULL ? NULL : sint->color); diff --git a/src/nano.h b/src/nano.h index 4f2d6bfb..4522701f 100644 --- a/src/nano.h +++ b/src/nano.h @@ -215,9 +215,24 @@ typedef struct regexlisttype { /* The next regex. */ } regexlisttype; +typedef struct extendsyntaxstruct { + char *filename; + /* The file where the syntax is extended. */ + ssize_t lineno; + /* The number of the line of the extendsyntax command. */ + char *data; + /* The text of the line. */ + struct extendsyntaxstruct *next; + /* Next node. */ +} extendsyntaxstruct; + typedef struct syntaxtype { char *name; /* The name of this syntax. */ + char *filename; + /* File where the syntax is defined, or NULL if not an included file. */ + struct extendsyntaxstruct *extendsyntax; + /* List of extendsyntax commands to apply when loaded. */ regexlisttype *extensions; /* The list of extensions that this syntax applies to. */ regexlisttype *headers; diff --git a/src/proto.h b/src/proto.h index 1788f6a6..10e13664 100644 --- a/src/proto.h +++ b/src/proto.h @@ -465,10 +465,12 @@ int do_yesno_prompt(bool all, const char *msg); /* Most functions in rcfile.c. */ #ifdef ENABLE_NANORC void display_rcfile_errors(); +bool parse_syntax_commands(char *keyword, char *ptr); +void parse_one_include(char *file, syntaxtype *syntax); #ifdef ENABLE_COLOR void grab_and_store(const char *kind, char *ptr, regexlisttype **storage); #endif -void parse_rcfile(FILE *rcstream, bool syntax_only); +void parse_rcfile(FILE *rcstream, bool syntax_only, bool headers_only); void do_rcfiles(void); #endif /* ENABLE_NANORC */ diff --git a/src/rcfile.c b/src/rcfile.c index c78e42d3..26594a21 100644 --- a/src/rcfile.c +++ b/src/rcfile.c @@ -288,7 +288,7 @@ bool nregcomp(const char *regex, int compile_flags) /* Parse the next syntax name and its possible extension regexes from the * line at ptr, and add it to the global linked list of color syntaxes. */ -void parse_syntax(char *ptr) +void parse_syntax(char *ptr, bool headers_only) { char *nameptr = ptr; @@ -324,6 +324,8 @@ void parse_syntax(char *ptr) /* Initialize a new syntax struct. */ live_syntax = (syntaxtype *)nmalloc(sizeof(syntaxtype)); live_syntax->name = mallocstrcpy(NULL, nameptr); + live_syntax->filename = (headers_only ? strdup(nanorc) : NULL); + live_syntax->extendsyntax = NULL; live_syntax->extensions = NULL; live_syntax->headers = NULL; live_syntax->magics = NULL; @@ -539,7 +541,7 @@ bool is_good_file(char *file) #ifdef ENABLE_COLOR /* Read and parse one included syntax file. */ -static void parse_one_include(char *file) +void parse_one_include(char *file, syntaxtype *syntax) { FILE *rcstream; @@ -560,7 +562,49 @@ static void parse_one_include(char *file) nanorc = file; lineno = 0; - parse_rcfile(rcstream, TRUE); + /* If this is the first pass, parse only the prologue. */ + if (syntax == NULL) { + parse_rcfile(rcstream, TRUE, TRUE); + return; + } + + live_syntax = syntax; + opensyntax = TRUE; + lastcolor = NULL; + + /* Parse the included file fully. */ + parse_rcfile(rcstream, TRUE, FALSE); + opensyntax = TRUE; + + lastcolor = syntax->color; + if (lastcolor != NULL) + while (lastcolor->next != NULL) + lastcolor = lastcolor->next; + + extendsyntaxstruct *es = syntax->extendsyntax; + + /* Apply any stored extendsyntax commands. */ + while (es != NULL) { + extendsyntaxstruct *next = es->next; + char *keyword = es->data, *ptr = parse_next_word(es->data); + + nanorc = es->filename; + lineno = es->lineno; + + if (!parse_syntax_commands(keyword, ptr)) + rcfile_error(N_("Command \"%s\" not understood"), keyword); + + free(es->filename); + free(es->data); + free(es); + + es = next; + } + + free(syntax->filename); + syntax->filename = NULL; + syntax->extendsyntax = NULL; + opensyntax = FALSE; } /* Expand globs in the passed name, and parse the resultant files. */ @@ -585,7 +629,7 @@ void parse_includes(char *ptr) * report an error if it's something other than zero matches. */ if (result == 0) { for (size_t i = 0; i < files.gl_pathc; ++i) - parse_one_include(files.gl_pathv[i]); + parse_one_include(files.gl_pathv[i], NULL); } else if (result != GLOB_NOMATCH) rcfile_error(_("Error expanding %s: %s"), pattern, strerror(errno)); @@ -931,10 +975,31 @@ static void check_vitals_mapped(void) } } +/* Parse syntax-only commands. */ +bool parse_syntax_commands(char *keyword, char *ptr) +{ + if (strcasecmp(keyword, "comment") == 0) +#ifdef ENABLE_COMMENT + pick_up_name("comment", ptr, &live_syntax->comment); +#else + ; +#endif + else if (strcasecmp(keyword, "color") == 0) + parse_colors(ptr, NANO_REG_EXTENDED); + else if (strcasecmp(keyword, "icolor") == 0) + parse_colors(ptr, NANO_REG_EXTENDED | REG_ICASE); + else if (strcasecmp(keyword, "linter") == 0) + pick_up_name("linter", ptr, &live_syntax->linter); + else + return FALSE; + + return TRUE; +} + /* Parse the rcfile, once it has been opened successfully at rcstream, * and close it afterwards. If syntax_only is TRUE, allow the file to * to contain only color syntax commands. */ -void parse_rcfile(FILE *rcstream, bool syntax_only) +void parse_rcfile(FILE *rcstream, bool syntax_only, bool headers_only) { char *buf = NULL; ssize_t len; @@ -981,6 +1046,26 @@ void parse_rcfile(FILE *rcstream, bool syntax_only) continue; } + /* When the syntax isn't loaded yet, store extendsyntax commands. */ + if (sint->filename != NULL) { + extendsyntaxstruct *newextendsyntax = nmalloc(sizeof(extendsyntaxstruct));; + + newextendsyntax->filename = strdup(nanorc); + newextendsyntax->lineno = lineno; + newextendsyntax->data = strdup(ptr); + newextendsyntax->next = NULL; + + if (sint->extendsyntax != NULL) { + extendsyntaxstruct *es = sint->extendsyntax; + while (es->next != NULL) + es = es->next; + es->next = newextendsyntax; + } else + sint->extendsyntax = newextendsyntax; + + continue; + } + live_syntax = sint; opensyntax = TRUE; @@ -996,31 +1081,27 @@ void parse_rcfile(FILE *rcstream, bool syntax_only) /* Try to parse the keyword. */ if (strcasecmp(keyword, "syntax") == 0) { - if (opensyntax && lastcolor == NULL) - rcfile_error(N_("Syntax \"%s\" has no color commands"), - live_syntax->name); - parse_syntax(ptr); + if (headers_only || !syntax_only) { + if (opensyntax && lastcolor == NULL && live_syntax->filename == NULL) + rcfile_error(N_("Syntax \"%s\" has no color commands"), + live_syntax->name); + parse_syntax(ptr, headers_only); + } } - else if (strcasecmp(keyword, "header") == 0) - grab_and_store("header", ptr, &live_syntax->headers); - else if (strcasecmp(keyword, "magic") == 0) + else if (strcasecmp(keyword, "header") == 0) { + if (headers_only || !syntax_only) + grab_and_store("header", ptr, &live_syntax->headers); + } else if (strcasecmp(keyword, "magic") == 0) #ifdef HAVE_LIBMAGIC - grab_and_store("magic", ptr, &live_syntax->magics); + if (headers_only || !syntax_only) + grab_and_store("magic", ptr, &live_syntax->magics); #else ; #endif - else if (strcasecmp(keyword, "comment") == 0) -#ifdef ENABLE_COMMENT - pick_up_name("comment", ptr, &live_syntax->comment); -#else + else if (headers_only) + break; + else if (parse_syntax_commands(keyword, ptr)) ; -#endif - else if (strcasecmp(keyword, "color") == 0) - parse_colors(ptr, NANO_REG_EXTENDED); - else if (strcasecmp(keyword, "icolor") == 0) - parse_colors(ptr, NANO_REG_EXTENDED | REG_ICASE); - else if (strcasecmp(keyword, "linter") == 0) - pick_up_name("linter", ptr, &live_syntax->linter); else if (syntax_only && (strcasecmp(keyword, "set") == 0 || strcasecmp(keyword, "unset") == 0 || strcasecmp(keyword, "bind") == 0 || @@ -1046,7 +1127,7 @@ void parse_rcfile(FILE *rcstream, bool syntax_only) #ifdef ENABLE_COLOR /* If a syntax was extended, it stops at the end of the command. */ - if (live_syntax != syntaxes) + if (!syntax_only && live_syntax != syntaxes) opensyntax = FALSE; #endif @@ -1210,7 +1291,7 @@ void parse_rcfile(FILE *rcstream, bool syntax_only) } #ifdef ENABLE_COLOR - if (opensyntax && lastcolor == NULL) + if (opensyntax && lastcolor == NULL && !headers_only) rcfile_error(N_("Syntax \"%s\" has no color commands"), live_syntax->name); @@ -1238,7 +1319,7 @@ void parse_one_nanorc(void) /* If opening the file succeeded, parse it. Otherwise, only * complain if the file actually exists. */ if (rcstream != NULL) - parse_rcfile(rcstream, FALSE); + parse_rcfile(rcstream, FALSE, FALSE); else if (errno != ENOENT) rcfile_error(N_("Error reading %s: %s"), nanorc, strerror(errno)); }