1 /* relpath - print the relative path
2 Copyright (C) 2012-2022 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
17 /* Written by Pádraig Brady. */
26 /* Return the length of the longest common prefix
27 of canonical PATH1 and PATH2, ensuring only full path components
28 are matched. Return 0 on no match. */
31 path_common_prefix (char const *path1
, char const *path2
)
36 /* We already know path1[0] and path2[0] are '/'. Special case
37 '//', which is only present in a canonical name on platforms
38 where it is distinct. */
39 if ((path1
[1] == '/') != (path2
[1] == '/'))
42 while (*path1
&& *path2
)
53 if ((!*path1
&& !*path2
)
54 || (!*path1
&& *path2
== '/')
55 || (!*path2
&& *path1
== '/'))
61 /* Either output STR to stdout or
62 if *PBUF is not NULL then append STR to *PBUF
63 and update *PBUF to point to the end of the buffer
64 and adjust *PLEN to reflect the remaining space.
65 Return TRUE on failure. */
67 buffer_or_output (char const *str
, char **pbuf
, size_t *plen
)
71 size_t slen
= strlen (str
);
74 memcpy (*pbuf
, str
, slen
+ 1);
86 /* Output the relative representation if possible.
87 If BUF is non-NULL, write to that buffer rather than to stdout. */
89 relpath (char const *can_fname
, char const *can_reldir
, char *buf
, size_t len
)
93 /* Skip the prefix common to --relative-to and path. */
94 int common_index
= path_common_prefix (can_reldir
, can_fname
);
98 char const *relto_suffix
= can_reldir
+ common_index
;
99 char const *fname_suffix
= can_fname
+ common_index
;
101 /* Skip over extraneous '/'. */
102 if (*relto_suffix
== '/')
104 if (*fname_suffix
== '/')
107 /* Replace remaining components of --relative-to with '..', to get
108 to a common directory. Then output the remainder of fname. */
111 buf_err
|= buffer_or_output ("..", &buf
, &len
);
112 for (; *relto_suffix
; ++relto_suffix
)
114 if (*relto_suffix
== '/')
115 buf_err
|= buffer_or_output ("/..", &buf
, &len
);
120 buf_err
|= buffer_or_output ("/", &buf
, &len
);
121 buf_err
|= buffer_or_output (fname_suffix
, &buf
, &len
);
126 buf_err
|= buffer_or_output (*fname_suffix
? fname_suffix
: ".",
131 error (0, ENAMETOOLONG
, "%s", _("generating relative path"));