Browse Source

Replace getopt with custom arg parser

Filip Klembara 4 years ago
parent
commit
4d407cf15e

+ 75 - 0
examples/streamer/ArgParser.cpp

@@ -0,0 +1,75 @@
+/*
+ * libdatachannel streamer example
+ * Copyright (c) 2020 Filip Klembara (in2core)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ArgParser.hpp"
+#include <iostream>
+
+ArgParser::ArgParser(std::vector<std::pair<std::string, std::string>> options, std::vector<std::pair<std::string, std::string>> flags) {
+    for(auto option: options) {
+        this->options.insert(option.first);
+        this->options.insert(option.second);
+        shortToLongMap.emplace(option.first, option.second);
+        shortToLongMap.emplace(option.second, option.second);
+    }
+    for(auto flag: flags) {
+        this->flags.insert(flag.first);
+        this->flags.insert(flag.second);
+        shortToLongMap.emplace(flag.first, flag.second);
+        shortToLongMap.emplace(flag.second, flag.second);
+    }
+}
+
+std::optional<std::string> ArgParser::toKey(std::string prefixedKey) {
+    if (prefixedKey.find("--") == 0) {
+        return prefixedKey.substr(2, prefixedKey.length());
+    } else if (prefixedKey.find("-") == 0) {
+        return prefixedKey.substr(1, prefixedKey.length());
+    } else {
+        return std::nullopt;
+    }
+}
+
+bool ArgParser::parse(int argc, char **argv, std::function<bool (std::string, std::string)> onOption, std::function<bool (std::string)> onFlag) {
+    std::optional<std::string> currentOption = std::nullopt;
+    for(int i = 1; i < argc; i++) {
+        std::string current = argv[i];
+        auto optKey = toKey(current);
+        if (!currentOption.has_value() && optKey.has_value() && flags.find(optKey.value()) != flags.end()) {
+            auto check = onFlag(shortToLongMap.at(optKey.value()));
+            if (!check) {
+                return false;
+            }
+        } else if (!currentOption.has_value() && optKey.has_value() && options.find(optKey.value()) != options.end()) {
+            currentOption = optKey.value();
+        } else if (currentOption.has_value()) {
+            auto check = onOption(shortToLongMap.at(currentOption.value()), current);
+            if (!check) {
+                return false;
+            }
+            currentOption = std::nullopt;
+        } else {
+            std::cerr << "Unrecognized option " << current << std::endl;
+            return false;
+        }
+    }
+    if (currentOption.has_value()) {
+        std::cerr << "Missing value for " << currentOption.value() << std::endl;
+        return false;
+    }
+    return true;
+}

+ 41 - 0
examples/streamer/ArgParser.hpp

@@ -0,0 +1,41 @@
+/*
+ * libdatachannel streamer example
+ * Copyright (c) 2020 Filip Klembara (in2core)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ArgParser_hpp
+#define ArgParser_hpp
+
+#include <functional>
+#include <vector>
+#include <utility>
+#include <string>
+#include <set>
+#include <unordered_map>
+#include <optional>
+
+struct ArgParser {
+private:
+    std::set<std::string> options{};
+    std::set<std::string> flags{};
+    std::unordered_map<std::string, std::string> shortToLongMap{};
+public:
+    ArgParser(std::vector<std::pair<std::string, std::string>> options, std::vector<std::pair<std::string, std::string>> flags);
+    std::optional<std::string> toKey(std::string prefixedKey);
+    bool parse(int argc, char **argv, std::function<bool (std::string, std::string)> onOption, std::function<bool (std::string)> onFlag);
+};
+
+#endif /* ArgParser_hpp */

+ 2 - 2
examples/streamer/CMakeLists.txt

@@ -4,10 +4,10 @@ if(POLICY CMP0079)
 endif()
 endif()
 
 
 if(WIN32)
 if(WIN32)
-add_executable(streamer main.cpp dispatchqueue.cpp dispatchqueue.hpp h264fileparser.cpp h264fileparser.hpp helpers.cpp helpers.hpp opusfileparser.cpp opusfileparser.hpp fileparser.cpp fileparser.hpp stream.cpp stream.hpp)
+add_executable(streamer main.cpp dispatchqueue.cpp dispatchqueue.hpp h264fileparser.cpp h264fileparser.hpp helpers.cpp helpers.hpp opusfileparser.cpp opusfileparser.hpp fileparser.cpp fileparser.hpp stream.cpp stream.hpp ArgParser.hpp ArgParser.cpp)
 target_compile_definitions(streamer PUBLIC STATIC_GETOPT)
 target_compile_definitions(streamer PUBLIC STATIC_GETOPT)
 else()
 else()
