1a0279435325a0d45be6e2e2811529483bcff195
[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 && size <= 0)
255       size = len;
256
257     if ((size_t) size < len)
258       {
259         errno = ERANGE;
260         goto lose2;
261       }
262     if (buf == NULL)
263       {
264         buf = (char *) malloc (size);
265         if (buf == NULL)
266           goto lose2;
267       }
268
269     (void) memcpy((PTR_T) buf, (PTR_T) pathp, len);
270   }
271
272   if (pathbuf != path)
273     free (pathbuf);
274
275   return (buf);
276
277  lose:
278   if ((dotlist != dots) && dotlist)
279     {
280       int e = errno;
281       free ((PTR_T) dotlist);
282       errno = e;
283     }
284
285  lose2:
286   if ((pathbuf != path) && pathbuf)
287     {
288       int e = errno;
289       free ((PTR_T) pathbuf);
290       errno = e;
291     }
292   return ((char *)NULL);
293 }
294
295 #if defined (TEST)
296 #  include <stdio.h>
297 main (argc, argv)
298      int argc;
299      char **argv;
300 {
301   char b[PATH_MAX];
302
303   if (getcwd(b, sizeof(b)))
304     {
305       printf ("%s\n", b);
306       exit (0);
307     }
308   else
309     {
310       perror ("cwd: getcwd");
311       exit (1);
312     }
313 }
314 #endif /* TEST */
315 #endif /* !HAVE_GETCWD */