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