84799bbc86614168d34a710cf858fa0c568d5901
[platform/upstream/bash.git] / lib / sh / getcwd.c
1 /* getcwd.c -- get pathname of current directory */
2
3 /* Copyright (C) 1991 Free Software Foundation, Inc.
4
5    This file is part of GNU Bash, the Bourne Again SHell.
6
7    Bash is free software: you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation, either version 3 of the License, or
10    (at your option) any later version.
11
12    Bash is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with Bash.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include <config.h>
22
23 #if !defined (HAVE_GETCWD)
24
25 #if !defined (__GNUC__) && !defined (HAVE_ALLOCA_H) && defined (_AIX)
26   #pragma alloca
27 #endif /* _AIX && RISC6000 && !__GNUC__ */
28
29 #if defined (__QNX__)
30 #  undef HAVE_LSTAT
31 #endif
32
33 #include <bashtypes.h>
34 #include <errno.h>
35
36 #if defined (HAVE_LIMITS_H)
37 #  include <limits.h>
38 #endif
39
40 #if defined (HAVE_UNISTD_H)
41 #  include <unistd.h>
42 #endif
43
44 #include <posixdir.h>
45 #include <posixstat.h>
46 #include <maxpath.h>
47 #include <memalloc.h>
48
49 #include <bashansi.h>
50
51 #include <xmalloc.h>
52
53 #if !defined (errno)
54 extern int errno;
55 #endif /* !errno */
56
57 #if !defined (HAVE_LSTAT)
58 #  define lstat stat
59 #endif
60
61 #if !defined (NULL)
62 #  define NULL 0
63 #endif
64
65 /* If the d_fileno member of a struct dirent doesn't return anything useful,
66    we need to check inode number equivalence the hard way.  Return 1 if
67    the inode corresponding to PATH/DIR is identical to THISINO. */
68 #if defined (BROKEN_DIRENT_D_INO)
69 static int
70 _path_checkino (dotp, name, thisino)
71      char *dotp;
72      char *name;
73      ino_t thisino;
74 {
75   char *fullpath;
76   int r, e;
77   struct stat st;
78
79   e = errno;
80   fullpath = sh_makepath (dotp, name, MP_RMDOT);
81   if (stat (fullpath, &st) < 0)
82     {
83       errno = e;
84       return 0;
85     }
86   free (fullpath);
87   errno = e;
88   return (st.st_ino == thisino);
89 }
90 #endif
91     
92 /* Get the pathname of the current working directory,
93    and put it in SIZE bytes of BUF.  Returns NULL if the
94    directory couldn't be determined or SIZE was too small.
95    If successful, returns BUF.  In GNU, if BUF is NULL,
96    an array is allocated with `malloc'; the array is SIZE
97    bytes long, unless SIZE <= 0, in which case it is as
98    big as necessary.  */
99 #if defined (__STDC__)
100 char *
101 getcwd (char *buf, size_t size)
102 #else /* !__STDC__ */
103 char *
104 getcwd (buf, size)
105      char *buf;
106      size_t size;
107 #endif /* !__STDC__ */
108 {
109   static const char dots[]
110     = "../../../../../../../../../../../../../../../../../../../../../../../\
111 ../../../../../../../../../../../../../../../../../../../../../../../../../../\
112 ../../../../../../../../../../../../../../../../../../../../../../../../../..";
113   const char *dotp, *dotlist;
114   size_t dotsize;
115   dev_t rootdev, thisdev;
116   ino_t rootino, thisino;
117   char path[PATH_MAX + 1];
118   register char *pathp;
119   char *pathbuf;
120   size_t pathsize;
121   struct stat st;
122   int saved_errno;
123
124   if (buf != NULL && size == 0)
125     {
126       errno = EINVAL;
127       return ((char *)NULL);
128     }
129
130   pathsize = sizeof (path);
131   pathp = &path[pathsize];
132   *--pathp = '\0';
133   pathbuf = path;
134
135   if (stat (".", &st) < 0)
136     return ((char *)NULL);
137   thisdev = st.st_dev;
138   thisino = st.st_ino;
139
140   if (stat ("/", &st) < 0)
141     return ((char *)NULL);
142   rootdev = st.st_dev;
143   rootino = st.st_ino;
144
145   saved_errno = 0;
146
147   dotsize = sizeof (dots) - 1;
148   dotp = &dots[sizeof (dots)];
149   dotlist = dots;
150   while (!(thisdev == rootdev && thisino == rootino))
151     {
152       register DIR *dirstream;
153       register struct dirent *d;
154       dev_t dotdev;
155       ino_t dotino;
156       char mount_point;
157       int namlen;
158
159       /* Look at the parent directory.  */
160       if (dotp == dotlist)
161         {
162           /* My, what a deep directory tree you have, Grandma.  */
163           char *new;
164           if (dotlist == dots)
165             {
166               new = (char *)malloc (dotsize * 2 + 1);
167               if (new == NULL)
168                 goto lose;
169               memcpy (new, dots, dotsize);
170             }
171           else
172             {
173               new = (char *)realloc ((PTR_T) dotlist, dotsize * 2 + 1);
174               if (new == NULL)
175                 goto lose;
176             }
177           memcpy (&new[dotsize], new, dotsize);
178           dotp = &new[dotsize];
179           dotsize *= 2;
180           new[dotsize] = '\0';
181           dotlist = new;
182         }
183
184       dotp -= 3;
185
186       /* Figure out if this directory is a mount point.  */
187       if (stat (dotp, &st) < 0)
188         goto lose;
189       dotdev = st.st_dev;
190       dotino = st.st_ino;
191       mount_point = dotdev != thisdev;
192
193       /* Search for the last directory.  */
194       dirstream = opendir (dotp);
195       if (dirstream == NULL)
196         goto lose;
197       while ((d = readdir (dirstream)) != NULL)
198         {
199           if (d->d_name[0] == '.' &&
200               (d->d_name[1] == '\0' ||
201                 (d->d_name[1] == '.' && d->d_name[2] == '\0')))
202             continue;
203 #if !defined (BROKEN_DIRENT_D_INO)
204           if (mount_point || d->d_fileno == thisino)
205 #else
206           if (mount_point || _path_checkino (dotp, d->d_name, thisino))
207 #endif
208             {
209               char *name;
210
211               namlen = D_NAMLEN(d);
212               name = (char *)
213                 alloca (dotlist + dotsize - dotp + 1 + namlen + 1);
214               memcpy (name, dotp, dotlist + dotsize - dotp);
215               name[dotlist + dotsize - dotp] = '/';
216               memcpy (&name[dotlist + dotsize - dotp + 1],
217                       d->d_name, namlen + 1);
218               if (lstat (name, &st) < 0)
219                 {
220 #if 0
221                   int save = errno;
222                   (void) closedir (dirstream);
223                   errno = save;
224                   goto lose;
225 #else
226                   saved_errno = errno;
227 #endif
228                 }
229               if (st.st_dev == thisdev && st.st_ino == thisino)
230                 break;
231             }
232         }
233       if (d == NULL)
234         {
235 #if 0
236           int save = errno;
237 #else
238           int save = errno ? errno : saved_errno;
239 #endif
240           (void) closedir (dirstream);
241           errno = save;
242           goto lose;
243         }
244       else
245         {
246           size_t space;
247
248           while ((space = pathp - pathbuf) <= namlen)
249             {
250               char *new;
251
252               if (pathbuf == path)
253                 {
254                   new = (char *)malloc (pathsize * 2);
255                   if (!new)
256                     goto lose;
257                 }
258               else
259                 {
260                   new = (char *)realloc ((PTR_T) pathbuf, (pathsize * 2));
261                   if (!new)
262                     goto lose;
263                   pathp = new + space;
264                 }
265               (void) memcpy (new + pathsize + space, pathp, pathsize - space);
266               pathp = new + pathsize + space;
267               pathbuf = new;
268               pathsize *= 2;
269             }
270
271           pathp -= namlen;
272           (void) memcpy (pathp, d->d_name, namlen);
273           *--pathp = '/';
274           (void) closedir (dirstream);
275         }
276
277       thisdev = dotdev;
278       thisino = dotino;
279     }
280
281   if (pathp == &path[sizeof(path) - 1])
282     *--pathp = '/';
283
284   if (dotlist != dots)
285     free ((PTR_T) dotlist);
286
287   {
288     size_t len = pathbuf + pathsize - pathp;
289     if (buf == NULL && size <= 0)
290       size = len;
291
292     if ((size_t) size < len)
293       {
294         errno = ERANGE;
295         goto lose2;
296       }
297     if (buf == NULL)
298       {
299         buf = (char *) malloc (size);
300         if (buf == NULL)
301           goto lose2;
302       }
303
304     (void) memcpy((PTR_T) buf, (PTR_T) pathp, len);
305   }
306
307   if (pathbuf != path)
308     free (pathbuf);
309
310   return (buf);
311
312  lose:
313   if ((dotlist != dots) && dotlist)
314     {
315       int e = errno;
316       free ((PTR_T) dotlist);
317       errno = e;
318     }
319
320  lose2:
321   if ((pathbuf != path) && pathbuf)
322     {
323       int e = errno;
324       free ((PTR_T) pathbuf);
325       errno = e;
326     }
327   return ((char *)NULL);
328 }
329
330 #if defined (TEST)
331 #  include <stdio.h>
332 main (argc, argv)
333      int argc;
334      char **argv;
335 {
336   char b[PATH_MAX];
337
338   if (getcwd(b, sizeof(b)))
339     {
340       printf ("%s\n", b);
341       exit (0);
342     }
343   else
344     {
345       perror ("cwd: getcwd");
346       exit (1);
347     }
348 }
349 #endif /* TEST */
350 #endif /* !HAVE_GETCWD */