Module Name:    src
Committed By:   kre
Date:           Wed Dec 12 11:51:33 UTC 2018

Modified Files:
        src/bin/sh: sh.1 var.c

Log Message:
Implement:
        readonly -q VAR...
        readonly -p VAR...
        export -q [-x] VAR...
        export -p [-x] VAR...

all available only in !SMALL shells - and while here, limit
"export -x" to full sized shells as well.

Also, do a better job of arg checking and validating of the
export and readonly commands (which is really just one built-in)
and of issuing error messages when something bogus is detected.

Since these commands are special builtin commands, any error
causes shell exit (for non-interactive shells).


To generate a diff of this commit:
cvs rdiff -u -r1.212 -r1.213 src/bin/sh/sh.1
cvs rdiff -u -r1.73 -r1.74 src/bin/sh/var.c

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/sh.1
diff -u src/bin/sh/sh.1:1.212 src/bin/sh/sh.1:1.213
--- src/bin/sh/sh.1:1.212	Tue Dec 11 13:31:20 2018
+++ src/bin/sh/sh.1	Wed Dec 12 11:51:33 2018
@@ -1,4 +1,4 @@
-.\"	$NetBSD: sh.1,v 1.212 2018/12/11 13:31:20 kre Exp $
+.\"	$NetBSD: sh.1,v 1.213 2018/12/12 11:51:33 kre 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 December 11, 2018
+.Dd December 12, 2018
 .Dt SH 1
 .\" everything except c o and s (keep them ordered)
 .ds flags abCEeFfhIiLmnpquVvXx
@@ -2274,6 +2274,7 @@ etc).
 .It Ic \&: Op Ar arg ...
 A null command that returns a 0 (true) exit value.
 Any arguments or redirects are evaluated, then ignored.
+.\"
 .It Ic \&. Ar file
 The dot command reads and executes the commands from the specified
 .Ar file
@@ -2293,6 +2294,7 @@ and continue) behave across a dot comman
 This implementation allows them to control loops surrounding the dot command,
 but obviously such behavior should not be relied on.
 It is now permitted by the standard, but not required.
+.\"
 .It Ic alias Op Ar name Ns Op Li = Ns Ar string ...
 If
 .Ar name Ns Li = Ns Ar string
@@ -2310,9 +2312,11 @@ With no arguments, the
 built-in prints the
 names and values of all defined aliases (see
 .Ic unalias ) .
+.\"
 .It Ic bg Op Ar job ...
 Continue the specified jobs (or the current job if no
 jobs are given) in the background.
+.\"
 .It Ic command Oo Fl pVv Oc Ar command Op Ar arg ...
 Execute the specified command but ignore shell functions when searching
 for it.
@@ -2335,6 +2339,7 @@ Do not execute the command but
 search for the command and print the absolute pathname
 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
 Switch to the specified directory (default
 .Ev $HOME ) .
@@ -2402,9 +2407,11 @@ mechanism was used
 or if the
 .Ar replace
 argument was used.
+.\"
 .It Ic eval Ar string ...
 Concatenate all the arguments with spaces.
 Then re-parse and execute the command.
+.\"
 .It Ic exec Op Ar command Op Ar arg ...
 Unless
 .Ar command
@@ -2447,14 +2454,17 @@ Alternatively, see the
 .Ic fdflags
 command below, which can set, or clear, this, and other,
 file descriptor flags.
+.\"
 .It Ic exit Op Ar exitstatus
 Terminate the shell process.
 If
 .Ar exitstatus
 is given it is used as the exit status of the shell; otherwise the
 exit status of the preceding command (the current value of $?) is used.
-.It Ic export Oo Fl npx Oc Ar name ...
-.It Ic export Fl p Op Fl x
+.\"
+.It Ic export Oo Fl nx Oc Ar name Ns Oo =value Oc ...
+.It Ic export Oo Fl x Oc Oo Fl p Oo Ar name ... Oc Oc
+.It Ic export Fl q Oo Fl x Oc Ar name ...
 With no options,
 but one or more names,
 the specified names are exported so that they will appear in the
@@ -2477,6 +2487,7 @@ provided the variable is not also read-o
 That is
 .Bd -literal -offset indent
 export -x FOO # FOO will now not be exported by default
