Path: utzoo!utgpu!jarvis.csri.toronto.edu!rutgers!ucsd!tut.cis.ohio-state.edu!ateng.ateng.com!chip From: chip@ateng.ateng.com (Chip Salzenberg) Newsgroups: gnu.utils.bug Subject: GNU Make 3.56: Porting/Speed patch: Doug Gwyn's getcwd ( ) Message-ID: Date: 11 Oct 89 15:58:12 GMT Sender: daemon@tut.cis.ohio-state.edu Distribution: gnu Organization: GNUs Not Usenet Lines: 232 [PORTING AND SPEED PATCH] Replace getcwd() and getwd() with Doug Gwyn's portable implementation. Besides being faster (no fork/exec required), this function is robust even when SIGCLD/SIGCHLD is caught, unlike the SCO Xenix version. Index: make.h *************** *** 89,92 **** --- 89,93 ---- extern struct dep *copy_dep_chain (); extern char *find_percent (); + extern char *getcurdir (); #ifndef NO_ARCHIVES *************** *** 131,142 **** extern char **environ; ! #ifdef USG ! extern char *getcwd (); ! #define getwd(buf) getcwd (buf, MAXPATHLEN - 2) ! #else /* Not USG. */ ! extern char *getwd (); ! #endif /* USG. */ - extern char *reading_filename; extern unsigned int *reading_lineno_ptr; --- 132,137 ---- extern char **environ; ! #define getwd(buf) getcurdir (buf, MAXPATHLEN - 2) extern char *reading_filename; extern unsigned int *reading_lineno_ptr; Index: dir.c *************** *** 385,386 **** --- 366,556 ---- printf (" impossibilities in %u directories.\n", dirs); } + + /* + getcurdir -- get current working directory name + (compatible with POSIX and SVID getcwd()) + + last edit: 21-Sep-1987 D A Gwyn + + This public-domain getcurdir() routine can be used to replace the UNIX + System V library routine (which uses popen() to capture the output of + the "pwd" command). Once that is done, "pwd" can be reimplemented as + just puts(getcurdir()). + + This implementation depends on every directory having entries for + "." and "..". It also depends on the internals of the readdir() + data structures to some degree. + + I considered using chdir() to ascend the hierarchy, followed by a + final chdir() to the path being returned by getcurdir() to restore the + location, but decided that error recovery was too difficult that way. + The algorithm I settled on was inspired by my rewrite of the "pwd" + utility, combined with the dotdots[] array trick from the SVR2 shell. + */ + + char * + getcurdir(buf, size) /* returns pointer to CWD pathname */ + char *buf; /* where to put name (NULL to malloc) */ + int size; /* size of buf[] or malloc()ed memory */ + { + static char dotdots[] = + "../../../../../../../../../../../../../../../../../../../../../../../../../.."; + char *dotdot; /* -> dotdots[.], right to left */ + DIR *dirp; /* -> parent directory stream */ + struct direct *dir; /* -> directory entry */ + struct stat stat1, + stat2; /* info from stat() */ + struct stat *d = &stat1; /* -> info about "." */ + struct stat *dd = &stat2; /* -> info about ".." */ + register char *buffer; /* local copy of buf, or malloc()ed */ + char *bufend; /* -> buffer[size] */ + register char *endp; /* -> end of reversed string */ + register char *dname; /* entry name ("" for root) */ + int serrno = errno; /* save entry errno */ + + if (size == 0) + { + errno = EINVAL; /* invalid argument */ + return NULL; + } + + if ((buffer = buf) == NULL /* wants us to malloc() the string */ + && (buffer = (char *) malloc((unsigned) size)) == NULL) + { + errno = ENOMEM; /* cannot malloc() specified size */ + return NULL; + } + + if (stat(".", dd) != 0) /* prime the pump */ + goto error; /* errno already set */ + + endp = buffer; /* initially, empty string */ + bufend = &buffer[size]; + + for (dotdot = &dotdots[sizeof(dotdots)]; dotdot != dotdots;) + { + /* include one more "/.." section */ + /* (first time is actually "..") */ + dotdot -= 3; + + /* swap stat() info buffers */ + { + register struct stat *temp = d; + + d = dd; /* new current dir is old parent dir */ + dd = temp; + } + + if ((dirp = opendir(dotdot)) == NULL) /* new parent */ + goto error; /* errno already set */ + + if (fstat(dirp->dd_fd, dd) != 0) + { + serrno = errno; /* set by fstat() */ + (void) closedir(dirp); + errno = serrno; /* in case closedir() clobbered it */ + goto error; + } + + if (d->st_dev == dd->st_dev) + { + /* not crossing a mount point */ + + if (d->st_ino == dd->st_ino) + { + /* root directory */ + dname = ""; + goto append; + } + + do + { + if ((dir = readdir(dirp)) == NULL) + { + (void) closedir(dirp); + errno = ENOENT; /* missing entry */ + goto error; + } + } while (dir->d_ino != d->st_ino); + } + else + { /* crossing a mount point */ + struct stat t; /* info re. test entry */ + char name[sizeof(dotdots) + 1 + MAXNAMLEN]; + + (void) strcpy(name, dotdot); + dname = &name[strlen(name)]; + *dname++ = '/'; + + do + { + if ((dir = readdir(dirp)) == NULL) + { + (void) closedir(dirp); + errno = ENOENT; /* missing entry */ + goto error; + } + + (void) strcpy(dname, dir->d_name); + /* must fit if MAXNAMLEN is not a lie */ + } while (stat(name, &t) != 0 + || t.st_ino != d->st_ino + || t.st_dev != d->st_dev); + } + + dname = dir->d_name; + + /* append "/" and reversed dname string onto buffer */ + append: + if (endp != buffer /* avoid trailing / in final name */ + || dname[0] == '\0' /* but allow "/" when CWD is root */ + ) + *endp++ = '/'; + + { + register char *app; /* traverses dname string */ + + for (app = dname; *app != '\0'; ++app) + ; + + if (app - dname >= bufend - endp) + { + (void) closedir(dirp); + errno = ERANGE; /* won't fit allotted space */ + goto error; + } + + while (app != dname) + *endp++ = *--app; + } + + (void) closedir(dirp); + + if (dname[0] == '\0') + { /* reached root; wrap it up */ + register char *startp; /* -> buffer[.] */ + + *endp = '\0'; /* plant null terminator */ + + /* straighten out reversed pathname string */ + for (startp = buffer; --endp > startp; ++startp) + { + char temp = *endp; + + *endp = *startp; + *startp = temp; + } + + errno = serrno; /* restore entry errno */ + return buffer; + } + } + + errno = ENOMEM; /* actually, algorithm failure */ + + error: + if (buf == NULL) + free((char *) buffer); + + return NULL; + }