diff --git a/cmd/arg.c b/cmd/arg.c index 55c509c..579b0da 100644 --- a/cmd/arg.c +++ b/cmd/arg.c @@ -16,6 +16,27 @@ struct b_command_arg *b_command_arg_create(void) return out; } +void b_command_arg_destroy(struct b_command_arg *arg) +{ + if (arg->arg_name) { + free(arg->arg_name); + } + + if (arg->arg_description) { + free(arg->arg_description); + } + + if (arg->arg_allowed_values) { + for (unsigned int i = 0; arg->arg_allowed_values[i]; i++) { + free(arg->arg_allowed_values[i]); + } + + free(arg->arg_allowed_values); + } + + free(arg); +} + b_status b_command_arg_set_name(struct b_command_arg *arg, const char *name) { char *n = b_strdup(name); diff --git a/cmd/command.c b/cmd/command.c index d0c000b..d951bb2 100644 --- a/cmd/command.c +++ b/cmd/command.c @@ -1,6 +1,7 @@ #include "command.h" #include +#include #include #include #include @@ -20,6 +21,21 @@ enum item_type { ITEM_SUBCOMMAND, }; +static void command_list_cleanup(void) +{ + struct b_btree_iterator it = {0}; + b_btree_iterator_begin(&command_list, &it); + while (b_btree_iterator_is_valid(&it)) { + struct b_command *cmd = b_unbox(struct b_command, it.node, b_node); + if (!cmd) { + break; + } + + b_btree_iterator_erase(&it); + b_command_destroy(cmd); + } +} + struct b_command *b_command_create(unsigned int id) { struct b_command *out = malloc(sizeof *out); @@ -34,8 +50,102 @@ struct b_command *b_command_create(unsigned int id) return out; } +static void command_usage_destroy(struct b_command_usage *usage) +{ + struct b_queue_iterator it = {0}; + + b_queue_iterator_begin(&usage->u_arg, &it); + while (b_queue_iterator_is_valid(&it)) { + struct b_command_usage_arg *arg = b_unbox( + struct b_command_usage_arg, it.entry, arg_entry); + if (!arg) { + continue; + } + + b_queue_iterator_erase(&it); + free(arg); + } + + b_queue_iterator_begin(&usage->u_opt, &it); + while (b_queue_iterator_is_valid(&it)) { + struct b_command_usage_opt *opt = b_unbox( + struct b_command_usage_opt, it.entry, opt_entry); + if (!opt) { + continue; + } + + b_queue_iterator_erase(&it); + free(opt); + } + + free(usage); +} + void b_command_destroy(struct b_command *cmd) { + struct b_queue_iterator it = {0}; + + if (cmd->b_name) { + free(cmd->b_name); + } + + if (cmd->b_long_name) { + free(cmd->b_long_name); + } + + if (cmd->b_description) { + free(cmd->b_description); + } + + b_queue_iterator_begin(&cmd->b_opt, &it); + while (b_queue_iterator_is_valid(&it)) { + struct b_command_option *opt + = b_unbox(struct b_command_option, it.entry, opt_entry); + if (!opt) { + break; + } + + b_queue_iterator_erase(&it); + b_command_option_destroy(opt); + } + + b_queue_iterator_begin(&cmd->b_arg, &it); + while (b_queue_iterator_is_valid(&it)) { + struct b_command_arg *arg + = b_unbox(struct b_command_arg, it.entry, arg_entry); + if (!arg) { + break; + } + + b_queue_iterator_erase(&it); + b_command_arg_destroy(arg); + } + + b_queue_iterator_begin(&cmd->b_arg, &it); + while (b_queue_iterator_is_valid(&it)) { + struct b_command *subcmd + = b_unbox(struct b_command, it.entry, b_entry); + if (!subcmd) { + break; + } + + b_queue_iterator_erase(&it); + b_command_destroy(subcmd); + } + + b_queue_iterator_begin(&cmd->b_usage, &it); + while (b_queue_iterator_is_valid(&it)) { + struct b_command_usage *usage + = b_unbox(struct b_command_usage, it.entry, u_entry); + if (!usage) { + break; + } + + b_queue_iterator_erase(&it); + command_usage_destroy(usage); + } + + free(cmd); } b_status b_command_register(struct b_command *cmd) @@ -45,6 +155,10 @@ b_status b_command_register(struct b_command *cmd) return B_ERR_NAME_EXISTS; } + if (b_btree_empty(&command_list)) { + atexit(command_list_cleanup); + } + put_command(&command_list, cmd); return B_SUCCESS; } @@ -336,6 +450,18 @@ static void get_command_description(struct b_command *cmd, b_string *out) b_string_append_cstr(out, cmd->b_description); } +/* criteria for printing the option description on a separate line: + * 1) the length of the usage string exceeds the newline threshold; and + * 2) the length of the description string exceeds the remaining line length + * (once the usage string has been printed. + * or, + * 3) the length of the description string is more than three terminal lines. + */ +#define description_on_separate_line(opt_len, desc_len) \ + ((opt_len >= newline_threshold \ + && desc_len >= (term_width - newline_threshold)) \ + || desc_len > (3 * term_width - newline_threshold)) + static void print_options_list(struct b_command *cmd) { unsigned int term_width = 0; @@ -344,13 +470,14 @@ static void print_options_list(struct b_command *cmd) unsigned int newline_threshold = 1000000; if (term_width) { - newline_threshold = term_width - 12 - 25; + newline_threshold = term_width - 12 - 35; } b_fprintf(OUTPUT_STREAM, "\n" F_YELLOW "OPTIONS:" F_RESET "\n"); size_t desb_margin = 0; - b_string *str = b_string_create(); + b_string *opt_str = b_string_create(); + b_string *desc_str = b_string_create(); b_queue_iterator it; b_queue_foreach (&it, &cmd->b_opt) { @@ -360,16 +487,21 @@ static void print_options_list(struct b_command *cmd) continue; } - b_string_clear(str); - z__b_get_option_usage_string(opt, CMD_STR_COLOUR, str); - size_t len = b_string_get_size(str, B_STRLEN_IGNORE_ESC) + 4; + b_string_clear(opt_str); + b_string_clear(desc_str); + z__b_get_option_usage_string(opt, CMD_STR_COLOUR, opt_str); + z__b_get_option_description(opt, desc_str); - if (len >= newline_threshold) { + size_t opt_len + = b_string_get_size(opt_str, B_STRLEN_IGNORE_ESC) + 4; + size_t desc_len = b_string_get_size(desc_str, B_STRLEN_IGNORE_ESC); + + if (description_on_separate_line(opt_len, desc_len)) { continue; } - if (len > desb_margin) { - desb_margin = len; + if (opt_len > desb_margin) { + desb_margin = opt_len; } } @@ -386,18 +518,26 @@ static void print_options_list(struct b_command *cmd) continue; } - b_string_clear(str); - z__b_get_option_usage_string(opt, CMD_STR_COLOUR, str); - size_t opt_len = b_string_get_size(str, B_STRLEN_IGNORE_ESC) + 4; + b_string_clear(opt_str); + b_string_clear(desc_str); - if (opt_len >= newline_threshold) { + z__b_get_option_usage_string(opt, CMD_STR_COLOUR, opt_str); + z__b_get_option_description(opt, desc_str); + size_t opt_len + = b_string_get_size(opt_str, B_STRLEN_IGNORE_ESC) + 4; + size_t desc_len = b_string_get_size(desc_str, B_STRLEN_IGNORE_ESC); + + bool new_paragraph + = description_on_separate_line(opt_len, desc_len); + + if (new_paragraph) { fputc('\n', OUTPUT_STREAM); } fputs(" ", OUTPUT_STREAM); - b_fputs(b_string_ptr(str), OUTPUT_STREAM); + b_fputs(b_string_ptr(opt_str), OUTPUT_STREAM); - if (opt_len >= newline_threshold) { + if (new_paragraph) { format.p_flags = 0; format.p_left_margin = 8; format.p_right_margin = 4; @@ -408,23 +548,21 @@ static void print_options_list(struct b_command *cmd) format.p_right_margin = 4; } - unsigned int len = b_string_get_size(str, B_STRLEN_IGNORE_ESC) + 4; + unsigned int len = opt_len; while (len < format.p_left_margin) { fputc(' ', OUTPUT_STREAM); len++; } - b_string_clear(str); - z__b_get_option_description(opt, str); + b_print_paragraph(b_string_ptr(desc_str), OUTPUT_STREAM, &format); - b_print_paragraph(b_string_ptr(str), OUTPUT_STREAM, &format); - - if (opt_len >= newline_threshold) { + if (new_paragraph) { fputc('\n', OUTPUT_STREAM); } } - b_string_release(str); + b_string_release(opt_str); + b_string_release(desc_str); } static void print_args_list(struct b_command *cmd) diff --git a/cmd/command.h b/cmd/command.h index c697d14..dd0794d 100644 --- a/cmd/command.h +++ b/cmd/command.h @@ -73,16 +73,6 @@ struct b_command_option { struct b_queue_entry opt_entry; }; -struct b_arglist_option { - unsigned int opt_id; - struct b_btree opt_values; - struct b_btree_node opt_node; -}; - -struct b_arglist { - struct b_btree list_options; -}; - struct b_command_arg { unsigned int arg_id; char *arg_name; @@ -94,6 +84,16 @@ struct b_command_arg { struct b_queue_entry arg_entry; }; +struct b_arglist_option { + unsigned int opt_id; + struct b_btree opt_values; + struct b_btree_node opt_node; +}; + +struct b_arglist { + struct b_btree list_options; +}; + extern struct b_command *b_command_get_subcommand_with_name( struct b_command *cmd, const char *name); extern struct b_command *b_command_get_subcommand_with_long_name( @@ -107,7 +107,11 @@ extern struct b_command_option *b_command_get_option_with_short_name( struct b_command *cmd, char short_name); extern struct b_command_option *b_command_option_create(void); +extern void b_command_option_destroy(struct b_command_option *opt); + extern struct b_command_arg *b_command_arg_create(void); +extern void b_command_arg_destroy(struct b_command_arg *arg); + extern struct b_arglist *b_arglist_create(void); extern b_status b_arglist_parse( struct b_arglist *args, struct b_command **cmd, int argc, diff --git a/cmd/option.c b/cmd/option.c index 7133f3b..cbc476b 100644 --- a/cmd/option.c +++ b/cmd/option.c @@ -16,6 +16,33 @@ struct b_command_option *b_command_option_create(void) return out; } +void b_command_option_destroy(struct b_command_option *opt) +{ + if (opt->opt_long_name) { + free(opt->opt_long_name); + } + + if (opt->opt_description) { + free(opt->opt_description); + } + + b_queue_iterator it = {0}; + + b_queue_iterator_begin(&opt->opt_args, &it); + while (b_queue_iterator_is_valid(&it)) { + struct b_command_arg *arg + = b_unbox(struct b_command_arg, it.entry, arg_entry); + if (!arg) { + break; + } + + b_queue_iterator_erase(&it); + b_command_arg_destroy(arg); + } + + free(opt); +} + b_status b_command_option_set_long_name( struct b_command_option *opt, const char *name) { diff --git a/misc/AllTests.c b/misc/AllTests.c index 50b135b..8817a26 100644 --- a/misc/AllTests.c +++ b/misc/AllTests.c @@ -7,15 +7,19 @@ CuSuite *get_all_tests(void); int RunAllTests(void) { CuString *output = CuStringNew(); - CuSuite *suite = CuSuiteNew(); - - CuSuiteAddSuite(suite, get_all_tests()); + CuSuite *suite = get_all_tests(); CuSuiteRun(suite); CuSuiteSummary(suite, output); CuSuiteDetails(suite, output); printf("%s\n", output->buffer); - return suite->failCount; + + int r = suite->failCount; + + CuSuiteDelete(suite); + CuStringDelete(output); + + return r; } int main(void) diff --git a/object-test/arrays.c b/object-test/arrays.c index 60626a4..01573d9 100644 --- a/object-test/arrays.c +++ b/object-test/arrays.c @@ -15,5 +15,6 @@ int main(void) printf("object %p\n", it.value); } + b_array_release(array); return 0; } diff --git a/object-test/numbers.c b/object-test/numbers.c index d1f6c1c..874ff69 100644 --- a/object-test/numbers.c +++ b/object-test/numbers.c @@ -6,5 +6,6 @@ int main(void) b_number *number = b_number_create_float(6.8); printf("number=%zd\n", B_NUMBER_IVAL(number)); + b_number_release(number); return 0; } diff --git a/object-test/trees.c b/object-test/trees.c index 6a1508a..4f78095 100644 --- a/object-test/trees.c +++ b/object-test/trees.c @@ -112,5 +112,8 @@ int main(void) printf("%d\n", item->value); } + b_tree_release(tree); + b_dict_release(dict); + return 0; } diff --git a/object-test/uuids.c b/object-test/uuids.c index 506b358..512bed9 100644 --- a/object-test/uuids.c +++ b/object-test/uuids.c @@ -8,5 +8,7 @@ int main(void) char str[B_UUID_STRING_MAX]; b_uuid_to_cstr(uuid, str); printf("%s\n", str); + + b_uuid_release(uuid); return 0; }