Commit Diff


commit - 2d331b3f3d70a5e5bf1362ff9e23627f0ca15288
commit + e84a83799e9828f1fe70428efafe33402a7572e8
blob - 08c1f3f3b37b555c2e7c4d573e06af93601a01d3
blob + 2c6b28c32e26056d4f1939cbe118d5492a29d10d
--- commands.c
+++ commands.c
@@ -94,6 +94,7 @@ static int	pr_conf(int, char **);
 static int	pr_s_conf(int, char **);
 static int	pr_a_conf(int, char **);
 static int	pr_conf_diff(int, char **);
+static int	pr_environment(int, char **);
 static int	show_hostname(int, char **);
 static int	wr_startup(void);
 static int	wr_conf(char *);
@@ -114,6 +115,8 @@ static int	int_traceroute6(char *, int, int, char **);
 static int	int_ssh(char *, int, int, char **);
 static int	int_telnet(char *, int, int, char **);
 static int	int_do(char *, int, int, char **);
+static int	int_setenv(char *, int, int, char **);
+static int	int_unsetenv(char *, int, int, char **);
 static int	int_show(char *, int, int, char **);
 static int	int_who(char *, int, int, char **);
 static int	int_doverbose(char *, int, int, char **);
@@ -126,6 +129,8 @@ static int	hostname(int, char **);
 static int	manual(int, char**);
 static int	nocmd(int, char **);
 static int	docmd(int, char **);
+static int	setenvcmd(int, char **);
+static int	unsetenvcmd(int, char **);
 static int	shell(int, char*[]);
 static int	ping(int, char*[]);
 static int	ping6(int, char*[]);
