[gnu.utils.bug] GNU Make 3.56: Porting/Speed patch: Doug Gwyn's getcwd

chip@ateng.ateng.com (Chip Salzenberg) (10/11/89)

[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;
+ }