Module Name:    src
Committed By:   martin
Date:           Sat Nov  6 13:42:18 UTC 2021

Modified Files:
        src/bin/sh [netbsd-9]: cd.c sh.1

Log Message:
Pull up following revision(s) (requested by kre in ticket #1372):

        bin/sh/sh.1: revision 1.236 (patch)
        bin/sh/cd.c: revision 1.51

PR bin/45390 - fix for folly four

In the pwd builtin, verify that curdir names '.' before
simply printing it.   Never alter PWD or OLDPWD in the
pwd command.

Also while here, implement the (new: coming in POSIX, but has existed
for a while in several other shells) -e option to cd (with -e, cd -P
will exit(1) if the chdir() succeeds, but PWD cannot be discovered).
cd now prints the directory name used (if different from that given,
or cdprint is on) if interactive or (the new bit)in posix mode.

Some additional/changed comments added, and a DEBUG mode trace call
that was accidentally put inside an #if 0 block moved to where it
can do some good.

XXX pullup -9

PR bin/45390

Be explicit about what happens to PWD after a successful cd command.
Also be very clear  that "cd" and "cd -P" are the same thing, and
the only cd variant implemented.
Also, when it is appropriate to print the new directory after a cd
command, note that it happens if interactive (as it always has here)
and also if the posix option is set (for POSIX compat, where "interactive"
is irrelevant).  Mention that "cd -" is a case where the new directory
is printed (along with paths relative to a non-empty CDPATH entry,
and where the "cd old new" (string replacement in curdir) is used.

While here document the new -e option to cd.

XXX pullup -9


To generate a diff of this commit:
cvs rdiff -u -r1.50 -r1.50.8.1 src/bin/sh/cd.c
cvs rdiff -u -r1.223.2.1 -r1.223.2.2 src/bin/sh/sh.1

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/bin/sh/cd.c
diff -u src/bin/sh/cd.c:1.50 src/bin/sh/cd.c:1.50.8.1
--- src/bin/sh/cd.c:1.50	Wed Jul  5 20:00:27 2017
+++ src/bin/sh/cd.c	Sat Nov  6 13:42:18 2021
@@ -1,4 +1,4 @@
-/*	$NetBSD: cd.c,v 1.50 2017/07/05 20:00:27 kre Exp $	*/
+/*	$NetBSD: cd.c,v 1.50.8.1 2021/11/06 13:42:18 martin Exp $	*/
 
 /*-
  * Copyright (c) 1991, 1993
@@ -37,12 +37,13 @@
 #if 0
 static char sccsid[] = "@(#)cd.c	8.2 (Berkeley) 5/4/95";
 #else
-__RCSID("$NetBSD: cd.c,v 1.50 2017/07/05 20:00:27 kre Exp $");
+__RCSID("$NetBSD: cd.c,v 1.50.8.1 2021/11/06 13:42:18 martin Exp $");
 #endif
 #endif /* not lint */
 
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
@@ -67,10 +68,11 @@ __RCSID("$NetBSD: cd.c,v 1.50 2017/07/05
 #include "show.h"
 #include "cd.h"
 
-STATIC int docd(const char *, int);
+STATIC int docd(const char *, bool, bool);
 STATIC char *getcomponent(void);
-STATIC void updatepwd(const char *);
+STATIC bool updatepwd(const char *);
 STATIC void find_curdir(int noerror);
+STATIC bool is_curdir(const char *);
 
 char *curdir = NULL;		/* current working directory */
 char *prevdir;			/* previous working directory */
@@ -84,10 +86,13 @@ cdcmd(int argc, char **argv)
 	char *p;
 	char *d;
 	struct stat statb;
+	char opt;
+	bool eopt = false;
 	int print = cdprint;	/* set -o cdprint to enable */
 
-	while (nextopt("P") != '\0')
-		;
+	while ((opt = nextopt("Pe")) != '\0')
+		if (opt == 'e')
+			eopt = true;
 
 	/*
 	 * Try (quite hard) to have 'curdir' defined, nothing has set
@@ -128,19 +133,13 @@ cdcmd(int argc, char **argv)
 		stunalloc(p);
 		if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
 			int dopr = print;
+			int x;
 
-			if (!print) {
-				/*
-				 * XXX - rethink
-				 */
-				if (p[0] == '.' && p[1] == '/' && p[2] != '\0')
-					dopr = strcmp(p + 2, dest);
-				else
-					dopr = strcmp(p, dest);
-			}
-			if (docd(p, dopr) >= 0)
-				return 0;
+			if (!print)
+				dopr = strcmp(p, dest);
 
+			if ((x = docd(p, dopr != 0, eopt)) >= 0)
+				return x;
 		}
 	}
 	error("can't cd to %s", dest);
@@ -154,8 +153,10 @@ cdcmd(int argc, char **argv)
  */
 
 STATIC int
