#include "config.h"
#include <sys/param.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif
#if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS)
# include <malloc.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifdef HAVE_DIRENT_H
# include <dirent.h>
# define NAMLEN(dirent) strlen((dirent)->d_name)
#else
# define dirent direct
# define NAMLEN(dirent) (dirent)->d_namlen
# ifdef HAVE_SYS_NDIR_H
# include <sys/ndir.h>
# endif
# ifdef HAVE_SYS_DIR_H
# include <sys/dir.h>
# endif
# ifdef HAVE_NDIR_H
# include <ndir.h>
# endif
#endif
#include "compat.h"
#ifndef dirfd
# define dirfd(dirp) ((dirp)->dd_fd)
#endif
#define ISDOT(dp) \
(dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \
(dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
#ifndef lint
static const char rcsid[] = "$Sudo: getcwd.c,v 1.22 2001/12/14 19:52:47 millert Exp $";
#endif
char *
getcwd(pt, size)
char *pt;
size_t size;
{
struct dirent *dp;
DIR *dir = NULL;
dev_t dev;
ino_t ino;
int first;
char *bpt, *bup;
struct stat s;
dev_t root_dev;
ino_t root_ino;
size_t ptsize, upsize;
int save_errno;
char *ept, *eup, *up;
if (pt) {
ptsize = 0;
if (!size) {
errno = EINVAL;
return (NULL);
}
ept = pt + size;
} else {
if ((pt = malloc(ptsize = 1024 - 4)) == NULL)
return (NULL);
ept = pt + ptsize;
}
bpt = ept - 1;
*bpt = '\0';
if ((up = malloc(upsize = 1024 - 4)) == NULL)
goto err;
eup = up + MAXPATHLEN;
bup = up;
up[0] = '.';
up[1] = '\0';
if (stat("/", &s))
goto err;
root_dev = s.st_dev;
root_ino = s.st_ino;
errno = 0;
for (first = 1;; first = 0) {
if (lstat(up, &s))
goto err;
ino = s.st_ino;
dev = s.st_dev;
if (root_dev == dev && root_ino == ino) {
*--bpt = '/';
bcopy(bpt, pt, ept - bpt);
free(up);
return (pt);
}
if (bup + 3 + MAXNAMLEN + 1 >= eup) {
char *nup;
if ((nup = realloc(up, upsize *= 2)) == NULL)
goto err;
up = nup;
bup = up;
eup = up + upsize;
}
*bup++ = '.';
*bup++ = '.';
*bup = '\0';
if (!(dir = opendir(up)) || fstat(dirfd(dir), &s))
goto err;
*bup++ = '/';
save_errno = 0;
if (s.st_dev == dev) {
for (;;) {
if (!(dp = readdir(dir)))
goto notfound;
if (dp->d_fileno == ino)
break;
}
} else
for (;;) {
if (!(dp = readdir(dir)))
goto notfound;
if (ISDOT(dp))
continue;
bcopy(dp->d_name, bup, NAMLEN(dp) + 1);
if (lstat(up, &s)) {
if (!save_errno)
save_errno = errno;
errno = 0;
continue;
}
if (s.st_dev == dev && s.st_ino == ino)
break;
}
if (bpt - pt <= NAMLEN(dp) + (first ? 1 : 2)) {
size_t len, off;
char *npt;
if (!ptsize) {
errno = ERANGE;
goto err;
}
off = bpt - pt;
len = ept - bpt;
if ((npt = realloc(pt, ptsize *= 2)) == NULL)
goto err;
pt = npt;
bpt = pt + off;
ept = pt + ptsize;
bcopy(bpt, ept - len, len);
bpt = ept - len;
}
if (!first)
*--bpt = '/';
bpt -= NAMLEN(dp);
bcopy(dp->d_name, bpt, NAMLEN(dp));
(void)closedir(dir);
*bup = '\0';
}
notfound:
if (!errno)
errno = save_errno ? save_errno : ENOENT;
err:
if (ptsize)
free(pt);
if (up)
free(up);
if (dir)
(void)closedir(dir);
return (NULL);
}