1 /* Return the canonical absolute name of a given file.
2 Copyright (C) 1996-2005 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 2, or (at your option)
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; see the file COPYING.
16 If not, write to the Free Software Foundation,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
23 #include "canonicalize.h"
31 #if defined STDC_HEADERS || defined HAVE_STRING_H
38 # include <sys/param.h>
50 #include "cycle-check.h"
51 #include "filenamecat.h"
52 #include "stat-macros.h"
57 # define __set_errno(Val) errno = (Val)
61 #include "xreadlink.h"
63 #if !HAVE_CANONICALIZE_FILE_NAME
64 /* Return the canonical absolute name of file NAME. A canonical name
65 does not contain any `.', `..' components nor any repeated file name
66 separators ('/') or symlinks. All components must exist.
67 The result is malloc'd. */
70 canonicalize_file_name (const char *name
)
74 char *resolved
, *extra_buf
= NULL
;
90 /* All known hosts with resolvepath (e.g. Solaris 7) don't turn
91 relative names into absolute ones, so prepend the working
92 directory if the file name is not absolute. */
97 if (!(wd
= xgetcwd ()))
100 extra_buf
= file_name_concat (wd
, name
, NULL
);
105 resolved_size
= strlen (name
);
108 resolved_size
= 2 * resolved_size
+ 1;
109 resolved
= xmalloc (resolved_size
);
110 resolved_len
= resolvepath (name
, resolved
, resolved_size
);
111 if (resolved_len
< 0)
117 if (resolved_len
< resolved_size
)
124 /* NUL-terminate the resulting name. */
125 resolved
[resolved_len
] = '\0';
131 return canonicalize_filename_mode (name
, CAN_EXISTING
);
133 # endif /* !HAVE_RESOLVEPATH */
135 #endif /* !HAVE_CANONICALIZE_FILE_NAME */
137 /* Return the canonical absolute name of file NAME. A canonical name
138 does not contain any `.', `..' components nor any repeated file name
139 separators ('/') or symlinks. Whether components must exist
140 or not depends on canonicalize mode. The result is malloc'd. */
143 canonicalize_filename_mode (const char *name
, canonicalize_mode_t can_mode
)
145 char *rname
, *dest
, *extra_buf
= NULL
;
148 char const *rname_limit
;
149 size_t extra_len
= 0;
150 struct cycle_check_state cycle_state
;
154 __set_errno (EINVAL
);
160 __set_errno (ENOENT
);
169 dest
= strchr (rname
, '\0');
170 if (dest
- rname
< PATH_MAX
)
172 char *p
= xrealloc (rname
, PATH_MAX
);
173 dest
= p
+ (dest
- rname
);
175 rname_limit
= rname
+ PATH_MAX
;
184 rname
= xmalloc (PATH_MAX
);
185 rname_limit
= rname
+ PATH_MAX
;
190 cycle_check_init (&cycle_state
);
191 for (start
= end
= name
; *start
; start
= end
)
193 /* Skip sequence of multiple file name separators. */
194 while (*start
== '/')
197 /* Find end of component. */
198 for (end
= start
; *end
&& *end
!= '/'; ++end
)
201 if (end
- start
== 0)
203 else if (end
- start
== 1 && start
[0] == '.')
205 else if (end
- start
== 2 && start
[0] == '.' && start
[1] == '.')
207 /* Back up to previous component, ignore if at root already. */
208 if (dest
> rname
+ 1)
209 while ((--dest
)[-1] != '/');
218 if (dest
+ (end
- start
) >= rname_limit
)
220 ptrdiff_t dest_offset
= dest
- rname
;
221 size_t new_size
= rname_limit
- rname
;
223 if (end
- start
+ 1 > PATH_MAX
)
224 new_size
+= end
- start
+ 1;
226 new_size
+= PATH_MAX
;
227 rname
= xrealloc (rname
, new_size
);
228 rname_limit
= rname
+ new_size
;
230 dest
= rname
+ dest_offset
;
233 dest
= memcpy (dest
, start
, end
- start
);
237 if (lstat (rname
, &st
) != 0)
239 if (can_mode
== CAN_EXISTING
)
241 if (can_mode
== CAN_ALL_BUT_LAST
&& *end
)
246 if (S_ISLNK (st
.st_mode
))
251 if (cycle_check (&cycle_state
, &st
))
254 if (can_mode
== CAN_MISSING
)
260 buf
= xreadlink (rname
, st
.st_size
);
263 if (can_mode
== CAN_MISSING
)
275 ((n
+ len
+ 1) > PATH_MAX
) ? (n
+ len
+ 1) : PATH_MAX
;
276 extra_buf
= xmalloc (extra_len
);
278 else if ((n
+ len
+ 1) > extra_len
)
280 extra_len
= n
+ len
+ 1;
281 extra_buf
= xrealloc (extra_buf
, extra_len
);
284 /* Careful here, end may be a pointer into extra_buf... */
285 memmove (&extra_buf
[n
], end
, len
+ 1);
286 name
= end
= memcpy (extra_buf
, buf
, n
);
289 dest
= rname
+ 1; /* It's an absolute symlink */
291 /* Back up to previous component, ignore if at root already: */
292 if (dest
> rname
+ 1)
293 while ((--dest
)[-1] != '/');
299 if (!S_ISDIR (st
.st_mode
) && *end
&& (can_mode
!= CAN_MISSING
))
307 if (dest
> rname
+ 1 && dest
[-1] == '/')