Fix Savannah bug #1454: skip over semicolons (and comments) inside variable

references in target definition lines.
This commit is contained in:
Paul Smith 2005-06-25 21:30:13 +00:00
parent 1dd9ed1c05
commit 6cdaff0948
6 changed files with 108 additions and 53 deletions

View File

@ -1,5 +1,18 @@
2005-06-25 Paul D. Smith <psmith@gnu.org> 2005-06-25 Paul D. Smith <psmith@gnu.org>
Fix Savannah bug #1454.
* read.c (find_char_unquote): Accept a new argument IGNOREVARS.
If it's set, then don't stop on STOPCHARs or BLANKs if they're
inside a variable reference. Make this function static as it's
only used here.
(eval): Call find_char_unquote() with IGNOREVARS set when we're
parsing an unexpanded line looking for semicolons.
* misc.c (remove_comments): Move this to read.c and make it static
as it's only used there. Call find_char_unquote() with new arg.
* make.h: Remove prototypes for find_char_unquote() and
remove_comments() since they're static now.
Implement the MAKE_RESTARTS variable, and disable -B if it's >0. Implement the MAKE_RESTARTS variable, and disable -B if it's >0.
Fixes Savannah bug #7566. Fixes Savannah bug #7566.

2
make.h
View File

@ -421,11 +421,9 @@ extern char *find_next_token PARAMS ((char **, unsigned int *));
extern char *next_token PARAMS ((const char *)); extern char *next_token PARAMS ((const char *));
extern char *end_of_token PARAMS ((const char *)); extern char *end_of_token PARAMS ((const char *));
extern void collapse_continuations PARAMS ((char *)); extern void collapse_continuations PARAMS ((char *));
extern void remove_comments PARAMS((char *));
extern char *lindex PARAMS ((const char *, const char *, int)); extern char *lindex PARAMS ((const char *, const char *, int));
extern int alpha_compare PARAMS ((const void *, const void *)); extern int alpha_compare PARAMS ((const void *, const void *));
extern void print_spaces PARAMS ((unsigned int)); extern void print_spaces PARAMS ((unsigned int));
extern char *find_char_unquote PARAMS ((char *, int, int, int));
extern char *find_percent PARAMS ((char *)); extern char *find_percent PARAMS ((char *));
extern FILE *open_tmpfile PARAMS ((char **, const char *)); extern FILE *open_tmpfile PARAMS ((char **, const char *));

16
misc.c
View File

@ -149,22 +149,6 @@ collapse_continuations (char *line)
*out = '\0'; *out = '\0';
} }
/* Remove comments from LINE.
This is done by copying the text at LINE onto itself. */
void
remove_comments (char *line)
{
char *comment;
comment = find_char_unquote (line, '#', 0, 0);
if (comment != 0)
/* Cut off the line at the #. */
*comment = '\0';
}
/* Print N spaces (used in debug for target-depth). */ /* Print N spaces (used in debug for target-depth). */

95
read.c
View File

