Mercurial > gnulib
view lib/relpath.c @ 16909:6b45aca5c4b2 akim/relpath
relpath: do not depend on xalloc.h.
Suggested by Bruno Haible.
* lib/relpath.c (convert_abs_rel): Embrace malloc failures.
Simplify some conditionals.
* lib/relpath.h: Adjust the documentation.
* modules/relpath (Depends-on): Remove xalloc.
author | Akim Demaille <akim@lrde.epita.fr> |
---|---|
date | Mon, 18 Jun 2012 11:41:32 +0200 |
parents | bb4ca9725d0a |
children |
line wrap: on
line source
/* 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 <stdbool.h> #include <stdlib.h> #include <string.h> #include <stdio.h> #include <errno.h> #include "gettext.h" #define _(msgid) gettext (msgid) #include "canonicalize.h" #include "dirname.h" #include "error.h" #include "relpath.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; } 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 = NULL; char *realfrom = NULL; char *relative_from = NULL; char *res = NULL; realtarget = canonicalize_filename_mode (target, CAN_MISSING); if (!realtarget) goto end; realfrom = canonicalize_filename_mode (from, CAN_MISSING); if (!realfrom) goto end; /* Write to a PATH_MAX buffer. */ relative_from = malloc (PATH_MAX); if (!relative_from) { /* It's easier to set errno to ENOMEM than to rely on the 'malloc-posix' gnulib module. */ errno = ENOMEM; goto end; } /* 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; } res = relative_from ? relative_from : xstrdup (from); end: { int saved_errno = errno; free (realtarget); free (realfrom); errno = saved_errno; } return res; }