Imported from ../bash-2.01.1.tar.gz.
[platform/upstream/bash.git] / 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
98   if (buf != NULL && size == 0)
99     {
100       errno = EINVAL;
101       return ((char *)NULL);
102     }
103
104   pathsize = sizeof (path);
105   pathp = &path[pathsize];
106   *--pathp = '\0';
107   pathbuf = path;
108
109   if (stat (".", &st) < 0)
110     return ((char *)NULL);
111   thisdev = st.st_dev;
112   thisino = st.st_ino;
113
114   if (stat ("/", &st) < 0)
115     return ((char *)NULL);
116   rootdev = st.st_dev;
117   rootino = st.st_ino;
118
119   dotsize = sizeof (dots) - 1;
120   dotp = &dots[sizeof (dots)];
121   dotlist = dots;
122   while (!(thisdev == rootdev && thisino == rootino))
123     {
124       register DIR *dirstream;
125       register struct dirent *d;
126       dev_t dotdev;
127       ino_t dotino;
128       char mount_point;
129       int namlen;
130
131       /* Look at the parent directory.  */
132       if (dotp == dotlist)
133         {
134           /* My, what a deep directory tree you have, Grandma.  */
135           char *new;
136           if (dotlist == dots)
137             {
138               new = malloc (dotsize * 2 + 1);
139               if (new == NULL)
140                 goto lose;
141               memcpy (new, dots, dotsize);
142             }
143           else
144             {
145               new = realloc ((PTR) dotlist, dotsize * 2 + 1);
146               if (new == NULL)
147                 goto lose;
148             }
149           memcpy (&new[dotsize], new, dotsize);
150           dotp = &new[dotsize];
151           dotsize *= 2;
152           new[dotsize] = '\0';
153           dotlist = new;
154         }
155
156       dotp -= 3;
157
158       /* Figure out if this directory is a mount point.  */
159       if (stat (dotp, &st) < 0)
160         goto lose;
161       dotdev = st.st_dev;
162       dotino = st.st_ino;
163       mount_point = dotdev != thisdev;
164
165       /* Search for the last directory.  */
166       dirstream = opendir (dotp);
167       if (dirstream == NULL)
168         goto lose;
169       while ((d = readdir (dirstream)) != NULL)
170         {
171           if (d->d_name[0] == '.' &&
172               (d->d_name[1] == '\0' ||
173                 (d->d_name[1] == '.' && d->d_name[2] == '\0')))
174             continue;
175           if (mount_point || d->d_fileno == thisino)
176             {
177               char *name;
178
179               namlen = D_NAMLEN(d);
180               name = (char *)
181                 alloca (dotlist + dotsize - dotp + 1 + namlen + 1);
182               memcpy (name, dotp, dotlist + dotsize - dotp);
183               name[dotlist + dotsize - dotp] = '/';
184               memcpy (&name[dotlist + dotsize - dotp + 1],
185                       d->d_name, namlen + 1);
186               if (lstat (name, &st) < 0)
187                 {
188                   int save = errno;
189                   (void) closedir (dirstream);
190                   errno = save;
191                   goto lose;
192                 }
193               if (st.st_dev == thisdev && st.st_ino == thisino)
194                 break;
195             }
196         }
197       if (d == NULL)
198         {
199           int save = errno;
200           (void) closedir (dirstream);
201           errno = save;
202           goto lose;
203         }
204       else
205         {
206           size_t space;
207
208           while ((space = pathp - pathbuf) <= namlen)
209             {
210               char *new;
211
212               if (pathbuf == path)
213                 {
214                   new = malloc (pathsize * 2);
215                   if (!new)
216                     goto lose;
217                 }
218               else
219                 {
220                   new = realloc ((PTR) pathbuf, (pathsize * 2));
221                   if (!new)
222                     goto lose;
223                   pathp = new + space;
224                 }
225               (void) memcpy (new + pathsize + space, pathp, pathsize - space);
226               pathp = new + pathsize + space;
227               pathbuf = new;
228               pathsize *= 2;
229             }
230
231           pathp -= namlen;
232           (void) memcpy (pathp, d->d_name, namlen);
233           *--pathp = '/';
234           (void) closedir (dirstream);
235         }
236
237       thisdev = dotdev;
238       thisino = dotino;
239     }
240
241   if (pathp == &path[sizeof(path) - 1])
242     *--pathp = '/';
243
244   if (dotlist != dots)
245     free ((PTR) dotlist);
246
247   {
248     size_t len = pathbuf + pathsize - pathp;
249     if (buf == NULL)
250       {
251         if (len < (size_t) size)
252           len = size;
253         buf = (char *) malloc (len);
254         if (buf == NULL)
255           goto lose2;
256       }
257     else if ((size_t) size < len)
258       {
259         errno = ERANGE;
260         goto lose2;
261       }
262     (void) memcpy((PTR) buf, (PTR) pathp, len);
263   }
264
265   if (pathbuf != path)
266     free (pathbuf);
267
268   return (buf);
269
270  lose:
271   if ((dotlist != dots) && dotlist)
272     {
273       int e = errno;
274       free ((PTR) dotlist);
275       errno = e;
276     }
277
278  lose2:
279   if ((pathbuf != path) && pathbuf)
280     {
281       int e = errno;
282       free ((PTR) pathbuf);
283       errno = e;
284     }
285   return ((char *)NULL);
286 }
287
288 #if defined (TEST)
289 #  include <stdio.h>
290 main (argc, argv)
291      int argc;
292      char **argv;
293 {
294   char b[PATH_MAX];
295
296   if (getcwd(b, sizeof(b)))
297     {
298       printf ("%s\n", b);
299       exit (0);
300     }
301   else
302     {
303       perror ("cwd: getcwd");
304       exit (1);
305     }
306 }
307 #endif /* TEST */
308 #endif /* !HAVE_GETCWD */