/* * 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.