summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog13
-rw-r--r--configure.in6
-rw-r--r--doc/make.texi32
-rw-r--r--function.c163
-rw-r--r--tests/ChangeLog8
-rw-r--r--tests/scripts/functions/abspath81
-rw-r--r--tests/scripts/functions/realpath71
7 files changed, 368 insertions, 6 deletions
diff --git a/ChangeLog b/ChangeLog
index e6e7334..31b15f4 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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
diff --git a/configure.in b/configure.in
index 15c2b88..39a8b66 100644
--- a/configure.in
+++ b/configure.in
@@ -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
diff --git a/doc/make.texi b/doc/make.texi
index 14d7278..e814ec2 100644
--- a/doc/make.texi
+++ b/doc/make.texi
@@ -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
diff --git a/function.c b/function.c
index ed5cc44..bbb86d2 100644
--- a/function.c
+++ b/function.c
@@ -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},
diff --git a/tests/ChangeLog b/tests/ChangeLog
index cef8a33..9f68a05 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -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;
diff --git a/tests/scripts/functions/abspath b/tests/scripts/functions/abspath
new file mode 100644
index 0000000..d419255
--- /dev/null
+++ b/tests/scripts/functions/abspath
@@ -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;
diff --git a/tests/scripts/functions/realpath b/tests/scripts/functions/realpath
new file mode 100644
index 0000000..720af8b
--- /dev/null
+++ b/tests/scripts/functions/realpath
@@ -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;