[net.sources] New & Improved ``pwd''

fred@umcp-cs.UUCP (07/29/83)

This is the 4.1BSD pwd.c with a few improvements. The standard pwd
didn't check the status returned by ``stat'' and ``chdir'' sys
calls. This can produce some annoying results if it runs into an
unreadable directory.  Specifically: if you did a:

	chmod 0 .
	pwd

in some directory other than the root, it would print ``/''.
Totally wrong, and since there is no error message it could be
misleading. When it does print out an error message it wouldn't
tell you which directory the error occurred in.  This version of
pwd catches these sort of errors, and prints out whatever partial
path it has built up, preceded by the device and inode numbers of
the highest-level directory it was able to chdir to.

Unfortunately this will become obsolete under 4.2BSD, but it should
be useful under other Unices as well.

*********************************************************************
 static char *sccsid =
 	"@(#)pwd.c	4.1 (Berkeley: UM modified) 26-Jul-1983";
 
 /*
  * Print working (current) directory
  *
  * Modified to handle ``stat'' and ``chdir'' errors gracefully,
  * and to print out partial paths preceded by device and inode
  * numbers.
  *	22-Jul-1983: Fred Blonder <fred@umcp-cs>
  *
  % cc -s -O pwd.c -o pwd
  */
 
 #include	<stdio.h>
 #include	<sys/param.h>
 #include	<sys/stat.h>
 #include	<sys/dir.h>
 
 char	dot[]	= ".";
 char	dotdot[] = "..";
 char	name[BUFSIZ], *np = &name[BUFSIZ];
 int	file;
 struct	stat	d, dd;
 struct	direct	dir;
 dev_t	c_dev;
 ino_t	c_ino;
 
 main()
 {
 	int rdev, rino;
 
 	*--np = '\0';
 
 	if (stat("/", &d) < 0) {
 		perror("pwd: Can't stat ``/''.");
 		exit(1);
 	}
 	rdev = d.st_dev;
 	rino = d.st_ino;
 	for (;;) {
 		if (stat(dot, &d) < 0) {
 			perror("pwd: Can't stat directory.");
 			dump_path();
 		}
 		if (d.st_ino==rino && d.st_dev==rdev)
 			prname();
 		if ((file = open(dotdot,0)) < 0) {
 			fprintf(stderr,"pwd: cannot open ..\n");
 			dump_path();
 		}
 		if (fstat(file, &dd) < 0) {
 			perror("pwd: fstat failed");
 			dump_path();
 		}
 		if (chdir(dotdot) < 0) {
 			perror("pwd: Can't chdir to ``..''.");
 			dump_path();
 		}
 		if(d.st_dev == dd.st_dev) {
 			if(d.st_ino == dd.st_ino)
 				prname();
 			do
 				if (read(file, (char *)&dir, sizeof(dir)) < sizeof(dir)) {
 					fprintf(stderr,"read error in ..\n");
 					dump_path();
 				}
 			while (dir.d_ino != d.st_ino);
 		}
 		else do {
 				if(read(file, (char *)&dir, sizeof(dir)) < sizeof(dir)) {
 					fprintf(stderr,"read error in ..\n");
 					dump_path();
 				}
 				if (stat(dir.d_name, &dd) < 0) /* error */
 					continue;	/* ignore this one */
 			} while(dd.st_ino != d.st_ino || dd.st_dev != d.st_dev);
 		close(file);
 		cat();
 	}
 }
 
 prname()
 {
 	printf("%s\n", np);
 	exit(0);
 }
 
 cat()
 {
 	register i;
 
 	c_dev = dd.st_dev;
 	c_ino = dd.st_ino;
 
 	if ((i = strlen(dir.d_name)) > DIRSIZ)
 		i = DIRSIZ;
 
 	if ((strlen(np) + i + 2) > BUFSIZ) {
 		fprintf("pwd: Path name too long: > %d characters.\n", BUFSIZ);
 		dump_path();
 	}
 
 	np = &np[-i];
 	strncpy(np, dir.d_name, i);
 	*--np = '/';
 }
 
 dump_path()
 {
 if (np < &name[BUFSIZ-1])
 	fprintf(stderr, "Known path is: dev (%d/%d) #%d%s\n",
 		(c_dev >> 8) & 0377, c_dev & 0377, c_ino, np);
 exit(1);
 }