* build-aux/depcomp, build-aux/install-sh, build-aux/mdate-sh,
[gnulib.git] / lib / canonicalize.c
1 /* Return the canonical absolute name of a given file.
2    Copyright (C) 1996-2005 Free Software Foundation, Inc.
3
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)
7    any later version.
8
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.
13
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.  */
18
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22
23 #ifdef STDC_HEADERS
24 # include <stdlib.h>
25 #else
26 void free ();
27 #endif
28
29 #if defined STDC_HEADERS || defined HAVE_STRING_H
30 # include <string.h>
31 #else
32 # include <strings.h>
33 #endif
34
35 #if HAVE_SYS_PARAM_H
36 # include <sys/param.h>
37 #endif
38
39 #include <sys/stat.h>
40
41 #if HAVE_UNISTD_H
42 # include <unistd.h>
43 #endif
44
45 #include <errno.h>
46
47 #include "cycle-check.h"
48 #include "path-concat.h"
49 #include "stat-macros.h"
50 #include "xalloc.h"
51 #include "xgetcwd.h"
52
53 #ifndef __set_errno
54 # define __set_errno(Val) errno = (Val)
55 #endif
56
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.  */
62
63 #ifdef __PTRDIFF_TYPE__
64 # define PTR_INT_TYPE __PTRDIFF_TYPE__
65 #else
66 # ifdef HAVE_STDDEF_H
67 #  include <stddef.h>
68 #  define PTR_INT_TYPE ptrdiff_t
69 # else
70 #  define PTR_INT_TYPE long
71 # endif
72 #endif
73
74 #include "canonicalize.h"
75 #include "pathmax.h"
76 #include "xreadlink.h"
77
78 #if !HAVE_CANONICALIZE_FILE_NAME
79 /* Return the canonical absolute name of file NAME.  A canonical name
80    does not contain any `.', `..' components nor any repeated path
81    separators ('/') or symlinks.  All path components must exist.
82    The result is malloc'd.  */
83
84 char *
85 canonicalize_file_name (const char *name)
86 {
87 # if HAVE_RESOLVEPATH
88
89   char *resolved, *extra_buf = NULL;
90   size_t resolved_size;
91   ssize_t resolved_len;
92
93   if (name == NULL)
94     {
95       __set_errno (EINVAL);
96       return NULL;
97     }
98
99   if (name[0] == '\0')
100     {
101       __set_errno (ENOENT);
102       return NULL;
103     }
104
105   /* All known hosts with resolvepath (e.g. Solaris 7) don't turn
106      relative names into absolute ones, so prepend the working
107      directory if the path is not absolute.  */
108   if (name[0] != '/')
109     {
110       char *wd;
111
112       if (!(wd = xgetcwd ()))
113         return NULL;
114
115       extra_buf = path_concat (wd, name, NULL);
116       name = extra_buf;
117       free (wd);
118     }
119
120   resolved_size = strlen (name);
121   while (1)
122     {
123       resolved_size = 2 * resolved_size + 1;
124       resolved = xmalloc (resolved_size);
125       resolved_len = resolvepath (name, resolved, resolved_size);
126       if (resolved_len < 0)
127         {
128           free (resolved);
129           free (extra_buf);
130           return NULL;
131         }
132       if (resolved_len < resolved_size)
133         break;
134       free (resolved);
135     }
136
137   free (extra_buf);
138
139   /* NUL-terminate the resulting name.  */
140   resolved[resolved_len] = '\0';
141
142   return resolved;
143
144 # else
145
146   return canonicalize_filename_mode (name, CAN_EXISTING);
147
148 # endif /* !HAVE_RESOLVEPATH */
149 }
150 #endif /* !HAVE_CANONICALIZE_FILE_NAME */
151
152 /* Return the canonical absolute name of file NAME.  A canonical name
153    does not contain any `.', `..' components nor any repeated path
154    separators ('/') or symlinks.  Whether path components must exist
155    or not depends on canonicalize mode.  The result is malloc'd.  */
156
157 char *
158 canonicalize_filename_mode (const char *name, canonicalize_mode_t can_mode)
159 {
160   char *rpath, *dest, *extra_buf = NULL;
161   const char *start, *end, *rpath_limit;
162   size_t extra_len = 0;
163   struct cycle_check_state cycle_state;
164
165   if (name == NULL)
166     {
167       __set_errno (EINVAL);
168       return NULL;
169     }
170
171   if (name[0] == '\0')
172     {
173       __set_errno (ENOENT);
174       return NULL;
175     }
176
177   if (name[0] != '/')
178     {
179       rpath = xgetcwd ();
180       if (!rpath)
181         return NULL;
182       dest = strchr (rpath, '\0');
183       if (dest - rpath < PATH_MAX)
184         {
185           char *p = xrealloc (rpath, PATH_MAX);
186           dest = p + (dest - rpath);
187           rpath = p;
188           rpath_limit = rpath + PATH_MAX;
189         }
190       else
191         {
192           rpath_limit = dest;
193         }
194     }
195   else
196     {
197       rpath = xmalloc (PATH_MAX);
198       rpath_limit = rpath + PATH_MAX;
199       rpath[0] = '/';
200       dest = rpath + 1;
201     }
202
203   cycle_check_init (&cycle_state);
204   for (start = end = name; *start; start = end)
205     {
206       /* Skip sequence of multiple path-separators.  */
207       while (*start == '/')
208         ++start;
209
210       /* Find end of path component.  */
211       for (end = start; *end && *end != '/'; ++end)
212         /* Nothing.  */;
213
214       if (end - start == 0)
215         break;
216       else if (end - start == 1 && start[0] == '.')
217         /* nothing */;
218       else if (end - start == 2 && start[0] == '.' && start[1] == '.')
219         {
220           /* Back up to previous component, ignore if at root already.  */
221           if (dest > rpath + 1)
222             while ((--dest)[-1] != '/');
223         }
224       else
225         {
226           struct stat st;
227
228           if (dest[-1] != '/')
229             *dest++ = '/';
230
231           if (dest + (end - start) >= rpath_limit)
232             {
233               PTR_INT_TYPE dest_offset = dest - rpath;
234               size_t new_size = rpath_limit - rpath;
235
236               if (end - start + 1 > PATH_MAX)
237                 new_size += end - start + 1;
238               else
239                 new_size += PATH_MAX;
240               rpath = xrealloc (rpath, new_size);
241               rpath_limit = rpath + new_size;
242
243               dest = rpath + dest_offset;
244             }
245
246           dest = memcpy (dest, start, end - start);
247           dest += end - start;
248           *dest = '\0';
249
250           if (lstat (rpath, &st) < 0)
251             {
252               if (can_mode == CAN_EXISTING)
253                 goto error;
254               if (can_mode == CAN_ALL_BUT_LAST && *end)
255                 goto error;
256               st.st_mode = 0;
257             }
258
259           if (S_ISLNK (st.st_mode))
260             {
261               char *buf;
262               size_t n, len;
263
264               if (cycle_check (&cycle_state, &st))
265                 {
266                   __set_errno (ELOOP);
267                   if (can_mode == CAN_MISSING)
268                     continue;
269                   else
270                     goto error;
271                 }
272
273               buf = xreadlink (rpath, st.st_size);
274               if (!buf)
275                 {
276                   if (can_mode == CAN_MISSING)
277                     continue;
278                   else
279                     goto error;
280                 }
281
282               n = strlen (buf);
283               len = strlen (end);
284
285               if (!extra_len)
286                 {
287                   extra_len =
288                     ((n + len + 1) > PATH_MAX) ? (n + len + 1) : PATH_MAX;
289                   extra_buf = xmalloc (extra_len);
290                 }
291               else if ((n + len + 1) > extra_len)
292                 {
293                   extra_len = n + len + 1;
294                   extra_buf = xrealloc (extra_buf, extra_len);
295                 }
296
297               /* Careful here, end may be a pointer into extra_buf... */
298               memmove (&extra_buf[n], end, len + 1);
299               name = end = memcpy (extra_buf, buf, n);
300
301               if (buf[0] == '/')
302                 dest = rpath + 1;       /* It's an absolute symlink */
303               else
304                 /* Back up to previous component, ignore if at root already: */
305                 if (dest > rpath + 1)
306                   while ((--dest)[-1] != '/');
307
308               free (buf);
309             }
310           else
311             {
312               if (!S_ISDIR (st.st_mode) && *end && (can_mode != CAN_MISSING))
313                 {
314                   errno = ENOTDIR;
315                   goto error;
316                 }
317             }
318         }
319     }
320   if (dest > rpath + 1 && dest[-1] == '/')
321     --dest;
322   *dest = '\0';
323
324   free (extra_buf);
325   return rpath;
326
327 error:
328   free (extra_buf);
329   free (rpath);
330   return NULL;
331 }