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