rcfile: fully read each included file, so all its syntaxes are seen

An included file can contain multiple syntaxes.  If reading would stop
as soon as a command different from 'syntax' and 'header' and 'magic'
is found, any later syntaxes would not be seen.  So each nanorc file
needs to be fully scanned -- it's just the interpretation of all the
color commands that we want to delay until the syntax gets actually
used.

This fixes https://savannah.gnu.org/bugs/?56478.

Bug existed since commit cba9d8d0 from a month ago.
master
Benno Schulenberg 2019-06-13 12:20:38 +02:00
parent d7df7c694a
commit 7028adf211
2 changed files with 37 additions and 16 deletions

View File

@ -224,6 +224,8 @@ typedef struct syntaxtype {
/* The name of this syntax. */ /* The name of this syntax. */
char *filename; char *filename;
/* File where the syntax is defined, or NULL if not an included file. */ /* File where the syntax is defined, or NULL if not an included file. */
size_t lineno;
/* The line number where the 'syntax' command was found. */
struct augmentstruct *augmentations; struct augmentstruct *augmentations;
/* List of extendsyntax commands to apply when loaded. */ /* List of extendsyntax commands to apply when loaded. */
regexlisttype *extensions; regexlisttype *extensions;

View File

@ -330,7 +330,8 @@ void begin_a_syntax(char *ptr, bool headers_only)
/* Initialize a new syntax struct. */ /* Initialize a new syntax struct. */
live_syntax = (syntaxtype *)nmalloc(sizeof(syntaxtype)); live_syntax = (syntaxtype *)nmalloc(sizeof(syntaxtype));
live_syntax->name = mallocstrcpy(NULL, nameptr); live_syntax->name = mallocstrcpy(NULL, nameptr);
live_syntax->filename = (headers_only ? strdup(nanorc) : NULL); live_syntax->filename = strdup(nanorc);
live_syntax->lineno = lineno;
live_syntax->augmentations = NULL; live_syntax->augmentations = NULL;
live_syntax->extensions = NULL; live_syntax->extensions = NULL;
live_syntax->headers = NULL; live_syntax->headers = NULL;
@ -989,6 +990,7 @@ bool parse_syntax_commands(char *keyword, char *ptr)
* to contain only color syntax commands. */ * to contain only color syntax commands. */
void parse_rcfile(FILE *rcstream, bool syntax_only, bool headers_only) void parse_rcfile(FILE *rcstream, bool syntax_only, bool headers_only)
{ {
bool seen_color_command = FALSE;
char *buf = NULL; char *buf = NULL;
ssize_t len; ssize_t len;
size_t n = 0; size_t n = 0;
@ -1003,6 +1005,11 @@ void parse_rcfile(FILE *rcstream, bool syntax_only, bool headers_only)
buf[len - 1] = '\0'; buf[len - 1] = '\0';
lineno++; lineno++;
/* If doing a full parse, skip to after the 'syntax' command. */
if (!headers_only && syntax_only && lineno <= live_syntax->lineno)
continue;
ptr = buf; ptr = buf;
while (isblank((unsigned char)*ptr)) while (isblank((unsigned char)*ptr))
ptr++; ptr++;
@ -1063,32 +1070,44 @@ void parse_rcfile(FILE *rcstream, bool syntax_only, bool headers_only)
/* Try to parse the keyword. */ /* Try to parse the keyword. */
if (strcasecmp(keyword, "syntax") == 0) { if (strcasecmp(keyword, "syntax") == 0) {
if (headers_only || !syntax_only) { if (headers_only) {
if (opensyntax && lastcolor == NULL && live_syntax->filename == NULL) if (opensyntax && !seen_color_command)
rcfile_error(N_("Syntax \"%s\" has no color commands"), rcfile_error(N_("Syntax \"%s\" has no color commands"),
live_syntax->name); live_syntax->name);
begin_a_syntax(ptr, headers_only); begin_a_syntax(ptr, headers_only);
} seen_color_command = FALSE;
} else
break;
} else if (strcasecmp(keyword, "header") == 0) { } else if (strcasecmp(keyword, "header") == 0) {
if (headers_only || !syntax_only) if (headers_only)
grab_and_store("header", ptr, &live_syntax->headers); grab_and_store("header", ptr, &live_syntax->headers);
} else if (strcasecmp(keyword, "magic") == 0) { } else if (strcasecmp(keyword, "magic") == 0) {
#ifdef HAVE_LIBMAGIC #ifdef HAVE_LIBMAGIC
if (headers_only || !syntax_only) if (headers_only)
grab_and_store("magic", ptr, &live_syntax->magics); grab_and_store("magic", ptr, &live_syntax->magics);
#endif #endif
} else if (headers_only) } else if (syntax_only && (strcasecmp(keyword, "set") == 0 ||
break;
else if (parse_syntax_commands(keyword, ptr))
;
else if (syntax_only && (strcasecmp(keyword, "set") == 0 ||
strcasecmp(keyword, "unset") == 0 || strcasecmp(keyword, "unset") == 0 ||
strcasecmp(keyword, "bind") == 0 || strcasecmp(keyword, "bind") == 0 ||
strcasecmp(keyword, "unbind") == 0 || strcasecmp(keyword, "unbind") == 0 ||
strcasecmp(keyword, "include") == 0 || strcasecmp(keyword, "include") == 0 ||
strcasecmp(keyword, "extendsyntax") == 0)) strcasecmp(keyword, "extendsyntax") == 0)) {
if (headers_only)
rcfile_error(N_("Command \"%s\" not allowed in included file"), rcfile_error(N_("Command \"%s\" not allowed in included file"),
keyword); keyword);
else
break;
} else if (headers_only && opensyntax &&
(strcasecmp(keyword, "color") == 0 ||
strcasecmp(keyword, "icolor") == 0)) {
seen_color_command = TRUE;
continue;
} else if (headers_only && opensyntax &&
(strcasecmp(keyword, "comment") == 0 ||
strcasecmp(keyword, "linter") == 0)) {
continue;
} else if (parse_syntax_commands(keyword, ptr))
;
else if (strcasecmp(keyword, "include") == 0) else if (strcasecmp(keyword, "include") == 0)
parse_includes(ptr); parse_includes(ptr);
else else
@ -1101,7 +1120,7 @@ void parse_rcfile(FILE *rcstream, bool syntax_only, bool headers_only)
parse_binding(ptr, TRUE); parse_binding(ptr, TRUE);
else if (strcasecmp(keyword, "unbind") == 0) else if (strcasecmp(keyword, "unbind") == 0)
parse_binding(ptr, FALSE); parse_binding(ptr, FALSE);
else else if (headers_only)
rcfile_error(N_("Command \"%s\" not understood"), keyword); rcfile_error(N_("Command \"%s\" not understood"), keyword);
#ifdef ENABLE_COLOR #ifdef ENABLE_COLOR
@ -1270,7 +1289,7 @@ void parse_rcfile(FILE *rcstream, bool syntax_only, bool headers_only)
} }
#ifdef ENABLE_COLOR #ifdef ENABLE_COLOR
if (opensyntax && lastcolor == NULL && !headers_only) if (headers_only && !seen_color_command)
rcfile_error(N_("Syntax \"%s\" has no color commands"), rcfile_error(N_("Syntax \"%s\" has no color commands"),
live_syntax->name); live_syntax->name);
@ -1298,7 +1317,7 @@ void parse_one_nanorc(void)
/* If opening the file succeeded, parse it. Otherwise, only /* If opening the file succeeded, parse it. Otherwise, only
* complain if the file actually exists. */ * complain if the file actually exists. */
if (rcstream != NULL) if (rcstream != NULL)
parse_rcfile(rcstream, FALSE, FALSE); parse_rcfile(rcstream, FALSE, TRUE);
else if (errno != ENOENT) else if (errno != ENOENT)
rcfile_error(N_("Error reading %s: %s"), nanorc, strerror(errno)); rcfile_error(N_("Error reading %s: %s"), nanorc, strerror(errno));
} }