mirror of
https://git.savannah.gnu.org/git/make.git
synced 2025-12-13 00:01:22 -05:00
Sanitize the registered function interface.
Expand the characters which are legal in a function name, and check the name for validity. Create a type for the function pointer. Convert the last argument from a boolean to flags, to allow for expansion.
This commit is contained in:
parent
f96c114e22
commit
2fb91e19a0
23
ChangeLog
23
ChangeLog
@ -1,3 +1,26 @@
|
|||||||
|
2013-10-05 Paul Smith <psmith@gnu.org>
|
||||||
|
|
||||||
|
* makeint.h (MAP_USERFUNC): A new map type for function names.
|
||||||
|
* main.c (initialize_stopchar_map): Set up the function name map.
|
||||||
|
|
||||||
|
* gnumake.h (gmk_func_ptr): Define a type for function pointers.
|
||||||
|
(gmk_add_function): Convert the last argument to FLAGS.
|
||||||
|
(GMK_FUNC_*): Define flags for the function. Change the default
|
||||||
|
behavior to "expand" since that's the most common one.
|
||||||
|
|
||||||
|
* function.c (function_table_entry): Use new function pointer type.
|
||||||
|
(lookup_function): Accept any valid function name character based
|
||||||
|
on the MAP_USERFUNC values.
|
||||||
|
(define_new_function): Use the new calling signature. Verify that
|
||||||
|
registered functions have valid names.
|
||||||
|
|
||||||
|
* guile.c (guile_gmake_setup): Use new calling signatures.
|
||||||
|
* loadapi.c (gmk_add_function): Ditto.
|
||||||
|
* variable.h (define_new_function): Ditto.
|
||||||
|
|
||||||
|
* doc/make.texi (Loaded Object API): Make the registered function
|
||||||
|
API documentation more clear.
|
||||||
|
|
||||||
2013-10-03 Eli Zaretskii <eliz@gnu.org>
|
2013-10-03 Eli Zaretskii <eliz@gnu.org>
|
||||||
|
|
||||||
* function.c (abspath): Reset root_len to one for Cygwin only when
|
* function.c (abspath): Reset root_len to one for Cygwin only when
|
||||||
|
|||||||
@ -11115,16 +11115,14 @@ arguments are as follows:
|
|||||||
@table @code
|
@table @code
|
||||||
@item name
|
@item name
|
||||||
The function name. This is what the makefile should use to invoke the
|
The function name. This is what the makefile should use to invoke the
|
||||||
function. The name must be between 1 and 255 characters long.
|
function. The name must be between 1 and 255 characters long and it
|
||||||
|
may only contain alphanumeric, period (@samp{.}), dash (@samp{-}), and
|
||||||
|
underscore (@samp{_}) characters. It may not begin with a period.
|
||||||
|
|
||||||
@item func_ptr
|
@item func_ptr
|
||||||
A pointer to a function that @code{make} will invoke when it expands
|
A pointer to a function that @code{make} will invoke when it expands
|
||||||
the function in a makefile. This function must be defined by the
|
the function in a makefile. This function must be defined by the
|
||||||
loaded object. GNU @code{make} will call it with three arguments:
|
loaded object.
|
||||||
@code{name} (the same name as above), @code{argc} (the number of
|
|
||||||
arguments to the function), and @code{argv} (the list of arguments to
|
|
||||||
the function). The last argument (that is, @code{argv[argc]} will be
|
|
||||||
null (@code{0}).
|
|
||||||
|
|
||||||
@item min_args
|
@item min_args
|
||||||
The minimum number of arguments the function will accept. Must be
|
The minimum number of arguments the function will accept. Must be
|
||||||
@ -11140,12 +11138,30 @@ arguments. If the value is 0, then any number of arguments is
|
|||||||
accepted. If the value is greater than 0, then it must be greater
|
accepted. If the value is greater than 0, then it must be greater
|
||||||
than or equal to @code{min_args}.
|
than or equal to @code{min_args}.
|
||||||
|
|
||||||
@item expand_args
|
@item flags
|
||||||
If this value is 0, then @code{make} will not expand the arguments to
|
Flags that specify how this function will operate; the desired flags
|
||||||
the function before passing them to @code{func_ptr}. If the value is
|
should be OR'd together. If the @code{GMK_FUNC_NOEXPAND} flag is
|
||||||
non-0, then the arguments will be expanded first.
|
given then the function arguments will not be expanded before the
|
||||||
|
function is called; otherwise they will be expanded first.
|
||||||
@end table
|
@end table
|
||||||
|
|
||||||
|
@subsubheading Registered Function Interface
|
||||||
|
@findex gmk_func_ptr
|
||||||
|
|
||||||
|
A function registered with @code{make} must match the
|
||||||
|
@code{gmk_func_ptr} type. It will be invoked with three parameters:
|
||||||
|
@code{name} (the name of the function), @code{argc} (the number of
|
||||||
|
arguments to the function), and @code{argv} (an array of pointers to
|
||||||
|
arguments to the function). The last pointer (that is,
|
||||||
|
@code{argv[argc]}) will be null (@code{0}).
|
||||||
|
|
||||||
|
The return value of the function is the result of expanding the
|
||||||
|
function. If the function expands to nothing the return value may be
|
||||||
|
null. Otherwise, it must be a pointer to a string created with
|
||||||
|
@code{gmk_alloc}. Once the function returns, @code{make} owns this
|
||||||
|
string and will free it when appropriate; it cannot be accessed by the
|
||||||
|
loaded object.
|
||||||
|
|
||||||
@subsubheading GNU @code{make} Facilities
|
@subsubheading GNU @code{make} Facilities
|
||||||
|
|
||||||
There are some facilities exported by GNU @code{make} for use by
|
There are some facilities exported by GNU @code{make} for use by
|
||||||
@ -11184,6 +11200,12 @@ should never pass memory that you've allocated directly to any
|
|||||||
memory returned to you by any @code{make} function. Instead, use the
|
memory returned to you by any @code{make} function. Instead, use the
|
||||||
@code{gmk_alloc} and @code{gmk_free} functions.
|
@code{gmk_alloc} and @code{gmk_free} functions.
|
||||||
|
|
||||||
|
In particular, the string returned to @code{make} by a function
|
||||||
|
registered using @code{gmk_add_function} @emph{must} be allocated
|
||||||
|
using @code{gmk_alloc}, and the string returned from the @code{make}
|
||||||
|
@code{gmk_expand} function @emph{must} be freed (when no longer
|
||||||
|
needed) using @code{gmk_free}.
|
||||||
|
|
||||||
@table @code
|
@table @code
|
||||||
@item gmk_alloc
|
@item gmk_alloc
|
||||||
@findex gmk_alloc
|
@findex gmk_alloc
|
||||||
|
|||||||
39
function.c
39
function.c
@ -31,7 +31,7 @@ struct function_table_entry
|
|||||||
{
|
{
|
||||||
union {
|
union {
|
||||||
char *(*func_ptr) (char *output, char **argv, const char *fname);
|
char *(*func_ptr) (char *output, char **argv, const char *fname);
|
||||||
char *(*alloc_func_ptr) (const char *fname, int argc, char **argv);
|
gmk_func_ptr alloc_func_ptr;
|
||||||
} fptr;
|
} fptr;
|
||||||
const char *name;
|
const char *name;
|
||||||
unsigned char len;
|
unsigned char len;
|
||||||
@ -269,20 +269,20 @@ patsubst_expand (char *o, const char *text, char *pattern, char *replace)
|
|||||||
static const struct function_table_entry *
|
static const struct function_table_entry *
|
||||||
lookup_function (const char *s)
|
lookup_function (const char *s)
|
||||||
{
|
{
|
||||||
|
struct function_table_entry function_table_entry_key;
|
||||||
const char *e = s;
|
const char *e = s;
|
||||||
|
|
||||||
while (*e && ( (*e >= 'a' && *e <= 'z') || *e == '-'))
|
while (STOP_SET (*e, MAP_USERFUNC))
|
||||||
e++;
|
e++;
|
||||||
if (*e == '\0' || isblank ((unsigned char) *e))
|
|
||||||
{
|
if (e == s || !STOP_SET(*e, MAP_NUL|MAP_SPACE))
|
||||||
struct function_table_entry function_table_entry_key;
|
return NULL;
|
||||||
|
|
||||||
function_table_entry_key.name = s;
|
function_table_entry_key.name = s;
|
||||||
function_table_entry_key.len = e - s;
|
function_table_entry_key.len = e - s;
|
||||||
|
|
||||||
return hash_find_item (&function_table, &function_table_entry_key);
|
return hash_find_item (&function_table, &function_table_entry_key);
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Return 1 if PATTERN matches STR, 0 if not. */
|
/* Return 1 if PATTERN matches STR, 0 if not. */
|
||||||
@ -2445,7 +2445,7 @@ func_call (char *o, char **argv, const char *funcname UNUSED)
|
|||||||
/* There is no way to define a variable with a space in the name, so strip
|
/* 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. */
|
leading and trailing whitespace as a favor to the user. */
|
||||||
fname = argv[0];
|
fname = argv[0];
|
||||||
while (*fname != '\0' && isspace ((unsigned char)*fname))
|
while (isspace ((unsigned char)*fname))
|
||||||
++fname;
|
++fname;
|
||||||
|
|
||||||
cp = fname + strlen (fname) - 1;
|
cp = fname + strlen (fname) - 1;
|
||||||
@ -2530,19 +2530,28 @@ func_call (char *o, char **argv, const char *funcname UNUSED)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
define_new_function (const gmk_floc *flocp,
|
define_new_function (const gmk_floc *flocp, const char *name,
|
||||||
const char *name, int min, int max, int expand,
|
unsigned int min, unsigned int max, unsigned int flags,
|
||||||
char *(*func)(const char *, int, char **))
|
gmk_func_ptr func)
|
||||||
{
|
{
|
||||||
|
const char *e = name;
|
||||||
struct function_table_entry *ent;
|
struct function_table_entry *ent;
|
||||||
size_t len = strlen (name);
|
size_t len;
|
||||||
|
|
||||||
|
while (STOP_SET (*e, MAP_USERFUNC))
|
||||||
|
e++;
|
||||||
|
len = e - name;
|
||||||
|
|
||||||
|
if (len == 0)
|
||||||
|
fatal (flocp, _("Empty function name\n"));
|
||||||
|
if (*name == '.' || *e != '\0')
|
||||||
|
fatal (flocp, _("Invalid function name: %s\n"), name);
|
||||||
if (len > 255)
|
if (len > 255)
|
||||||
fatal (flocp, _("Function name too long: %s\n"), name);
|
fatal (flocp, _("Function name too long: %s\n"), name);
|
||||||
if (min < 0 || min > 255)
|
if (min > 255)
|
||||||
fatal (flocp, _("Invalid minimum argument count (%d) for function %s\n"),
|
fatal (flocp, _("Invalid minimum argument count (%d) for function %s\n"),
|
||||||
min, name);
|
min, name);
|
||||||
if (max < 0 || max > 255 || (max && max < min))
|
if (max > 255 || (max && max < min))
|
||||||
fatal (flocp, _("Invalid maximum argument count (%d) for function %s\n"),
|
fatal (flocp, _("Invalid maximum argument count (%d) for function %s\n"),
|
||||||
max, name);
|
max, name);
|
||||||
|
|
||||||
@ -2551,7 +2560,7 @@ define_new_function (const gmk_floc *flocp,
|
|||||||
ent->len = len;
|
ent->len = len;
|
||||||
ent->minimum_args = min;
|
ent->minimum_args = min;
|
||||||
ent->maximum_args = max;
|
ent->maximum_args = max;
|
||||||
ent->expand_args = expand ? 1 : 0;
|
ent->expand_args = ANY_SET(flags, GMK_FUNC_NOEXPAND) ? 0 : 1;
|
||||||
ent->alloc_fn = 1;
|
ent->alloc_fn = 1;
|
||||||
ent->fptr.alloc_func_ptr = func;
|
ent->fptr.alloc_func_ptr = func;
|
||||||
|
|
||||||
|
|||||||
20
gnumake.h
20
gnumake.h
@ -26,6 +26,7 @@ typedef struct
|
|||||||
unsigned long lineno;
|
unsigned long lineno;
|
||||||
} gmk_floc;
|
} gmk_floc;
|
||||||
|
|
||||||
|
typedef char *(*gmk_func_ptr)(const char *nm, unsigned int argc, char **argv);
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
# ifdef GMK_BUILDING_MAKE
|
# ifdef GMK_BUILDING_MAKE
|
||||||
@ -60,14 +61,19 @@ GMK_EXPORT char *gmk_expand (const char *str);
|
|||||||
|
|
||||||
MIN_ARGS is the minimum number of arguments the function requires.
|
MIN_ARGS is the minimum number of arguments the function requires.
|
||||||
MAX_ARGS is the maximum number of arguments (or 0 if there's no maximum).
|
MAX_ARGS is the maximum number of arguments (or 0 if there's no maximum).
|
||||||
MIN_ARGS and MAX_ARGS must be >= 0 and <= 255.
|
MIN_ARGS and MAX_ARGS may not exceed 255.
|
||||||
|
|
||||||
If EXPAND_ARGS is 0, the arguments to the function will not be expanded
|
The FLAGS value may be GMK_FUNC_DEFAULT, or one or more of the following
|
||||||
before FUNC is called. If EXPAND_ARGS is non-0, they will be expanded.
|
flags OR'd together:
|
||||||
|
|
||||||
|
GMK_FUNC_NOEXPAND: the arguments to the function will be not be expanded
|
||||||
|
before FUNC is called.
|
||||||
*/
|
*/
|
||||||
GMK_EXPORT void gmk_add_function (const char *name,
|
GMK_EXPORT void gmk_add_function (const char *name, gmk_func_ptr func,
|
||||||
char *(*func)(const char *nm,
|
unsigned int min_args, unsigned int max_args,
|
||||||
int argc, char **argv),
|
unsigned int flags);
|
||||||
int min_args, int max_args, int expand_args);
|
|
||||||
|
#define GMK_FUNC_DEFAULT 0x00
|
||||||
|
#define GMK_FUNC_NOEXPAND 0x01
|
||||||
|
|
||||||
#endif /* _GNUMAKE_H_ */
|
#endif /* _GNUMAKE_H_ */
|
||||||
|
|||||||
4
guile.c
4
guile.c
@ -115,7 +115,7 @@ internal_guile_eval (void *arg)
|
|||||||
|
|
||||||
/* This is the function registered with make */
|
/* This is the function registered with make */
|
||||||
static char *
|
static char *
|
||||||
func_guile (const char *funcname UNUSED, int argc UNUSED, char **argv)
|
func_guile (const char *funcname UNUSED, unsigned int argc UNUSED, char **argv)
|
||||||
{
|
{
|
||||||
static int init = 0;
|
static int init = 0;
|
||||||
|
|
||||||
@ -140,7 +140,7 @@ int
|
|||||||
guile_gmake_setup (const gmk_floc *flocp UNUSED)
|
guile_gmake_setup (const gmk_floc *flocp UNUSED)
|
||||||
{
|
{
|
||||||
/* Create a make function "guile". */
|
/* Create a make function "guile". */
|
||||||
gmk_add_function ("guile", func_guile, 0, 1, 1);
|
gmk_add_function ("guile", func_guile, 0, 1, GMK_FUNC_DEFAULT);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -54,9 +54,8 @@ gmk_expand (const char *ref)
|
|||||||
|
|
||||||
/* Register a function to be called from makefiles. */
|
/* Register a function to be called from makefiles. */
|
||||||
void
|
void
|
||||||
gmk_add_function (const char *name,
|
gmk_add_function (const char *name, gmk_func_ptr func,
|
||||||
char *(*func)(const char *nm, int argc, char **argv),
|
unsigned int min, unsigned int max, unsigned int flags)
|
||||||
int min, int max, int expand)
|
|
||||||
{
|
{
|
||||||
define_new_function (reading_file, name, min, max, expand, func);
|
define_new_function (reading_file, name, min, max, flags, func);
|
||||||
}
|
}
|
||||||
|
|||||||
7
main.c
7
main.c
@ -612,10 +612,13 @@ initialize_stopchar_map ()
|
|||||||
stopchar_map[(int)':'] = MAP_COLON;
|
stopchar_map[(int)':'] = MAP_COLON;
|
||||||
stopchar_map[(int)'%'] = MAP_PERCENT;
|
stopchar_map[(int)'%'] = MAP_PERCENT;
|
||||||
stopchar_map[(int)'|'] = MAP_PIPE;
|
stopchar_map[(int)'|'] = MAP_PIPE;
|
||||||
stopchar_map[(int)'.'] = MAP_DOT;
|
stopchar_map[(int)'.'] = MAP_DOT | MAP_USERFUNC;
|
||||||
stopchar_map[(int)','] = MAP_COMMA;
|
stopchar_map[(int)','] = MAP_COMMA;
|
||||||
stopchar_map[(int)'$'] = MAP_VARIABLE;
|
stopchar_map[(int)'$'] = MAP_VARIABLE;
|
||||||
|
|
||||||
|
stopchar_map[(int)'-'] = MAP_USERFUNC;
|
||||||
|
stopchar_map[(int)'_'] = MAP_USERFUNC;
|
||||||
|
|
||||||
stopchar_map[(int)'/'] = MAP_PATHSEP;
|
stopchar_map[(int)'/'] = MAP_PATHSEP;
|
||||||
#if defined(VMS)
|
#if defined(VMS)
|
||||||
stopchar_map[(int)']'] = MAP_PATHSEP;
|
stopchar_map[(int)']'] = MAP_PATHSEP;
|
||||||
@ -629,6 +632,8 @@ initialize_stopchar_map ()
|
|||||||
stopchar_map[i] = MAP_BLANK;
|
stopchar_map[i] = MAP_BLANK;
|
||||||
if (isspace(i))
|
if (isspace(i))
|
||||||
stopchar_map[i] |= MAP_SPACE;
|
stopchar_map[i] |= MAP_SPACE;
|
||||||
|
if (isalnum(i))
|
||||||
|
stopchar_map[i] = MAP_USERFUNC;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -396,6 +396,8 @@ extern int unixy_shell;
|
|||||||
#define MAP_DOT 0x0200
|
#define MAP_DOT 0x0200
|
||||||
#define MAP_COMMA 0x0400
|
#define MAP_COMMA 0x0400
|
||||||
|
|
||||||
|
/* These are the valid characters for a user-defined function. */
|
||||||
|
#define MAP_USERFUNC 0x2000
|
||||||
/* This means not only a '$', but skip the variable reference. */
|
/* This means not only a '$', but skip the variable reference. */
|
||||||
#define MAP_VARIABLE 0x4000
|
#define MAP_VARIABLE 0x4000
|
||||||
/* The set of characters which are path separators is OS-specific. */
|
/* The set of characters which are path separators is OS-specific. */
|
||||||
|
|||||||
@ -1,3 +1,9 @@
|
|||||||
|
2013-10-05 Paul Smith <psmith@gnu.org>
|
||||||
|
|
||||||
|
* scripts/features/loadapi: Use new calling signatures. Verify
|
||||||
|
the NOEXPAND flag works. Test with all valid function name
|
||||||
|
characters.
|
||||||
|
|
||||||
2013-09-29 Paul Smith <psmith@gnu.org>
|
2013-09-29 Paul Smith <psmith@gnu.org>
|
||||||
|
|
||||||
* scripts/variables/SHELL: Solaris /bin/sh can't handle options in
|
* scripts/variables/SHELL: Solaris /bin/sh can't handle options in
|
||||||
|
|||||||
@ -36,7 +36,15 @@ test_expand (const char *val)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static char *
|
static char *
|
||||||
func_test (const char *funcname, int argc, char **argv)
|
test_noexpand (const char *val)
|
||||||
|
{
|
||||||
|
char *str = gmk_alloc (strlen (val));
|
||||||
|
strcpy (str, val);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
func_test (const char *funcname, unsigned int argc, char **argv)
|
||||||
{
|
{
|
||||||
char *mem;
|
char *mem;
|
||||||
|
|
||||||
@ -46,7 +54,10 @@ func_test (const char *funcname, int argc, char **argv)
|
|||||||
if (strcmp (funcname, "test-eval") == 0)
|
if (strcmp (funcname, "test-eval") == 0)
|
||||||
return test_eval (argv[0]);
|
return test_eval (argv[0]);
|
||||||
|
|
||||||
mem = gmk_alloc (strlen ("unknown") + 1);
|
if (strcmp (funcname, "test-noexpand") == 0)
|
||||||
|
return test_noexpand (argv[0]);
|
||||||
|
|
||||||
|
mem = gmk_alloc (sizeof ("unknown"));
|
||||||
strcpy (mem, "unknown");
|
strcpy (mem, "unknown");
|
||||||
return mem;
|
return mem;
|
||||||
}
|
}
|
||||||
@ -54,8 +65,10 @@ func_test (const char *funcname, int argc, char **argv)
|
|||||||
int
|
int
|
||||||
testapi_gmk_setup ()
|
testapi_gmk_setup ()
|
||||||
{
|
{
|
||||||
gmk_add_function ("test-expand", func_test, 1, 1, 1);
|
gmk_add_function ("test-expand", func_test, 1, 1, GMK_FUNC_DEFAULT);
|
||||||
gmk_add_function ("test-eval", func_test, 1, 1, 1);
|
gmk_add_function ("test-noexpand", func_test, 1, 1, GMK_FUNC_NOEXPAND);
|
||||||
|
gmk_add_function ("test-eval", func_test, 1, 1, GMK_FUNC_DEFAULT);
|
||||||
|
gmk_add_function ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.", func_test, 0, 0, 0);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
@ -84,6 +97,15 @@ all:;@echo '$(VAR)'
|
|||||||
!,
|
!,
|
||||||
'', "hi there\n");
|
'', "hi there\n");
|
||||||
|
|
||||||
|
# TEST 2
|
||||||
|
# Check the no-expand capability
|
||||||
|
run_make_test(q!
|
||||||
|
load testapi.so
|
||||||
|
TEST = hi
|
||||||
|
all:;@echo '$(test-noexpand $(TEST))'
|
||||||
|
!,
|
||||||
|
'', "\$(TEST)\n");
|
||||||
|
|
||||||
unlink(qw(testapi.c testapi.so)) unless $keep;
|
unlink(qw(testapi.c testapi.so)) unless $keep;
|
||||||
|
|
||||||
# This tells the test driver that the perl test script executed properly.
|
# This tells the test driver that the perl test script executed properly.
|
||||||
|
|||||||
@ -165,9 +165,9 @@ struct variable *try_variable_definition (const gmk_floc *flocp, char *line,
|
|||||||
int target_var);
|
int target_var);
|
||||||
void init_hash_global_variable_set (void);
|
void init_hash_global_variable_set (void);
|
||||||
void hash_init_function_table (void);
|
void hash_init_function_table (void);
|
||||||
void define_new_function(const gmk_floc *flocp,
|
void define_new_function(const gmk_floc *flocp, const char *name,
|
||||||
const char *name, int min, int max, int expand,
|
unsigned int min, unsigned int max, unsigned int flags,
|
||||||
char *(*func)(const char *, int, char **));
|
gmk_func_ptr func);
|
||||||
struct variable *lookup_variable (const char *name, unsigned int length);
|
struct variable *lookup_variable (const char *name, unsigned int length);
|
||||||
struct variable *lookup_variable_in_set (const char *name, unsigned int length,
|
struct variable *lookup_variable_in_set (const char *name, unsigned int length,
|
||||||
const struct variable_set *set);
|
const struct variable_set *set);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user