modprobe: switch to getopt_long for argument parsing

Add support for long options, and fit existing options to upstream
behaviors and extensions.  Fix some missing std::endl and
android::base::Join() usage.

Bug: 159424228
Bug: 151950334
Test: manually test long options work
Change-Id: Id792d87d4407628e706aeccecb6e2bce22bcad10
diff --git a/toolbox/modprobe.cpp b/toolbox/modprobe.cpp
index 3ffa74e..0033b65 100644
--- a/toolbox/modprobe.cpp
+++ b/toolbox/modprobe.cpp
@@ -17,11 +17,15 @@
 #include <ctype.h>
 #include <getopt.h>
 #include <stdlib.h>
+
 #include <iostream>
 
+#include <android-base/file.h>
 #include <android-base/strings.h>
 #include <modprobe/modprobe.h>
 
+namespace {
+
 enum modprobe_mode {
     AddModulesMode,
     RemoveModulesMode,
@@ -29,21 +33,24 @@
     ShowDependenciesMode,
 };
 
-static void print_usage(void) {
+void print_usage(void) {
     std::cerr << "Usage:" << std::endl;
     std::cerr << std::endl;
-    std::cerr << "  modprobe [-alrqvsDb] [-d DIR] [MODULE]+" << std::endl;
-    std::cerr << "  modprobe [-alrqvsDb] [-d DIR] MODULE [symbol=value][...]" << std::endl;
+    // -d option is required on Android
+    std::cerr << "  modprobe [options] -d DIR MODULE..." << std::endl;
+    std::cerr << "  modprobe [options] -d DIR MODULE [symbol=value]..." << std::endl;
     std::cerr << std::endl;
     std::cerr << "Options:" << std::endl;
-    std::cerr << "  -b: Apply blocklist to module names too" << std::endl;
-    std::cerr << "  -d: Load modules from DIR, option may be used multiple times" << std::endl;
-    std::cerr << "  -D: Print dependencies for modules only, do not load";
-    std::cerr << "  -h: Print this help" << std::endl;
-    std::cerr << "  -l: List modules matching pattern" << std::endl;
-    std::cerr << "  -r: Remove MODULE (multiple modules may be specified)" << std::endl;
-    std::cerr << "  -q: Quiet" << std::endl;
-    std::cerr << "  -v: Verbose" << std::endl;
+    std::cerr << "  -b, --use-blocklist: Apply blocklist to module names too" << std::endl;
+    std::cerr << "  -d, --dirname=DIR: Load modules from DIR, option may be used multiple times"
+              << std::endl;
+    std::cerr << "  -D, --show-depends: Print dependencies for modules only, do not load"
+              << std::endl;
+    std::cerr << "  -h, --help: Print this help" << std::endl;
+    std::cerr << "  -l, --list: List modules matching pattern" << std::endl;
+    std::cerr << "  -r, --remove: Remove MODULE (multiple modules may be specified)" << std::endl;
+    std::cerr << "  -q, --quiet: disable messages" << std::endl;
+    std::cerr << "  -v, --verbose: enable more messages" << std::endl;
     std::cerr << std::endl;
 }
 
@@ -54,6 +61,8 @@
         return EXIT_FAILURE;                                              \
     }
 
+}  // anonymous namespace
+
 extern "C" int modprobe_main(int argc, char** argv) {
     std::vector<std::string> modules;
     std::string module_parameters;
@@ -64,7 +73,23 @@
     int rv = EXIT_SUCCESS;
 
     int opt;
-    while ((opt = getopt(argc, argv, "abd:Dhlqrv")) != -1) {
+    int option_index = 0;
+    // NB: We have non-standard short options -l and -D to make it easier for
+    // OEMs to transition from toybox.
+    // clang-format off
+    static struct option long_options[] = {
+        { "all",                 no_argument,       0, 'a' },
+        { "use-blocklist",       no_argument,       0, 'b' },
+        { "dirname",             required_argument, 0, 'd' },
+        { "show-depends",        no_argument,       0, 'D' },
+        { "help",                no_argument,       0, 'h' },
+        { "list",                no_argument,       0, 'l' },
+        { "quiet",               no_argument,       0, 'q' },
+        { "remove",              no_argument,       0, 'r' },
+        { "verbose",             no_argument,       0, 'v' },
+    };
+    // clang-format on
+    while ((opt = getopt_long(argc, argv, "abd:Dhlqrv", long_options, &option_index)) != -1) {
         switch (opt) {
             case 'a':
                 // toybox modprobe supported -a to load multiple modules, this
@@ -121,9 +146,9 @@
     if (verbose) {
         std::cout << "mode is " << mode << std::endl;
         std::cout << "verbose is " << verbose << std::endl;
-        std::cout << "mod_dirs is: " << android::base::Join(mod_dirs, "") << std::endl;
-        std::cout << "modules is: " << android::base::Join(modules, "") << std::endl;
-        std::cout << "module parameters is: " << android::base::Join(module_parameters, "")
+        std::cout << "mod_dirs is: " << android::base::Join(mod_dirs, " ") << std::endl;
+        std::cout << "modules is: " << android::base::Join(modules, " ") << std::endl;
+        std::cout << "module parameters is: " << android::base::Join(module_parameters, " ")
                   << std::endl;
     }
 
@@ -159,13 +184,13 @@
         switch (mode) {
             case AddModulesMode:
                 if (!m.LoadWithAliases(module, true, module_parameters)) {
-                    std::cerr << "Failed to load module " << module;
+                    std::cerr << "Failed to load module " << module << std::endl;
                     rv = EXIT_FAILURE;
                 }
                 break;
             case RemoveModulesMode:
                 if (!m.Remove(module)) {
-                    std::cerr << "Failed to remove module " << module;
+                    std::cerr << "Failed to remove module " << module << std::endl;
                     rv = EXIT_FAILURE;
                 }
                 break;
@@ -192,7 +217,7 @@
                 break;
             }
             default:
-                std::cerr << "Bad mode";
+                std::cerr << "Bad mode" << std::endl;
                 rv = EXIT_FAILURE;
         }
     }