+export FOO    # this command will fail (non-fatally)
 FOO=some_value my_command
 .Ed
 .Pp
@@ -2487,30 +2498,57 @@ to
 through the environment.
 .Pp
 The shell allows the value of a variable to be set at the
-same time it is exported by writing
+same time it is exported (or unexported, etc) by writing
 .Pp
-.Dl export name=value
+.Dl export [-nx] name=value
 .Pp
-With no arguments the export command lists the names of all exported variables,
+With no arguments the export command lists the names of all
+set exported variables,
 or if
 .Fl x
-was given, all variables marked not for export.
+was given, all set variables marked not for export.
 With the
 .Fl p
-option specified the output will be formatted suitably for non-interactive use.
+option specified, the output will be formatted suitably for
+non-interactive use, and unset variables are included.
+When
+.Fl p
+is given, variable names, but not values, may also be
+given, in which case output is limited to the variables named.
 .Pp
-The
+With
+.Fl q
+and a list of variable names, the
+.Ic export
+command will exit with status 0 if all the named
+variables have been marked for export, or 1 if
+any are not so marked.
+If
+.Fl x
+is also given,
+the test is instead for variables marked not to be exported.
+.Pp
+Other than with
+.Fl q ,
+the
 .Ic export
 built-in exits with status 0,
-unless an invalid option, or option combination, is given,
-or unless an attempt is made to export a variable which has
+unless an attempt is made to export a variable which has
 been marked as unavailable for export,
 in which cases it exits with status 1.
+In all cases if
+an invalid option, or option combination, is given,
+or an invalid variable name is present,
+.Ic export
+will write a message to the standard error output,
+and exit with a non-zero status.
+A non-interactive shell will terminate.
 .Pp
 Note that there is no restriction upon exporting,
 or un-exporting, read-only variables.
 The no-export flag can be reset by unsetting the variable
