1 /* relpath - print the relative path
2 Copyright (C) 2012-2024 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. */
25 /* Return the length of the longest common prefix
26 of canonical PATH1 and PATH2, ensuring only full path components
27 are matched. Return 0 on no match. */
30 path_common_prefix (char const *path1
, char const *path2
)
35 /* We already know path1[0] and path2[0] are '/'. Special case
36 '//', which is only present in a canonical name on platforms
37 where it is distinct. */
38 if ((path1
[1] == '/') != (path2
[1] == '/'))
41 while (*path1
&& *path2
)
52 if ((!*path1
&& !*path2
)
53 || (!*path1
&& *path2
== '/')
54 || (!*path2
&& *path1
== '/'))
60 /* Either output STR to stdout or
61 if *PBUF is not null then append STR to *PBUF
62 and update *PBUF to point to the end of the buffer
63 and adjust *PLEN to reflect the remaining space.
64 Return TRUE on failure. */
66 buffer_or_output (char const *str
, char **pbuf
, size_t *plen
)
70 size_t slen
= strlen (str
);
73 memcpy (*pbuf
, str
, slen
+ 1);
85 /* Output the relative representation if possible.
86 If BUF is non-null, write to that buffer rather than to stdout. */
88 relpath (char const *can_fname
, char const *can_reldir
, char *buf
, size_t len
)
92 /* Skip the prefix common to --relative-to and path. */
93 int common_index
= path_common_prefix (can_reldir
, can_fname
);
97 char const *relto_suffix
= can_reldir
+ common_index
;
98 char const *fname_suffix
= can_fname
+ common_index
;
100 /* Skip over extraneous '/'. */
101 if (*relto_suffix
== '/')
103 if (*fname_suffix
== '/')
106 /* Replace remaining components of --relative-to with '..', to get
107 to a common directory. Then output the remainder of fname. */
110 buf_err
|= buffer_or_output ("..", &buf
, &len
);
111 for (; *relto_suffix
; ++relto_suffix
)
113 if (*relto_suffix
== '/')
114 buf_err
|= buffer_or_output ("/..", &buf
, &len
);
119 buf_err
|= buffer_or_output ("/", &buf
, &len
);
120 buf_err
|= buffer_or_output (fname_suffix
, &buf
, &len
);
125 buf_err
|= buffer_or_output (*fname_suffix
? fname_suffix
: ".",
130 error (0, ENAMETOOLONG
, "%s", _("generating relative path"));