Add run_command_list() to run a list of commands

This new function runs a list of commands separated by semicolon or newline.
We move this out of cmd_source so that it can be used by other code. The
PXE code also uses the new function.

Suggested-by: Michael Walle <michael@walle.cc>
Signed-off-by: Simon Glass <sjg@chromium.org>
diff --git a/common/cmd_pxe.c b/common/cmd_pxe.c
index 77a7dd1..6b31dea 100644
--- a/common/cmd_pxe.c
+++ b/common/cmd_pxe.c
@@ -554,33 +554,19 @@
  */
 static int label_localboot(struct pxe_label *label)
 {
-	char *localcmd, *dupcmd;
-	int ret;
+	char *localcmd;
 
 	localcmd = from_env("localcmd");
 
 	if (!localcmd)
 		return -ENOENT;
 
-	/*
-	 * dup the command to avoid any issues with the version of it existing
-	 * in the environment changing during the execution of the command.
-	 */
-	dupcmd = strdup(localcmd);
-
-	if (!dupcmd)
-		return -ENOMEM;
-
 	if (label->append)
 		setenv("bootargs", label->append);
 
-	printf("running: %s\n", dupcmd);
+	debug("running: %s\n", localcmd);
 
-	ret = run_command(dupcmd, 0);
-
-	free(dupcmd);
-
-	return ret;
+	return run_command_list(localcmd, strlen(localcmd), 0);
 }
 
 /*
diff --git a/common/cmd_source.c b/common/cmd_source.c
index 32fff5c..c4cde98 100644
--- a/common/cmd_source.c
+++ b/common/cmd_source.c
@@ -39,9 +39,6 @@
 #if defined(CONFIG_8xx)
 #include <mpc8xx.h>
 #endif
-#ifdef CONFIG_SYS_HUSH_PARSER
-#include <hush.h>
-#endif
 
 int
 source (ulong addr, const char *fit_uname)
@@ -49,8 +46,6 @@
 	ulong		len;
 	image_header_t	*hdr;
 	ulong		*data;
-	char		*cmd;
-	int		rcode = 0;
 	int		verify;
 #if defined(CONFIG_FIT)
 	const void*	fit_hdr;
@@ -151,49 +146,7 @@
 	}
 
 	debug ("** Script length: %ld\n", len);
-
-	if ((cmd = malloc (len + 1)) == NULL) {
-		return 1;
-	}
-
-	/* make sure cmd is null terminated */
-	memmove (cmd, (char *)data, len);
-	*(cmd + len) = 0;
-
-#ifdef CONFIG_SYS_HUSH_PARSER /*?? */
-	rcode = parse_string_outer (cmd, FLAG_PARSE_SEMICOLON);
-#else
-	{
-		char *line = cmd;
-		char *next = cmd;
-
-		/*
-		 * break into individual lines,
-		 * and execute each line;
-		 * terminate on error.
-		 */
-		while (*next) {
-			if (*next == '\n') {
-				*next = '\0';
-				/* run only non-empty commands */
-				if (*line) {
-					debug ("** exec: \"%s\"\n",
-						line);
-					if (run_command(line, 0) < 0) {
-						rcode = 1;
-						break;
-					}
-				}
-				line = next + 1;
-			}
-			++next;
-		}
-		if (rcode == 0 && *line)
-			rcode = (run_command(line, 0) >= 0);
-	}
-#endif
-	free (cmd);
-	return rcode;
+	return run_command_list((char *)data, len, 0);
 }
 
 /**************************************************/
diff --git a/common/main.c b/common/main.c
index a933357..d96ba0a 100644
--- a/common/main.c
+++ b/common/main.c
@@ -30,6 +30,7 @@
 #include <common.h>
 #include <watchdog.h>
 #include <command.h>
+#include <malloc.h>
 #include <version.h>
 #ifdef CONFIG_MODEM_SUPPORT
 #include <malloc.h>		/* for free() prototype */
@@ -1373,6 +1374,90 @@
 #endif
 }
 
+#ifndef CONFIG_SYS_HUSH_PARSER
+/**
+ * Execute a list of command separated by ; or \n using the built-in parser.
+ *
+ * This function cannot take a const char * for the command, since if it
+ * finds newlines in the string, it replaces them with \0.
+ *
+ * @param cmd	String containing list of commands
+ * @param flag	Execution flags (CMD_FLAG_...)
+ * @return 0 on success, or != 0 on error.
+ */
+static int builtin_run_command_list(char *cmd, int flag)
+{
+	char *line, *next;
+	int rcode = 0;
+
+	/*
+	 * Break into individual lines, and execute each line; terminate on
+	 * error.
+	 */
+	line = next = cmd;
+	while (*next) {
+		if (*next == '\n') {
+			*next = '\0';
+			/* run only non-empty commands */
+			if (*line) {
+				debug("** exec: \"%s\"\n", line);
+				if (builtin_run_command(line, 0) < 0) {
+					rcode = 1;
+					break;
+				}
+			}
+			line = next + 1;
+		}
+		++next;
+	}
+	if (rcode == 0 && *line)
+		rcode = (builtin_run_command(line, 0) >= 0);
+
+	return rcode;
+}
+#endif
+
+int run_command_list(const char *cmd, int len, int flag)
+{
+	int need_buff = 1;
+	char *buff = (char *)cmd;	/* cast away const */
+	int rcode = 0;
+
+	if (len == -1) {
+		len = strlen(cmd);
+#ifdef CONFIG_SYS_HUSH_PARSER
+		/* hush will never change our string */
+		need_buff = 0;
+#else
+		/* the built-in parser will change our string if it sees \n */
+		need_buff = strchr(cmd, '\n') != NULL;
+#endif
+	}
+	if (need_buff) {
+		buff = malloc(len + 1);
+		if (!buff)
+			return 1;
+		memcpy(buff, cmd, len);
+		buff[len] = '\0';
+	}
+#ifdef CONFIG_SYS_HUSH_PARSER
+	rcode = parse_string_outer(buff, FLAG_PARSE_SEMICOLON);
+#else
+	/*
+	 * This function will overwrite any \n it sees with a \0, which
+	 * is why it can't work with a const char *. Here we are making
+	 * using of internal knowledge of this function, to avoid always
+	 * doing a malloc() which is actually required only in a case that
+	 * is pretty rare.
+	 */
+	rcode = builtin_run_command_list(buff, flag);
+	if (need_buff)
+		free(buff);
+#endif
+
+	return rcode;
+}
+
 /****************************************************************************/
 
 #if defined(CONFIG_CMD_RUN)
diff --git a/include/common.h b/include/common.h
index 39859d3..55025c0 100644
--- a/include/common.h
+++ b/include/common.h
@@ -286,6 +286,19 @@
 /* common/main.c */
 void	main_loop	(void);
 int run_command(const char *cmd, int flag);
+
+/**
+ * Run a list of commands separated by ; or even \0
+ *
+ * Note that if 'len' is not -1, then the command does not need to be nul
+ * terminated, Memory will be allocated for the command in that case.
+ *
+ * @param cmd	List of commands to run, each separated bu semicolon
+ * @param len	Length of commands excluding terminator if known (-1 if not)
+ * @param flag	Execution flags (CMD_FLAG_...)
+ * @return 0 on success, or != 0 on error.
+ */
+int run_command_list(const char *cmd, int len, int flag);
 int	readline	(const char *const prompt);
 int	readline_into_buffer(const char *const prompt, char *buffer,
 			int timeout);