summaryrefslogtreecommitdiff
path: root/usr/src/lib/libast/common/misc/getcwd.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libast/common/misc/getcwd.c')
-rw-r--r--usr/src/lib/libast/common/misc/getcwd.c290
1 files changed, 290 insertions, 0 deletions
diff --git a/usr/src/lib/libast/common/misc/getcwd.c b/usr/src/lib/libast/common/misc/getcwd.c
new file mode 100644
index 0000000000..54a4978a66
--- /dev/null
+++ b/usr/src/lib/libast/common/misc/getcwd.c
@@ -0,0 +1,290 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1985-2007 AT&T Knowledge Ventures *
+* and is licensed under the *
+* Common Public License, Version 1.0 *
+* by AT&T Knowledge Ventures *
+* *
+* A copy of the License is available at *
+* http://www.opensource.org/licenses/cpl1.0.txt *
+* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* Phong Vo <kpv@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * Glenn Fowler
+ * AT&T Research
+ *
+ * pwd library support
+ */
+
+#include <ast.h>
+
+#if _WINIX
+
+NoN(getcwd)
+
+#else
+
+#include <ast_dir.h>
+#include <error.h>
+#include <fs3d.h>
+
+#ifndef ERANGE
+#define ERANGE E2BIG
+#endif
+
+#define ERROR(e) { errno = e; goto error; }
+
+struct dirlist /* long path chdir(2) component */
+{
+ struct dirlist* next; /* next component */
+ int index; /* index from end of buf */
+};
+
+/*
+ * pop long dir component chdir stack
+ */
+
+static int
+popdir(register struct dirlist* d, register char* end)
+{
+ register struct dirlist* dp;
+ int v;
+
+ v = 0;
+ while (dp = d)
+ {
+ d = d->next;
+ if (!v)
+ {
+ if (d) *(end - d->index - 1) = 0;
+ v = chdir(end - dp->index);
+ if (d) *(end - d->index - 1) = '/';
+ }
+ free(dp);
+ }
+ return v;
+}
+
+/*
+ * push long dir component onto stack
+ */
+
+static struct dirlist*
+pushdir(register struct dirlist* d, char* dots, char* path, char* end)
+{
+ register struct dirlist* p;
+
+ if (!(p = newof(0, struct dirlist, 1, 0)) || chdir(dots))
+ {
+ if (p) free(p);
+ if (d) popdir(d, end);
+ return 0;
+ }
+ p->index = end - path;
+ p->next = d;
+ return p;
+}
+
+/*
+ * return a pointer to the absolute path name of .
+ * this path name may be longer than PATH_MAX
+ *
+ * a few environment variables are checked before the search algorithm
+ * return value is placed in buf of len chars
+ * if buf is 0 then space is allocated via malloc() with
+ * len extra chars after the path name
+ * 0 is returned on error with errno set as appropriate
+ */
+
+char*
+getcwd(char* buf, size_t len)
+{
+ register char* d;
+ register char* p;
+ register char* s;
+ DIR* dirp = 0;
+ int n;
+ int x;
+ size_t namlen;
+ ssize_t extra = -1;
+ struct dirent* entry;
+ struct dirlist* dirstk = 0;
+ struct stat* cur;
+ struct stat* par;
+ struct stat* tmp;
+ struct stat curst;
+ struct stat parst;
+ struct stat tstst;
+ char dots[PATH_MAX];
+
+ static struct
+ {
+ char* name;
+ char* path;
+ dev_t dev;
+ ino_t ino;
+ } env[] =
+ {
+ { /*previous*/0 },
+ { "PWD" },
+ { "HOME" },
+ };
+
+ if (buf && !len) ERROR(EINVAL);
+ if (fs3d(FS3D_TEST) && (namlen = mount(".", dots, FS3D_GET|FS3D_VIEW|FS3D_SIZE(sizeof(dots)), NiL)) > 1 && namlen < sizeof(dots))
+ {
+ p = dots;
+ easy:
+ namlen++;
+ if (buf)
+ {
+ if (len < namlen) ERROR(ERANGE);
+ }
+ else if (!(buf = newof(0, char, namlen, len))) ERROR(ENOMEM);
+ return (char*)memcpy(buf, p, namlen);
+ }
+ cur = &curst;
+ par = &parst;
+ if (stat(".", par)) ERROR(errno);
+ for (n = 0; n < elementsof(env); n++)
+ {
+ if ((env[n].name && (p = getenv(env[n].name)) || (p = env[n].path)) && *p == '/' && !stat(p, cur))
+ {
+ env[n].path = p;
+ env[n].dev = cur->st_dev;
+ env[n].ino = cur->st_ino;
+ if (cur->st_ino == par->st_ino && cur->st_dev == par->st_dev)
+ {
+ namlen = strlen(p);
+ goto easy;
+ }
+ }
+ }
+ if (!buf)
+ {
+ extra = len;
+ len = PATH_MAX;
+ if (!(buf = newof(0, char, len, extra))) ERROR(ENOMEM);
+ }
+ d = dots;
+ p = buf + len - 1;
+ *p = 0;
+ n = elementsof(env);
+ for (;;)
+ {
+ tmp = cur;
+ cur = par;
+ par = tmp;
+ if ((d - dots) > (PATH_MAX - 4))
+ {
+ if (!(dirstk = pushdir(dirstk, dots, p, buf + len - 1))) ERROR(ERANGE);
+ d = dots;
+ }
+ *d++ = '.';
+ *d++ = '.';
+ *d = 0;
+ if (!(dirp = opendir(dots))) ERROR(errno);
+#if !_dir_ok || _mem_dd_fd_DIR
+ if (fstat(dirp->dd_fd, par)) ERROR(errno);
+#else
+ if (stat(dots, par)) ERROR(errno);
+#endif
+ *d++ = '/';
+ if (par->st_dev == cur->st_dev)
+ {
+ if (par->st_ino == cur->st_ino)
+ {
+ closedir(dirp);
+ *--p = '/';
+ pop:
+ if (p != buf)
+ {
+ d = buf;
+ while (*d++ = *p++);
+ len = d - buf;
+ if (extra >= 0 && !(buf = newof(buf, char, len, extra))) ERROR(ENOMEM);
+ }
+ if (dirstk && popdir(dirstk, buf + len - 1))
+ {
+ dirstk = 0;
+ ERROR(errno);
+ }
+ if (env[0].path)
+ free(env[0].path);
+ env[0].path = strdup(buf);
+ return buf;
+ }
+#ifdef D_FILENO
+ while (entry = readdir(dirp))
+ if (D_FILENO(entry) == cur->st_ino)
+ {
+ namlen = D_NAMLEN(entry);
+ goto found;
+ }
+#endif
+
+ /*
+ * this fallthrough handles logical naming
+ */
+
+ rewinddir(dirp);
+ }
+ do
+ {
+ if (!(entry = readdir(dirp))) ERROR(ENOENT);
+ namlen = D_NAMLEN(entry);
+ if ((d - dots) > (PATH_MAX - 1 - namlen))
+ {
+ *d = 0;
+ if (namlen >= PATH_MAX || !(dirstk = pushdir(dirstk, dots + 3, p, buf + len - 1))) ERROR(ERANGE);
+ d = dots + 3;
+ }
+ memcpy(d, entry->d_name, namlen + 1);
+ } while (stat(dots, &tstst) || tstst.st_ino != cur->st_ino || tstst.st_dev != cur->st_dev);
+ found:
+ if (*p) *--p = '/';
+ while ((p -= namlen) <= (buf + 1))
+ {
+ x = (buf + len - 1) - (p += namlen);
+ s = buf + len;
+ if (extra < 0 || !(buf = newof(buf, char, len += PATH_MAX, extra))) ERROR(ERANGE);
+ p = buf + len;
+ while (p > buf + len - 1 - x) *--p = *--s;
+ }
+ if (n < elementsof(env))
+ {
+ memcpy(p, env[n].path, namlen);
+ goto pop;
+ }
+ memcpy(p, entry->d_name, namlen);
+ closedir(dirp);
+ dirp = 0;
+ for (n = 0; n < elementsof(env); n++)
+ if (env[n].ino == par->st_ino && env[n].dev == par->st_dev)
+ {
+ namlen = strlen(env[n].path);
+ goto found;
+ }
+ }
+ error:
+ if (buf)
+ {
+ if (dirstk) popdir(dirstk, buf + len - 1);
+ if (extra >= 0) free(buf);
+ }
+ if (dirp) closedir(dirp);
+ return 0;
+}
+
+#endif