#include #include #include #include #include #include #include #include #define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x]))))) #define dump(x) fprintf(stderr, "getopt c '%c' (%d), optind %d ('%s'), optarg '%s', optopt '%c' (%d)\n", \ (x), (x), optind, (size_t)optind >= COUNT_OF(test_argv) ? "out of range" : test_argv[optind], optarg, optopt, optopt); #define dumpargv(argv) do { \ fprintf(stderr, "args(%zu) {\n", COUNT_OF(argv)); \ for (size_t i = 0; i < COUNT_OF(argv); i++) { \ fprintf(stderr, "\targv[%zu] = '%s'\n", i, argv[i]); \ } \ fprintf(stderr, "}\n"); \ } while (0) void test1() { const char *shortopts = "b:cdef"; const struct option longopts[] = { {"foo", required_argument, NULL, 'f'}, {NULL, no_argument, NULL, 0} }; int test_argc = 7; char *test_argv[] = { "dummy", "--foo", "abc", "-b", "abc", "abc", "abc" }; optind = 0; int c = getopt_long(test_argc, test_argv, shortopts, longopts, NULL); dump(c) assert(c == 'f'); c = getopt_long(test_argc, test_argv, shortopts, longopts, NULL); dump(c) assert(optarg && !strcmp(optarg, "abc")); assert(c == 'b'); c = getopt_long(test_argc, test_argv, shortopts, longopts, NULL); dump(c); assert(c == -1); assert(optarg == NULL); // we have 2 non-option arguments assert((optind + 2) == test_argc); } void test2() { const struct option longopts[] = { {"foo", required_argument, NULL, 'f'}, {NULL, no_argument, NULL, 0} }; char *test_argv[] = { "dummy", "-c", }; fputs("Situation: we pass a non-existant short option in argv\n", stderr); optind = 0; opterr = 0; int c = getopt_long(COUNT_OF(test_argv), test_argv, "ab:", longopts, NULL); // Expected result: return '?', optopt is set to the offender assert(c == '?'); assert(optopt == 'c'); fputs("Situation: we pass a non-existant short option in argv, while passing a leading colon in optstring\n", stderr); optind = 0; c = getopt_long(COUNT_OF(test_argv), test_argv, ":ab:", longopts, NULL); // Expected result: return '?', optopt is set to the offender assert(c == '?'); assert(optopt == 'c'); fputs("Situation: we omit the required arg to a short option\n", stderr); optind = 0; c = getopt_long(COUNT_OF(test_argv), test_argv, "ab:c:", longopts, NULL); // Expected result: return '?', optopt is set to the offender assert(c == '?'); assert(optopt == 'c'); fputs("Situation: we omit the required arg to a short option, while passing a leading colon in optstring\n", stderr); optind = 0; c = getopt_long(COUNT_OF(test_argv), test_argv, ":ab:c:", longopts, NULL); // Expected result: return ':', optopt is set to the offender assert(c == ':'); assert(optopt == 'c'); } void test3() { const struct option longopts[] = { {"foo", required_argument, NULL, 'f'}, {NULL, no_argument, NULL, 0} }; char *test_argv[] = { "dummy", "-cmanagarm", }; fputs("Situation: we pass a concatenated argument to a short option\n", stderr); optind = 0; int c = getopt_long(COUNT_OF(test_argv), test_argv, "ab:c:", longopts, NULL); dump(c); // Expected result: assert(c == 'c'); assert(!strcmp(optarg, "managarm")); } void test4() { const struct option longopts[] = { {"foo", required_argument, NULL, 'f'}, {NULL, no_argument, NULL, 0} }; char *test_argv[] = { "dummy", "-acmanagarm", }; fputs("Situation: we pass concatenated short options to getopt\n", stderr); optind = 0; int c = getopt_long(COUNT_OF(test_argv), test_argv, "ab:c:", longopts, NULL); assert(c == 'a'); c = getopt_long(COUNT_OF(test_argv), test_argv, "ab:c:", longopts, NULL); // Expected result: assert(c == 'c'); assert(!strcmp(optarg, "managarm")); } void test5() { const struct option longopts[] = { {"foo", required_argument, NULL, 'f'}, {NULL, no_argument, NULL, 0} }; char *test_argv[] = { "su", "-", "managarm", 0 }; int test_argc = 3; fputs("Situation: testing `su - managarm`\n", stderr); optind = 0; int c = getopt_long(test_argc, test_argv, "ab:", longopts, NULL); dump(c); assert(c == -1); if (optind < test_argc && !strcmp(test_argv[optind], "-")) { ++optind; } assert(optind < test_argc); assert(!strcmp(test_argv[optind++], "managarm")); assert(optind == test_argc); } void test6() { char *test_argv[] = { "telescope", "gemini://electrode.codes", "-S", 0 }; int test_argc = 3; optind = 0; const struct option longopts[] = { {"colors", no_argument, NULL, 'C'}, {"colours", no_argument, NULL, 'C'}, {"help", no_argument, NULL, 'h'}, {"safe", no_argument, NULL, 'S'}, {"version", no_argument, NULL, 'v'}, {NULL, 0, NULL, 0}, }; int c = getopt_long(test_argc, test_argv, "Cc:hnST:v", longopts, NULL); dump(c); dumpargv(test_argv); assert(c == 'S'); c = getopt_long(test_argc, test_argv, "Cc:hnST:v", longopts, NULL); dump(c); dumpargv(test_argv); assert(optind == 2); assert(!optarg); } void test7() { int c; static const struct option options[] = { { "debug", no_argument, NULL, 'd' }, { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'V' }, {} }; const char *command; char *test_argv[] = { "udevadm", "hwdb", "--update", 0 }; int test_argc = 3; setenv("POSIXLY_CORRECT", "1", 1); optind = 0; while ((c = getopt_long(test_argc, test_argv, "+dhV", options, NULL)) >= 0) { switch (c) { case 'd': break; case 'h': break; case 'V': break; default: break; } } dump(c); command = test_argv[optind]; assert(command); assert(!strcmp(command, "hwdb")); unsetenv("POSIXLY_CORRECT"); } void test8() { const char *shortopts = "b:cde"; int foo = false; int bar = false; const struct option longopts[] = { {"foo", required_argument, &foo, 'x'}, {"bar", required_argument, &bar, 'y'}, {NULL, no_argument, NULL, 0} }; int test_argc = 6; char *test_argv[] = { "dummy", "-foo", "abc", "-bar=def", "ghi", "jkl" }; #ifdef __GLIBC__ optind = 0; #else optreset = 1; #endif optopt = 0; int c = getopt_long_only(test_argc, test_argv, shortopts, longopts, NULL); dump(c); assert(foo && !c); assert(optind == 3); c = getopt_long_only(test_argc, test_argv, shortopts, longopts, NULL); dump(c); assert(bar && !c); assert(optind == 4); } void test9() { char *test_argv[] = { "foo", }; struct option opts[] = { {0} }; optind = 0; int c = getopt_long(1, test_argv, "abc", opts, NULL); dump(c); assert(c == -1); } void test10() { char *test_argv[] = { "foo", "nonoption", "-a", "nonoption2", "nonoption3", "-b", "-c", }; struct option opts[] = { {0} }; optind = 2; dump(0); int c = getopt_long(COUNT_OF(test_argv), test_argv, "abc", opts, NULL); dump(c); dumpargv(test_argv); assert(c == 'a'); c = getopt_long(COUNT_OF(test_argv), test_argv, "abc", opts, NULL); dump(c); dumpargv(test_argv); assert(c == 'b'); c = getopt_long(COUNT_OF(test_argv), test_argv, "abc", opts, NULL); dump(c); dumpargv(test_argv); assert(c == 'c'); c = getopt_long(COUNT_OF(test_argv), test_argv, "abc", opts, NULL); dump(c); dumpargv(test_argv); assert(c == -1); assert(!strcmp(test_argv[0], "foo")); assert(!strcmp(test_argv[1], "nonoption")); assert(!strcmp(test_argv[2], "-a")); assert(!strcmp(test_argv[3], "-b")); assert(!strcmp(test_argv[4], "-c")); assert(!strcmp(test_argv[5], "nonoption2")); assert(!strcmp(test_argv[6], "nonoption3")); } void test11() { const struct option longopts[] = { {"op", optional_argument, NULL, 'o'}, {NULL, 0, NULL, 0} }; char *test_argv[] = { "dummy", "--op", "--op=arg", "non-option", "--op" }; fputs("Situation: optional argument to long option\n", stderr); optind = 0; int c = getopt_long(COUNT_OF(test_argv), test_argv, "", longopts, NULL); assert(c == 'o'); assert(optarg == NULL); c = getopt_long(COUNT_OF(test_argv), test_argv, "", longopts, NULL); assert(c == 'o'); assert(!strcmp(optarg, "arg")); c = getopt_long(COUNT_OF(test_argv), test_argv, "", longopts, NULL); assert(c == 'o'); assert(optarg == NULL); } void test12() { char *test_argv[] = { "dummy", "-a", "-b", "arg", "-carg", "arg", }; fputs("Situation: optional argument to short option\n", stderr); optind = 0; int c = getopt_long(COUNT_OF(test_argv), test_argv, "ab::c::", NULL, NULL); assert(c == 'a'); assert(optarg == NULL); c = getopt_long(COUNT_OF(test_argv), test_argv, "ab::c::", NULL, NULL); assert(c == 'b'); assert(!optarg); c = getopt_long(COUNT_OF(test_argv), test_argv, "ab::c::", NULL, NULL); assert(c == 'c'); assert(!strcmp(optarg, "arg")); c = getopt_long(COUNT_OF(test_argv), test_argv, "ab::c::", NULL, NULL); assert(c == -1); assert(optind == 4); assert(!strcmp(test_argv[optind], "arg")); } void test13() { const struct option longopts[] = { {"ambiguous-opt", no_argument, NULL, 0}, {"ambiguous-option", no_argument, NULL, 1}, {NULL, 0, NULL, 0} }; char *test_argv[] = { "dummy", "--ambiguous-o" }; fputs("Situation: ambiguous long option\n", stderr); optind = 0; opterr = 0; int c = getopt_long(COUNT_OF(test_argv), test_argv, "", longopts, NULL); assert(c == '?'); } void test14() { const struct option longopts[] = { {"foo", required_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; char *test_argv[] = { "dummy", "-Wfoo=bar", }; fputs("Situation: -W for long options\n", stderr); optind = 0; int c = getopt_long(COUNT_OF(test_argv), test_argv, "W;", longopts, NULL); assert(c == 'f'); assert(!strcmp(optarg, "bar")); } void test15() { char *test_argv[] = { "dummy", "-c", "-a", "--", "-b" }; fputs("Situation: -- to stop option processing\n", stderr); optind = 0; int c = getopt(COUNT_OF(test_argv), test_argv, "+ab"); assert(c == '?'); assert(optind == 2); c = getopt(COUNT_OF(test_argv), test_argv, "+ab"); assert(c == 'a'); assert(optind == 3); c = getopt(COUNT_OF(test_argv), test_argv, "+ab"); assert(c == -1); assert(optind == 4); assert(!strcmp(test_argv[optind], "-b")); } void test16() { const struct option longopts[] = { {"add", required_argument, NULL, 'a'}, {"append", no_argument, NULL, 'p'}, {NULL, 0, NULL, 0} }; char *test_argv[] = { "dummy", "--ad=foo", "--appe" }; fputs("Situation: non-ambiguous long option prefix\n", stderr); optind = 0; int c = getopt_long(COUNT_OF(test_argv), test_argv, "", longopts, NULL); assert(c == 'a'); assert(!strcmp(optarg, "foo")); c = getopt_long(COUNT_OF(test_argv), test_argv, "", longopts, NULL); assert(c == 'p'); assert(optarg == NULL); } void test17() { const struct option longopts[] = { {"foo", no_argument, NULL, 'l'}, {NULL, 0, NULL, 0} }; char *test_argv[] = { "dummy", "-foo" }; fputs("Situation: getopt_long_only with what could be short opts or a long opt\n", stderr); // Case 1: long option "foo" exists, so "-foo" should be treated as "--foo" optind = 0; int c = getopt_long_only(COUNT_OF(test_argv), test_argv, "f:o", longopts, NULL); assert(c == 'l'); // Case 2: long option "foo" does not exist. const struct option longopts2[] = { {"bar", no_argument, NULL, 'b'}, {NULL, 0, NULL, 0} }; optind = 0; c = getopt_long_only(COUNT_OF(test_argv), test_argv, "f:o", longopts2, NULL); assert(c == 'f'); assert(!strcmp(optarg, "oo")); } void test18() { const struct option longopts[] = { {"op", optional_argument, NULL, 'o'}, {NULL, 0, NULL, 0} }; char *test_argv[] = { "dummy", "-Wop=val", }; fputs("Situation: -W with a long option with an optional argument (with value)\n", stderr); optind = 0; int c = getopt_long(COUNT_OF(test_argv), test_argv, "W;", longopts, NULL); assert(c == 'o'); assert(!strcmp(optarg, "val")); char *test_argv2[] = { "dummy", "-Wop", }; fputs("Situation: -W with a long option with an optional argument (no value)\n", stderr); optind = 0; c = getopt_long(COUNT_OF(test_argv2), test_argv2, "W;", longopts, NULL); assert(c == 'o'); assert(optarg == NULL); } void test19() { char *test_argv[] = { "dummy", "-a", "non-option", "-b", }; fputs("Situation: POSIXLY_CORRECT behavior with '+'\n", stderr); optind = 0; int c = getopt(COUNT_OF(test_argv), test_argv, "+ab"); assert(c == 'a'); c = getopt(COUNT_OF(test_argv), test_argv, "+ab"); assert(c == -1); assert(optind == 2); assert(!strcmp(test_argv[optind], "non-option")); } void test20() { const struct option longopts[] = { {"option", required_argument, NULL, 'o'}, {NULL, 0, NULL, 0} }; char *test_argv[] = { "dummy", "--option", "-x", }; fputs("Situation: long option with argument that looks like an option\n", stderr); optind = 0; int c = getopt_long(COUNT_OF(test_argv), test_argv, "x", longopts, NULL); assert(c == 'o'); assert(!strcmp(optarg, "-x")); assert(optind == 3); } void test21() { const struct option longopts[] = { {"ambig-one", no_argument, NULL, '1'}, {"ambig-two", no_argument, NULL, '2'}, {NULL, 0, NULL, 0} }; char *test_argv[] = { "dummy", "-ambig", }; fputs("Situation: ambiguous long option with getopt_long_only\n", stderr); optind = 0; opterr = 0; int c = getopt_long_only(COUNT_OF(test_argv), test_argv, "", longopts, NULL); assert(c == '?'); } void test22() { const struct option longopts[] = { {"foo", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; char *test_argv[] = { "dummy", "-Wbar", }; fputs("Situation: -W with non-existent long option\n", stderr); optind = 0; opterr = 0; int c = getopt_long(COUNT_OF(test_argv), test_argv, "W;", longopts, NULL); assert(c == '?'); assert(optind == 2); } void test23() { const struct option longopts[] = { {"foo", required_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; char *test_argv[] = { "dummy", "-foo", "val1", "-foo=val2", }; fputs("Situation: getopt_long_only with required argument\n", stderr); optind = 0; int c = getopt_long_only(COUNT_OF(test_argv), test_argv, "", longopts, NULL); assert(c == 'f'); assert(!strcmp(optarg, "val1")); assert(optind == 3); c = getopt_long_only(COUNT_OF(test_argv), test_argv, "", longopts, NULL); assert(c == 'f'); assert(!strcmp(optarg, "val2")); assert(optind == 4); } void test24() { char *test_argv[] = { "dummy", "-W", }; fputs("Situation: -W as a short option, not for passing long options\n", stderr); optind = 0; int c = getopt(COUNT_OF(test_argv), test_argv, "W"); assert(c == 'W'); assert(optind == 2); } void test25() { const struct option longopts[] = { {"foo", required_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; char *test_argv[] = { "dummy", "-Wfoo", }; fputs("Situation: -W with missing argument for a long option, with ':' in optstring\n", stderr); optind = 0; opterr = 0; int c = getopt_long(COUNT_OF(test_argv), test_argv, ":W;", longopts, NULL); assert(c == ':'); assert(optopt == 'f'); assert(optind == 2); } void test26() { const struct option longopts[] = { {"foo", no_argument, NULL, 0}, {"ambig-one", no_argument, NULL, 1}, {"ambig-two", no_argument, NULL, 2}, {NULL, 0, NULL, 0} }; char *test_argv[] = { "dummy", "-W", }; fputs("Situation: -W without an argument\n", stderr); optind = 0; opterr = 1; int c = getopt_long(COUNT_OF(test_argv), test_argv, ":W;", longopts, NULL); assert(c == ':'); assert(optopt == 'W'); assert(optind == 2); } void test27() { char *test_argv[] = { "dummy", "-Wambig", }; const struct option longopts[] = { {"foo", no_argument, NULL, 0}, {"ambig-one", no_argument, NULL, 1}, {"ambig-two", no_argument, NULL, 2}, {NULL, 0, NULL, 0} }; fputs("Situation: -W with an ambiguous long option\n", stderr); optind = 0; opterr = 0; int c = getopt_long(COUNT_OF(test_argv), test_argv, "W;", longopts, NULL); assert(c == '?'); assert(optind == 2); } void test28() { char *test_argv[] = { "dummy", "-Wfoo=bar", "-Wfoo=", }; const struct option longopts[] = { {"foo", no_argument, NULL, 0}, {"ambig-one", no_argument, NULL, 1}, {"ambig-two", no_argument, NULL, 2}, {NULL, 0, NULL, 0} }; fputs("Situation: -W with an argument to a long option that does not take one\n", stderr); optind = 0; opterr = 0; int c = getopt_long(COUNT_OF(test_argv), test_argv, "W;", longopts, NULL); assert(c == '?'); assert(optind == 2); c = getopt_long(COUNT_OF(test_argv), test_argv, "W;", longopts, NULL); assert(c == '?'); assert(optind == 3); c = getopt_long(COUNT_OF(test_argv), test_argv, "W;", longopts, NULL); assert(c == -1); } void test29() { char *test_argv[] = { "dummy", "--a", }; const struct option longopts[] = { {"load-credentials", no_argument, NULL, 0x103}, {NULL, 0, NULL, 0}, }; optind = 0; opterr = 1; int c = getopt_long(COUNT_OF(test_argv), test_argv, "el:sSRp:m:t:Vh", longopts, NULL); dump(c); assert(c == '?'); assert(optind == 2); } void test30() { const struct option longopts[] = { {"long-opt", required_argument, NULL, 'l'}, {NULL, 0, NULL, 0} }; char *test_argv[] = { "getopt_test", "non-arg1", "--long-opt", "val", "non-arg2" }; fputs("Situation: non-arguments before valid long arguments\n", stderr); optind = 0; int c = getopt_long(COUNT_OF(test_argv), test_argv, "+", longopts, NULL); assert(c == -1); assert(optind == 1); assert(!strcmp(test_argv[0], "getopt_test")); assert(!strcmp(test_argv[1], "non-arg1")); assert(!strcmp(test_argv[2], "--long-opt")); assert(!strcmp(test_argv[3], "val")); assert(!strcmp(test_argv[4], "non-arg2")); optind = 2; while ((c = getopt_long(COUNT_OF(test_argv), test_argv, "+", longopts, NULL)) != -1) { switch (c) { case 'l': assert(!strcmp(optarg, "val")); break; default: break; } } assert(c == -1); assert(optind == 4); optind = 0; while ((c = getopt_long(COUNT_OF(test_argv), test_argv, "", longopts, NULL)) != -1) { switch (c) { case 'l': assert(!strcmp(optarg, "val")); break; default: break; } } assert(optind == 3); assert(!strcmp(test_argv[0], "getopt_test")); assert(!strcmp(test_argv[1], "--long-opt")); assert(!strcmp(test_argv[2], "val")); assert(!strcmp(test_argv[3], "non-arg1")); assert(!strcmp(test_argv[4], "non-arg2")); } int main() { test1(); test2(); test3(); test4(); test5(); test6(); test7(); test8(); test9(); test10(); test11(); test12(); test13(); test14(); test15(); test16(); test17(); test18(); test19(); test20(); test21(); test22(); test23(); test24(); test25(); test26(); test27(); test28(); test29(); test30(); return 0; }