Implemented realpath' and abspath' built-in functions.

This commit is contained in:
Boris Kolpackov 2004-11-30 19:51:24 +00:00
parent be6a8bc869
commit 49ca261bd5
7 changed files with 368 additions and 6 deletions

View file

@ -1,3 +1,16 @@
2004-11-30 Boris Kolpackov <boris@kolpackov.net>
Implementation of `realpath' and `abspath' built-in functions.
* configure.in: Check for realpath.
* function.c (abspath): Return an absolute file name that does
not contain any `.' or `..' components, nor repeated `/'.
* function.c (func_abspath): For each name call abspath.
* function.c (func_realpath): For each name call realpath
from libc or delegate to abspath if realpath is not available.
* doc/make.texi (Functions for File Names): Document new functions.
* doc/make.texi (Quick Reference): Ditto.
2004-11-28 Paul D. Smith <psmith@gnu.org>
* main.c (main) [WINDOWS32]: Remove any trailing slashes from -C

View file

@ -134,9 +134,9 @@ if test "$ac_cv_func_gettimeofday" = yes; then
fi
AC_CHECK_FUNCS( memcpy memmove strchr strdup mkstemp mktemp fdopen \
bsd_signal dup2 getcwd sigsetmask sigaction getgroups \
seteuid setegid setlinebuf setreuid setregid setvbuf pipe \
strerror strsignal)
bsd_signal dup2 getcwd realpath sigsetmask sigaction \
getgroups seteuid setegid setlinebuf setreuid setregid \
setvbuf pipe strerror strsignal)
AC_FUNC_SETVBUF_REVERSED

View file

@ -6188,6 +6188,27 @@ wildcard characters (as in shell file name patterns). The result of
@code{wildcard} is a space-separated list of the names of existing files
that match the pattern.
@xref{Wildcards, ,Using Wildcard Characters in File Names}.
@item $(realpath @var{names}@dots{})
@findex realpath
@cindex realpath
@cindex file name, realpath of
For each file name in @var{names} return the canonical absolute name.
A canonical name does not contain any @code{.} or @code{..} components,
nor any repeated path separators (@code{/}) or symlinks. In case of a
failure the empty string is returned. Consult the @code{realpath(3)}
documentation for a list of possible failure causes.
@item $(abspath @var{names}@dots{})
@findex abspath
@cindex abspath
@cindex file name, abspath of
For each file name in @var{names} return an absolute name that does
not contain any @code{.} or @code{..} components, nor any repeated path
separators (@code{/}). Note that in contrast to @code{realpath}
function, @code{abspath} does not resolve symlinks and does not require
the file names to refer to an existing file or directory. Use the
@code{wildcard} function to test for existence.
@end table
@node Foreach Function, If Function, File Name Functions, Functions
@ -9756,6 +9777,17 @@ Find file names matching a shell file name pattern (@emph{not} a
@samp{%} pattern).@*
@xref{Wildcard Function, ,The Function @code{wildcard}}.
@item $(realpath @var{names}@dots{})
For each file name in @var{names}, expand to an absolute name that
does not contain any @code{.}, @code{..}, nor symlinks.@*
@xref{File Name Functions, ,Functions for File Names}.
@item $(abspath @var{names}@dots{})
For each file name in @var{names}, expand to an absolute name that
does not contain any @code{.} or @code{..} components, but preserves
symlinks.@*
@xref{File Name Functions, ,Functions for File Names}.
@item $(error @var{text}@dots{})
When this function is evaluated, @code{make} generates a fatal error

View file