@@ -455,6 +460,7 @@ Menu showlist[] = {
 	{ "startup-config", "Startup configuration", CMPL0 0, 0, 0, 0, pr_s_conf },
 	{ "active-config", "Configuration of active context", CMPL0 0, 0, 0, 0, pr_a_conf },
 	{ "diff-config", "Show differences between startup and running config", CMPL0 0, 0, 0, 0, pr_conf_diff },
+	{ "environment", "Show environment variables",	CMPL(e) 0, 0, 0, 1, pr_environment },
 	{ "?",		"Options",		CMPL0 0, 0, 0, 0, show_help },
 	{ "help",	0,			CMPL0 0, 0, 0, 0, show_help },
 	{ 0, 0, 0, 0, 0 }
@@ -1069,6 +1075,8 @@ static char crontabhelp[];
 static char showhelp[];
 static char whohelp[];
 static char dohelp[];
+static char setenvhelp[];
+static char unsetenvhelp[];
 static char verbosehelp[];
 static char editinghelp[];
 static char shellhelp[];
@@ -1127,6 +1135,8 @@ struct intlist Intlist[] = {
 	{ "ssh",	sshhelp,				CMPL0 0, 0, int_ssh, 0 },
 	{ "telnet",	telnethelp,				CMPL0 0, 0, int_telnet,	0 },
 	{ "do",		dohelp,					CMPL(c) 0, 0, int_do, 0 },
+	{ "setenv",	setenvhelp,				CMPL(e) 0, 0, int_setenv, 0 },
+	{ "unsetenv",	unsetenvhelp,				CMPL(e) 0, 0, int_unsetenv, 0 },
 	{ "keepalive",	"GRE tunnel keepalive",			CMPL0 0, 0, intkeepalive, 1},
 	{ "mplslabel",	"MPLS local label",			CMPL0 0, 0, intmpls, 1 },
 	{ "pwe",	"MPLS PWE3",				CMPL0 0, 0, intpwe3, 1 },
@@ -1209,6 +1219,8 @@ struct intlist Bridgelist[] = {
 	{ "ssh",	sshhelp,				CMPL0 0, 0, int_ssh, 0 },
 	{ "telnet",	telnethelp,				CMPL0 0, 0, int_telnet,	0 },
 	{ "do",		dohelp,					CMPL(c) 0, 0, int_do, 0 },
+	{ "setenv",	setenvhelp,				CMPL(e) 0, 0, int_setenv, 0 },
+	{ "unsetenv",	unsetenvhelp,				CMPL(e) 0, 0, int_unsetenv, 0 },
 	{ "rule",	"Bridge layer 2 filtering rules",	CMPL0 0, 0, brrule, 0 },
 	{ "static",	"Static bridge address entry",		CMPL0 0, 0, brstatic, 1 },
 	{ "ifpriority",	"Spanning priority of a member on an 802.1D bridge",	CMPL0 0, 0, brpri, 1 },
@@ -1568,6 +1580,20 @@ static int
 int_do(char *ifname, int ifs, int argc, char **argv)
 {
 	docmd(argc, argv);
+	return 0; /* do not leave interface context */
+}
+
+static int
+int_setenv(char *ifname, int ifs, int argc, char **argv)
+{
+	setenvcmd(argc, argv);
+	return 0; /* do not leave interface context */
+}
+
+static int
+int_unsetenv(char *ifname, int ifs, int argc, char **argv)
+{
+	unsetenvcmd(argc, argv);
 	return 0; /* do not leave interface context */
 }
 
@@ -1710,6 +1736,8 @@ static char
 	confighelp[] =	"Set configuration mode",
 	whohelp[] =	"Display system users",
 	dohelp[] =	"Superfluous, do is ignored and its arguments executed",
+	setenvhelp[] =	"Set an environment variable",
+	unsetenvhelp[] ="Delete an environment variable",
 	shellhelp[] =	"Invoke a subshell",
 	savehelp[] =	"Save the current configuration",
 	nreboothelp[] =	"Reboot the system",
@@ -1856,6 +1884,7 @@ struct ghs mantab[] = {
 	{ "sasyncd", "Search for tag sasyncd", CMPL0 NULL, 0 },
 	{ "scheduler", "Search for tag scheduler", CMPL0 NULL, 0 },
 	{ "sensor", "Search for tag sensor", CMPL0 NULL, 0 },
+	{ "setenv", "Search for tag setenv", CMPL0 NULL, 0 },
 	{ "sh", "Search for tag sh", CMPL0 NULL, 0 },
 	{ "shell", "Search for tag shell", CMPL0 NULL, 0 },
 	{ "show", "Search for tag show", CMPL0 NULL, 0 },
@@ -1879,6 +1908,7 @@ struct ghs mantab[] = {
 	{ "traceroute", "Search for tag traceroute", CMPL0 NULL, 0 },
 	{ "traceroute6", "Search for tag traceroute6", CMPL0 NULL, 0 },
 	{ "unprivileged", "Search for tag unprivileged", CMPL0 NULL, 0 },
+	{ "unsetenv", "Search for tag setenv", CMPL0 NULL, 0 },
 	{ "veb", "Search for tag veb", CMPL0 NULL, 0 },
 	{ "verbose", "Search for tag verbose", CMPL0 NULL, 0 },
 	{ "vlan", "Search for tag vlan", CMPL0 NULL, 0 },
@@ -1965,6 +1995,8 @@ Command cmdtab[] = {
 	{ "who",	whohelp,	CMPL0 0, 0, who,		0, 0, 0, 0 },
 	{ "no",		0,		CMPL(c) 0, 0, nocmd,		0, 0, 0, 0 },
 	{ "do",		dohelp,		CMPL(c) 0, 0, docmd,		0, 0, 0, 0 },
+	{ "setenv",	setenvhelp,	CMPL(E) 0, 0, setenvcmd,	0, 0, 0, 0 },
+	{ "unsetenv",	unsetenvhelp,	CMPL(e) 0, 0, unsetenvcmd,	0, 0, 0, 0 },
 	{ "!",		shellhelp,	CMPL0 0, 0, shell,		1, 0, 0, 0 },
 	{ "?",		helphelp,	CMPL(c) 0, 0, help,		0, 0, 0, 0 },
 	{ "manual",	manhelp,	CMPL(H) (char **)mantab, sizeof(struct ghs), manual,0, 0, 0, 0 },
@@ -2280,6 +2312,62 @@ docmd(int argc, char **argv)
 	return 0;
 }
 
+static void
+usage_setenv(void)
+{
+	printf("%% setenv NAME=VALUE\n");
+	printf("%% setenv NAME=\"VALUE with spaces\"\n");
+	printf("%% setenv \"NAME with spaces\"=VALUE\n");
+}
+
+static int
+setenvcmd(int argc, char **argv)
+{
+	char *name = NULL, *value = NULL, *eq;
+
+	if (argc != 2) {
+		usage_setenv();
+		return 0;
+	}
+
+	eq = strchr(argv[1], '=');
+	if (eq == NULL) {
+		usage_setenv();
+		return 0;
+	}
+
+	name = strndup(argv[1], eq - argv[1]);
+	if (name == NULL) {
+		printf("%% setenvcmd: strndup: %s\n", strerror(errno));
+		return 0;
+	}
+
+	value = eq + 1;
+	if (setenv(name, value, 1) == -1)
+		printf("%% setenv %s=%s: %s\n", name, value, strerror(errno));
+
+	free(name);
+	return 0;
+}
+
+static int
+unsetenvcmd(int argc, char **argv)
+{
+	char *name;
+
+	if (argc != 2) {
+		printf("%% unsetenv NAME\n");
+		return 0;
+	}
+
+	name = argv[1];
+
+	if (unsetenv(name) == -1)
+		printf("%% unsetenv %s: %s\n", name, strerror(errno));
+
+	return 0;
+}
+
 /*
  * Shell command.
  */
@@ -3367,4 +3455,79 @@ pr_dhcp(int argc, char **argv)
 	}
 	printf("%% show dhcp leases\n");
 	return(1);
+}
+
+static int
+envcmp(const void *item1, const void *item2)
+{
+	const char *a = *(const char **)item1;
+	const char *b = *(const char **)item2;
+
+	return strcmp(a, b);
 }
+
+static int
+pr_environment(int argc, char **argv)
+{
+	extern char **environ;
+	char **ep;
+	int fd;
+	char path[PATH_MAX];
+
+	if (strlcpy(path, "/tmp/nshrc.env.XXXXXXXX", sizeof(path)) >=
+	    sizeof(path))
+		return 0;
+
+	fd = mkstemp(path);
+	if (fd == -1) {
+		printf("%% mkstemp %s: %s\n", path, strerror(errno));
+		return 0;
+	}
+
+	if (argc >= 3) {
+		char *name, *eq, *value;
+
+		name = argv[2];
+		for (ep = environ; *ep; ep++) {
+			eq = strchr(*ep, '=');
+			if (eq && strncmp(name, *ep, eq - *ep) == 0) {
+				value = eq + 1;
+				dprintf(fd, "%s\n", value);
+				break;
+			}
+		}
+	} else {
+		char **sorted_environ;
+		int nenv;
+
+		for (nenv = 0, ep = environ; *ep; ep++) {
+			if (strchr(*ep, '=') != NULL)
+				nenv++;
+		}	
+			
+		sorted_environ = calloc(nenv + 1, sizeof(*sorted_environ));
+		if (sorted_environ == NULL) {
+			printf("%% pr_environment: calloc: %s\n", strerror(errno));
+			goto done;
+		}
+
+		for (nenv = 0, ep = environ; *ep; ep++) {
+			if (strchr(*ep, '=') != NULL)
+				sorted_environ[nenv++] = *ep;
+		}
+
+		qsort(sorted_environ, nenv, sizeof(*sorted_environ), envcmp);
+		sorted_environ[nenv] = NULL;
+
+		for (ep = sorted_environ; *ep; ep++)
+			dprintf(fd, "%s\n", *ep);
+	}
+
+	fsync(fd);
+
+	more(path);
+done:
+	unlink(path);
+	close(fd);
+	return 0;
+}
blob - 1aa4e516adf58328b4cf0e36ccae19bbe005a6cc
blob + b3d3ffb85ca317f5281700df862ec443d010c534
--- complete.c
+++ complete.c
@@ -51,7 +51,8 @@
 unsigned char complete(EditLine *, char **, size_t);
 
 static int	     comparstr(const void *, const void *);
-static unsigned char complete_ambiguous(char *, int, StringList *, EditLine *);
+static unsigned char complete_ambiguous(char *, int, StringList *, EditLine *,
+				char *);
 static unsigned char complete_command(char *, int, EditLine *, char **, int);
 static unsigned char complete_subcommand(char *, int, EditLine *, char **, int);
 static unsigned char complete_local(char *, int, EditLine *);
@@ -59,6 +60,7 @@ static unsigned char complete_ifname(char *, int, Edit
 static unsigned char complete_ifgroup(char *, int, EditLine *);
 static unsigned char complete_ifbridge(char *, int, EditLine *);
 static unsigned char complete_rtable(char *, int, EditLine *);
+static unsigned char complete_environment(char *, int, EditLine *, int);
 static unsigned char complete_nocmd(struct ghs *, char *, int, EditLine *,
 				   char **, int, int);
 static unsigned char complete_docmd(struct ghs *, char *, int, EditLine *,
@@ -87,9 +89,11 @@ comparstr(const void *a, const void *b)
  *	word	word which started the match
  *	list	list by default
  *	words	stringlist containing possible matches
+ *	sep	separator to insert after completed word; usually " "
  */
 static unsigned char
-complete_ambiguous(char *word, int list, StringList *words, EditLine *el)
+complete_ambiguous(char *word, int list, StringList *words, EditLine *el,
+    char *sep)
 {
 	char insertstr[MAXPATHLEN];
 	char *lastmatch;
@@ -102,7 +106,7 @@ complete_ambiguous(char *word, int list, StringList *w
 
 	if (words->sl_cur == 1) {	/* only one choice available */
 		(void)strlcpy(insertstr, words->sl_str[0], sizeof insertstr);
-		(void)strlcat(insertstr, " ", sizeof insertstr);
+		(void)strlcat(insertstr, sep, sizeof insertstr);
 		if (el_insertstr(el, insertstr + wordlen) == -1)
 			return (CC_ERROR);
 		else
@@ -164,7 +168,7 @@ complete_command(char *word, int list, EditLine *el, c
 			sl_add(words, ghs->name);
 	}
 
-	rv = complete_ambiguous(word, list, words, el);
+	rv = complete_ambiguous(word, list, words, el, " ");
 	sl_free(words, 0);
 	return (rv);
 }
@@ -240,7 +244,7 @@ complete_local(char *word, int list, EditLine *el)
 	}
 	closedir(dd);
 
-	rv = complete_ambiguous(file, list, words, el);
+	rv = complete_ambiguous(file, list, words, el, " ");
 	sl_free(words, 1);
 	return (rv);
 }
@@ -377,7 +381,7 @@ complete_ifname(char *word, int list, EditLine *el)
 		sl_add(words, s);
 	}
 
-        rv = complete_ambiguous(word, list, words, el);
+        rv = complete_ambiguous(word, list, words, el, " ");
 	if_freenameindex(ifn_list);
         sl_free(words, 0);
         return (rv);
@@ -434,7 +438,7 @@ complete_ifgroup(char *word, int list, EditLine *el)
 			sl_add(words, ifg->ifgrq_group);
 	}
 
-	rv = complete_ambiguous(word, list, words, el);
+	rv = complete_ambiguous(word, list, words, el, " ");
 	sl_free(words, 0);
 	free(ifgr.ifgr_groups);
 	close(ifs);
@@ -470,7 +474,7 @@ complete_ifbridge(char *word, int list, EditLine *el)
 			sl_add(words, ifnp->if_name);
 	}
 
-	rv = complete_ambiguous(word, list, words, el);
+	rv = complete_ambiguous(word, list, words, el, " ");
 	if_freenameindex(ifn_list);
 	sl_free(words, 0);
 	close(ifs);
@@ -512,7 +516,7 @@ complete_rtable(char *word, int list, EditLine *el)
 			sl_add(words, rtable);
 	}
 
-	rv = complete_ambiguous(word, list, words, el);
+	rv = complete_ambiguous(word, list, words, el, " ");
 done:
 	sl_free(rtables, 1);
 	sl_free(words, 0);
@@ -520,6 +524,51 @@ done:
 	return (rv);
 }
 
