vlib: process startup config exec scripts line by line

This fixes long standing annoyance that CLIs with optional args cannot
be executed from file, as they cannot distinguish between valid optional
args and next line in the file.

Multiline statements can be provided simply by using backslash before \n.
Also comments are supported - everything after # is ignored up to the
end of the line.

Example:

 # multiline cli using backslash
show version \
       verbose # end of line comment

packet-generator new { \
   name x \
   limit 5 \
   # comment inside cmultiline cli \
   size 128-128 \
   interface local0 \
   node null-node \
   data { \
       incrementing 30 \
   } \
}

Type: fix

Change-Id: Ia6d588169bae14e6e3f18effe94820d05ace1dbf
Signed-off-by: Damjan Marion <damarion@cisco.com>
diff --git a/src/vlib/cli.c b/src/vlib/cli.c
index 01409b8..80da2d5 100644
--- a/src/vlib/cli.c
+++ b/src/vlib/cli.c
@@ -199,6 +199,64 @@
   return 1;
 }
 
+uword
+unformat_vlib_cli_line (unformat_input_t *i, va_list *va)
+{
+  unformat_input_t *result = va_arg (*va, unformat_input_t *);
+  u8 *line = 0;
+  uword c;
+  int skip;
+
+next_line:
+  skip = 0;
+
+  /* skip leading whitespace if any */
+  unformat_skip_white_space (i);
+
+  if (unformat_is_eof (i))
+    return 0;
+
+  while ((c = unformat_get_input (i)) != UNFORMAT_END_OF_INPUT)
+    {
+      if (c == '\\')
+	{
+	  c = unformat_get_input (i);
+
+	  if (c == '\n')
+	    {
+	      if (!skip)
+		vec_add1 (line, '\n');
+	      skip = 0;
+	      continue;
+	    }
+
+	  if (!skip)
+	    vec_add1 (line, '\\');
+
+	  if (c == UNFORMAT_END_OF_INPUT)
+	    break;
+
+	  if (!skip)
+	    vec_add1 (line, c);
+	  continue;
+	}
+
+      if (c == '#')
+	skip = 1;
+      else if (c == '\n')
+	break;
+
+      if (!skip)
+	vec_add1 (line, c);
+    }
+
+  if (line == 0)
+    goto next_line;
+
+  unformat_init_vector (result, line);
+  return 1;
+}
+
 /* Looks for string based sub-input formatted { SUB-INPUT }. */
 uword
 unformat_vlib_cli_sub_input (unformat_input_t * i, va_list * args)
diff --git a/src/vlib/cli.h b/src/vlib/cli.h
index 32e46f9..86913e9 100644
--- a/src/vlib/cli.h
+++ b/src/vlib/cli.h
@@ -205,6 +205,7 @@
 					    vlib_cli_parse_rule_t * c);
 
 uword unformat_vlib_cli_args (unformat_input_t *i, va_list *va);
+unformat_function_t unformat_vlib_cli_line;
 uword unformat_vlib_cli_sub_input (unformat_input_t * i, va_list * args);
 
 /* Return an vector of strings consisting of possible auto-completions
diff --git a/src/vlib/unix/main.c b/src/vlib/unix/main.c
index 3710d8e..fd8a7e8 100644
--- a/src/vlib/unix/main.c
+++ b/src/vlib/unix/main.c
@@ -347,8 +347,20 @@
 
       if (vec_len (buf))
 	{
+	  unformat_input_t in;
 	  unformat_init_vector (&sub_input, buf);
-	  vlib_cli_input (vm, &sub_input, 0, 0);
+
+	  while (unformat_user (&sub_input, unformat_vlib_cli_line, &in))
+	    {
+	      if (vlib_cli_input (vm, &in, 0, 0) != 0)
+		{
+		  /* cli failed - stop */
+		  unformat_free (&in);
+		  break;
+		}
+	      unformat_free (&in);
+	    }
+
 	  /* frees buf for us */
 	  unformat_free (&sub_input);
 	}