1 /* Libiberty realpath. Like realpath, but more consistent behavior.
2 Based on gdb_realpath from GDB.
4 Copyright (C) 2003-2024 Free Software Foundation, Inc.
6 This file is part of the libiberty library.
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street - Fifth Floor,
21 Boston, MA 02110-1301, USA. */
25 @deftypefn Replacement {const char*} lrealpath (const char *@var{name})
27 Given a pointer to a string containing a pathname, returns a canonical
28 version of the filename. Symlinks will be resolved, and ``.'' and ``..''
29 components will be simplified. The returned value will be allocated using
30 @code{malloc}, or @code{NULL} will be returned on a memory allocation error.
38 #include "libiberty.h"
53 /* On GNU libc systems the declaration is only visible with _GNU_SOURCE. */
54 #if defined(HAVE_CANONICALIZE_FILE_NAME) \
55 && defined(NEED_DECLARATION_CANONICALIZE_FILE_NAME)
56 extern char *canonicalize_file_name (const char *);
59 #if defined(HAVE_REALPATH)
60 # if defined (PATH_MAX)
61 # define REALPATH_LIMIT PATH_MAX
63 # if defined (MAXPATHLEN)
64 # define REALPATH_LIMIT MAXPATHLEN
68 /* cygwin has realpath, so it won't get here. */
70 # define WIN32_LEAN_AND_MEAN
71 # include <windows.h> /* for GetFullPathName/GetFinalPathNameByHandle/
72 CreateFile/CloseHandle */
73 # define WIN32_REPLACE_SLASHES(_ptr, _len) \
74 for (unsigned i = 0; i != (_len); ++i) \
75 if ((_ptr)[i] == '\\') (_ptr)[i] = '/';
77 # define WIN32_UNC_PREFIX "//?/UNC/"
78 # define WIN32_UNC_PREFIX_LEN (sizeof(WIN32_UNC_PREFIX)-1)
79 # define WIN32_IS_UNC_PREFIX(ptr) \
80 (0 == memcmp(ptr, WIN32_UNC_PREFIX, WIN32_UNC_PREFIX_LEN))
82 # define WIN32_NON_UNC_PREFIX "//?/"
83 # define WIN32_NON_UNC_PREFIX_LEN (sizeof(WIN32_NON_UNC_PREFIX)-1)
84 # define WIN32_IS_NON_UNC_PREFIX(ptr) \
85 (0 == memcmp(ptr, WIN32_NON_UNC_PREFIX, WIN32_NON_UNC_PREFIX_LEN))
87 /* Get full path name without symlinks resolution.
88 It also converts all forward slashes to back slashes.
90 char* get_full_path_name(const char *filename
) {
92 char *buf
, *ptr
, *res
;
94 /* determining the required buffer size.
95 from the man: `If the lpBuffer buffer is too small to contain
96 the path, the return value is the size, in TCHARs, of the buffer
97 that is required to hold the path _and_the_terminating_null_character_`
99 len
= GetFullPathName(filename
, 0, NULL
, NULL
);
102 return strdup(filename
);
104 buf
= (char *)malloc(len
);
106 /* no point to check the result again */
107 len
= GetFullPathName(filename
, len
, buf
, NULL
);
110 /* replace slashes */
111 WIN32_REPLACE_SLASHES(buf
, len
);
113 /* calculate offset based on prefix type */
114 len
= WIN32_IS_UNC_PREFIX(buf
)
115 ? (WIN32_UNC_PREFIX_LEN
- 2)
116 : WIN32_IS_NON_UNC_PREFIX(buf
)
117 ? WIN32_NON_UNC_PREFIX_LEN
122 if ( WIN32_IS_UNC_PREFIX(buf
) ) {
134 # if _WIN32_WINNT >= 0x0600
136 /* Get full path name WITH symlinks resolution.
137 It also converts all forward slashes to back slashes.
139 char* get_final_path_name(HANDLE fh
) {
141 char *buf
, *ptr
, *res
;
143 /* determining the required buffer size.
144 from the man: `If the function fails because lpszFilePath is too
145 small to hold the string plus the terminating null character,
146 the return value is the required buffer size, in TCHARs. This
147 value _includes_the_size_of_the_terminating_null_character_`.
148 but in my testcase I have path with 26 chars, the function
149 returns 26 also, ie without the trailing zero-char...
151 len
= GetFinalPathNameByHandle(
155 ,FILE_NAME_NORMALIZED
| VOLUME_NAME_DOS
161 len
+= 1; /* for zero-char */
162 buf
= (char *)malloc(len
);
164 /* no point to check the result again */
165 len
= GetFinalPathNameByHandle(
169 ,FILE_NAME_NORMALIZED
| VOLUME_NAME_DOS
173 /* replace slashes */
174 WIN32_REPLACE_SLASHES(buf
, len
);
176 /* calculate offset based on prefix type */
177 len
= WIN32_IS_UNC_PREFIX(buf
)
178 ? (WIN32_UNC_PREFIX_LEN
- 2)
179 : WIN32_IS_NON_UNC_PREFIX(buf
)
180 ? WIN32_NON_UNC_PREFIX_LEN
185 if ( WIN32_IS_UNC_PREFIX(buf
) ) {
197 # endif // _WIN32_WINNT >= 0x0600
203 lrealpath (const char *filename
)
205 /* Method 1: The system has a compile time upper bound on a filename
206 path. Use that and realpath() to canonicalize the name. This is
207 the most common case. Note that, if there isn't a compile time
208 upper bound, you want to avoid realpath() at all costs. */
209 #if defined(REALPATH_LIMIT)
211 char buf
[REALPATH_LIMIT
];
212 const char *rp
= realpath (filename
, buf
);
217 #endif /* REALPATH_LIMIT */
219 /* Method 2: The host system (i.e., GNU) has the function
220 canonicalize_file_name() which malloc's a chunk of memory and
221 returns that, use that. */
222 #if defined(HAVE_CANONICALIZE_FILE_NAME)
224 char *rp
= canonicalize_file_name (filename
);
226 return strdup (filename
);
232 /* Method 3: Now we're getting desperate! The system doesn't have a
233 compile time buffer size and no alternative function. Query the
234 OS, using pathconf(), for the buffer limit. Care is needed
235 though, some systems do not limit PATH_MAX (return -1 for
236 pathconf()) making it impossible to pass a correctly sized buffer
237 to realpath() (it could always overflow). On those systems, we
239 #if defined (HAVE_REALPATH) && defined (HAVE_UNISTD_H)
241 /* Find out the max path size. */
242 long path_max
= pathconf ("/", _PC_PATH_MAX
);
245 /* PATH_MAX is bounded. */
246 char *buf
, *rp
, *ret
;
247 buf
= (char *) malloc (path_max
);
250 rp
= realpath (filename
, buf
);
251 ret
= strdup (rp
? rp
: filename
);
258 /* The MS Windows method */
263 /* For Windows Vista and greater */
264 #if _WIN32_WINNT >= 0x0600
266 /* For some reason the function receives just empty `filename`, but not NULL.
267 What should we do in that case?
268 According to `strdup()` implementation
269 (https://elixir.bootlin.com/glibc/latest/source/string/strdup.c)
270 it will alloc 1 byte even for empty but non NULL string.
271 OK, will use `strdup()` for that case.
273 if ( 0 == strlen(filename
) )
274 return strdup(filename
);
276 HANDLE fh
= CreateFile(
278 ,FILE_READ_ATTRIBUTES
279 ,FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
282 ,FILE_FLAG_BACKUP_SEMANTICS
286 if ( fh
== INVALID_HANDLE_VALUE
) {
287 res
= get_full_path_name(filename
);
289 res
= get_final_path_name(fh
);
293 res
= get_full_path_name(filename
);
299 res
= get_full_path_name(filename
);
301 #endif // _WIN32_WINNT >= 0x0600