-docd(const char *dest, int print)
+docd(const char *dest, bool print, bool eopt)
 {
+	bool gotpwd;
+
 #if 0		/* no "cd -L" (ever) so all this is just a waste of time ... */
 	char *p;
 	char *q;
@@ -164,8 +165,6 @@ docd(const char *dest, int print)
 	int first;
 	int badstat;
 
-	CTRACE(DBG_CMDS, ("docd(\"%s\", %d) called\n", dest, print));
-
 	/*
 	 *  Check each component of the path. If we find a symlink or
 	 *  something we can't stat, clear curdir to force a getcwd()
@@ -199,16 +198,19 @@ docd(const char *dest, int print)
 	}
 #endif
 
+	CTRACE(DBG_CMDS, ("docd(\"%s\", %s, %s) called\n", dest,
+	    print ? "true" : "false", eopt ? "true" : "false"));
+
 	INTOFF;
 	if (chdir(dest) < 0) {
 		INTON;
 		return -1;
 	}
-	updatepwd(NULL);	/* only do cd -P, no "pretend" -L mode */
+	gotpwd = updatepwd(NULL);   /* only do cd -P, no "pretend" -L mode */
 	INTON;
-	if (print && iflag == 1 && curdir)
-		out1fmt("%s\n", curdir);
-	return 0;
+	if (print && (iflag || posix))
+		out1fmt("%s\n", gotpwd ? curdir : dest);
+	return gotpwd || !eopt ? 0 : 1;
 }
 
 
@@ -245,7 +247,7 @@ getcomponent(void)
  * that the current directory has changed.
  */
 
-STATIC void
+STATIC bool
 updatepwd(const char *dir)
 {
 	char *new;
@@ -256,7 +258,7 @@ updatepwd(const char *dir)
 	/*
 	 * If our argument is NULL, we don't know the current directory
 	 * any more because we traversed a symbolic link or something
-	 * we couldn't stat().
+	 * we couldn't stat().   Or we simply don't trust what we had.
 	 */
 	if (dir == NULL || curdir == NULL)  {
 		if (prevdir)
@@ -269,10 +271,14 @@ updatepwd(const char *dir)
 		if (curdir) {
 			setvar("OLDPWD", prevdir, VEXPORT);
 			setvar("PWD", curdir, VEXPORT);
+			return true;
 		} else
 			unsetvar("PWD", 0);
-		return;
+		return false;
 	}
+
+	/* XXX none of the following code is ever executed any more */
+
 	cdcomppath = stalloc(strlen(dir) + 1);
 	scopy(dir, cdcomppath);
 	STARTSTACKSTR(new);
@@ -303,6 +309,25 @@ updatepwd(const char *dir)
 	setvar("OLDPWD", prevdir, VEXPORT);
 	setvar("PWD", curdir, VEXPORT);
 	INTON;
+	return true;
+}
+
+/*
+ * Test whether we are currently in the direcory given
+ * (provided it is given, and is absolute)
+ * ie: determine if path is fully qualified pathname of "."
+ */
+STATIC bool
+is_curdir(const char *path)
+{
+	struct stat stdot, stpath;
+
+	return	path != NULL &&
+		*path == '/' &&
+		stat(".", &stdot) != -1 &&
+		stat(path, &stpath) != -1 &&
+		stdot.st_dev == stpath.st_dev &&
+		stdot.st_ino == stpath.st_ino;
 }
 
 /*
@@ -329,8 +354,16 @@ pwdcmd(int argc, char **argv)
 	else
 		find_curdir(0);
 
+#if 0	/* posix has been changed to forbid this */
 	setvar("OLDPWD", prevdir, VEXPORT);
 	setvar("PWD", curdir, VEXPORT);
+#endif
+
+	if (!is_curdir(curdir)) {
+		find_curdir(1);
+		if (curdir == NULL)
+			error("Unable to find current directory");
+	}
 	out1str(curdir);
 	out1c('\n');
 	return 0;
