diff options
-rw-r--r-- | po/POTFILES.in | 1 | ||||
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/realpath.c | 96 | ||||
-rw-r--r-- | src/relpath.c | 133 | ||||
-rw-r--r-- | src/relpath.h | 25 |
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 |