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
< 0)
156 if (resolved_len
< resolved_size
)
163 /* NUL-terminate the resulting name. */
164 resolved
[resolved_len
] = '\0';
168 #else /* !HAVE_RESOLVEPATH */
175 dest
= strchr (rpath
, '\0');
176 if (dest
- rpath
< PATH_MAX
)
178 char *p
= xrealloc (rpath
, PATH_MAX
);
179 dest
= p
+ (dest
- rpath
);
181 rpath_limit
= rpath
+ PATH_MAX
;
190 rpath
= xmalloc (PATH_MAX
);
191 rpath_limit
= rpath
+ PATH_MAX
;
196 for (start
= end
= name
; *start
; start
= end
)
198 /* Skip sequence of multiple path-separators. */
199 while (*start
== '/')
202 /* Find end of path component. */
203 for (end
= start
; *end
&& *end
!= '/'; ++end
)
206 if (end
- start
== 0)
208 else if (end
- start
== 1 && start
[0] == '.')
210 else if (end
- start
== 2 && start
[0] == '.' && start
[1] == '.')
212 /* Back up to previous component, ignore if at root already. */
213 if (dest
> rpath
+ 1)
214 while ((--dest
)[-1] != '/');
223 if (dest
+ (end
- start
) >= rpath_limit
)
225 PTR_INT_TYPE dest_offset
= dest
- rpath
;
226 size_t new_size
= rpath_limit
- rpath
;
228 if (end
- start
+ 1 > PATH_MAX
)
229 new_size
+= end
- start
+ 1;
231 new_size
+= PATH_MAX
;
232 rpath
= xrealloc (rpath
, new_size
);
233 rpath_limit
= rpath
+ new_size
;
235 dest
= rpath
+ dest_offset
;
238 dest
= memcpy (dest
, start
, end
- start
);
242 if (lstat (rpath
, &st
) < 0)
246 if (S_ISLNK (st
.st_mode
))
252 if (++num_links
> MAXSYMLINKS
)
257 # endif /* MAXSYMLINKS */
259 buf
= xreadlink (rpath
);
269 ((n
+ len
+ 1) > PATH_MAX
) ? (n
+ len
+ 1) : PATH_MAX
;
270 extra_buf
= xmalloc (extra_len
);
272 else if ((n
+ len
+ 1) > extra_len
)
274 extra_len
= n
+ len
+ 1;
275 extra_buf
= xrealloc (extra_buf
, extra_len
);
278 /* Careful here, end may be a pointer into extra_buf... */
279 memmove (&extra_buf
[n
], end
, len
+ 1);
280 name
= end
= memcpy (extra_buf
, buf
, n
);
283 dest
= rpath
+ 1; /* It's an absolute symlink */
285 /* Back up to previous component, ignore if at root already: */
286 if (dest
> rpath
+ 1)
287 while ((--dest
)[-1] != '/');
291 # endif /* S_ISLNK */
294 if (dest
> rpath
+ 1 && dest
[-1] == '/')
305 #endif /* !HAVE_RESOLVEPATH */