[SV 46995] Strip leading/trailing space from variable names

* makeint.h: Change MAP_SPACE to MAP_NEWLINE, and add MAP_PATHSEP
and MAP_SPACE which is now MAP_BLANK|MAP_NEWLINE.  Create
NEW_TOKEN(), END_OF_TOKEN(), ISBLANK(), ISSPACE() macros.
* main.c (initialize_stopchar_map): Set MAP_NEWLINE only for
newline characters.
* Convert all uses of isblank() and isspace() to macros.
* Examine all uses of isblank() (doesn't accept newlines) and
change them wherever possible to ISSPACE() (does accept newlines).
* function.c (func_foreach): Strip leading/trailing space.
* variable.c (parse_variable_definition): Clean up.
* tests/scripts/functions/foreach: Test settings and errors.
* tests/scripts/functions/call: Rewrite to new-style.
* tests/scripts/misc/bs-nl: Add many more tests for newlines.
This commit is contained in:
Paul Smith 2016-03-21 00:36:55 -04:00
parent 2b9dd215d5
commit e97159745d
15 changed files with 291 additions and 187 deletions

View file

@ -414,7 +414,7 @@ chop_commands (struct commands *cmds)
unsigned char flags = 0;
const char *p = lines[idx];
while (isblank (*p) || *p == '-' || *p == '@' || *p == '+')
while (ISBLANK (*p) || *p == '-' || *p == '@' || *p == '+')
switch (*(p++))
{
case '+':
@ -451,7 +451,7 @@ execute_file_commands (struct file *file)
the commands are nothing but whitespace. */
for (p = file->cmds->commands; *p != '\0'; ++p)
if (!isspace ((unsigned char)*p) && *p != '-' && *p != '@')
if (!ISSPACE (*p) && *p != '-' && *p != '@' && *p != '+')
break;
if (*p == '\0')
{

View file

@ -384,7 +384,7 @@ variable_expand_string (char *line, const char *string, long length)
break;
default:
if (isblank ((unsigned char)p[-1]))
if (ISSPACE (p[-1]))
break;
/* A $ followed by a random char is a variable reference:

View file

@ -115,8 +115,8 @@ subst_expand (char *o, const char *text, const char *subst, const char *replace,
/* If we're substituting only by fully matched words,
or only at the ends of words, check that this case qualifies. */
if (by_word
&& ((p > text && !isblank ((unsigned char)p[-1]))
|| ! STOP_SET (p[slen], MAP_BLANK|MAP_NUL)))
&& ((p > text && !ISSPACE (p[-1]))
|| ! STOP_SET (p[slen], MAP_SPACE|MAP_NUL)))
/* Struck out. Output the rest of the string that is
no longer to be replaced. */
o = variable_buffer_output (o, subst, slen);
@ -754,9 +754,9 @@ func_words (char *o, char **argv, const char *funcname UNUSED)
char *
strip_whitespace (const char **begpp, const char **endpp)
{
while (*begpp <= *endpp && isspace ((unsigned char)**begpp))
while (*begpp <= *endpp && ISSPACE (**begpp))
(*begpp) ++;
while (*endpp >= *begpp && isspace ((unsigned char)**endpp))
while (*endpp >= *begpp && ISSPACE (**endpp))
(*endpp) --;
return (char *)*begpp;
}
@ -869,8 +869,12 @@ func_foreach (char *o, char **argv, const char *funcname UNUSED)
unsigned int len;
struct variable *var;
/* Clean up the variable name by removing whitespace. */
char *vp = next_token (varname);
end_of_token (vp)[0] = '\0';
push_new_variable_scope ();
var = define_variable (varname, strlen (varname), "", o_automatic, 0);
var = define_variable (vp, strlen (vp), "", o_automatic, 0);
/* loop through LIST, put the value in VAR and expand BODY */
while ((p = find_next_token (&list_iterator, &len)) != 0)
@ -1080,10 +1084,9 @@ func_strip (char *o, char **argv, const char *funcname UNUSED)
int i=0;
const char *word_start;
while (isspace ((unsigned char)*p))
++p;
NEXT_TOKEN (p);
word_start = p;
for (i=0; *p != '\0' && !isspace ((unsigned char)*p); ++p, ++i)
for (i=0; *p != '\0' && !ISSPACE (*p); ++p, ++i)
{}
if (!i)
break;
@ -1614,8 +1617,7 @@ msdos_openpipe (int* pipedes, int *pidp, char *text)
extern int dos_command_running, dos_status;
/* Make sure not to bother processing an empty line. */
while (isblank ((unsigned char)*text))
++text;
NEXT_TOKEN (text);
if (*text == '\0')
return 0;
@ -2001,8 +2003,7 @@ func_not (char *o, char **argv, char *funcname UNUSED)
{
const char *s = argv[0];
int result = 0;
while (isspace ((unsigned char)*s))
s++;
NEXT_TOKEN (s);
result = ! (*s);
o = variable_buffer_output (o, result ? "1" : "", result);
return o;
@ -2206,7 +2207,7 @@ func_file (char *o, char **argv, const char *funcname UNUSED)
mode = "a";
++fn;
}
fn = next_token (fn);
NEXT_TOKEN (fn);
if (fn[0] == '\0')
O (fatal, *expanding_var, _("file: missing filename"));
@ -2231,7 +2232,8 @@ func_file (char *o, char **argv, const char *funcname UNUSED)
char *preo = o;
FILE *fp;
fn = next_token (++fn);
++fn;
NEXT_TOKEN (fn);
if (fn[0] == '\0')
O (fatal, *expanding_var, _("file: missing filename"));
@ -2441,7 +2443,8 @@ handle_function (char **op, const char **stringp)
/* We found a builtin function. Find the beginning of its arguments (skip
whitespace after the name). */
beg = next_token (beg + entry_p->len);
beg += entry_p->len;
NEXT_TOKEN (beg);
/* Find the end of the function invocation, counting nested use of
whichever kind of parens we use. Since we're looking, count commas
@ -2541,7 +2544,6 @@ func_call (char *o, char **argv, const char *funcname UNUSED)
{
static int max_args = 0;
char *fname;
char *cp;
char *body;
int flen;
int i;
@ -2549,16 +2551,9 @@ func_call (char *o, char **argv, const char *funcname UNUSED)
const struct function_table_entry *entry_p;
struct variable *v;
/* There is no way to define a variable with a space in the name, so strip
leading and trailing whitespace as a favor to the user. */
fname = argv[0];
while (isspace ((unsigned char)*fname))
++fname;
cp = fname + strlen (fname) - 1;
while (cp > fname && isspace ((unsigned char)*cp))
--cp;
cp[1] = '\0';
/* Clean up the name of the variable to be invoked. */
fname = next_token (argv[0]);
end_of_token (fname)[0] = '\0';
/* Calling nothing is a no-op */
if (*fname == '\0')

View file

@ -72,8 +72,7 @@ get_next_word (const char *buffer, unsigned int *length)
char c;
/* Skip any leading whitespace. */
while (isblank ((unsigned char)*p))
++p;
NEXT_TOKEN (p);
beg = p;
c = *(p++);

71
job.c
View file

@ -1099,7 +1099,8 @@ start_job_command (struct child *child)
flags |= COMMANDS_RECURSE;
else if (*p == '-')
child->noerror = 1;
else if (!isblank ((unsigned char)*p))
/* Don't skip newlines. */
else if (!ISBLANK (*p))
break;
++p;
}
@ -1137,7 +1138,7 @@ start_job_command (struct child *child)
/* Skip any leading whitespace */
while (*p)
{
if (!isspace(*p))
if (!ISSPACE (*p))
{
if (*p != '\\')
break;
@ -1712,14 +1713,13 @@ new_job (struct file *file)
*out++ = *in++;
else
{
/* Skip the backslash, newline and
any following whitespace. */
in = next_token (in + 2);
/* Skip the backslash, newline, and whitespace. */
in += 2;
NEXT_TOKEN (in);
/* Discard any preceding whitespace that has
already been written to the output. */
while (out > outref
&& isblank ((unsigned char)out[-1]))
while (out > outref && ISBLANK (out[-1]))
--out;
/* Replace it all with a single space. */
@ -2553,8 +2553,8 @@ construct_command_argv_internal (char *line, char **restp, const char *shell,
if (restp != NULL)
*restp = NULL;
/* Make sure not to bother processing an empty line. */
while (isblank ((unsigned char)*line))
/* Make sure not to bother processing an empty line but stop at newline. */
while (ISBLANK (*line))
++line;
if (*line == '\0')
return 0;
@ -2729,15 +2729,16 @@ construct_command_argv_internal (char *line, char **restp, const char *shell,
/* Throw out the backslash and newline. */
++p;
/* If there's nothing in this argument yet, skip any
whitespace before the start of the next word. */
/* At the beginning of the argument, skip any whitespace other
than newline before the start of the next word. */
if (ap == new_argv[i])
p = next_token (p + 1) - 1;
while (ISBLANK (p[1]))
++p;
}
#ifdef WINDOWS32
/* Backslash before whitespace is not special if our shell
is not Unixy. */
else if (isspace (p[1]) && !unixy_shell)
else if (ISSPACE (p[1]) && !unixy_shell)
{
*ap++ = *p;
break;
@ -2764,7 +2765,7 @@ construct_command_argv_internal (char *line, char **restp, const char *shell,
else
#endif
if (p[1] != '\\' && p[1] != '\''
&& !isspace ((unsigned char)p[1])
&& !ISSPACE (p[1])
&& strchr (sh_chars_sh, p[1]) == 0)
/* back up one notch, to copy the backslash */
--p;
@ -2828,8 +2829,9 @@ construct_command_argv_internal (char *line, char **restp, const char *shell,
}
}
/* Ignore multiple whitespace chars. */
p = next_token (p) - 1;
/* Skip whitespace chars, but not newlines. */
while (ISBLANK (p[1]))
++p;
break;
default:
@ -2922,8 +2924,7 @@ construct_command_argv_internal (char *line, char **restp, const char *shell,
*/
/* Make sure not to bother processing an empty line. */
while (isspace ((unsigned char)*line))
++line;
NEXT_TOKEN (line);
if (*line == '\0')
return 0;
#endif /* WINDOWS32 */
@ -2983,9 +2984,9 @@ construct_command_argv_internal (char *line, char **restp, const char *shell,
{
int esc = 0;
/* This is the start of a new recipe line.
Skip whitespace and prefix characters. */
while (isblank (*f) || *f == '-' || *f == '@' || *f == '+')
/* This is the start of a new recipe line. Skip whitespace
and prefix characters but not newlines. */
while (ISBLANK (*f) || *f == '-' || *f == '@' || *f == '+')
++f;
/* Copy until we get to the next logical recipe line. */
@ -3035,21 +3036,21 @@ construct_command_argv_internal (char *line, char **restp, const char *shell,
[@+-]: they're meaningless in .ONESHELL mode. */
while (*f != '\0')
{
/* This is the start of a new recipe line.
Skip whitespace and prefix characters. */
while (isblank (*f) || *f == '-' || *f == '@' || *f == '+')
/* This is the start of a new recipe line. Skip whitespace
and prefix characters but not newlines. */
while (ISBLANK (*f) || *f == '-' || *f == '@' || *f == '+')
++f;
/* Copy until we get to the next logical recipe line. */
while (*f != '\0')
{
/* Remove the escaped newlines in the command, and
the whitespace that follows them. Windows
shells cannot handle escaped newlines. */
/* Remove the escaped newlines in the command, and the
blanks that follow them. Windows shells cannot handle
escaped newlines. */
if (*f == '\\' && f[1] == '\n')
{
f += 2;
while (isblank (*f))
while (ISBLANK (*f))
++f;
}
*(t++) = *(f++);
@ -3161,7 +3162,7 @@ construct_command_argv_internal (char *line, char **restp, const char *shell,
/* DOS shells don't know about backslash-escaping. */
if (unixy_shell && !batch_mode_shell &&
(*p == '\\' || *p == '\'' || *p == '"'
|| isspace ((unsigned char)*p)
|| ISSPACE (*p)
|| strchr (sh_chars, *p) != 0))
*ap++ = '\\';
#ifdef __MSDOS__
@ -3366,13 +3367,11 @@ construct_command_argv (char *line, char **restp, struct file *file,
cptr = line;
for (;;)
{
while ((*cptr != 0)
&& (isspace ((unsigned char)*cptr)))
while ((*cptr != 0) && (ISSPACE (*cptr)))
cptr++;
if (*cptr == 0)
break;
while ((*cptr != 0)
&& (!isspace ((unsigned char)*cptr)))
while ((*cptr != 0) && (!ISSPACE (*cptr)))
cptr++;
argc++;
}
@ -3385,15 +3384,13 @@ construct_command_argv (char *line, char **restp, struct file *file,
argc = 0;
for (;;)
{
while ((*cptr != 0)
&& (isspace ((unsigned char)*cptr)))
while ((*cptr != 0) && (ISSPACE (*cptr)))
cptr++;
if (*cptr == 0)
break;
DB (DB_JOBS, ("argv[%d] = [%s]\n", argc, cptr));
argv[argc++] = cptr;
while ((*cptr != 0)
&& (!isspace ((unsigned char)*cptr)))
while ((*cptr != 0) && (!ISSPACE (*cptr)))
cptr++;
if (*cptr != 0)
*cptr++ = 0;

29
main.c
View file

@ -661,21 +661,22 @@ initialize_stopchar_map ()
stopchar_map[(int)'/'] = MAP_DIRSEP;
#if defined(VMS)
stopchar_map[(int)':'] = MAP_COLON | MAP_DIRSEP;
stopchar_map[(int)']'] = MAP_DIRSEP;
stopchar_map[(int)'>'] = MAP_DIRSEP;
stopchar_map[(int)':'] |= MAP_DIRSEP;
stopchar_map[(int)']'] |= MAP_DIRSEP;
stopchar_map[(int)'>'] |= MAP_DIRSEP;
#elif defined(HAVE_DOS_PATHS)
stopchar_map[(int)'\\'] = MAP_DIRSEP;
stopchar_map[(int)'\\'] |= MAP_DIRSEP;
#endif
for (i = 1; i <= UCHAR_MAX; ++i)
{
if (isblank(i))
stopchar_map[i] = MAP_BLANK;
if (isspace(i))
stopchar_map[i] |= MAP_SPACE;
if (isalnum(i))
stopchar_map[i] = MAP_USERFUNC;
if (isblank (i))
stopchar_map[i] |= MAP_BLANK;
else if (isspace (i))
/* Don't mark blank characters as newline characters. */
stopchar_map[i] |= MAP_NEWLINE;
else if (isalnum (i))
stopchar_map[i] |= MAP_USERFUNC;
}
}
@ -2985,7 +2986,7 @@ decode_env_switches (const char *envar, unsigned int len)
value = variable_expand (varref);
/* Skip whitespace, and check for an empty value. */
value = next_token (value);
NEXT_TOKEN (value);
len = strlen (value);
if (len == 0)
return;
@ -3008,14 +3009,14 @@ decode_env_switches (const char *envar, unsigned int len)
{
if (*value == '\\' && value[1] != '\0')
++value; /* Skip the backslash. */
else if (isblank ((unsigned char)*value))
else if (ISBLANK (*value))
{
/* End of the word. */
*p++ = '\0';
argv[++argc] = p;
do
++value;
while (isblank ((unsigned char)*value));
while (ISBLANK (*value));
continue;
}
*p++ = *value++;
@ -3046,7 +3047,7 @@ quote_for_env (char *out, const char *in)
{
if (*in == '$')
*out++ = '$';
else if (isblank ((unsigned char)*in) || *in == '\\')
else if (ISBLANK (*in) || *in == '\\')
*out++ = '\\';
*out++ = *in++;
}

View file

@ -95,10 +95,6 @@ char *alloca ();
extern int errno;
#endif
#ifndef isblank
# define isblank(c) ((c) == ' ' || (c) == '\t')
#endif
#ifdef __VMS
/* In strict ANSI mode, VMS compilers should not be defining the
VMS macro. Define it here instead of a bulk edit for the correct code.
@ -348,21 +344,6 @@ char *strsignal (int signum);
#define N_(msgid) gettext_noop (msgid)
#define S_(msg1,msg2,num) ngettext (msg1,msg2,num)
/* Handle other OSs.
To overcome an issue parsing paths in a DOS/Windows environment when
built in a unix based environment, override the PATH_SEPARATOR_CHAR
definition unless being built for Cygwin. */
#if defined(HAVE_DOS_PATHS) && !defined(__CYGWIN__)
# undef PATH_SEPARATOR_CHAR
# define PATH_SEPARATOR_CHAR ';'
#elif !defined(PATH_SEPARATOR_CHAR)
# if defined (VMS)
# define PATH_SEPARATOR_CHAR (vms_comma_separator ? ',' : ':')
# else
# define PATH_SEPARATOR_CHAR ':'
# endif
#endif
/* This is needed for getcwd() and chdir(), on some W32 systems. */
#if defined(HAVE_DIRECT_H)
# include <direct.h>
@ -398,7 +379,7 @@ extern int unixy_shell;
# endif
/* Include only the minimal stuff from windows.h. */
#define WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
#endif /* WINDOWS32 */
#define ANY_SET(_v,_m) (((_v)&(_m)) != 0)
@ -406,7 +387,7 @@ extern int unixy_shell;
#define MAP_NUL 0x0001
#define MAP_BLANK 0x0002
#define MAP_SPACE 0x0004
#define MAP_NEWLINE 0x0004
#define MAP_COMMENT 0x0008
#define MAP_SEMI 0x0010
#define MAP_EQUALS 0x0020
@ -429,7 +410,40 @@ extern int unixy_shell;
# define MAP_VMSCOMMA 0x0000
#endif
#define STOP_SET(_v,_m) ANY_SET (stopchar_map[(unsigned char)(_v)],(_m))
#define MAP_SPACE (MAP_BLANK|MAP_NEWLINE)
/* Handle other OSs.
To overcome an issue parsing paths in a DOS/Windows environment when
built in a unix based environment, override the PATH_SEPARATOR_CHAR
definition unless being built for Cygwin. */
#if defined(HAVE_DOS_PATHS) && !defined(__CYGWIN__)
# undef PATH_SEPARATOR_CHAR
# define PATH_SEPARATOR_CHAR ';'
# define MAP_PATHSEP MAP_SEMI
#elif !defined(PATH_SEPARATOR_CHAR)
# if defined (VMS)
# define PATH_SEPARATOR_CHAR (vms_comma_separator ? ',' : ':')
# define MAP_PATHSEP (vms_comma_separator ? MAP_COMMA : MAP_SEMI)
# else
# define PATH_SEPARATOR_CHAR ':'
# define MAP_PATHSEP MAP_COLON
# endif
#elif PATH_SEPARATOR_CHAR == ':'
# define MAP_PATHSEP MAP_COLON
#elif PATH_SEPARATOR_CHAR == ';'
# define MAP_PATHSEP MAP_SEMI
#elif PATH_SEPARATOR_CHAR == ','
# define MAP_PATHSEP MAP_COMMA
#else
# error "Unknown PATH_SEPARATOR_CHAR"
#endif
#define STOP_SET(_v,_m) ANY_SET(stopchar_map[(unsigned char)(_v)],(_m))
#define ISBLANK(c) STOP_SET((c),MAP_BLANK)
#define ISSPACE(c) STOP_SET((c),MAP_SPACE)
#define NEXT_TOKEN(s) while (ISSPACE (*(s))) ++(s)
#define END_OF_TOKEN(s) while (! STOP_SET (*(s), MAP_SPACE|MAP_NUL)) ++(s)
#if defined(HAVE_SYS_RESOURCE_H) && defined(HAVE_GETRLIMIT) && defined(HAVE_SETRLIMIT)
# define SET_STACK_SIZE

15
misc.c
View file

@ -91,12 +91,13 @@ collapse_continuations (char *line)
{
/* Backslash/newline handling:
In traditional GNU make all trailing whitespace, consecutive
backslash/newlines, and any leading whitespace on the next line
is reduced to a single space.
backslash/newlines, and any leading non-newline whitespace on the
next line is reduced to a single space.
In POSIX, each backslash/newline and is replaced by a space. */
in = next_token (in);
while (ISBLANK (*in))
++in;
if (! posix_pedantic)
while (out > line && isblank ((unsigned char)out[-1]))
while (out > line && ISBLANK (out[-1]))
--out;
*out++ = ' ';
}
@ -314,8 +315,7 @@ lindex (const char *s, const char *limit, int c)
char *
end_of_token (const char *s)
{
while (! STOP_SET (*s, MAP_BLANK|MAP_NUL))
++s;
END_OF_TOKEN (s);
return (char *)s;
}
@ -324,8 +324,7 @@ end_of_token (const char *s)
char *
next_token (const char *s)
{
while (isblank ((unsigned char)*s))
++s;
NEXT_TOKEN (s);
return (char *)s;
}

42
read.c
View file

@ -511,7 +511,7 @@ parse_var_assignment (const char *line, struct vmodifiers *vmod)
memset (vmod, '\0', sizeof (*vmod));
/* Find the start of the next token. If there isn't one we're done. */
line = next_token (line);
NEXT_TOKEN (line);
if (*line == '\0')
return (char *)line;
@ -720,8 +720,7 @@ eval (struct ebuffer *ebuf, int set_default)
/* Get rid if starting space (including formfeed, vtab, etc.) */
p = collapsed;
while (isspace ((unsigned char)*p))
++p;
NEXT_TOKEN (p);
/* See if this is a variable assignment. We need to do this early, to
allow variables with names like 'ifdef', 'export', 'private', etc. */
@ -769,7 +768,7 @@ eval (struct ebuffer *ebuf, int set_default)
p2 = end_of_token (p);
wlen = p2 - p;
p2 = next_token (p2);
NEXT_TOKEN (p2);
/* If we're in an ignored define, skip this line (but maybe get out). */
if (in_ignored_define)
@ -1242,8 +1241,7 @@ eval (struct ebuffer *ebuf, int set_default)
The rule is that it's only a target, if there are TWO :'s
OR a space around the :.
*/
if (p && !(isspace ((unsigned char)p[1]) || !p[1]
|| isspace ((unsigned char)p[-1])))
if (p && !(ISSPACE (p[1]) || !p[1] || ISSPACE (p[-1])))
p = 0;
#endif
#ifdef HAVE_DOS_PATHS
@ -1436,7 +1434,7 @@ do_undefine (char *name, enum variable_origin origin, struct ebuffer *ebuf)
if (*name == '\0')
O (fatal, &ebuf->floc, _("empty variable name"));
p = name + strlen (name) - 1;
while (p > name && isblank ((unsigned char)*p))
while (p > name && ISBLANK (*p))
--p;
p[1] = '\0';
@ -1481,7 +1479,7 @@ do_define (char *name, enum variable_origin origin, struct ebuffer *ebuf)
if (name[0] == '\0')
O (fatal, &defstart, _("empty variable name"));
p = name + strlen (name) - 1;
while (p > name && isblank ((unsigned char)*p))
while (p > name && ISBLANK (*p))
--p;
p[1] = '\0';
@ -1509,13 +1507,13 @@ do_define (char *name, enum variable_origin origin, struct ebuffer *ebuf)
len = strlen (p);
/* If this is another 'define', increment the level count. */
if ((len == 6 || (len > 6 && isblank ((unsigned char)p[6])))
if ((len == 6 || (len > 6 && ISBLANK (p[6])))
&& strneq (p, "define", 6))
++nlevels;
/* If this is an 'endef', decrement the count. If it's now 0,
we've found the last one. */
else if ((len == 5 || (len > 5 && isblank ((unsigned char)p[5])))
else if ((len == 5 || (len > 5 && ISBLANK (p[5])))
&& strneq (p, "endef", 5))
{
p += 5;
@ -1591,7 +1589,8 @@ conditional_line (char *line, int len, const gmk_floc *flocp)
return -2;
/* Found one: skip past it and any whitespace after it. */
line = next_token (line + len);
line += len;
NEXT_TOKEN (line);
#define EXTRATEXT() OS (error, flocp, _("extraneous text after '%s' directive"), cmdname)
#define EXTRACMD() OS (fatal, flocp, _("extraneous '%s'"), cmdname)
@ -1712,7 +1711,7 @@ conditional_line (char *line, int len, const gmk_floc *flocp)
/* Make sure there's only one variable name to test. */
p = end_of_token (var);
i = p - var;
p = next_token (p);
NEXT_TOKEN (p);
if (*p != '\0')
return -1;
@ -1758,7 +1757,7 @@ conditional_line (char *line, int len, const gmk_floc *flocp)
{
/* Strip blanks after the first string. */
char *p = line++;
while (isblank ((unsigned char)p[-1]))
while (ISBLANK (p[-1]))
--p;
*p = '\0';
}
@ -1774,7 +1773,7 @@ conditional_line (char *line, int len, const gmk_floc *flocp)
if (termin != ',')
/* Find the start of the second string. */
line = next_token (line);
NEXT_TOKEN (line);
termin = termin == ',' ? ')' : *line;
if (termin != ')' && termin != '"' && termin != '\'')
@ -1809,8 +1808,8 @@ conditional_line (char *line, int len, const gmk_floc *flocp)
if (*line == '\0')
return -1;
*line = '\0';
line = next_token (++line);
*(line++) = '\0';
NEXT_TOKEN (line);
if (*line != '\0')
EXTRATEXT ();
@ -2641,7 +2640,7 @@ get_next_mword (char *buffer, char *delim, char **startp, unsigned int *length)
char c;
/* Skip any leading whitespace. */
while (isblank ((unsigned char)*p))
while (ISBLANK (*p))
++p;
beg = p;
@ -3076,7 +3075,7 @@ parse_file_seq (char **stringp, unsigned int size, int stopmap,
int i;
/* Skip whitespace; at the end of the string or STOPCHAR we're done. */
p = next_token (p);
NEXT_TOKEN (p);
if (STOP_SET (*p, stopmap))
break;
@ -3091,8 +3090,7 @@ parse_file_seq (char **stringp, unsigned int size, int stopmap,
#endif
#ifdef _AMIGA
if (p && STOP_SET (*p, stopmap & MAP_COLON)
&& !(isspace ((unsigned char)p[1]) || !p[1]
|| isspace ((unsigned char)p[-1])))
&& !(ISSPACE (p[1]) || !p[1] || ISSPACE (p[-1])))
p = find_char_unquote (p+1, stopmap|MAP_VMSCOMMA|MAP_BLANK);
#endif
#ifdef HAVE_DOS_PATHS
@ -3101,7 +3099,7 @@ parse_file_seq (char **stringp, unsigned int size, int stopmap,
Note that tokens separated by spaces should be treated as separate
tokens since make doesn't allow path names with spaces */
if (stopmap | MAP_COLON)
while (p != 0 && !isspace ((unsigned char)*p) &&
while (p != 0 && !ISSPACE (*p) &&
(p[1] == '\\' || p[1] == '/') && isalpha ((unsigned char)p[-1]))
p = find_char_unquote (p + 1, stopmap|MAP_VMSCOMMA|MAP_BLANK);
#endif
@ -3196,7 +3194,7 @@ parse_file_seq (char **stringp, unsigned int size, int stopmap,
do
{
const char *o = e;
e = next_token (e);
NEXT_TOKEN (e);
/* Find the end of this word. We don't want to unquote and
we don't care about quoting since we're looking for the
last char in the word. */

View file

@ -4,11 +4,7 @@ $description = "Test the call function.\n";
$details = "Try various uses of call and ensure they all give the correct
results.\n";
open(MAKEFILE, "> $makefile");
# The Contents of the MAKEFILE ...
print MAKEFILE <<'EOMAKE';
run_make_test(q!
# Simple, just reverse two things
#
reverse = $2 $1
@ -48,35 +44,22 @@ all: ; @echo '$(call reverse,bar,foo)'; \
echo '$(call my-foreach,a,,,)'; \
echo '$(call my-if,a,b,c)'; \
echo '$(call two,bar,baz)'; \
echo '$(call tclose,foo)'
echo '$(call tclose,foo)';
!,
"", "foo bar\ndefault file file\nb d f\n\n\nb\nbar foo baz\nfoo bar baz blarp quux \n");
EOMAKE
# These won't work until/unless PR/1527 is resolved.
# echo '$(call my-foreach,a,x y z,$(a)$(a))'; \
# echo '$(call my-if,,$(warning don't print this),ok)'
# These won't work because call expands all its arguments first, before
# passing them on, then marks them as resolved/simple, so they're not
# expanded again by the function.
#
# $answer = "xx yy zz\nok\n";
# END of Contents of MAKEFILE
close(MAKEFILE);
&run_make_with_options($makefile, "", &get_logfile);
$answer = "foo bar\ndefault file file\nb d f\n\n\nb\nbar foo baz\nfoo bar baz blarp quux \n";
&compare_output($answer, &get_logfile(1));
# echo '$(call my-foreach,a,x y z,$$(a)$$(a))'; \
# echo '$(call my-if,,$$(info don't print this),$$(info do print this))'
#
# $answer = "xx yy zz\ndo print this\n";
# TEST eclipsing of arguments when invoking sub-calls
$makefile2 = &get_tmpfile;
open(MAKEFILE,"> $makefile2");
print MAKEFILE <<'EOF';
run_make_test(q!
all = $1 $2 $3 $4 $5 $6 $7 $8 $9
level1 = $(call all,$1,$2,$3,$4,$5)
@ -88,13 +71,8 @@ all:
@echo $(call level1,1,2,3,4,5,6,7,8)
@echo $(call level2,1,2,3,4,5,6,7,8)
@echo $(call level3,1,2,3,4,5,6,7,8)
EOF
close(MAKEFILE);
&run_make_with_options($makefile2, "", &get_logfile);
$answer = "1 2 3 4 5 6 7 8 9\n1 2 3 4 5\n1 2 3\n1 2 3\n";
&compare_output($answer,&get_logfile(1));
!,
"", "1 2 3 4 5 6 7 8 9\n1 2 3 4 5\n1 2 3\n1 2 3\n");
# Ensure that variables are defined in global scope even in a $(call ...)
@ -108,3 +86,7 @@ all: ; @echo "$${X123-not set}"
'', "\n");
1;
### Local Variables:
### eval: (setq whitespace-action (delq 'auto-cleanup whitespace-action))
### End:

View file

@ -53,8 +53,26 @@ $(foreach x,FOREACH,$(eval $(value mktarget)))',
'',
'FOREACH');
# Allow variable names with trailing space
run_make_test(q!
$(foreach \
a \
, b c d \
, $(info $a))
all:;@:
!,
"", "b\nc\nd\n");
# TEST 2: Check some error conditions.
# Allow empty variable names. We still expand the body.
run_make_test('
x = $(foreach ,1 2 3,a)
y := $x
all: ; @echo $y',
'', "a a a\n");
# Check some error conditions.
run_make_test('
x = $(foreach )
@ -66,12 +84,12 @@ all: ; @echo $y',
512);
run_make_test('
x = $(foreach )
x = $(foreach x,y)
y := $x
all: ; @echo $y',
'',
"#MAKEFILE#:2: *** insufficient number of arguments (1) to function 'foreach'. Stop.",
"#MAKEFILE#:2: *** insufficient number of arguments (2) to function 'foreach'. Stop.",
512);
1;

View file

@ -42,6 +42,10 @@ A boy captured_by days end, has jazz_and_a midnight moon_light rise
run_make_test("FOO = a b\tc\rd\fe \f \f \f \f \ff
all: ; \@echo \$(words \$(sort \$(FOO)))\n",
'', "5\n");
'', "6\n");
1;
### Local Variables:
### eval: (setq whitespace-action (delq 'auto-cleanup whitespace-action))
### End:

View file

@ -125,5 +125,103 @@ close(MAKEFILE);
run_make_with_options($m2, '', get_logfile());
compare_output("foo bar\n", get_logfile(1));
# Test different types of whitespace, and bsnl inside functions
sub xlate
{
$_ = $_[0];
s/\\r/\r/g;
s/\\t/\t/g;
s/\\f/\f/g;
s/\\v/\v/g;
s/\\n/\n/g;
return $_;
}
run_make_test(xlate(q!
$(foreach\r a \t , b\t c \r ,$(info $a \r ) )
all:;@:
!),
'', "b \r \nc \r \n");
run_make_test(xlate(q!
all:;@:$(foreach\r a \t , b\t c \r ,$(info $a \r ) )
!),
'', "b \r \nc \r \n");
run_make_test(xlate(q!
$(foreach \
\r a \t\
, b\t \
c \r ,$(info \
$a \r ) \
)
all:;@:
!),
'', "b \r \nc \r \n");
run_make_test(xlate(q!
all:;@:$(foreach \
\r a \t\
, b\t \
c \r ,$(info \
$a \r ) \
)
!),
'', "b \r \nc \r \n");
run_make_test(xlate(q!
define FOO
$(foreach
\r a \t
, b\t
c \r ,$(info
$a \r )
)
endef
$(FOO)
all:;@:
!),
'', "b \r \nc \r \n");
run_make_test(xlate(q!
define FOO
$(foreach
\r a \t
, b\t
c \r ,$(info
$a \r )
)
endef
all:;@:$(FOO)
!),
'', "b \r \nc \r \n");
# Test variables in recipes that expand to multiple lines
run_make_test(q!
define var
echo foo
echo bar
endef
all:;$(var)
!,
'', "echo foo\nfoo\necho bar\nbar\n");
run_make_test(q!
define var
echo foo
@
echo bar
endef
all:;$(var)
!,
'', "echo foo\nfoo\necho bar\nbar\n");
1;

View file

@ -1431,7 +1431,7 @@ parse_variable_definition (const char *p, struct variable *var)
int wspace = 0;
const char *e = NULL;
p = next_token (p);
NEXT_TOKEN (p);
var->name = (char *)p;
var->length = 0;
@ -1448,7 +1448,7 @@ parse_variable_definition (const char *p, struct variable *var)
/* This begins a variable expansion reference. Make sure we don't
treat chars inside the reference as assignment tokens. */
char closeparen;
int count;
c = *p++;
if (c == '(')
closeparen = ')';
@ -1462,26 +1462,25 @@ parse_variable_definition (const char *p, struct variable *var)
/* P now points past the opening paren or brace.
Count parens or braces until it is matched. */
count = 0;
for (; *p != '\0'; ++p)
for (unsigned int count = 1; *p != '\0'; ++p)
{
if (*p == c)
++count;
else if (*p == closeparen && --count < 0)
if (*p == closeparen && --count == 0)
{
++p;
break;
}
if (*p == c)
++count;
}
continue;
}
/* If we find whitespace skip it, and remember we found it. */
if (isblank ((unsigned char)c))
if (ISBLANK (c))
{
wspace = 1;
e = p - 1;
p = next_token (p);
NEXT_TOKEN (p);
c = *p;
if (c == '\0')
return NULL;

View file

@ -208,7 +208,7 @@ construct_vpath_list (char *pattern, char *dirpath)
#endif
/* Skip over any initial separators and blanks. */
while (*dirpath == PATH_SEPARATOR_CHAR || isblank ((unsigned char)*dirpath))
while (STOP_SET (*dirpath, MAP_BLANK|MAP_PATHSEP))
++dirpath;
/* Figure out the maximum number of VPATH entries and put it in
@ -218,7 +218,7 @@ construct_vpath_list (char *pattern, char *dirpath)
maxelem = 2;
p = dirpath;
while (*p != '\0')
if (*p++ == PATH_SEPARATOR_CHAR || isblank ((unsigned char)*p))
if (STOP_SET (*p++, MAP_BLANK|MAP_PATHSEP))
++maxelem;
vpath = xmalloc (maxelem * sizeof (const char *));
@ -244,7 +244,7 @@ construct_vpath_list (char *pattern, char *dirpath)
#else
&& *p != PATH_SEPARATOR_CHAR
#endif
&& !isblank ((unsigned char)*p))
&& !ISBLANK (*p))
++p;
len = p - v;
@ -266,7 +266,7 @@ construct_vpath_list (char *pattern, char *dirpath)
}
/* Skip over separators and blanks between entries. */
while (*p == PATH_SEPARATOR_CHAR || isblank ((unsigned char)*p))
while (STOP_SET (*p, MAP_BLANK|MAP_PATHSEP))
++p;
}