dd: output final progress before syncing
[coreutils.git] / src / relpath.c
blobbec34f7f32e23410f368a06699589197a9027dae
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. */
19 #include <config.h>
21 #include "error.h"
22 #include "system.h"
23 #include "relpath.h"
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. */
29 ATTRIBUTE_PURE
30 static int
31 path_common_prefix (char const *path1, char const *path2)
33 int i = 0;
34 int ret = 0;
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] == '/'))
40 return 0;
42 while (*path1 && *path2)
44 if (*path1 != *path2)
45 break;
46 if (*path1 == '/')
47 ret = i + 1;
48 path1++;
49 path2++;
50 i++;
53 if ((!*path1 && !*path2)
54 || (!*path1 && *path2 == '/')
55 || (!*path2 && *path1 == '/'))
56 ret = i;
58 return ret;
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. */
66 static bool
67 buffer_or_output (char const *str, char **pbuf, size_t *plen)
69 if (*pbuf)
71 size_t slen = strlen (str);
72 if (slen >= *plen)
73 return true;
74 memcpy (*pbuf, str, slen + 1);
75 *pbuf += slen;
76 *plen -= slen;
78 else
80 fputs (str, stdout);
83 return false;
86 /* Output the relative representation if possible.
87 If BUF is non-NULL, write to that buffer rather than to stdout. */
88 bool
89 relpath (char const *can_fname, char const *can_reldir, char *buf, size_t len)
91 bool buf_err = false;
93 /* Skip the prefix common to --relative-to and path. */
94 int common_index = path_common_prefix (can_reldir, can_fname);
95 if (!common_index)
96 return false;
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 == '/')
103 relto_suffix++;
104 if (*fname_suffix == '/')
105 fname_suffix++;
107 /* Replace remaining components of --relative-to with '..', to get
108 to a common directory. Then output the remainder of fname. */
109 if (*relto_suffix)
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);
118 if (*fname_suffix)
120 buf_err |= buffer_or_output ("/", &buf, &len);
121 buf_err |= buffer_or_output (fname_suffix, &buf, &len);
124 else
126 buf_err |= buffer_or_output (*fname_suffix ? fname_suffix : ".",
127 &buf, &len);
130 if (buf_err)
131 error (0, ENAMETOOLONG, "%s", _("generating relative path"));
133 return !buf_err;