-add_executable(streamer main.cpp dispatchqueue.cpp dispatchqueue.hpp h264fileparser.cpp h264fileparser.hpp helpers.cpp helpers.hpp opusfileparser.cpp opusfileparser.hpp fileparser.cpp fileparser.hpp stream.cpp stream.hpp)
+add_executable(streamer main.cpp dispatchqueue.cpp dispatchqueue.hpp h264fileparser.cpp h264fileparser.hpp helpers.cpp helpers.hpp opusfileparser.cpp opusfileparser.hpp fileparser.cpp fileparser.hpp stream.cpp stream.hpp ArgParser.hpp ArgParser.cpp)
 endif()
 endif()
 set_target_properties(streamer PROPERTIES
 set_target_properties(streamer PROPERTIES
 	CXX_STANDARD 17
 	CXX_STANDARD 17

+ 29 - 34
examples/streamer/main.cpp

@@ -27,6 +27,7 @@
 #include "h264fileparser.hpp"
 #include "h264fileparser.hpp"
 #include "opusfileparser.hpp"
 #include "opusfileparser.hpp"
 #include "helpers.hpp"
 #include "helpers.hpp"
+#include "ArgParser.hpp"
 
 
 using namespace rtc;
 using namespace rtc;
 using namespace std;
 using namespace std;
@@ -111,42 +112,36 @@ int main(int argc, char **argv) try {
     bool enableDebugLogs = false;
     bool enableDebugLogs = false;
     bool printHelp = false;
     bool printHelp = false;
     int c = 0;
     int c = 0;
-    // parse arguments
-    while ((c = getopt (argc, argv, "a:b:d:p:vh")) != -1) {
-        switch (c) {
-            case 'a':
-                opusSamplesDirectory = string(optarg) + "/";
-                break;
-            case 'b':
-                h264SamplesDirectory = string(optarg) + "/";
-                break;
-            case 'd':
-                ip_address = string(optarg);
-                break;
-            case 'p':
-                port = atoi(optarg);
-                break;
-            case 'v':
-                enableDebugLogs = true;
-                break;
-            case 'h':
-                printHelp = true;
-                break;
-            case '?':
-                if (optopt == 'a' || optopt == 'b' || optopt == 'd' || optopt == 'p') {
-                    string s(1, optopt);
-                    cerr << "Option -" << s <<" requires an argument." << endl;
-                } else if (isprint(optopt)) {
-                    string s(1, optopt);
-                    cerr << "Unknown option `-" << s << "'." << endl;
-                } else {
-                    fprintf(stderr, "Unknown option character `\\x%x'.\n", optopt);
-                }
-                return 1;
-            default:
-                abort();
+    auto parser = ArgParser({{"a", "audio"}, {"b", "video"}, {"d", "ip"}, {"p","port"}}, {{"h", "help"}, {"v", "verbose"}});
+    auto parsingResult = parser.parse(argc, argv, [](string key, string value) {
+        if (key == "audio") {
+            opusSamplesDirectory = value + "/";
+        } else if (key == "video") {
+            h264SamplesDirectory = value + "/";
+        } else if (key == "ip") {
+            ip_address = value;
+        } else if (key == "port") {
+            port = atoi(value.data());
+        } else {
+            cerr << "Invalid option --" << key << " with value " << value << endl;
+            return false;
+        }
+        return true;
+    }, [&enableDebugLogs, &printHelp](string flag){
+        if (flag == "verbose") {
+            enableDebugLogs = true;
+        } else if (flag == "help") {
+            printHelp = true;
+        } else {
+            cerr << "Invalid flag --" << flag << endl;
+            return false;
         }
         }
+        return true;
+    });
+    if (!parsingResult) {
+        return 1;
     }
     }
+
     if (printHelp) {
     if (printHelp) {
         cout << "usage: stream-h264 [-a opus_samples_folder] [-b h264_samples_folder] [-d ip_address] [-p port] [-v] [-h]" << endl
         cout << "usage: stream-h264 [-a opus_samples_folder] [-b h264_samples_folder] [-d ip_address] [-p port] [-v] [-h]" << endl
         << "Arguments:" << endl
         << "Arguments:" << endl