+static unsigned char
+complete_environment(char *word, int dolist, EditLine *el, int set)
+{
+	StringList *words;
+	extern char **environ;
+	char **ep, *eq, *name;
+	size_t wordlen = strlen(word);
+	int rv = CC_ERROR;
+
+	words = sl_init();
+
+	for (ep = environ; *ep; ep++) {
+		eq = strchr(*ep, '=');
+		if (eq == NULL)
+			continue;
+		name = strndup(*ep, eq - *ep);
+		if (name == NULL) {
+			sl_free(words, 1);
+			return CC_ERROR;
+		}
+		if (strncmp(word, name, wordlen) == 0)
+			sl_add(words, name);
+		else
+			free(name);
+	}
+
+	/*
+	 * When a new environment variable is created then hitting the
+	 * TAB key makes '=' appear.
+	 */
+	if (set && words->sl_cur == 0 && wordlen > 0 &&
+	    word[wordlen - 1] != '=' && strchr(word, '=') == NULL) {
+		name = strdup(word);
+		if (name == NULL) {
+			sl_free(words, 1);
+			return CC_ERROR;
+		}
+		sl_add(words, name);
+	}
+
+	rv = complete_ambiguous(word, dolist, words, el, set ? "=" : " ");
+	sl_free(words, 1);
+	return rv;
+}
+
 /*
  * Generic complete routine
  */
