Skip to content

Commit

Permalink
correctly handle options like '-f a.txt'
Browse files Browse the repository at this point in the history
  • Loading branch information
duyanning committed Dec 1, 2023
1 parent 059f19e commit a999db6
Show file tree
Hide file tree
Showing 2 changed files with 177 additions and 2 deletions.
167 changes: 166 additions & 1 deletion src/tbox/utils/option.c
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ tb_bool_t tb_option_find(tb_option_ref_t option, tb_char_t const* name)
// find it
return tb_oc_dictionary_value(impl->list, name)? tb_true : tb_false;
}
tb_bool_t tb_option_done(tb_option_ref_t option, tb_size_t argc, tb_char_t** argv)
tb_bool_t tb_norm_option_done(tb_option_ref_t option, tb_size_t argc, tb_char_t** argv)
{
// check
tb_option_impl_t* impl = (tb_option_impl_t*)option;
Expand Down Expand Up @@ -565,6 +565,171 @@ tb_bool_t tb_option_done(tb_option_ref_t option, tb_size_t argc, tb_char_t** arg
// ok
return tb_true;//tb_option_check(impl);
}
static void tb_print_argv(tb_size_t argc, tb_char_t* argv[])
{
tb_trace_i("argc: %d", argc);
tb_size_t i = 0;
for (i = 0; i < argc; i++)
{
tb_trace_i("argv[%d]: %s", i, argv[i]);
}
}
tb_bool_t tb_option_done(tb_option_ref_t option, tb_size_t argc, tb_char_t** argv)
{
tb_bool_t ok = tb_false;
// check
tb_option_impl_t* impl = (tb_option_impl_t*)option;
tb_assert_and_check_return_val(impl && impl->list && impl->opts, tb_false);

// Prints the command-line arguments before normalization
// tb_trace_i("OLD:");
// tb_print_argv(argc, argv);

// Normalize the parameters, i.e., transform the form '-f a.txt' into the form '-f=a.txt'
// Make a two-dimensional character array to hold command-line arguments
// The number of first dimensions will not exceed 'argc'
// The number of the second dimension will not exceed twice the number of the longest 'argv[i]' +1
// (why +1, because of the equal sign)
// Let's find out how long the longest argv[i] is
tb_size_t i = 0;
tb_size_t longest = 0;
for (i = 0; i < argc; i++)
{
tb_size_t argvi_len = tb_strlen(argv[i]);
if (argvi_len > longest)
{
longest = argvi_len;
}
}

// Allocate space
tb_char_t* data = tb_malloc(argc * (2*longest+1+1)); // including the trailing '\0'
// Allocate space for normalized argv array. Array elements are of tb_char_t*
tb_char_t** new_argv = tb_malloc(argc * sizeof (tb_char_t*));
for (i = 0; i < argc; i++)
{
new_argv[i] = data + i*(2*longest+1+1);
}
tb_size_t new_argc = 0;

// Examine each element of the original 'argv' array one by one
for (i = 0; i < argc; i++)
{
// the argument
tb_char_t* p = argv[i];
tb_char_t* e = p + tb_strlen(p);
tb_assert_and_check_return_val(p && p < e, tb_false);

// Determine the kind of argv[i].
// 0 - long KEY;
// 1 - short KEY
// 2 - not KEY (It may be an independent VAL, or it may be a VAL attached to the previous KEY
// It depends on whether the previous KEY has VAL or not)
tb_size_t kind;
if (p + 2 < e && p[0] == '-' && p[1] == '-' && tb_isalpha(p[2]))
kind = 0;
else if (p + 1 < e && p[0] == '-' && tb_isalpha(p[1]))
kind = 1;
else
kind = 2;

// If it is is a KEY, you need to determine whether it is a simple KEY or a KEY with VAL
// based on the option definition
if (kind == 0 || kind == 1)
{
tb_char_t key[512] = {0};
tb_char_t* val;
tb_option_item_t const* find;
if (kind == 0)
{
// the long key
{
tb_char_t* k = key;
tb_char_t* e = key + 511;
for (p += 2; *p && *p != '=' && k < e; p++, k++) *k = *p;
}
// the val
val = (*p == '=')? (p + 1) : tb_null;
find = tb_option_item_find(impl->opts, key, '\0');
}
else
{
// the short key
{
tb_char_t* k = key;
tb_char_t* e = key + 511;
for (p += 1; *p && *p != '=' && k < e; p++, k++) *k = *p;
}
// the val
val = (*p == '=')? (p + 1) : tb_null;
find = tb_option_item_find(impl->opts, tb_null, key[0]);
}

// If it is a KEY with VAL
if (find->mode == TB_OPTION_MODE_KEY_VAL)
{
// Then determine whether VAL is in argv[i] or argv[i+1].
// If it's in argv[i], copy it directly to the new_argv array
// If it's in argv[i+1], copy argv[i]=argv[i+1] to the new_argv array
if (val)
{
tb_strcpy(new_argv[new_argc], argv[i]);
new_argc++;
}
else
{
// If argv[i+1] is also preceded by a '-',
// that is, another KEY, an error will be reported
if (i+1 < argc && argv[i+1][0]=='-')
{
tb_trace_e("%s: no option value '--%s='", impl->name, key);
tb_strcpy(new_argv[new_argc], argv[i]);
new_argc++;
}
else
{
tb_strcpy(new_argv[new_argc], argv[i]);
tb_strcat(new_argv[new_argc], "=");
tb_strcat(new_argv[new_argc], argv[i+1]);
i++;
new_argc++;
}
}
}
else
{
// If it is a simple KEY, copy it to the new_argv array
tb_strcpy(new_argv[new_argc], argv[i]);
new_argc++;
}

}
else
{
// If it's something else, copy directly to the new argv array.
// This one is necessarily an independent VAL,
// because the subsidiary VAL has already been skipped.
tb_strcpy(new_argv[new_argc], argv[i]);
new_argc++;
}

}

// Prints the normalized command-line arguments
// tb_trace_i("NEW:");
// tb_print_argv(new_argc, new_argv);

// This is the original tb_option_done,
// renamed to tb_normalized_option_done because it can only
// handle normalized options (i.e., options like '-f=a.txt').
// In the style of tbox, use norm instead of normalized
ok = tb_norm_option_done(option, argc, argv);

tb_free(new_argv);
tb_free(data);

return ok;
}
tb_void_t tb_option_dump(tb_option_ref_t option)
{
// check
Expand Down
12 changes: 11 additions & 1 deletion src/tbox/utils/option.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,17 @@ tb_void_t tb_option_exit(tb_option_ref_t option);
*/
tb_bool_t tb_option_find(tb_option_ref_t option, tb_char_t const* name);

/*! done option
/*! done normalized option (normalized option is like '-f=a.txt')
*
* @param option the option
* @param argc the arguments count
* @param argv the arguments value
*
* @return tb_true or tb_false
*/
tb_bool_t tb_norm_option_done(tb_option_ref_t option, tb_size_t argc, tb_char_t** argv);

/*! done option (also supports '-f a.txt')
*
* @param option the option
* @param argc the arguments count
Expand Down

0 comments on commit a999db6

Please sign in to comment.