/*
* getwd - get working directory
*
* Probably should use lstat rather than stat throughout, if run on
* a system with symbolic links.
*/
#include <stdio.h>
#include <sys/types.h>
#include <ndir.h>
#include <sys/stat.h>
/*
* getwd - master control
*/
char *
getwd(pathname)
register char pathname[];
{
register char *ret;
register FILE *pwd;
extern char *trygetwd();
extern FILE *popen();
ret = trygetwd(pathname);
if (ret != NULL)
return(pathname);
/*
* The simple approach failed. Try doing it the hard way.
*/
pwd = popen("PATH=/bin:/usr/bin pwd", "r");
ret = fgets(pathname, 1024, pwd);
pclose(pwd);
if (ret != NULL) {
pathname[strlen(pathname)-1] = '\0'; /* Junk the \n. */
return(pathname);
}
/*
* Total failure.
*/
strcpy(pathname, "getwd-failed");
return(NULL);
}
/*
* trygetwd - try to get the path without resorting to extreme measures
*/
static char *
trygetwd(pathname)
char pathname[];
{
char parent[1024]; /* ../../.. and so forth. */
char *parend; /* See comment where used. */
register DIR *par;
register struct direct *direntry;
struct stat parstat;
struct stat maybe;
register int statall; /* Looking for a mounted fs? */
ino_t lastino;
dev_t lastdev;
ino_t rootino;
dev_t rootdev;
if (stat(".", &parstat) < 0)
return(NULL);
lastino = parstat.st_ino;
lastdev = parstat.st_dev;
if (stat("/", &parstat) < 0)
return(NULL);
rootino = parstat.st_ino;
rootdev = parstat.st_dev;
strcpy(parent, "..");
pathname[0] = '\0';
/*
* Build up the pathname, ascending one level of
* directory on each iteration.
*/
while (lastino != rootino || lastdev != rootdev) {
if (stat(parent, &parstat) < 0)
return(NULL);
/*
* Scan directory, looking for an inode-number match with
* the child directory. There are two tricky cases:
*
* First, getting an inode-number match is not sufficient,
* because inode numbers are unique only within a filesystem.
* We must check that a promising-looking directory entry
* really does point to the place we came up from. So we
* use stat() to verify number matches.
*
* Second, getting an inode-number match is not necessary
* either, because the directory entry for the top of a
* mounted filesystem carries the inode number of the place
* where the filesystem is mounted, so the entry doesn't
* look like it's for the-place-we-came-from until we do
* a stat. So if we run out of directory entries without
* finding the child, we go through again statting everything.
*/
par = opendir(parent);
if (par == NULL)
return(NULL);
statall = 0;
for (;;) {
direntry = readdir(par);
if (direntry == NULL && statall)
return(NULL); /* Both passes failed. */
if (direntry == NULL && !statall) {
/* Maybe we've hit a mount boundary... */
rewinddir(par);
statall = 1;
direntry = readdir(par);
}
if (direntry->d_ino == lastino || statall) {
/*
* Use stat to check things out. Build
* a suitable pathname on the end of the
* "parent" string, remembering where it
* ended so we can put it back later.
*/
parend = parent + strlen(parent);
strcat(parent, "/");
strcat(parent, direntry->d_name);
if (stat(parent, &maybe) < 0)
return(NULL);
*parend = '\0';
if (maybe.st_dev == lastdev && maybe.st_ino == lastino)
break; /* Found child! */
}
}
if (pathname[0] != '\0')
prepend(direntry->d_name, pathname);
else
strcpy(pathname, direntry->d_name);
closedir(par);
lastino = parstat.st_ino;
lastdev = parstat.st_dev;
strcat(parent, "/..");
}
prepend("", pathname); /* Supply leading slash. */
return(pathname);
}
/*
* prepend - prepend a new component to a filename, with / in between
*/
static
prepend(cpt, name)
char *cpt;
char *name;
{
char tmpname[1024];
strcpy(tmpname, name);
strcpy(name, cpt);
strcat(name, "/");
strcat(name, tmpname);
}
#ifdef TESTING
main()
{
char buf[1024];
printf("%s\n", getwd(buf));
}
#endif
-----
.TH GETWD 3 local
.DA 4 July 1984
.SH NAME
getwd \- get current working directory pathname
.SH SYNOPSIS
.B char *getwd(pathname)
.br
.B char *pathname;
.SH DESCRIPTION
.I Getwd
copies the absolute pathname of the current working directory to
.I pathname
and returns a pointer to the result.
.I Getwd
uses the directory-scanning routines of
.IR directory (3)
and hence requires the
.B \-lndir
loader option.
.PP
.I Getwd
will try to traverse the directory tree itself first;
failing this, it will use
.IR popen (3)
to invoke
.IR pwd (1),
which runs setuid-root and can get past some permission problems
that a C function can't.
.SH SEE ALSO
pwd(1)
.SH DIAGNOSTICS
.I Getwd
returns NULL and places a message in
.I pathname
if an error occurs.
.SH HISTORY
Local product, written to match 4.2BSD semantics.
.SH BUGS
Pathnames longer than 1023 bytes will cause trouble.