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