No specific user configuration
[platform/upstream/bash.git] / lib / sh / getcwd.c
1 /* getcwd.c -- get pathname of current directory */
2
3 /* Copyright (C) 1991 Free Software Foundation, Inc.
4
5    This file is part of GNU Bash, the Bourne Again SHell.
6
7    Bash is free software: you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation, either version 3 of the License, or
10    (at your option) any later version.
11
12    Bash is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with Bash.  If not, see <http://www.gnu.org/licenses/>.
19 */
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 #if defined (__QNX__)
30 #  undef HAVE_LSTAT
31 #endif
32
33 #include <bashtypes.h>
34 #include <errno.h>
35
36 #if defined (HAVE_LIMITS_H)
37 #  include <limits.h>
38 #endif
39
40 #if defined (HAVE_UNISTD_H)
41 #  include <unistd.h>
42 #endif
43
44 #include <posixdir.h>
45 #include <posixstat.h>
46 #include <maxpath.h>
47 #include <memalloc.h>
48
49 #include <bashansi.h>
50
51 #if !defined (D_FILENO_AVAILABLE)
52 #  include "command.h"
53 #  include "general.h"
54 #  include "externs.h"
55 #endif
56
57 #include <xmalloc.h>
58
59 #if !defined (errno)
60 extern int errno;
61 #endif /* !errno */
62
63 #if !defined (HAVE_LSTAT)
64 #  define lstat stat
65 #endif
66
67 #if !defined (NULL)
68 #  define NULL 0
69 #endif
70
71 /* If the d_fileno member of a struct dirent doesn't return anything useful,
72    we need to check inode number equivalence the hard way.  Return 1 if
73    the inode corresponding to PATH/DIR is identical to THISINO. */
74 #if !defined (D_FILENO_AVAILABLE)
75 static int
76 _path_checkino (dotp, name, thisino)
77      char *dotp;
78      char *name;
79      ino_t thisino;
80 {
81   char *fullpath;
82   int r, e;
83   struct stat st;
84
85   e = errno;
86   fullpath = sh_makepath (dotp, name, MP_RMDOT);
87   if (stat (fullpath, &st) < 0)
88     {
89       errno = e;
90       return 0;
91     }
92   free (fullpath);
93   errno = e;
94   return (st.st_ino == thisino);
95 }
96 #endif
97     
98 /* Get the pathname of the current working directory,
99    and put it in SIZE bytes of BUF.  Returns NULL if the
100    directory couldn't be determined or SIZE was too small.
101    If successful, returns BUF.  In GNU, if BUF is NULL,
102    an array is allocated with `malloc'; the array is SIZE
103    bytes long, unless SIZE <= 0, in which case it is as
104    big as necessary.  */
105 #if defined (__STDC__)
106 char *
107 getcwd (char *buf, size_t size)
108 #else /* !__STDC__ */
109 char *
110 getcwd (buf, size)
111      char *buf;
112      size_t size;
113 #endif /* !__STDC__ */
114 {
115   static const char dots[]
116     = "../../../../../../../../../../../../../../../../../../../../../../../\
117 ../../../../../../../../../../../../../../../../../../../../../../../../../../\
118 ../../../../../../../../../../../../../../../../../../../../../../../../../..";
119   const char *dotp, *dotlist;
120   size_t dotsize;
121   dev_t rootdev, thisdev;
122   ino_t rootino, thisino;
123   char path[PATH_MAX + 1];
124   register char *pathp;
125   char *pathbuf;
126   size_t pathsize;
127   struct stat st;
128   int saved_errno;
129
130   if (buf != NULL && size == 0)
131     {
132       errno = EINVAL;
133       return ((char *)NULL);
134     }
135
136   pathsize = sizeof (path);
137   pathp = &path[pathsize];
138   *--pathp = '\0';
139   pathbuf = path;
140
141   if (stat (".", &st) < 0)
142     return ((char *)NULL);
143   thisdev = st.st_dev;
144   thisino = st.st_ino;
145
146   if (stat ("/", &st) < 0)
147     return ((char *)NULL);
148   rootdev = st.st_dev;
149   rootino = st.st_ino;
150
151   saved_errno = 0;
152
153   dotsize = sizeof (dots) - 1;
154   dotp = &dots[sizeof (dots)];
155   dotlist = dots;
156   while (!(thisdev == rootdev && thisino == rootino))
157     {
158       register DIR *dirstream;
159       register struct dirent *d;
160       dev_t dotdev;
161       ino_t dotino;
162       char mount_point;
163       int namlen;
164
165       /* Look at the parent directory.  */
166       if (dotp == dotlist)
167         {
168           /* My, what a deep directory tree you have, Grandma.  */
169           char *new;
170           if (dotlist == dots)
171             {
172               new = (char *)malloc (dotsize * 2 + 1);
173               if (new == NULL)
174                 goto lose;
175               memcpy (new, dots, dotsize);
176             }
177           else
178             {
179               new = (char *)realloc ((PTR_T) dotlist, dotsize * 2 + 1);
180               if (new == NULL)
181                 goto lose;
182             }
183           memcpy (&new[dotsize], new, dotsize);
184           dotp = &new[dotsize];
185           dotsize *= 2;
186           new[dotsize] = '\0';
187           dotlist = new;
188         }
189
190       dotp -= 3;
191
192       /* Figure out if this directory is a mount point.  */
193       if (stat (dotp, &st) < 0)
194         goto lose;
195       dotdev = st.st_dev;
196       dotino = st.st_ino;
197       mount_point = dotdev != thisdev;
198
199       /* Search for the last directory.  */
200       dirstream = opendir (dotp);
201       if (dirstream == NULL)
202         goto lose;
203       while ((d = readdir (dirstream)) != NULL)
204         {
205           if (d->d_name[0] == '.' &&
206               (d->d_name[1] == '\0' ||
207                 (d->d_name[1] == '.' && d->d_name[2] == '\0')))
208             continue;
209 #if defined (D_FILENO_AVAILABLE)
210           if (mount_point || d->d_fileno == thisino)
211 #else
212           if (mount_point || _path_checkino (dotp, d->d_name, thisino))
213 #endif
214             {
215               char *name;
216
217               namlen = D_NAMLEN(d);
218               name = (char *)
219                 alloca (dotlist + dotsize - dotp + 1 + namlen + 1);
220               memcpy (name, dotp, dotlist + dotsize - dotp);
221               name[dotlist + dotsize - dotp] = '/';
222               memcpy (&name[dotlist + dotsize - dotp + 1],
223                       d->d_name, namlen + 1);
224               if (lstat (name, &st) < 0)
225                 {
226 #if 0
227                   int save = errno;
228                   (void) closedir (dirstream);
229                   errno = save;
230                   goto lose;
231 #else
232                   saved_errno = errno;
233 #endif
234                 }
235               if (st.st_dev == thisdev && st.st_ino == thisino)
236                 break;
237             }
238         }
239       if (d == NULL)
240         {
241 #if 0
242           int save = errno;
243 #else
244           int save = errno ? errno : saved_errno;
245 #endif
246           (void) closedir (dirstream);
247           errno = save;
248           goto lose;
249         }
250       else
251         {
252           size_t space;
253
254           while ((space = pathp - pathbuf) <= namlen)
255             {
256               char *new;
257
258               if (pathbuf == path)
259                 {
260                   new = (char *)malloc (pathsize * 2);
261                   if (!new)
262                     goto lose;
263                 }
264               else
265                 {
266                   new = (char *)realloc ((PTR_T) pathbuf, (pathsize * 2));
267                   if (!new)
268                     goto lose;
269                   pathp = new + space;
270                 }
271               (void) memcpy (new + pathsize + space, pathp, pathsize - space);
272               pathp = new + pathsize + space;
273               pathbuf = new;
274               pathsize *= 2;
275             }
276
277           pathp -= namlen;
278           (void) memcpy (pathp, d->d_name, namlen);
279           *--pathp = '/';
280           (void) closedir (dirstream);
281         }
282
283       thisdev = dotdev;
284       thisino = dotino;
285     }
286
287   if (pathp == &path[sizeof(path) - 1])
288     *--pathp = '/';
289
290   if (dotlist != dots)
291     free ((PTR_T) dotlist);
292
293   {
294     size_t len = pathbuf + pathsize - pathp;
295     if (buf == NULL && size <= 0)
296       size = len;
297
298     if ((size_t) size < len)
299       {
300         errno = ERANGE;
301         goto lose2;
302       }
303     if (buf == NULL)
304       {
305         buf = (char *) malloc (size);
306         if (buf == NULL)
307           goto lose2;
308       }
309
310     (void) memcpy((PTR_T) buf, (PTR_T) pathp, len);
311   }
312
313   if (pathbuf != path)
314     free (pathbuf);
315
316   return (buf);
317
318  lose:
319   if ((dotlist != dots) && dotlist)
320     {
321       int e = errno;
322       free ((PTR_T) dotlist);
323       errno = e;
324     }
325
326  lose2:
327   if ((pathbuf != path) && pathbuf)
328     {
329       int e = errno;
330       free ((PTR_T) pathbuf);
331       errno = e;
332     }
333   return ((char *)NULL);
334 }
335
336 #if defined (TEST)
337 #  include <stdio.h>
338 main (argc, argv)
339      int argc;
340      char **argv;
341 {
342   char b[PATH_MAX];
343
344   if (getcwd(b, sizeof(b)))
345     {
346       printf ("%s\n", b);
347       exit (0);
348     }
349   else
350     {
351       perror ("cwd: getcwd");
352       exit (1);
353     }
354 }
355 #endif /* TEST */
356 #endif /* !HAVE_GETCWD */