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