summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--po/POTFILES.in1
-rw-r--r--src/Makefile.am1
-rw-r--r--src/realpath.c96
-rw-r--r--src/relpath.c133
-rw-r--r--src/relpath.h25
5 files changed, 164 insertions, 92 deletions
diff --git a/po/POTFILES.in b/po/POTFILES.in
index c92dd3c3e..7b3de0b01 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -103,6 +103,7 @@ src/ptx.c
src/pwd.c
src/readlink.c
src/realpath.c
+src/relpath.c
src/remove.c
src/rm.c
src/rmdir.c
diff --git a/src/Makefile.am b/src/Makefile.am
index b12406477..85f12d65a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -477,6 +477,7 @@ ls_SOURCES = ls.c ls-ls.c
chown_SOURCES = chown.c chown-core.c
chgrp_SOURCES = chgrp.c chown-core.c
kill_SOURCES = kill.c operand2sig.c
+realpath_SOURCES = realpath.c relpath.c relpath.h
timeout_SOURCES = timeout.c operand2sig.c
mv_SOURCES = mv.c remove.c $(copy_sources)
diff --git a/src/realpath.c b/src/realpath.c
index 206f8006d..cd595b8c7 100644
--- a/src/realpath.c
+++ b/src/realpath.c
@@ -25,6 +25,7 @@
#include "canonicalize.h"
#include "error.h"
#include "quote.h"
+#include "relpath.h"
/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "realpath"
@@ -136,97 +137,6 @@ path_prefix (const char *prefix, const char *path)
return (!*prefix && (*path == '/' || !*path));
}
-/* Return the length of the longest common prefix
- of canonical PATH1 and PATH2, ensuring only full path components
- are matched. Return 0 on no match. */
-static int _GL_ATTRIBUTE_PURE
-path_common_prefix (const char *path1, const char *path2)
-{
- int i = 0;
- int ret = 0;
-
- /* We already know path1[0] and path2[0] are '/'. Special case
- '//', which is only present in a canonical name on platforms
- where it is distinct. */
- if ((path1[1] == '/') != (path2[1] == '/'))
- return 0;
-
- while (*path1 && *path2)
- {
- if (*path1 != *path2)
- break;
- if (*path1 == '/')
- ret = i + 1;
- path1++;
- path2++;
- i++;
- }
-
- if (!*path1 && !*path2)
- ret = i;
- if (!*path1 && *path2 == '/')
- ret = i;
- if (!*path2 && *path1 == '/')
- ret = i;
-
- return ret;
-}
-
-/* Output the relative representation if requested. */
-static bool
-relpath (const char *can_fname)
-{
- if (can_relative_to)
- {
- /* Enforce --relative-base. */
- if (can_relative_base && !path_prefix (can_relative_base, can_fname))
- return false;
-
- /* Skip the prefix common to --relative-to and path. */
- int common_index = path_common_prefix (can_relative_to, can_fname);
- if (!common_index)
- return false;
-
- const char *relto_suffix = can_relative_to + common_index;
- const char *fname_suffix = can_fname + common_index;
-
- /* skip over extraneous '/'. */
- if (*relto_suffix == '/')
- relto_suffix++;
- if (*fname_suffix == '/')
- fname_suffix++;
-
- /* Replace remaining components of --relative-to with '..', to get
- to a common directory. Then output the remainder of fname. */
- if (*relto_suffix)
- {
- fputs ("..", stdout);
- for (; *relto_suffix; ++relto_suffix)
- {
- if (*relto_suffix == '/')
- fputs ("/..", stdout);
- }
-
- if (*fname_suffix)
- {
- putchar ('/');
- fputs (fname_suffix, stdout);
- }
- }
- else
- {
- if (*fname_suffix)
- fputs (fname_suffix, stdout);
- else
- putchar ('.');
- }
-
- return true;
- }
-
- return false;
-}
-
static bool
isdir (const char *path)
{
@@ -247,7 +157,9 @@ process_path (const char *fname, int can_mode)
return false;
}
- if (!relpath (can_fname))
+ if (!can_relative_to
+ || (can_relative_base && !path_prefix (can_relative_base, can_fname))
+ || (can_relative_to && !relpath (can_fname, can_relative_to, NULL, 0)))
fputs (can_fname, stdout);
putchar (use_nuls ? '\0' : '\n');
diff --git a/src/relpath.c b/src/relpath.c
new file mode 100644
index 000000000..4405361c3
--- /dev/null
+++ b/src/relpath.c
@@ -0,0 +1,133 @@
+/* relpath - print the relative path
+ Copyright (C) 2012 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* Written by Pádraig Brady. */
+
+#include <config.h>
+
+#include "error.h"
+#include "system.h"
+#include "relpath.h"
+
+
+/* Return the length of the longest common prefix
+ of canonical PATH1 and PATH2, ensuring only full path components
+ are matched. Return 0 on no match. */
+static int _GL_ATTRIBUTE_PURE
+path_common_prefix (const char *path1, const char *path2)
+{
+ int i = 0;
+ int ret = 0;
+
+ /* We already know path1[0] and path2[0] are '/'. Special case
+ '//', which is only present in a canonical name on platforms
+ where it is distinct. */
+ if ((path1[1] == '/') != (path2[1] == '/'))
+ return 0;
+
+ while (*path1 && *path2)
+ {
+ if (*path1 != *path2)
+ break;
+ if (*path1 == '/')
+ ret = i + 1;
+ path1++;
+ path2++;
+ i++;
+ }
+
+ if ((!*path1 && !*path2)
+ || (!*path1 && *path2 == '/')
+ || (!*path2 && *path1 == '/'))
+ ret = i;
+
+ return ret;
+}
+
+/* Either output STR to stdout or
+ if *PBUF is not NULL then append STR to *PBUF
+ and update *PBUF to point to the end of the buffer
+ and adjust *PLEN to reflect the remaining space.
+ Return TRUE on failure. */
+static bool
+buffer_or_output (const char* str, char **pbuf, size_t *plen)
+{
+ if (*pbuf)
+ {
+ size_t slen = strlen (str);
+ if (slen >= *plen)
+ return true;
+ memcpy (*pbuf, str, slen + 1);
+ *pbuf += slen;
+ *plen -= slen;
+ }
+ else
+ {
+ fputs (str, stdout);
+ }
+
+ return false;
+}
+
+/* Output the relative representation if possible.
+ If BUF is non NULL, write to that buffer rather than to stdout. */
+bool
+relpath (const char *can_fname, const char *can_reldir, char *buf, size_t len)
+{
+ bool buf_err = false;
+
+ /* Skip the prefix common to --relative-to and path. */
+ int common_index = path_common_prefix (can_reldir, can_fname);
+ if (!common_index)
+ return false;
+
+ const char *relto_suffix = can_reldir + common_index;
+ const char *fname_suffix = can_fname + common_index;
+
+ /* Skip over extraneous '/'. */
+ if (*relto_suffix == '/')
+ relto_suffix++;
+ if (*fname_suffix == '/')
+ fname_suffix++;
+
+ /* Replace remaining components of --relative-to with '..', to get
+ to a common directory. Then output the remainder of fname. */
+ if (*relto_suffix)
+ {
+ buf_err |= buffer_or_output ("..", &buf, &len);
+ for (; *relto_suffix; ++relto_suffix)
+ {
+ if (*relto_suffix == '/')
+ buf_err |= buffer_or_output ("/..", &buf, &len);
+ }
+
+ if (*fname_suffix)
+ {
+ buf_err |= buffer_or_output ("/", &buf, &len);
+ buf_err |= buffer_or_output (fname_suffix, &buf, &len);
+ }
+ }
+ else
+ {
+ buf_err |= buffer_or_output (*fname_suffix ? fname_suffix : ".",
+ &buf, &len);
+ }
+
+ if (buf_err)
+ error (0, ENAMETOOLONG, "%s", _("generating relative path"));
+
+ return !buf_err;
+}
diff --git a/src/relpath.h b/src/relpath.h
new file mode 100644
index 000000000..e25f82b22
--- /dev/null
+++ b/src/relpath.h
@@ -0,0 +1,25 @@
+/* relpath - print the relative path
+ Copyright (C) 2012 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* Written by Pádraig Brady. */
+
+#ifndef _RELPATH_H
+# define _RELPATH_H
+
+extern bool
+relpath (const char *can_fname, const char *can_reldir, char *buf, size_t len);
+
+#endif