@ -1715,7 +1715,7 @@ func_shell (char *o, char **argv, const char *funcname)
equality. Return is string-boolean, ie, the empty string is false.
*/
static char *
func_eq (char* o, char **argv, char *funcname)
func_eq (char *o, char **argv, char *funcname)
{
int result = ! strcmp (argv[0], argv[1]);
o = variable_buffer_output (o, result ? "1" : "", result);
@ -1727,9 +1727,9 @@ func_eq (char* o, char **argv, char *funcname)
string-boolean not operator.
*/
static char *
func_not (char* o, char **argv, char *funcname)
func_not (char *o, char **argv, char *funcname)
{
char * s = argv[0];
char *s = argv[0];
int result = 0;
while (isspace ((unsigned char)*s))
s++;
@ -1740,6 +1740,161 @@ func_not (char* o, char **argv, char *funcname)
#endif
/* Return the absolute name of file NAME which does not contain any `.',
`..' components nor any repeated path separators ('/'). */
static char *
abspath (const char *name, char *apath)
{
char *dest;
const char *start, *end, *apath_limit;
if (name[0] == '\0' || apath == NULL)
return NULL;
apath_limit = apath + PATH_MAX;
if (name[0] != '/')
{
/* It is unlikely we would make it until here but just to make sure. */
if (!starting_directory)
return NULL;
strcpy (apath, starting_directory);
dest = strchr (apath, '\0');
}
else
{
apath[0] = '/';
dest = apath + 1;
}
for (start = end = name; *start != '\0'; start = end)
{
unsigned long len;
/* Skip sequence of multiple path-separators. */
while (*start == '/')
++start;
/* Find end of path component. */
for (end = start; *end != '\0' && *end != '/'; ++end)
;
len = end - start;
if (len == 0)
break;
else if (len == 1 && start[0] == '.')
/* nothing */;
else if (len == 2 && start[0] == '.' && start[1] == '.')
{
/* Back up to previous component, ignore if at root already. */
if (dest > apath + 1)
while ((--dest)[-1] != '/');
}
else
{
if (dest[-1] != '/')
*dest++ = '/';
if (dest + len >= apath_limit)
return NULL;
dest = memcpy (dest, start, len);
dest += len;
*dest = '\0';
}
}
/* Unless it is root strip trailing separator. */
if (dest > apath + 1 && dest[-1] == '/')
--dest;
*dest = '\0';
return apath;
}
static char *
func_realpath (char *o, char **argv, const char *funcname UNUSED)
{
/* Expand the argument. */
char *p = argv[0];
char *path = 0;
int doneany = 0;
unsigned int len = 0;
char in[PATH_MAX];
char out[PATH_MAX];
while ((path = find_next_token (&p, &len)) != 0)
{
if (len < PATH_MAX)
{
strncpy (in, path, len);
in[len] = '\0';
if
(
#ifdef HAVE_REALPATH
realpath (in, out)
#else
abspath (in, out)
#endif
)
{
o = variable_buffer_output (o, out, strlen (out));
o = variable_buffer_output (o, " ", 1);
doneany = 1;
}
}
}
/* Kill last space. */
if (doneany)
--o;
return o;
}
static char *
func_abspath (char *o, char **argv, const char *funcname UNUSED)
{
/* Expand the argument. */
char *p = argv[0];
char *path = 0;
int doneany = 0;
unsigned int len = 0;
char in[PATH_MAX];
char out[PATH_MAX];
while ((path = find_next_token (&p, &len)) != 0)
{
if (len < PATH_MAX)
{
strncpy (in, path, len);
in[len] = '\0';
if (abspath (in, out))
{
o = variable_buffer_output (o, out, strlen (out));
o = variable_buffer_output (o, " ", 1);
doneany = 1;
}
}
}
/* Kill last space. */
if (doneany)
--o;
return o;
}
/* Lookup table for builtin functions.
This doesn't have to be sorted; we use a straight lookup. We might gain
@ -1758,6 +1913,7 @@ static char *func_call PARAMS ((char *o, char **argv, const char *funcname));
static struct function_table_entry function_table_init[] =
{
/* Name/size */ /* MIN MAX EXP? Function */
{ STRING_SIZE_TUPLE("abspath"), 0, 1, 1, func_abspath},
{ STRING_SIZE_TUPLE("addprefix"), 2, 2, 1, func_addsuffix_addprefix},
{ STRING_SIZE_TUPLE("addsuffix"), 2, 2, 1, func_addsuffix_addprefix},
{ STRING_SIZE_TUPLE("basename"), 0, 1, 1, func_basename_dir},
@ -1772,6 +1928,7 @@ static struct function_table_entry function_table_init[] =
{ STRING_SIZE_TUPLE("join"), 2, 2, 1, func_join},
{ STRING_SIZE_TUPLE("lastword"), 0, 1, 1, func_lastword},
{ STRING_SIZE_TUPLE("patsubst"), 3, 3, 1, func_patsubst},
{ STRING_SIZE_TUPLE("realpath"), 0, 1, 1, func_realpath},
{ STRING_SIZE_TUPLE("shell"), 0, 1, 1, func_shell},
{ STRING_SIZE_TUPLE("sort"), 0, 1, 1, func_sort},
{ STRING_SIZE_TUPLE("strip"), 0, 1, 1, func_strip},

View file

@ -1,3 +1,11 @@
2004-11-30 Boris Kolpackov <boris@kolpackov.net>
* tests/scripts/functions/abspath: New file: test `abspath'
built-in function.
* tests/scripts/functions/realpath: New file: test `realpath'
built-in function.
2004-11-28 Paul D. Smith <psmith@gnu.org>
* scripts/options/dash-C [WINDOWS32]: Add a test for bug #10252;

View file

@ -0,0 +1,81 @@
# -*-perl-*-
$description = "Test the abspath functions.";
$details = "";
run_make_test('
ifneq ($(realpath $(abspath .)),$(CURDIR))
$(error )
endif
ifneq ($(realpath $(abspath ./)),$(CURDIR))
$(error )
endif
ifneq ($(realpath $(abspath .///)),$(CURDIR))
$(error )
endif
ifneq ($(abspath /),/)
$(error )
endif
ifneq ($(abspath ///),/)
$(error )
endif
ifneq ($(abspath /.),/)
$(error )
endif
ifneq ($(abspath ///.),/)
$(error )
endif
ifneq ($(abspath /./),/)
$(error )
endif
ifneq ($(abspath /.///),/)
$(error )
endif
ifneq ($(abspath /..),/)
$(error )
endif
ifneq ($(abspath ///..),/)
$(error )
endif
ifneq ($(abspath /../),/)
$(error )
endif
ifneq ($(abspath /..///),/)
$(error )
endif
ifneq ($(abspath /foo/bar/..),/foo)
$(error )
endif
ifneq ($(abspath /foo/bar/../../../baz),/baz)
$(error )
endif
ifneq ($(abspath /foo/bar/../ /..),/foo /)
$(error )
endif
.PHONY: all
all: ; @:
',
'',
'');
# This tells the test driver that the perl test script executed properly.
1;

View file

@ -0,0 +1,71 @@
# -*-perl-*-
$description = "Test the realpath functions.";
$details = "";
run_make_test('
ifneq ($(realpath .),$(CURDIR))
$(error )
endif
ifneq ($(realpath ./),$(CURDIR))
$(error )
endif
ifneq ($(realpath .///),$(CURDIR))
$(error )
endif
ifneq ($(realpath /),/)
$(error )
endif
ifneq ($(realpath ///),/)
$(error )
endif
ifneq ($(realpath /.),/)
$(error )
endif
ifneq ($(realpath ///.),/)
$(error )
endif
ifneq ($(realpath /./),/)
$(error )
endif
ifneq ($(realpath /.///),/)
$(error )
endif
ifneq ($(realpath /..),/)
$(error )
endif
ifneq ($(realpath ///..),/)
$(error )
endif
ifneq ($(realpath /../),/)
$(error )
endif
ifneq ($(realpath /..///),/)
$(error )
endif
ifneq ($(realpath . /..),$(CURDIR) /)
$(error )
endif
.PHONY: all
all: ; @:
',
'',
'');
# This tells the test driver that the perl test script executed properly.
1;