From: Justin Cinkelj <[email protected]>
Committer: Nadav Har'El <[email protected]>
Branch: master

command line: allow multiple commands per runscript file

Extend runscript to allow file with content like:
"/prog1 prm1; /prog2 prm2"

Tests are included.

Signed-off-by: Justin Cinkelj <[email protected]>
Message-Id: <[email protected]>

---
diff --git a/core/commands.cc b/core/commands.cc
--- a/core/commands.cc
+++ b/core/commands.cc
@@ -85,50 +85,65 @@ parse_command_line_min(const std::string line, bool &ok)

 /*
 If cmd starts with "runcript file", read content of file and
-update cmd with the content.
+return vector of all programs to be run.
+File can contain multiple commands per line.
 ok flag is set to false on parse error, and left unchanged otherwise.
+
+If cmd doesn't start with runscript, then vector with size 0 is returned.
 */
-void runscript_expand(std::vector<std::string>& cmd, bool &ok)
+std::vector<std::vector<std::string>> runscript_expand(const std::vector<std::string>& cmd, bool &ok)
 {
+    std::vector<std::vector<std::string> > result;
     if (cmd[0] == "runscript") {
-        if (cmd.size()<2) {
-            puts("Failed expanding runscript - filename missing.");
+        /*
+        The cmd vector ends with additional ";" or "\0" element.
+        */
+        if (cmd.size() != 3 && cmd[2].c_str()[0] != 0x00) {
+ puts("Failed expanding runscript - filename missing or extra parameters present.");
             ok = false;
-            return;
+            return result;
         }
         auto fn = cmd[1];

         std::ifstream in(fn);
         std::string line;
         // only first line up to \n is used.
         getline(in, line);
-        std::vector<std::vector<std::string> > result;
         bool ok2;
         result = parse_command_line_min(line, ok2);
debug("runscript expand fn='%s' line='%s'\n", fn.c_str(), line.c_str());
         if (ok2 == false) {
printf("Failed expanding runscript file='%s' line='%s'.\n", fn.c_str(), line.c_str());
+            result.clear();
             ok = false;
         }
-        else {
-            cmd = result[0];
-        }
     }
+    return result;
 }

 std::vector<std::vector<std::string>>
 parse_command_line(const std::string line,  bool &ok)
 {
-    std::vector<std::vector<std::string> > result;
+    std::vector<std::vector<std::string> > result, result2;
     result = parse_command_line_min(line, ok);

     /*
     If command starts with runscript, we need to read actual command to
     execute from the given file.
     */
     std::vector<std::vector<std::string>>::iterator cmd_iter;
- for (cmd_iter=result.begin(); ok && cmd_iter!=result.end(); cmd_iter++) {
-        runscript_expand(*cmd_iter, ok);
+    for (cmd_iter=result.begin(); ok && cmd_iter!=result.end(); ) {
+        result2 = runscript_expand(*cmd_iter, ok);
+        if (result2.size() > 0) {
+            cmd_iter = result.erase(cmd_iter);
+            int pos;
+            pos = cmd_iter - result.begin();
+            result.insert(cmd_iter, result2.begin(), result2.end());
+            cmd_iter = result.begin() + pos + result2.size();
+        }
+        else {
+            cmd_iter++;
+        }
     }

     return result;
diff --git a/tests/tst-commands.cc b/tests/tst-commands.cc
--- a/tests/tst-commands.cc
+++ b/tests/tst-commands.cc
@@ -353,6 +353,168 @@ static bool test_runscript_multiple_with_args_quotes()
     return true;
 }

+static bool test_runscript_multiple_commands_per_line()
+{
+    std::ofstream of1("/myscript", std::ios::out | std::ios::binary);
+    of1 << "/prog1; /prog2";
+    of1.close();
+
+    std::vector<std::vector<std::string> > result;
+    std::vector<std::string> cmd = { "/prog1", "/prog2" };
+    bool ok;
+
+    result = osv::parse_command_line(
+        std::string("runscript \"/myscript\""),
+        ok);
+
+    if (!ok) {
+        return false;
+    }
+
+    if (result.size() != 2) {
+        return false;
+    }
+
+    if (result[0].size() != 2) {
+        return false;
+    }
+
+    if (result[1].size() != 2) {
+        return false;
+    }
+
+    for (size_t i = 0; i < result.size(); i++) {
+        if (result[i][0] != cmd[i]) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+static bool test_runscript_multiple_commands_per_line_with_args()
+{
+    std::ofstream of1("/myscript", std::ios::out | std::ios::binary);
+    of1 << "/prog1 pp1a ; /prog2 pp2a pp2b";
+    of1.close();
+
+    std::vector<std::vector<std::string> > result;
+    std::vector<std::string> cmd = { "/prog1", "/prog2" };
+    bool ok;
+
+    result = osv::parse_command_line(
+        std::string("runscript \"/myscript\""),
+        ok);
+
+    if (!ok) {
+        return false;
+    }
+
+    if (result.size() != 2) {
+        return false;
+    }
+
+    if (result[0].size() != 3) {
+        return false;
+    }
+
+    if (result[1].size() != 4) {
+        return false;
+    }
+
+    for (size_t i = 0; i < result.size(); i++) {
+        if (result[i][0] != cmd[i]) {
+            return false;
+        }
+    }
+
+    if (result[0][1] != std::string("pp1a")) {
+        return false;
+    }
+
+    if (result[1][1] != std::string("pp2a")) {
+        return false;
+    }
+    if (result[1][2] != std::string("pp2b")) {
+        return false;
+    }
+
+    return true;
+}
+
+static bool test_runscript_multiple_commands_per_line_with_args_quotes()
+{
+    std::ofstream of1("/myscript", std::ios::out | std::ios::binary);
+ of1 << "/prog1 pp1a ; /prog2 pp2a pp2b; /prog3 pp3a \"pp3b1 pp3b2\" \"pp3c1;pp3c2\" \"pp3d\" \" ;; --onx -fon;x \\t\"; ";
+    of1.close();
+
+    std::vector<std::vector<std::string> > result;
+    std::vector<std::string> cmd = { "/prog1", "/prog2", "/prog3" };
+    bool ok;
+
+    result = osv::parse_command_line(
+        std::string("runscript \"/myscript\""),
+        ok);
+
+    if (!ok) {
+        return false;
+    }
+
+    if (result.size() != 3) {
+        return false;
+    }
+
+    if (result[0].size() != 3) {
+        return false;
+    }
+
+    if (result[1].size() != 4) {
+        return false;
+    }
+
+    if (result[2].size() != 7) {
+        return false;
+    }
+
+    for (size_t i = 0; i < result.size(); i++) {
+        if (result[i][0] != cmd[i]) {
+            return false;
+        }
+    }
+
+    if (result[0][1] != std::string("pp1a")) {
+        return false;
+    }
+
+    if (result[1][1] != std::string("pp2a")) {
+        return false;
+    }
+    if (result[1][2] != std::string("pp2b")) {
+        return false;
+    }
+
+    if (result[2][1] != std::string("pp3a")) {
+        return false;
+    }
+    if (result[2][2] != std::string("pp3b1 pp3b2")) {
+        return false;
+    }
+    if (result[2][3] != std::string("pp3c1;pp3c2")) {
+        return false;
+    }
+    if (result[2][4] != std::string("pp3d")) {
+        return false;
+    }
+    if (result[2][5] != std::string(" ;; --onx -fon;x \t")) {
+        return false;
+    }
+    if (result[2][6] != std::string(";")) {
+        return false;
+    }
+
+    return true;
+}
+
 int main(int argc, char *argv[])
 {
     report(test_parse_simplest(), "simplest command line");
@@ -369,6 +531,12 @@ int main(int argc, char *argv[])
            "cpiod upload and haproxy launch");
     report(test_runscript_multiple_with_args_quotes(),
            "runscript multiple with args and quotes");
+    report(test_runscript_multiple_commands_per_line(),
+           "runscript multiple commands per line");
+    report(test_runscript_multiple_commands_per_line_with_args(),
+           "runscript multiple commands per line with args");
+    report(test_runscript_multiple_commands_per_line_with_args_quotes(),
+           "runscript multiple commands per line with args and quotes");
     printf("SUMMARY: %d tests, %d failures\n", tests, fails);
     return 0;
 }

--
You received this message because you are subscribed to the Google Groups "OSv 
Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to