mirror of
https://github.com/mirror/make.git
synced 2025-03-10 01:30:37 +08:00
Fix Savannah bug #1454: skip over semicolons (and comments) inside variable
references in target definition lines.
This commit is contained in:
parent
1dd9ed1c05
commit
6cdaff0948
13
ChangeLog
13
ChangeLog
@ -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
2
make.h
@ -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
16
misc.c
@ -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
95
read.c
@ -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);
|
||||||
|
@ -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.
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user