@ -143,6 +143,9 @@ static void record_target_var PARAMS ((struct nameseq *filenames, char *defn,
const struct floc *flocp)); const struct floc *flocp));
static enum make_word_type get_next_mword PARAMS ((char *buffer, char *delim, static enum make_word_type get_next_mword PARAMS ((char *buffer, char *delim,
char **startp, unsigned int *length)); char **startp, unsigned int *length));
static void remove_comments PARAMS ((char *line));
static char *find_char_unquote PARAMS ((char *string, int stop1,
int stop2, int blank, int ignorevars));
/* Read in all the makefiles and return the chain of their names. */ /* Read in all the makefiles and return the chain of their names. */
@ -882,7 +885,7 @@ eval (struct ebuffer *ebuf, int set_default)
/* Search the line for an unquoted ; that is not after an /* Search the line for an unquoted ; that is not after an
unquoted #. */ unquoted #. */
cmdleft = find_char_unquote (line, ';', '#', 0); cmdleft = find_char_unquote (line, ';', '#', 0, 1);
if (cmdleft != 0 && *cmdleft == '#') if (cmdleft != 0 && *cmdleft == '#')
{ {
/* We found a comment before a semicolon. */ /* We found a comment before a semicolon. */
@ -929,7 +932,7 @@ eval (struct ebuffer *ebuf, int set_default)
if (cmdleft == 0) if (cmdleft == 0)
{ {
/* Look for a semicolon in the expanded line. */ /* Look for a semicolon in the expanded line. */
cmdleft = find_char_unquote (p2, ';', 0, 0); cmdleft = find_char_unquote (p2, ';', 0, 0, 0);
if (cmdleft != 0) if (cmdleft != 0)
{ {
@ -956,7 +959,7 @@ eval (struct ebuffer *ebuf, int set_default)
} }
} }
colonp = find_char_unquote(p2, ':', 0, 0); colonp = find_char_unquote(p2, ':', 0, 0, 0);
#ifdef HAVE_DOS_PATHS #ifdef HAVE_DOS_PATHS
/* The drive spec brain-damage strikes again... */ /* The drive spec brain-damage strikes again... */
/* Note that the only separators of targets in this context /* Note that the only separators of targets in this context
@ -965,7 +968,7 @@ eval (struct ebuffer *ebuf, int set_default)
while (colonp && (colonp[1] == '/' || colonp[1] == '\\') && while (colonp && (colonp[1] == '/' || colonp[1] == '\\') &&
colonp > p2 && isalpha ((unsigned char)colonp[-1]) && colonp > p2 && isalpha ((unsigned char)colonp[-1]) &&
(colonp == p2 + 1 || strchr (" \t(", colonp[-2]) != 0)) (colonp == p2 + 1 || strchr (" \t(", colonp[-2]) != 0))
colonp = find_char_unquote(colonp + 1, ':', 0, 0); colonp = find_char_unquote(colonp + 1, ':', 0, 0, 0);
#endif #endif
if (colonp != 0) if (colonp != 0)
break; break;
@ -1079,7 +1082,7 @@ eval (struct ebuffer *ebuf, int set_default)
/* This is a normal target, _not_ a target-specific variable. /* This is a normal target, _not_ a target-specific variable.
Unquote any = in the dependency list. */ Unquote any = in the dependency list. */
find_char_unquote (lb_next, '=', 0, 0); find_char_unquote (lb_next, '=', 0, 0, 0);
/* We have some targets, so don't ignore the following commands. */ /* We have some targets, so don't ignore the following commands. */
no_targets = 0; no_targets = 0;
@ -1094,7 +1097,7 @@ eval (struct ebuffer *ebuf, int set_default)
/* Look for a semicolon in the expanded line. */ /* Look for a semicolon in the expanded line. */
if (cmdleft == 0) if (cmdleft == 0)
{ {
cmdleft = find_char_unquote (p2, ';', 0, 0); cmdleft = find_char_unquote (p2, ';', 0, 0, 0);
if (cmdleft != 0) if (cmdleft != 0)
*(cmdleft++) = '\0'; *(cmdleft++) = '\0';
} }
@ -1310,8 +1313,23 @@ eval (struct ebuffer *ebuf, int set_default)
return 1; return 1;
} }
/* Remove comments from LINE.
This is done by copying the text at LINE onto itself. */
static void
remove_comments (char *line)
{
char *comment;
comment = find_char_unquote (line, '#', 0, 0, 0);
if (comment != 0)
/* Cut off the line at the #. */
*comment = '\0';
}
/* Execute a `define' directive. /* Execute a `define' directive.
The first line has already been read, and NAME is the name of The first line has already been read, and NAME is the name of
the variable to be defined. The following lines remain to be read. */ the variable to be defined. The following lines remain to be read. */
@ -2158,34 +2176,75 @@ record_files (struct nameseq *filenames, char *pattern, char *pattern_percent,
Backslashes quote STOPCHAR, blanks if BLANK is nonzero, and backslash. Backslashes quote STOPCHAR, blanks if BLANK is nonzero, and backslash.
Quoting backslashes are removed from STRING by compacting it into Quoting backslashes are removed from STRING by compacting it into
itself. Returns a pointer to the first unquoted STOPCHAR if there is itself. Returns a pointer to the first unquoted STOPCHAR if there is
one, or nil if there are none. */ one, or nil if there are none. STOPCHARs inside variable references are
ignored if IGNOREVARS is true.
char * STOPCHAR _cannot_ be '$' if IGNOREVARS is true. */
find_char_unquote (char *string, int stop1, int stop2, int blank)
static char *
find_char_unquote (char *string, int stop1, int stop2, int blank,
int ignorevars)
{ {
unsigned int string_len = 0; unsigned int string_len = 0;
register char *p = string; register char *p = string;
int pcount = 0;
char openparen;
char closeparen;
if (ignorevars)
ignorevars = '$';
while (1) while (1)
{ {
if (stop2 && blank) if (stop2 && blank)
while (*p != '\0' && *p != stop1 && *p != stop2 while (*p != '\0' && *p != ignorevars && *p != stop1 && *p != stop2
&& ! isblank ((unsigned char) *p)) && ! isblank ((unsigned char) *p))
++p; ++p;
else if (stop2) else if (stop2)
while (*p != '\0' && *p != stop1 && *p != stop2) while (*p != '\0' && *p != ignorevars && *p != stop1 && *p != stop2)
++p; ++p;
else if (blank) else if (blank)
while (*p != '\0' && *p != stop1 while (*p != '\0' && *p != ignorevars && *p != stop1
&& ! isblank ((unsigned char) *p)) && ! isblank ((unsigned char) *p))
++p; ++p;
else else
while (*p != '\0' && *p != stop1) while (*p != '\0' && *p != ignorevars && *p != stop1)
++p; ++p;
if (*p == '\0') if (*p == '\0')
break; break;
/* If we stopped due to a variable reference, skip over its contents. */
if (*p == ignorevars)
{
char openparen = p[1];
p += 2;
/* Skip the contents of a non-quoted, multi-char variable ref. */
if (openparen == '(' || openparen == '{')
{
unsigned int pcount = 1;
char closeparen = (openparen == '(' ? ')' : '}');
while (*p)
{
if (*p == openparen)
++pcount;
else if (*p == closeparen)
if (--pcount == 0)
{
++p;
break;
}
++p;
}
}
/* Skipped the variable reference: look for STOPCHARS again. */
continue;
}
if (p > string && p[-1] == '\\') if (p > string && p[-1] == '\\')
{ {
/* Search for more backslashes. */ /* Search for more backslashes. */
@ -2221,7 +2280,7 @@ find_char_unquote (char *string, int stop1, int stop2, int blank)
char * char *
find_percent (char *pattern) find_percent (char *pattern)
{ {
return find_char_unquote (pattern, '%', 0, 0); return find_char_unquote (pattern, '%', 0, 0, 0);
} }
/* Parse a string into a sequence of filenames represented as a /* Parse a string into a sequence of filenames represented as a
@ -2263,7 +2322,7 @@ parse_file_seq (char **stringp, int stopchar, unsigned int size, int strip)
/* Yes, find end of next name. */ /* Yes, find end of next name. */
q = p; q = p;
p = find_char_unquote (q, stopchar, VMS_COMMA, 1); p = find_char_unquote (q, stopchar, VMS_COMMA, 1, 0);
#ifdef VMS #ifdef VMS
/* convert comma separated list to space separated */ /* convert comma separated list to space separated */
if (p && *p == ',') if (p && *p == ',')
@ -2274,7 +2333,7 @@ parse_file_seq (char **stringp, int stopchar, unsigned int size, int strip)
&& !(isspace ((unsigned char)p[1]) || !p[1] && !(isspace ((unsigned char)p[1]) || !p[1]
|| isspace ((unsigned char)p[-1]))) || isspace ((unsigned char)p[-1])))
{ {
p = find_char_unquote (p+1, stopchar, VMS_COMMA, 1); p = find_char_unquote (p+1, stopchar, VMS_COMMA, 1, 0);
} }
#endif #endif
#ifdef HAVE_DOS_PATHS #ifdef HAVE_DOS_PATHS
@ -2285,7 +2344,7 @@ parse_file_seq (char **stringp, int stopchar, unsigned int size, int strip)
if (stopchar == ':') if (stopchar == ':')
while (p != 0 && !isspace ((unsigned char)*p) && while (p != 0 && !isspace ((unsigned char)*p) &&
(p[1] == '\\' || p[1] == '/') && isalpha ((unsigned char)p[-1])) (p[1] == '\\' || p[1] == '/') && isalpha ((unsigned char)p[-1]))
p = find_char_unquote (p + 1, stopchar, VMS_COMMA, 1); p = find_char_unquote (p + 1, stopchar, VMS_COMMA, 1, 0);
#endif #endif
if (p == 0) if (p == 0)
p = q + strlen (q); p = q + strlen (q);

View File

@ -1,5 +1,8 @@
2005-06-25 Paul D. Smith <psmith@gnu.org> 2005-06-25 Paul D. Smith <psmith@gnu.org>
* scripts/misc/general3: Test semicolons in variable references.
Tests fix for Savannah bug #1454.
* scripts/variables/MAKE_RESTARTS: New file: test the * scripts/variables/MAKE_RESTARTS: New file: test the
MAKE_RESTARTS variable. MAKE_RESTARTS variable.
* scripts/options/dash-B: Test re-exec doesn't loop infinitely. * scripts/options/dash-B: Test re-exec doesn't loop infinitely.

View File

@ -5,13 +5,7 @@ This tests random features of the parser that need to be supported, and
which have either broken at some point in the past or seem likely to which have either broken at some point in the past or seem likely to
break."; break.";
$makefile2 = &get_tmpfile; run_make_test("
open(MAKEFILE,"> $makefile");
# The contents of the Makefile ...
print MAKEFILE <<EOF;
# We want to allow both empty commands _and_ commands that resolve to empty. # We want to allow both empty commands _and_ commands that resolve to empty.
EMPTY = EMPTY =
@ -31,20 +25,15 @@ TAB = \t \# A TAB and some spaces
\$(STR) \$(STR)
\$(STR) \$(TAB) \$(STR) \$(TAB)",
'', "#MAKE#: Nothing to be done for `all'.");
EOF
close(MAKEFILE);
&run_make_with_options($makefile,"",&get_logfile);
$answer = "$make_name: Nothing to be done for `all'.\n";
&compare_output($answer,&get_logfile(1));
# TEST 2 # TEST 2
# Make sure files without trailing newlines are handled properly. # Make sure files without trailing newlines are handled properly.
# Have to use the old style invocation to test this.
$makefile2 = &get_tmpfile;
open(MAKEFILE, "> $makefile2"); open(MAKEFILE, "> $makefile2");
print MAKEFILE "all:;\@echo FOO = \$(FOO)\nFOO = foo"; print MAKEFILE "all:;\@echo FOO = \$(FOO)\nFOO = foo";
@ -54,5 +43,14 @@ close(MAKEFILE);
$answer = "FOO = foo\n"; $answer = "FOO = foo\n";
&compare_output($answer,&get_logfile(1)); &compare_output($answer,&get_logfile(1));
# TEST 3
# Check semicolons in variable references
run_make_test('
$(if true,$(info true; true))
all: ; @:
',
'', 'true; true');
1; 1;