1 /* Return the canonical absolute name of a given file.
2 Copyright (C) 1996-2001, 2002, 2003 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 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
29 #if defined STDC_HEADERS || defined HAVE_STRING_H
36 # include <sys/param.h>
43 #include "path-concat.h"
52 # define __set_errno(Val) errno = (Val)
57 /* If __PTRDIFF_TYPE__ is
58 defined, as with GNU C, use that; that way we don't pollute the
59 namespace with <stddef.h>'s symbols. Otherwise, if <stddef.h> is
60 available, include it and use ptrdiff_t. In traditional C, long is
61 the best that we can do. */
63 # ifdef __PTRDIFF_TYPE__
64 # define PTR_INT_TYPE __PTRDIFF_TYPE__
68 # define PTR_INT_TYPE ptrdiff_t
70 # define PTR_INT_TYPE long
75 # include "xreadlink.h"
77 # ifdef STAT_MACROS_BROKEN
83 # define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
87 #endif /* !HAVE_RESOLVEPATH */
89 /* Return the canonical absolute name of file NAME. A canonical name
90 does not contain any `.', `..' components nor any repeated path
91 separators ('/') or symlinks. All path components must exist.
92 The result is malloc'd. */
95 canonicalize_file_name (const char *name
)
99 char *resolved
, *extra_buf
= NULL
;
100 size_t resolved_size
;
101 ssize_t resolved_len
;
103 #else /* !HAVE_RESOLVEPATH */
105 char *rpath
, *dest
, *extra_buf
= NULL
;
106 const char *start
, *end
, *rpath_limit
;
107 size_t extra_len
= 0;
110 #endif /* !HAVE_RESOLVEPATH */
114 __set_errno (EINVAL
);
120 __set_errno (ENOENT
);
126 /* All known hosts with resolvepath (e.g. Solaris 7) don't turn
127 relative names into absolute ones, so prepend the working
128 directory if the path is not absolute. */
133 if (!(wd
= xgetcwd ()));
136 extra_buf
= path_concat (wd
, name
, NULL
);
144 resolved_size
= strlen (name
);
147 resolved_size
= 2 * resolved_size
+ 1;
148 resolved
= xmalloc (resolved_size
);
149 resolved_len
= resolvepath (name
, resolved
, resolved_size
);
150 if (resolved_len
< resolved_size
)
155 if (resolved_len
< 0)
164 #else /* !HAVE_RESOLVEPATH */
171 dest
= strchr (rpath
, '\0');
172 if (dest
< rpath
+ PATH_MAX
)
174 rpath
= xrealloc (rpath
, PATH_MAX
);
175 rpath_limit
= rpath
+ PATH_MAX
;
184 rpath
= xmalloc (PATH_MAX
);
185 rpath_limit
= rpath
+ PATH_MAX
;
190 for (start
= end
= name
; *start
; start
= end
)
192 /* Skip sequence of multiple path-separators. */
193 while (*start
== '/')
196 /* Find end of path component. */
197 for (end
= start
; *end
&& *end
!= '/'; ++end
)
200 if (end
- start
== 0)
202 else if (end
- start
== 1 && start
[0] == '.')
204 else if (end
- start
== 2 && start
[0] == '.' && start
[1] == '.')
206 /* Back up to previous component, ignore if at root already. */
207 if (dest
> rpath
+ 1)
208 while ((--dest
)[-1] != '/');
217 if (dest
+ (end
- start
) >= rpath_limit
)
219 PTR_INT_TYPE dest_offset
= dest
- rpath
;
220 size_t new_size
= rpath_limit
- rpath
;
222 if (end
- start
+ 1 > PATH_MAX
)
223 new_size
+= end
- start
+ 1;
225 new_size
+= PATH_MAX
;
226 rpath
= (char *) xrealloc (rpath
, new_size
);
227 rpath_limit
= rpath
+ new_size
;
229 dest
= rpath
+ dest_offset
;
232 dest
= memcpy (dest
, start
, end
- start
);
236 if (lstat (rpath
, &st
) < 0)
240 if (S_ISLNK (st
.st_mode
))
246 if (++num_links
> MAXSYMLINKS
)
251 # endif /* MAXSYMLINKS */
253 buf
= xreadlink (rpath
);
263 ((n
+ len
+ 1) > PATH_MAX
) ? (n
+ len
+ 1) : PATH_MAX
;
264 extra_buf
= xmalloc (extra_len
);
266 else if ((n
+ len
+ 1) > extra_len
)
268 extra_len
= n
+ len
+ 1;
269 extra_buf
= xrealloc (extra_buf
, extra_len
);
272 /* Careful here, end may be a pointer into extra_buf... */
273 memmove (&extra_buf
[n
], end
, len
+ 1);
274 name
= end
= memcpy (extra_buf
, buf
, n
);
277 dest
= rpath
+ 1; /* It's an absolute symlink */
279 /* Back up to previous component, ignore if at root already: */
280 if (dest
> rpath
+ 1)
281 while ((--dest
)[-1] != '/');
285 # endif /* S_ISLNK */
288 if (dest
> rpath
+ 1 && dest
[-1] == '/')
299 #endif /* !HAVE_RESOLVEPATH */