@@ -358,7 +391,6 @@ void
 getpwd(int noerror)
 {
 	char *pwd;
-	struct stat stdot, stpwd;
 	static int first = 1;
 
 	if (curdir)
@@ -367,10 +399,7 @@ getpwd(int noerror)
 	if (first) {
 		first = 0;
 		pwd = getenv("PWD");
-		if (pwd && *pwd == '/' && stat(".", &stdot) != -1 &&
-		    stat(pwd, &stpwd) != -1 &&
-		    stdot.st_dev == stpwd.st_dev &&
-		    stdot.st_ino == stpwd.st_ino) {
+		if (is_curdir(pwd)) {
 			curdir = savestr(pwd);
 			return;
 		}
@@ -392,6 +421,13 @@ find_curdir(int noerror)
 	 * getcwd, but traditionally getcwd is implemented using popen
 	 * to /bin/pwd. This creates a problem for us, since we cannot
 	 * keep track of the job if it is being ran behind our backs.
+	 * XXX That's not actually the problem, a process created and
+	 * XXX destroyed that we know nothing about is harmless.  The
+	 * XXX problem is that old popen() implementations would use
+	 * XXX wait(2) to await completion of the command, and that might
+	 * XXX collect (and ignore) our children.   As long as we are
+	 * XXX confident that popen() uses waitpid() (or the equv) there
+	 * XXX would not be a problem.   But how do we know that?
 	 * So we re-implement getcwd(), and we suppress interrupts
 	 * throughout the process. This is not completely safe, since
 	 * the user can still break out of it by killing the pwd program.

Index: src/bin/sh/sh.1
diff -u src/bin/sh/sh.1:1.223.2.1 src/bin/sh/sh.1:1.223.2.2
--- src/bin/sh/sh.1:1.223.2.1	Sat Nov  6 13:35:43 2021
+++ src/bin/sh/sh.1	Sat Nov  6 13:42:18 2021
@@ -1,4 +1,4 @@
-.\"	$NetBSD: sh.1,v 1.223.2.1 2021/11/06 13:35:43 martin Exp $
+.\"	$NetBSD: sh.1,v 1.223.2.2 2021/11/06 13:42:18 martin Exp $
 .\" Copyright (c) 1991, 1993
 .\"	The Regents of the University of California.  All rights reserved.
 .\"
@@ -31,7 +31,7 @@
 .\"
 .\"	@(#)sh.1	8.6 (Berkeley) 5/4/95
 .\"
-.Dd October 25, 2021
+.Dd October 31, 2021
 .Dt SH 1
 .\" everything except c o and s (keep them ordered)
 .ds flags abCEeFfhIiLmnpquVvXx
@@ -83,10 +83,7 @@
 .Sh DESCRIPTION
 .Nm
 is the standard command interpreter for the system.
-The current version of
-.Nm
-is in the process of being changed to conform more closely to the
-POSIX 1003.2 and 1003.2a specifications for the shell.
+It is a re-implementation and extension of the Bourne shell.
 This version has many
 features which make it appear similar in some respects to the Korn shell,
 but it is not a Korn shell clone (see
@@ -2399,7 +2396,7 @@ search for the command and print the abs
 of utilities, the name for built-ins or the expansion of aliases.
 .El
 .\"
-.It Ic cd Oo Fl P Oc Op Ar directory Op Ar replace
+.It Ic cd Oo Fl Pe Oc Op Ar directory Op Ar replace
 Switch to the specified directory (default
 .Ev $HOME ) .
 If
@@ -2434,14 +2431,44 @@ is the same as that of
 .Pp
 The
 .Fl P
-option instructs the shell to update
+option
+(which is the unalterable default in this
+.Nm )
+instructs the shell to
+change to the directory specified (or determined)
+and if successful
+update
+.Ev PWD
+with the new physical directory path.
+That is the path name, not traversing any symbolic links,
+of the altered working directory of the shell.
+.Pp
+The
+.Fl e
+option alters the interpretation of the exit status.
+.Ic cd
+will exit with status 0 if successful.
+If the directory was successfully changed, but
 .Ev PWD
-with the specified physical directory path and change to that directory.
-This is the default.
+was unable to be updated,
+.Ic cd
+will exit with status 1 if the
+.Fl e
+option was given, and status 0 otherwise.
+Upon any other error,
+including usage errors,
+and failing to successfully change directory,
+.Ic cd
+will exit with status 2.
 .Pp
-When the directory changes, the variable
+When the directory changes,
+and
+.Ev PWD
+is updated, the variable
 .Ev OLDPWD
-is set to the working directory before the change.
+is set to the working directory
+.Po \&\$ Ns Ev PWD Ns Pc
+as it was before the change.
 .Pp
 Some shells also support a
 .Fl L
@@ -2451,21 +2478,32 @@ with the logical path and to change the 
 accordingly.
 This is not supported.
 .Pp
-In an interactive shell, the
+In an interactive shell, or if the
+.Cm posix
+option is set, the
 .Ic cd
 command will print out the name of the
-directory that it actually switched to if this is different from the name
+directory that it actually switched to
+.Po that is, the pathname passed to the successful
+.Xr chdir 2
+.No system call Pc
+if this is different from the name
 that the user gave,
-or always if the
+or if the
 .Cm cdprint
 option is set.
-The destination may be different either because the
+The destination may be different because
+a non-empty element of the
 .Ev CDPATH
-mechanism was used
-.\" or because a symbolic link was crossed.
-or if the
+mechanism was used,
+.\" or because a symbolic link was crossed.   XXX Definitively not.
+or because the
 .Ar replace
-argument was used.
+argument was used,
+or because the
+.Ar directory
+parameter was specified as
+.Dq \&\- .
 .\"
 .It Ic eval Ar string ...
 Concatenate all the arguments with spaces.

Reply via email to