TEST parg_getopt_long_flag(void) { struct parg_state ps; char *argv[] = { "app", "--flag" }; int li = -1; int flag = 0; struct parg_option po_flag[] = { { "flag", PARG_NOARG, 0, 1 }, { 0, 0, 0, 0 } }; po_flag[0].flag = &flag; parg_init(&ps); ASSERT_EQ(0, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_flag, &li)); ASSERT_EQ(0, li); ASSERT_EQ(1, flag); ASSERT_EQ(2, ps.optind); ASSERT_EQ(-1, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_flag, &li)); ASSERT_EQ(NULL, ps.optarg); ASSERT_EQ(2, ps.optind); PASS(); }
TEST parg_getopt_long_no_match(void) { struct parg_state ps; char *argv[] = { "app", "--unknown" }; int li = -1; parg_init(&ps); ASSERT_EQ('?', parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li)); ASSERT_EQ(2, ps.optind); ASSERT_EQ(-1, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li)); ASSERT_EQ(NULL, ps.optarg); ASSERT_EQ(2, ps.optind); PASS(); }
TEST parg_getopt_long_prefix_unambiguous(void) { struct parg_state ps; char *argv[] = { "app", "--foob" }; int li = -1; parg_init(&ps); ASSERT_EQ('b', parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li)); ASSERT_EQ(4, li); ASSERT_EQ(NULL, ps.optarg); ASSERT_EQ(2, ps.optind); ASSERT_EQ(-1, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li)); ASSERT_EQ(NULL, ps.optarg); ASSERT_EQ(2, ps.optind); PASS(); }
TEST parg_getopt_long_reqarg_nextarg(void) { struct parg_state ps; char *argv[] = { "app", "--reqarg", "arg" }; int li = -1; parg_init(&ps); ASSERT_EQ('r', parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li)); ASSERT_EQ(2, li); ASSERT_EQ(argv[2], ps.optarg); ASSERT_EQ(3, ps.optind); ASSERT_EQ(-1, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li)); ASSERT_EQ(NULL, ps.optarg); ASSERT_EQ(3, ps.optind); PASS(); }
TEST parg_getopt_long_optarg_inline(void) { struct parg_state ps; char *argv[] = { "app", "--optarg=arg" }; int li = -1; parg_init(&ps); ASSERT_EQ('o', parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li)); ASSERT_EQ(1, li); ASSERT_EQ(&argv[1][9], ps.optarg); ASSERT_EQ(2, ps.optind); ASSERT_EQ(-1, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li)); ASSERT_EQ(NULL, ps.optarg); ASSERT_EQ(2, ps.optind); PASS(); }
TEST parg_getopt_long_app_only(void) { struct parg_state ps; char *argv[] = { "app" }; int li = -1; parg_init(&ps); ASSERT_EQ(-1, parg_getopt_long(&ps, ARRAY_SIZE(argv), argv, ":", po_def, &li)); ASSERT_EQ(NULL, ps.optarg); ASSERT_EQ(1, ps.optind); PASS(); }
int parg_reorder(int argc, char *argv[], const char *optstring, const struct parg_option *longopts) { struct parg_state ps; int lastind; int optend; int c; assert(argv != NULL); assert(optstring != NULL); if (argc < 2) { return argc; } parg_init(&ps); /* Find end of normal arguments */ do { lastind = ps.optind; c = parg_getopt_long(&ps, argc, argv, optstring, longopts, NULL); /* Check for trailing option with error */ if ((c == '?' || c == ':') && is_argv_end(&ps, argc, argv)) { lastind = ps.optind - 1; break; } } while (c != -1); optend = parg_reorder_simple(lastind, argv, optstring, longopts); /* Rotate `--` or trailing option with error into position */ if (lastind < argc) { reverse(argv, optend, lastind); reverse(argv, optend, lastind + 1); ++optend; } return optend; }
int main (int argc, char** argv) { SquashStatus res; SquashCodec* codec = NULL; SquashOptions* options = NULL; SquashStreamType direction = SQUASH_STREAM_COMPRESS; FILE* input = NULL; FILE* output = NULL; char* input_name = NULL; char* output_name = NULL; bool list_codecs = false; bool list_plugins = false; char** option_keys = NULL; char** option_values = NULL; bool keep = false; bool force = false; int opt; int optc = 0; char* tmp_string; int retval = EXIT_SUCCESS; struct parg_state ps; int optend; const struct parg_option squash_options[] = { {"keep", PARG_NOARG, NULL, 'k'}, {"option", PARG_REQARG, NULL, 'o'}, {"codec", PARG_REQARG, NULL, 'c'}, {"list-codecs", PARG_NOARG, NULL, 'L'}, {"list-plugins", PARG_NOARG, NULL, 'P'}, {"force", PARG_NOARG, NULL, 'f'}, {"decompress", PARG_NOARG, NULL, 'd'}, {"version", PARG_NOARG, NULL, 'V'}, {"help", PARG_NOARG, NULL, 'h'}, {NULL, 0, NULL, 0} }; option_keys = (char**) malloc (sizeof (char*)); option_values = (char**) malloc (sizeof (char*)); *option_keys = NULL; *option_values = NULL; optend = parg_reorder (argc, argv, "c:ko:123456789LPfdhb:V", squash_options); parg_init(&ps); while ( (opt = parg_getopt_long (&ps, optend, argv, "c:ko:123456789LPfdhb:V", squash_options, NULL)) != -1 ) { switch ( opt ) { case 'c': codec = squash_get_codec (ps.optarg); if ( codec == NULL ) { fprintf (stderr, "Unable to find codec '%s'\n", ps.optarg); retval = exit_failure (); goto cleanup; } break; case 'k': keep = true; break; case 'o': parse_option (&option_keys, &option_values, ps.optarg); break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': tmp_string = malloc (8); snprintf (tmp_string, 8, "level=%c", (char) opt); parse_option (&option_keys, &option_values, tmp_string); free (tmp_string); break; case 'L': list_codecs = true; break; case 'P': list_plugins = true; break; case 'f': force = true; break; case 'h': print_help_and_exit (argc, argv, EXIT_SUCCESS); break; case 'd': direction = SQUASH_STREAM_DECOMPRESS; break; case 'V': print_version_and_exit (argc, argv, EXIT_SUCCESS); break; } optc++; } if (list_plugins) { if (list_codecs) squash_foreach_plugin (list_plugins_and_codecs_foreach_cb, NULL); else squash_foreach_plugin (list_plugins_foreach_cb, NULL); goto cleanup; } else if (list_codecs) { squash_foreach_codec (list_codecs_foreach_cb, NULL); goto cleanup; } if ( ps.optind < argc ) { input_name = argv[ps.optind++]; if ( (direction == SQUASH_STREAM_DECOMPRESS) && codec == NULL ) { char* extension; extension = strrchr (input_name, '.'); if (extension != NULL) extension++; if (extension != NULL) codec = squash_get_codec_from_extension (extension); } } else { fprintf (stderr, "You must provide an input file name.\n"); retval = exit_failure (); goto cleanup; } if ( ps.optind < argc ) { output_name = strdup (argv[ps.optind++]); if ( codec == NULL && direction == SQUASH_STREAM_COMPRESS ) { const char* extension = strrchr (output_name, '.'); if (extension != NULL) extension++; if (extension != NULL) codec = squash_get_codec_from_extension (extension); } } else { if ( codec != NULL ) { const char* extension = squash_codec_get_extension (codec); if (extension != NULL) { if (strcmp (input_name, "-") == 0) { output_name = strdup ("-"); } else { size_t extension_length = strlen (extension); size_t input_name_length = strlen (input_name); if ( (extension_length + 1) < input_name_length && input_name[input_name_length - (1 + extension_length)] == '.' && strcasecmp (extension, input_name + (input_name_length - (extension_length))) == 0 ) { output_name = squash_strndup (input_name, input_name_length - (1 + extension_length)); } } } } } if ( ps.optind < argc ) { fprintf (stderr, "Too many arguments.\n"); } if ( codec == NULL ) { fprintf (stderr, "Unable to determine codec. Please pass -c \"codec\", or -L to see a list of available codecs.\n"); retval = exit_failure (); goto cleanup; } if ( output_name == NULL ) { fprintf (stderr, "Unable to determine output file.\n"); retval = exit_failure (); goto cleanup; } if ( strcmp (input_name, "-") == 0 ) { input = stdin; } else { input = fopen (input_name, "rb"); if ( input == NULL ) { perror ("Unable to open input file"); retval = exit_failure (); goto cleanup; } } if ( strcmp (output_name, "-") == 0 ) { output = stdout; } else { int output_fd = open (output_name, #if !defined(_WIN32) O_RDWR | O_CREAT | (force ? O_TRUNC : O_EXCL), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH #else O_RDWR | O_CREAT | (force ? O_TRUNC : O_EXCL) | O_BINARY, S_IREAD | S_IWRITE #endif ); if ( output_fd < 0 ) { perror ("Unable to open output file"); retval = exit_failure (); goto cleanup; } output = fdopen (output_fd, "wb"); if ( output == NULL ) { perror ("Unable to open output"); retval = exit_failure (); goto cleanup; } } options = squash_options_newa (codec, (const char * const*) option_keys, (const char * const*) option_values); res = squash_splice_with_options (codec, direction, output, input, 0, options); if ( res != SQUASH_OK ) { fprintf (stderr, "Failed to %s: %s\n", (direction == SQUASH_STREAM_COMPRESS) ? "compress" : "decompress", squash_status_to_string (res)); retval = exit_failure (); goto cleanup; } if ( !keep && input != stdin ) { fclose (input); if ( unlink (input_name) != 0 ) { perror ("Unable to remove input file"); } input = NULL; } cleanup: if (input != stdin && input != NULL) fclose (stdin); if (output != stdout) fclose (stdout); if (option_keys != NULL) { for (opt = 0 ; option_keys[opt] != NULL ; opt++) { free(option_keys[opt]); } free (option_keys); } if (option_values != NULL) { for (opt = 0 ; option_values[opt] != NULL ; opt++) { free(option_values[opt]); } free (option_values); } free (output_name); return retval; }
/* * Reorder elements of `argv` with no special cases. * * This function assumes there is no `--` element, and the last element * is not an option missing a required argument. * * The algorithm is described here: * http://hardtoc.com/2016/11/07/reordering-arguments.html */ static int parg_reorder_simple(int argc, char *argv[], const char *optstring, const struct parg_option *longopts) { struct parg_state ps; int change; int l = 0; int m = 0; int r = 0; if (argc < 2) { return argc; } do { int nextind; int c; parg_init(&ps); nextind = ps.optind; /* Parse until end of argument */ do { c = parg_getopt_long(&ps, argc, argv, optstring, longopts, NULL); } while (ps.nextchar != NULL && *ps.nextchar != '\0'); change = 0; do { /* Find next non-option */ for (l = nextind; c != 1 && c != -1;) { l = ps.optind; do { c = parg_getopt_long(&ps, argc, argv, optstring, longopts, NULL); } while (ps.nextchar != NULL && *ps.nextchar != '\0'); } /* Find next option */ for (m = l; c == 1;) { m = ps.optind; do { c = parg_getopt_long(&ps, argc, argv, optstring, longopts, NULL); } while (ps.nextchar != NULL && *ps.nextchar != '\0'); } /* Find next non-option */ for (r = m; c != 1 && c != -1;) { r = ps.optind; do { c = parg_getopt_long(&ps, argc, argv, optstring, longopts, NULL); } while (ps.nextchar != NULL && *ps.nextchar != '\0'); } /* Find next option */ for (nextind = r; c == 1;) { nextind = ps.optind; do { c = parg_getopt_long(&ps, argc, argv, optstring, longopts, NULL); } while (ps.nextchar != NULL && *ps.nextchar != '\0'); } if (m < r) { change = 1; reverse(argv, l, m); reverse(argv, m, r); reverse(argv, l, r); } } while (c != -1); } while (change != 0); return l + (r - m); }
int parg_getopt(struct parg_state *ps, int argc, char *const argv[], const char *optstring) { return parg_getopt_long(ps, argc, argv, optstring, NULL, NULL); }