-and creating it again \(en provided it is not also read-only.
+and creating it again \(en provided the variable is not also read-only.
+.\"
 .It Ic fc Oo Fl e Ar editor Oc Op Ar first Op Ar last
 .It Ic fc Fl l Oo Fl nr Oc Op Ar first Op Ar last
 .It Ic fc Fl s Oo Ar old=new Oc Op Ar first
@@ -2602,10 +2640,12 @@ Name of the editor to use.
 .It Ev HISTSIZE
 The number of previous commands that are accessible.
 .El
+.\"
 .It Ic fg Op Ar job
 Move the specified job or the current job to the foreground.
 A foreground job can interact with the user via standard input,
 and receive signals from the terminal.
+.\"
 .It Ic fdflags Oo Fl v Oc Op Ar fd ...
 .It Ic fdflags Oo Fl v Oc Fl s Ar flags fd Op ...
 Get or set file descriptor flags.
@@ -2642,6 +2682,7 @@ See
 and
 .Xr open 2
 for more information.
+.\"
 .It Ic getopts Ar optstring var
 The POSIX
 .Ic getopts
@@ -2742,6 +2783,7 @@ cmd \-a \-c arg file file
 cmd \-carg -a file file
 cmd \-a \-carg \-\- file file
 .Ed
+.\"
 .It Ic hash Oo Fl rv Oc Op Ar command ...
 The shell maintains a hash table which remembers the
 locations of commands.
@@ -2765,11 +2807,13 @@ The
 .Fl r
 option causes the hash command to delete all the entries in the hash table
 except for functions.
+.\"
 .It Ic inputrc Ar file
 Read the
 .Ar file
 to set key bindings as defined by
 .Xr editrc 5 .
+.\"
 .It Ic jobid Oo Fl g Ns \&| Ns Fl j Ns \&| Ns Fl p Oc  Op Ar job
 With no flags, print the process identifiers of the processes in the job.
 If the
@@ -2815,6 +2859,7 @@ or with
 .Fl p
 there is no process group leader (should not happen),
 and otherwise exits with status 0.
+.\"
 .It Ic jobs Oo Fl l Ns \&| Ns Fl p Oc Op Ar job ...
 Without
 .Ar job
@@ -2856,6 +2901,7 @@ and cleans up the job table entry for th
 Non-interactive shells need to execute
 .Ic wait
 commands to clean up terminated background jobs.
+.\"
 .It Ic local Oo Fl INx Oc Oo Ar variable | \- Oc ...
 Define local variables for a function.
 Local variables have their attributes, and values,
@@ -3050,6 +3096,7 @@ See the section
 below for details of the effects of making the variable
 .Dv LINENO
 local.
+.\"
 .It Ic pwd Op Fl \&LP
 Print the current directory.
 If
@@ -3090,6 +3137,7 @@ The built-in command may differ from the
 the program will use
 .Ev PWD
 and the built-in uses a separately cached value.
+.\"
 .It Ic read Oo Fl p Ar prompt Oc Oo Fl r Oc Ar variable Op Ar ...
 The
 .Ar prompt
@@ -3120,9 +3168,12 @@ acts as an escape character, causing the
 literally.
 If a backslash is followed by a newline, the backslash and the
 newline will be deleted.
-.It Ic readonly Ar name ...
-.It Ic readonly Op Fl p
-The specified names are marked as read only, so that they cannot be
+.\"
+.It Ic readonly Ar name Ns Oo =value Oc Ns ...
+.It Ic readonly Oo Fl p Op name... Oc
+.It Ic readonly Oo Fl q Oc name...
+With no options,
+the specified names are marked as read only, so that they cannot be
 subsequently modified or unset.
 The shell allows the value of a variable
 to be set at the same time it is marked read only by writing
@@ -3131,10 +3182,38 @@ to be set at the same time it is marked 
 .Pp
 With no arguments the
 .Ic readonly
-command lists the names of all read only variables.
+command lists the names of all set read only variables.
 With the
 .Fl p
-option specified the output will be formatted suitably for non-interactive use.
+option specified,
+the output will be formatted suitably for non-interactive use,
+and unset variables are included.
+When the
+.Fl p
+option is given,
+a list of variable names (without values) may also be specified,
+in which case output is limited to the named variables.
+.Pp
+With the
+.Fl q
+option, the
+.Ic readonly
+command tests the read-only status of the variables listed
+and exits with status 0 if all named variables are read-only,
+or with status 1 if any are not read-only.
+.Pp
+Other than as specified for
+.Fl q
+the
+.Ic readonly
+command normally exits with status 0.
+In all cases, if an unknown option, or an invalid option combination,
+or an invalid variable name, is given;
+or a variable which was already read-only is attempted to be set;
+the exit status will not be zero, a diagnostic
+message will be written to the standard error output,
+and a non-interactive shell will terminate.
+.\"
 .It Ic return Op Ar n
 Stop executing the current function or a dot command with return value of
 .Ar n
@@ -3152,6 +3231,7 @@ Use the
 .Ic exit
 command instead, if you want to return from a script or exit
 your shell.
+.\"
 .It Ic set Oo { Fl options | Cm +options | Cm \-- } Oc Ar arg ...
 The
 .Ic set
@@ -3216,6 +3296,7 @@ Otherwise the following arguments become
 and
 .Li \&$#
 is set to the number of arguments present.
+.\"
 .It Ic setvar Ar variable Ar value
 Assigns
 .Ar value
@@ -3229,6 +3310,7 @@ rather than using
 is intended to be used in
 functions that assign values to variables whose names are passed as
 parameters.)
+.\"
 .It Ic shift Op Ar n
 Shift the positional parameters
 .Ar n
@@ -3254,6 +3336,7 @@ The shift count must be less than or equ
 positional parameters (
 .Dq Li $# )
 before the shift.
+.\"
 .It Ic times
 Prints two lines to standard output.
 Each line contains two accumulated time values, expressed
@@ -3272,6 +3355,7 @@ for more information.
 .Ic times
 has no parameters, and exits with an exit status of 0 unless
 an attempt is made to give it an option.
+.\"
 .It Ic trap Ar action signal ...
 .It Ic trap \-
 .It Ic trap Op Fl l
@@ -3389,6 +3473,7 @@ untrapped (in their default states)
 and which were altered during the intermediate code,
 would not be reset by the final
 .Ic eval .
+.\"
 .It Ic type Op Ar name ...
 Interpret each
 .Ar name
@@ -3399,6 +3484,7 @@ command, tracked alias and not found.
 For aliases the alias expansion is
 printed; for commands and tracked aliases the complete pathname of the
 command is printed.
+.\"
 .It Ic ulimit Oo Fl H Ns \*(Ba Ns Fl S Oc Op Fl a \*(Ba Fl btfdscmlrpnv Op Ar value
 Inquire about or set the hard or soft limits on processes or set new
 limits.
@@ -3521,6 +3607,7 @@ The exit status is 0, unless an attempt 
 a readonly variable, in which case the exit status is 1.
 It is not an error to unset (or undefine) a variable (or function)
 that is not currently set (or defined.)
+.\"
 .It Ic wait Oo Fl n Oc Oo Fl p Ar var Oc Op Ar job ...
 Wait for the specified jobs to complete
 and return the exit status of the last job in the parameter list,

Index: src/bin/sh/var.c
diff -u src/bin/sh/var.c:1.73 src/bin/sh/var.c:1.74
--- src/bin/sh/var.c:1.73	Wed Dec 12 07:56:57 2018
+++ src/bin/sh/var.c	Wed Dec 12 11:51:33 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: var.c,v 1.73 2018/12/12 07:56:57 kre Exp $	*/
+/*	$NetBSD: var.c,v 1.74 2018/12/12 11:51:33 kre Exp $	*/
 
 /*-
  * Copyright (c) 1991, 1993
@@ -37,7 +37,7 @@
 #if 0
 static char sccsid[] = "@(#)var.c	8.3 (Berkeley) 5/4/95";
 #else
-__RCSID("$NetBSD: var.c,v 1.73 2018/12/12 07:56:57 kre Exp $");
+__RCSID("$NetBSD: var.c,v 1.74 2018/12/12 11:51:33 kre Exp $");
 #endif
 #endif /* not lint */
 
@@ -192,6 +192,8 @@ struct var *vartab[VTABSIZE];
 
 STATIC int strequal(const char *, const char *);
 STATIC struct var *find_var(const char *, struct var ***, int *);
+STATIC void showvar(struct var *, const char *, const char *, int);
+static void export_usage(const char *) __dead;
 
 /*
  * Initialize the varable symbol tables and import the environment
@@ -756,12 +758,35 @@ sort_var(const void *v_v1, const void *v
  * For now just roll 'em through qsort for printing...
  */
 
+STATIC void
+showvar(struct var *vp, const char *cmd, const char *xtra, int show_value)
+{
+	const char *p;
+
+	if (cmd)
+		out1fmt("%s ", cmd);
+	if (xtra)
+		out1fmt("%s ", xtra);
+	p = vp->text;
+	if (vp->rfunc && (vp->flags & VFUNCREF) != 0) {
+		p = (*vp->rfunc)(vp);
+		if (p == NULL)
+			p = vp->text;
+	}
+	for ( ; *p != '=' ; p++)
+		out1c(*p);
+	if (!(vp->flags & VUNSET) && show_value) {
+		out1fmt("=");
+		print_quoted(++p);
+	}
+	out1c('\n');
+}
+
 int
-showvars(const char *name, int flag, int show_value, const char *xtra)
+showvars(const char *cmd, int flag, int show_value, const char *xtra)
 {
 	struct var **vpp;
 	struct var *vp;
-	const char *p;
 
 	static struct var **list;	/* static in case we are interrupted */
 	static int list_len;
@@ -789,26 +814,11 @@ showvars(const char *name, int flag, int
 
 	qsort(list, count, sizeof *list, sort_var);
 
-	for (vpp = list; count--; vpp++) {
-		vp = *vpp;
-		if (name)
-			out1fmt("%s ", name);
-		if (xtra)
-			out1fmt("%s ", xtra);
-		p = vp->text;
-		if (vp->rfunc && (vp->flags & VFUNCREF) != 0) {
-			p = (*vp->rfunc)(vp);
-			if (p == NULL)
-				p = vp->text;
-		}
-		for ( ; *p != '=' ; p++)
-			out1c(*p);
-		if (!(vp->flags & VUNSET) && show_value) {
-			out1fmt("=");
-			print_quoted(++p);
-		}
-		out1c('\n');
-	}
+	for (vpp = list; count--; vpp++)
+		showvar(*vpp, cmd, xtra, show_value);
+
+	/* no free(list), will be used again next time ... */
+
 	return 0;
 }
 
@@ -818,53 +828,137 @@ showvars(const char *name, int flag, int
  * The export and readonly commands.
  */
 
+static void __dead
+export_usage(const char *cmd)
+{
+#ifdef SMALL
+	if (*cmd == 'r')
+	    error("Usage: %s [ -p | var[=val]... ]", cmd);
+	else
+	    error("Usage: %s [ -p | [-n] var[=val]... ]", cmd);
+#else
+	if (*cmd == 'r')
+	    error("Usage: %s [-p [var...] | -q var... | var[=val]... ]", cmd);
+	else
+	    error(
+	     "Usage: %s [ -px [var...] | -q[x] var... | [-n|x] var[=val]... ]",
+		cmd);
+#endif
+}
+
 int
 exportcmd(int argc, char **argv)
 {
 	struct var *vp;
 	char *name;
-	const char *p;
-	int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
+	const char *p = argv[0];
+	int flag = p[0] == 'r'? VREADONLY : VEXPORT;
 	int pflg = 0;
 	int nflg = 0;
+#ifndef SMALL
 	int xflg = 0;
+	int qflg = 0;
+#endif
 	int res;
 	int c;
 	int f;
 
-	while ((c = nextopt("npx")) != '\0') {
+#ifdef SMALL
+#define EXPORT_OPTS "np"
+#else
+#define	EXPORT_OPTS "npqx"
+#endif
+
+	while ((c = nextopt(EXPORT_OPTS)) != '\0') {
+
+#undef EXPORT_OPTS
+
 		switch (c) {
+		case 'n':
+			if (pflg || flag == VREADONLY
+#ifndef SMALL
+				|| qflg || xflg
+#endif
+						)
+				export_usage(p);
+			nflg = 1;
+			break;
 		case 'p':
-			if (nflg)
-				return 1;
+			if (nflg
+#ifndef SMALL
+				|| qflg
+#endif
+					)
+				export_usage(p);
 			pflg = 3;
 			break;
-		case 'n':
-			if (pflg || xflg || flag == VREADONLY)
-				return 1;
-			nflg = 1;
+#ifndef SMALL
+		case 'q':
+			if (nflg || pflg)
+				export_usage(p);
+			qflg = 1;
 			break;
 		case 'x':
 			if (nflg || flag == VREADONLY)
-				return 1;
+				export_usage(p);
 			flag = VNOEXPORT;
 			xflg = 1;
 			break;
-		default:
-			return 1;
+#endif
 		}
 	}
 
-	if (nflg && *argptr == NULL)
-		return 1;
+	if ((nflg
+#ifndef SMALL
+		|| qflg
+#endif
+		 ) && *argptr == NULL)
+		export_usage(p);
+
+#ifndef SMALL
+	if (pflg && *argptr != NULL) {
+		while ((name = *argptr++) != NULL) {
+			int len;
+
+			vp = find_var(name, NULL, &len);
+			if (name[len] == '=')
+				export_usage(p);
+			if (!goodname(name))
+				error("%s: bad variable name", name);
 
-	if (pflg || *argptr == NULL) {
-		showvars( pflg ? argv[0] : 0, flag, pflg,
-		    pflg && xflg ? "-x" : NULL );
+			if (vp && vp->flags & flag)
+				showvar(vp, p, xflg ? "-x" : NULL, 1);
+		}
 		return 0;
 	}
+#endif
+
+	if (pflg || *argptr == NULL)
+		return showvars( pflg ? p : 0, flag, pflg,
+#ifndef SMALL
+		    pflg && xflg ? "-x" :
+#endif
+					    NULL );
 
 	res = 0;
+#ifndef SMALL
+	if (qflg) {
+		while ((name = *argptr++) != NULL) {
+			int len;
+
+			vp = find_var(name, NULL, &len);
+			if (name[len] == '=')
+				export_usage(p);
+			if (!goodname(name))
+				error("%s: bad variable name", name);
+
+			if (vp == NULL || !(vp->flags & flag))
+				res = 1;
+		}
+		return res;
+	}
+#endif
+
 	while ((name = *argptr++) != NULL) {
 		int len;
 
@@ -893,7 +987,8 @@ exportcmd(int argc, char **argv)
 			}
 			if (p == NULL)
 				continue;
-		} 
+		} else if (nflg && p == NULL && !goodname(name))
+			error("%s: bad variable name", name);
 
 		if (!nflg || p != NULL)
 			setvar(name, p, f);

Reply via email to