aboutsummaryrefslogtreecommitdiffstats
path: root/lib/sh/getcwd.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sh/getcwd.c')
-rw-r--r--lib/sh/getcwd.c356
1 files changed, 356 insertions, 0 deletions
diff --git a/lib/sh/getcwd.c b/lib/sh/getcwd.c
new file mode 100644
index 0000000..07eb817
--- /dev/null
+++ b/lib/sh/getcwd.c
@@ -0,0 +1,356 @@
+/* getcwd.c -- get pathname of current directory */
+
+/* Copyright (C) 1991 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#if !defined (HAVE_GETCWD)
+
+#if !defined (__GNUC__) && !defined (HAVE_ALLOCA_H) && defined (_AIX)
+ #pragma alloca
+#endif /* _AIX && RISC6000 && !__GNUC__ */
+
+#if defined (__QNX__)
+# undef HAVE_LSTAT
+#endif
+
+#include <bashtypes.h>
+#include <errno.h>
+
+#if defined (HAVE_LIMITS_H)
+# include <limits.h>
+#endif
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#include <posixdir.h>
+#include <posixstat.h>
+#include <maxpath.h>
+#include <memalloc.h>
+
+#include <bashansi.h>
+
+#if defined (BROKEN_DIRENT_D_INO)
+# include "command.h"
+# include "general.h"
+# include "externs.h"
+#endif
+
+#include <xmalloc.h>
+
+#if !defined (errno)
+extern int errno;
+#endif /* !errno */
+
+#if !defined (HAVE_LSTAT)
+# define lstat stat
+#endif
+
+#if !defined (NULL)
+# define NULL 0
+#endif
+
+/* If the d_fileno member of a struct dirent doesn't return anything useful,
+ we need to check inode number equivalence the hard way. Return 1 if
+ the inode corresponding to PATH/DIR is identical to THISINO. */
+#if defined (BROKEN_DIRENT_D_INO)
+static int
+_path_checkino (dotp, name, thisino)
+ char *dotp;
+ char *name;
+ ino_t thisino;
+{
+ char *fullpath;
+ int r, e;
+ struct stat st;
+
+ e = errno;
+ fullpath = sh_makepath (dotp, name, MP_RMDOT);
+ if (stat (fullpath, &st) < 0)
+ {
+ errno = e;
+ return 0;
+ }
+ free (fullpath);
+ errno = e;
+ return (st.st_ino == thisino);
+}
+#endif
+
+/* Get the pathname of the current working directory,
+ and put it in SIZE bytes of BUF. Returns NULL if the
+ directory couldn't be determined or SIZE was too small.
+ If successful, returns BUF. In GNU, if BUF is NULL,
+ an array is allocated with `malloc'; the array is SIZE
+ bytes long, unless SIZE <= 0, in which case it is as
+ big as necessary. */
+#if defined (__STDC__)
+char *
+getcwd (char *buf, size_t size)
+#else /* !__STDC__ */
+char *
+getcwd (buf, size)
+ char *buf;
+ size_t size;
+#endif /* !__STDC__ */
+{
+ static const char dots[]
+ = "../../../../../../../../../../../../../../../../../../../../../../../\
+../../../../../../../../../../../../../../../../../../../../../../../../../../\
+../../../../../../../../../../../../../../../../../../../../../../../../../..";
+ const char *dotp, *dotlist;
+ size_t dotsize;
+ dev_t rootdev, thisdev;
+ ino_t rootino, thisino;
+ char path[PATH_MAX + 1];
+ register char *pathp;
+ char *pathbuf;
+ size_t pathsize;
+ struct stat st;
+ int saved_errno;
+
+ if (buf != NULL && size == 0)
+ {
+ errno = EINVAL;
+ return ((char *)NULL);
+ }
+
+ pathsize = sizeof (path);
+ pathp = &path[pathsize];
+ *--pathp = '\0';
+ pathbuf = path;
+
+ if (stat (".", &st) < 0)
+ return ((char *)NULL);
+ thisdev = st.st_dev;
+ thisino = st.st_ino;
+
+ if (stat ("/", &st) < 0)
+ return ((char *)NULL);
+ rootdev = st.st_dev;
+ rootino = st.st_ino;
+
+ saved_errno = 0;
+
+ dotsize = sizeof (dots) - 1;
+ dotp = &dots[sizeof (dots)];
+ dotlist = dots;
+ while (!(thisdev == rootdev && thisino == rootino))
+ {
+ register DIR *dirstream;
+ register struct dirent *d;
+ dev_t dotdev;
+ ino_t dotino;
+ char mount_point;
+ int namlen;
+
+ /* Look at the parent directory. */
+ if (dotp == dotlist)
+ {
+ /* My, what a deep directory tree you have, Grandma. */
+ char *new;
+ if (dotlist == dots)
+ {
+ new = (char *)malloc (dotsize * 2 + 1);
+ if (new == NULL)
+ goto lose;
+ memcpy (new, dots, dotsize);
+ }
+ else
+ {
+ new = (char *)realloc ((PTR_T) dotlist, dotsize * 2 + 1);
+ if (new == NULL)
+ goto lose;
+ }
+ memcpy (&new[dotsize], new, dotsize);
+ dotp = &new[dotsize];
+ dotsize *= 2;
+ new[dotsize] = '\0';
+ dotlist = new;
+ }
+
+ dotp -= 3;
+
+ /* Figure out if this directory is a mount point. */
+ if (stat (dotp, &st) < 0)
+ goto lose;
+ dotdev = st.st_dev;
+ dotino = st.st_ino;
+ mount_point = dotdev != thisdev;
+
+ /* Search for the last directory. */
+ dirstream = opendir (dotp);
+ if (dirstream == NULL)
+ goto lose;
+ while ((d = readdir (dirstream)) != NULL)
+ {
+ if (d->d_name[0] == '.' &&
+ (d->d_name[1] == '\0' ||
+ (d->d_name[1] == '.' && d->d_name[2] == '\0')))
+ continue;
+#if !defined (BROKEN_DIRENT_D_INO)
+ if (mount_point || d->d_fileno == thisino)
+#else
+ if (mount_point || _path_checkino (dotp, d->d_name, thisino))
+#endif
+ {
+ char *name;
+
+ namlen = D_NAMLEN(d);
+ name = (char *)
+ alloca (dotlist + dotsize - dotp + 1 + namlen + 1);
+ memcpy (name, dotp, dotlist + dotsize - dotp);
+ name[dotlist + dotsize - dotp] = '/';
+ memcpy (&name[dotlist + dotsize - dotp + 1],
+ d->d_name, namlen + 1);
+ if (lstat (name, &st) < 0)
+ {
+#if 0
+ int save = errno;
+ (void) closedir (dirstream);
+ errno = save;
+ goto lose;
+#else
+ saved_errno = errno;
+#endif
+ }
+ if (st.st_dev == thisdev && st.st_ino == thisino)
+ break;
+ }
+ }
+ if (d == NULL)
+ {
+#if 0
+ int save = errno;
+#else
+ int save = errno ? errno : saved_errno;
+#endif
+ (void) closedir (dirstream);
+ errno = save;
+ goto lose;
+ }
+ else
+ {
+ size_t space;
+
+ while ((space = pathp - pathbuf) <= namlen)
+ {
+ char *new;
+
+ if (pathbuf == path)
+ {
+ new = (char *)malloc (pathsize * 2);
+ if (!new)
+ goto lose;
+ }
+ else
+ {
+ new = (char *)realloc ((PTR_T) pathbuf, (pathsize * 2));
+ if (!new)
+ goto lose;
+ pathp = new + space;
+ }
+ (void) memcpy (new + pathsize + space, pathp, pathsize - space);
+ pathp = new + pathsize + space;
+ pathbuf = new;
+ pathsize *= 2;
+ }
+
+ pathp -= namlen;
+ (void) memcpy (pathp, d->d_name, namlen);
+ *--pathp = '/';
+ (void) closedir (dirstream);
+ }
+
+ thisdev = dotdev;
+ thisino = dotino;
+ }
+
+ if (pathp == &path[sizeof(path) - 1])
+ *--pathp = '/';
+
+ if (dotlist != dots)
+ free ((PTR_T) dotlist);
+
+ {
+ size_t len = pathbuf + pathsize - pathp;
+ if (buf == NULL && size <= 0)
+ size = len;
+
+ if ((size_t) size < len)
+ {
+ errno = ERANGE;
+ goto lose2;
+ }
+ if (buf == NULL)
+ {
+ buf = (char *) malloc (size);
+ if (buf == NULL)
+ goto lose2;
+ }
+
+ (void) memcpy((PTR_T) buf, (PTR_T) pathp, len);
+ }
+
+ if (pathbuf != path)
+ free (pathbuf);
+
+ return (buf);
+
+ lose:
+ if ((dotlist != dots) && dotlist)
+ {
+ int e = errno;
+ free ((PTR_T) dotlist);
+ errno = e;
+ }
+
+ lose2:
+ if ((pathbuf != path) && pathbuf)
+ {
+ int e = errno;
+ free ((PTR_T) pathbuf);
+ errno = e;
+ }
+ return ((char *)NULL);
+}
+
+#if defined (TEST)
+# include <stdio.h>
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ char b[PATH_MAX];
+
+ if (getcwd(b, sizeof(b)))
+ {
+ printf ("%s\n", b);
+ exit (0);
+ }
+ else
+ {
+ perror ("cwd: getcwd");
+ exit (1);
+ }
+}
+#endif /* TEST */
+#endif /* !HAVE_GETCWD */