@@ -872,6 +921,10 @@ complete_args(struct ghs *c, char *word, int dolist, E
 			return(CC_ERROR);
 		return (complete_showhelp(word, el, c->table, c->stlen, c->name,
 		    help_vertical));
+	case 'E':
+		return complete_environment(word, dolist, el, 1);
+	case 'e':
+		return complete_environment(word, dolist, el, 0);
 	case 'n':			/* no complete */
 		return (CC_ERROR);
 	}
blob - 1e1153b1ba8b2989aead1c19f5b9d61ee615af12
blob + 12f1978a735352ff8ea0aa9bb806d0832ed16d1a
--- nsh.8
+++ nsh.8
@@ -2687,7 +2687,42 @@ firewall rules, and other information compiled by
 Display differences between the startup configuration and the running
 configuration.
 This command requires root user privileges.
+.Pp
+.Ic show environment Op Ar NAME
+.Pp
+Display environment variables. 
+If the
+.Ar NAME
+of a variable is specified then display the value of this variable.
+Otherwise, display all existing environment variable names and values.
 .Pp
+.Tg setenv
+.Ic setenv Ar NAME=VALUE
+.Pp
+Set the environment variable
+.Ar NAME
+to the specified
+.Ar VALUE.
+If a
+.Ar NAME
+or
+.Ar VALUE
+contains whitespace then it must be quoted in double-quotes.
+For example:
+.Bd -literal -offset indent
+nsh/setenv EDITOR=/usr/local/bin/emacs
+nsh/setenv MY_VARIABLE="this value contains whitespace"
+nsh/setenv "MY OTHER VARIABLE"=my-name-contains-whitespace
+.Ed
+.Pp
+.Tg unsetenv
+.Pp
+.Ic unsetenv Ar NAME
+.Pp
+Delete the variable
+.Ar NAME
+from the environment.
+.Pp
 .Tg flush
 .Ic flush
 .Op routes | arp | ndp | line | bridge-dyn | bridge-all | bridge-rule | pf | history |\&? | help