Wed May 22 22:10:01 1996 Roland McGrath <roland@delasyd.gnu.ai.mit.edu>
[kopensolaris-gnu/glibc.git] / stdlib / canonicalize.c
1 /* Return the canonical absolute name of a given file.
2 Copyright (C) 1996 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public
16 License along with the GNU C Library; see the file COPYING.LIB.  If
17 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
18 Cambridge, MA 02139, USA.  */
19
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <limits.h>
24 #include <sys/stat.h>
25 #include <errno.h>
26
27 /* Return the canonical absolute name of file NAME.  The last file name
28    component need not exist, and may be a symlink to a nonexistent file.
29    If RESOLVED is null, the result is malloc'd; otherwise, if the canonical
30    name is PATH_MAX chars or more, returns null with `errno' set to
31    ENAMETOOLONG; if the name fits in fewer than PATH_MAX chars, returns the
32    name in RESOLVED.  */
33
34 static char *
35 canonicalize (const char *name, char *resolved)
36 {
37   struct stat st;
38   const char *p;
39   long int path_max;
40   char *result, *dir, *end;
41   size_t namelen;
42
43   if (! resolved)
44     path_max = 0;
45   else
46     {
47 #ifdef PATH_MAX
48       path_max = PATH_MAX;
49 #else
50       path_max = sysconf (_SC_PATH_MAX);
51       if (path_max <= 0)
52         path_max = 1024;
53 #endif
54     }
55
56   p = strrchr (name, '/');
57   if (!p)
58     {
59       dir = (char *) ".";
60       p = name;
61     }
62   else
63     {
64       if (p++ == name)
65         dir = (char *) "/";
66       else
67         {
68           dir = __alloca (p - name);
69           memcpy (dir, name, p - name - 1);
70           dir[p - name] = '\0';
71         }
72     }
73
74   result = __canonicalize_directory_name_internal (dir, resolved, path_max);
75   if (!result)
76     return NULL;
77
78   /* Reconstruct the file name in the canonicalized directory.  */
79   namelen = strlen (name);
80   end = strchr (result, '\0');
81   if (resolved)
82     {
83       /* Make sure the name is not too long.  */
84       if (end - result + namelen > path_max)
85         {
86           errno = ENAMETOOLONG;
87           return NULL;
88         }
89     }
90   else
91     {
92       /* The name is dynamically allocated.  Extend it.  */
93       char *new = realloc (result, end - result + namelen + 1);
94       if (! new)
95         {
96           free (result);
97           return NULL;
98         }
99       end = new + (end - result);
100       result = new;
101     }
102   memcpy (end, name, namelen + 1);
103
104   if (__lstat (result, &st) == 0 && S_ISLNK (st.st_mode))
105     {
106       /* The file is a symlink.  Read its contents.  */
107       ssize_t n;
108     read_link_contents:
109       n = readlink (result, end,
110                     resolved ? result + path_max - end : namelen + 1);
111       if (n < 0)
112         /* Error reading the link contents.  */
113         return NULL;
114
115       if (end[0] == '/')
116         {
117           /* Absolute symlink.  */
118           if (resolved ? (end + n < result + path_max) : (n < namelen + 1))
119             {
120               /* It fit in our buffer, so we have the whole thing.  */
121               memcpy (result, end, n);
122               result[n] = '\0';
123             }
124           else if (resolved)
125             {
126             }
127         }
128
129       if (resolved)
130         {
131           if (end + n == result + path_max)
132             {
133               /* The link contents we read fill the buffer completely.
134                  There may be even more to read, and there is certainly no
135                  space for the null terminator.  */
136               errno = ENAMETOOLONG;
137               return NULL;
138             }
139         }
140       else if (n == namelen + 1)
141         {
142           /* The name buffer is dynamically allocated.  Extend it.  */
143           char *new;
144
145           /* Copy back the unresolved name onto the canonical directory.  */
146           memcpy (end, name, namelen + 1);
147
148           /* Make more space for readlink.  */
149           namelen *= 2;
150           new = realloc (result, end - result + namelen + 1);
151           if (! new)
152             {
153               free (result);
154               return NULL;
155             }
156           end = new + (end - result);
157           result = new;
158
159           goto read_link_contents;
160         }
161
162       /* Terminate the string; readlink does not.  */
163       end[n] = '\0';
164     }
165
166   return result;
167 }
168
169 weak_alias (canonicalize, realpath)
170
171 char *
172 canonicalize_file_name (const char *name)
173 {
174   return canonicalize (name, NULL);
175 }