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