diff options
Diffstat (limited to 'usr/src/lib/libast/common/misc/getcwd.c')
-rw-r--r-- | usr/src/lib/libast/common/misc/getcwd.c | 290 |
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 |