# HG changeset patch # User Akim Demaille # Date 1339618917 -7200 # Node ID bb4ca9725d0a31cf4a8bf976a6206a11851e3f8e # Parent 3cd0e20dcf8e6978c52ea57d7646ed6ecb85930b relpath: new module * lib/relpath.c: New. * lib/relpath.h: New. * modules/relpath: New. diff -r 3cd0e20dcf8e -r bb4ca9725d0a lib/relpath.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/relpath.c Wed Jun 13 22:21:57 2012 +0200 @@ -0,0 +1,176 @@ +/* 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 . */ + +/* Written by Pádraig Brady. */ + +#include + +#include +#include +#include +#include +#include + +#include "gettext.h" +#define _(msgid) gettext (msgid) + +#include "canonicalize.h" +#include "dirname.h" +#include "error.h" +#include "relpath.h" +#include "xalloc.h" + +#include "pathmax.h" +#ifndef PATH_MAX +# define PATH_MAX 8192 +#endif + + +/* 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; +} + +/* Return FROM represented as relative to the dir of TARGET. + The result is malloced. */ + +char * +convert_abs_rel (const char *from, const char *target) +{ + char *realtarget = canonicalize_filename_mode (target, CAN_MISSING); + char *realfrom = canonicalize_filename_mode (from, CAN_MISSING); + + /* Write to a PATH_MAX buffer. */ + char *relative_from = xmalloc (PATH_MAX); + + /* Get dirname to generate paths relative to. */ + realtarget[dir_len (realtarget)] = '\0'; + + if (!relpath (realfrom, realtarget, relative_from, PATH_MAX)) + { + free (relative_from); + relative_from = NULL; + } + + free (realtarget); + free (realfrom); + + return relative_from ? relative_from : xstrdup (from); +} diff -r 3cd0e20dcf8e -r bb4ca9725d0a lib/relpath.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/relpath.h Wed Jun 13 22:21:57 2012 +0200 @@ -0,0 +1,34 @@ +/* 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 . */ + +/* Written by Pádraig Brady. */ + +#ifndef _RELPATH_H +# define _RELPATH_H + +/* 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); + +/* Return FROM represented as relative to the dir of TARGET. + The result is malloced. */ + +char * +convert_abs_rel (const char *from, const char *target); + +#endif diff -r 3cd0e20dcf8e -r bb4ca9725d0a modules/relpath --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/relpath Wed Jun 13 22:21:57 2012 +0200 @@ -0,0 +1,28 @@ +Description: +Convert absolute paths to relative. + +Files: +lib/relpath.h +lib/relpath.c + +Depends-on: +canonicalize +dirname +error +pathmax +stdbool +xalloc + +configure.ac: + +Makefile.am: +lib_SOURCES += relpath.c + +Include: +"relpath.h" + +License: +GPLv3+ + +Maintainer: +Pádraig Brady