X-Git-Url: https://git.saurik.com/apple/libc.git/blobdiff_plain/e1ee4b85c2dcd2825b98e4d31d829e56ae31f66a..refs/heads/master:/libdarwin/ctl.c diff --git a/libdarwin/ctl.c b/libdarwin/ctl.c index adcef27..28daa35 100755 --- a/libdarwin/ctl.c +++ b/libdarwin/ctl.c @@ -2,8 +2,8 @@ #pragma mark Definitions #define CTL_OUTPUT_WIDTH (80) -#define CTL_OUTPUT_OPTARG_PAD (28) -#define CTL_OUTPUT_LIST_PAD (4) +#define CTL_OUTPUT_OPTARG_INDENT (32) +#define CTL_OUTPUT_OPTARG_OVERFLOW (CTL_OUTPUT_OPTARG_INDENT - 4) #define SUBCOMMAND_LINKER_SET "__subcommands" #define OS_SUBCOMMAND_OPTIONS_FOREACH(_osco_i, _osc, _which, _i) \ @@ -31,6 +31,9 @@ static void _os_subcommand_print_usage( static void _os_subcommand_print_help_line( const os_subcommand_t *osc, FILE *f); +static void _print_subcommand_list( + const os_subcommand_t *osc, + FILE *f); #pragma mark Module Globals static const os_subcommand_t __help_cmd; @@ -40,6 +43,56 @@ static const os_subcommand_t __main_cmd; static const os_subcommand_t *_main_cmd = &__main_cmd; static const os_subcommand_t *_internal_main_cmd = &__main_cmd; +static struct ttysize __ttys = { + .ts_lines = 24, + .ts_cols = CTL_OUTPUT_WIDTH, +}; + +static const struct ttysize *_ttys = &__ttys; + +#pragma mark Module Private +static void +_init_column_count(void) +{ + const char *columns_env = NULL; + char *end = NULL; + struct ttysize ttys = { + .ts_lines = 24, + .ts_cols = CTL_OUTPUT_WIDTH, + }; + int ret = -1; + + columns_env = getenv("COLUMNS"); + if (columns_env) { + unsigned short cols = -1; + + cols = strtoul(columns_env, &end, 0); + if (end != columns_env && end[0] != 0) { + ttys.ts_lines = cols; + } + } else { + ret = ioctl(0, TIOCGSIZE, &ttys); + if (ret) { + ttys.ts_lines = 24; + ttys.ts_cols = CTL_OUTPUT_WIDTH; + } + } + + __ttys = ttys; +} + +static void +_stoupper(char *str) +{ + size_t i = 0; + size_t len = strlen(str); + + for (i = 0; i < len; i++) { + char *cp = &str[i]; + *cp = ___toupper(*cp); + } +} + #pragma mark Main Subcommand static int _main_invoke(const os_subcommand_t *osc, int argc, @@ -85,7 +138,7 @@ static const os_subcommand_option_t _help_positional[] = { .osco_version = OS_SUBCOMMAND_OPTION_VERSION, .osco_flags = 0, .osco_option = NULL, - .osco_argument_usage = "subcommand", + .osco_argument_usage = "SUBCOMMAND", .osco_argument_human = "The subcommand to query for help", }, OS_SUBCOMMAND_OPTION_TERMINATOR, @@ -116,19 +169,14 @@ _help_invoke(const os_subcommand_t *osc, int argc, const char *argv[]) cmdname = argv[1]; } - if (cmdname) { - // Print usage information for the requested subcommand. - target = _os_subcommand_find(argv[1]); - if (!target) { - os_subcommand_fprintf(osc, stderr, "unrecognized subcommand: %s", - argv[1]); - xit = EX_USAGE; - } else { - xit = 0; - } - } else { - // Print general, top-level usage followed by a list of subcommands. + // Print usage information for the requested subcommand. + target = _os_subcommand_find(cmdname); + if (!target) { + // If it's a bogus subcommand, just print top-level usage. + fprintf(stderr, "unrecognized subcommand: %s\n", cmdname); target = _main_cmd; + xit = EX_UNAVAILABLE; + } else { xit = 0; } @@ -139,20 +187,7 @@ _help_invoke(const os_subcommand_t *osc, int argc, const char *argv[]) _os_subcommand_print_usage(target, f); if (target == _main_cmd) { - const os_subcommand_t **oscip = NULL; - const os_subcommand_t *osci = NULL; - bool header_printed = false; - - LINKER_SET_FOREACH(oscip, const os_subcommand_t **, - SUBCOMMAND_LINKER_SET) { - osci = *oscip; - - _print_header(f, "subcommands", &header_printed); - _os_subcommand_print_help_line(osci, f); - } - - // Print the help subcommand last. - _os_subcommand_print_help_line(osc, f); + _print_subcommand_list(_help_cmd, f); } return xit; @@ -162,13 +197,17 @@ _help_invoke(const os_subcommand_t *osc, int argc, const char *argv[]) static void _print_header(FILE *f, const char *hdr, bool *already_done) { - if (*already_done) { + if (already_done && *already_done) { return; } crfprintf_np(f, ""); crfprintf_np(f, "%s:", hdr); - *already_done = true; + crfprintf_np(f, ""); + + if (already_done) { + *already_done = true; + } } #pragma mark Module Routines @@ -199,8 +238,15 @@ _os_subcommand_copy_option_spec_short(const os_subcommand_t *osc, default: __builtin_unreachable(); } + + if (!(osco->osco_flags & OS_SUBCOMMAND_OPTION_FLAG_ENUM)) { + _stoupper(argbuff); + } } else { snprintf(optbuff, sizeof(optbuff), "%s", osco->osco_argument_usage); + if (!(osco->osco_flags & OS_SUBCOMMAND_OPTION_FLAG_ENUM)) { + _stoupper(optbuff); + } } ret = asprintf(&final, "%s%s", optbuff, argbuff); @@ -238,8 +284,15 @@ _os_subcommand_copy_option_spec_long(const os_subcommand_t *osc, default: __builtin_unreachable(); } + + if (!(osco->osco_flags & OS_SUBCOMMAND_OPTION_FLAG_ENUM)) { + _stoupper(argbuff); + } } else { snprintf(optbuff, sizeof(optbuff), "%s", osco->osco_argument_usage); + if (!(osco->osco_flags & OS_SUBCOMMAND_OPTION_FLAG_ENUM)) { + _stoupper(optbuff); + } } ret = asprintf(&final, "%s%s", optbuff, argbuff); @@ -270,7 +323,7 @@ _os_subcommand_copy_option_spec(const os_subcommand_t *osc, if (osco->osco_option) { spec_old = spec; - ret = asprintf(&spec, "%s | -%c", spec, osco->osco_option->val); + ret = asprintf(&spec, "-%c | %s", osco->osco_option->val, spec); if (ret < 0) { os_assert_zero(ret); } @@ -355,13 +408,13 @@ _os_subcommand_copy_usage_line(const os_subcommand_t *osc) // Always include the positional subcommand when printing usage for the // main subcommand. We do not expect it to be specified in a user- // provided main subcommand. + const os_subcommand_option_t *subopt = &_main_positional[0]; char *__os_free usage_line_old = NULL; char *__os_free osco_spec = NULL; usage_line_old = usage_line; - osco_spec = _os_subcommand_copy_option_spec_long(osc, - &_main_positional[0]); + osco_spec = _os_subcommand_copy_option_spec_long(osc, subopt); ret = asprintf(&usage_line, "%s <%s>", usage_line, osco_spec); if (ret < 0) { os_assert_zero(ret); @@ -376,38 +429,36 @@ _os_subcommand_print_option_usage(const os_subcommand_t *osc, const os_subcommand_option_t *osco, FILE *f) { char *__os_free opt_spec = NULL; - ssize_t initpad = -CTL_OUTPUT_OPTARG_PAD; + ssize_t initpad = -CTL_OUTPUT_OPTARG_INDENT; opt_spec = _os_subcommand_copy_option_spec(osc, osco, OS_SUBCOMMAND_OPTION_SPEC_COMBINED); - fprintf(f, " %-24s", opt_spec); + fprintf(f, " %-24s ", opt_spec); // If the usage specifier is long, start the description on the next line. - if (strlen(opt_spec) >= CTL_OUTPUT_OPTARG_PAD) { - initpad = CTL_OUTPUT_OPTARG_PAD; + if (strlen(opt_spec) >= CTL_OUTPUT_OPTARG_OVERFLOW) { + initpad = CTL_OUTPUT_OPTARG_INDENT; crfprintf_np(f, ""); } - wfprintf_np(f, initpad, CTL_OUTPUT_OPTARG_PAD, - CTL_OUTPUT_WIDTH, "%s", + wfprintf_np(f, initpad, CTL_OUTPUT_OPTARG_INDENT, _ttys->ts_cols, "%s", osco->osco_argument_human); } static void _os_subcommand_print_help_line(const os_subcommand_t *osc, FILE *f) { - ssize_t initpad = -CTL_OUTPUT_OPTARG_PAD; + ssize_t initpad = -CTL_OUTPUT_OPTARG_INDENT; - fprintf(f, " %-24s", osc->osc_name); + fprintf(f, " %-24s ", osc->osc_name); // If the usage specifier is long, start the description on the next line. - if (strlen(osc->osc_name) >= CTL_OUTPUT_OPTARG_PAD) { - initpad = CTL_OUTPUT_OPTARG_PAD; + if (strlen(osc->osc_name) >= CTL_OUTPUT_OPTARG_OVERFLOW) { + initpad = CTL_OUTPUT_OPTARG_INDENT; crfprintf_np(f, ""); } - wfprintf_np(f, initpad, CTL_OUTPUT_OPTARG_PAD, - CTL_OUTPUT_WIDTH, "%s", + wfprintf_np(f, initpad, CTL_OUTPUT_OPTARG_INDENT, _ttys->ts_cols, "%s", osc->osc_desc); } @@ -420,35 +471,44 @@ _os_subcommand_print_usage(const os_subcommand_t *osc, FILE *f) bool header_printed = false; usage_line = _os_subcommand_copy_usage_line(osc); - wfprintf_np(f, 0, 0, CTL_OUTPUT_WIDTH, "usage: %s", usage_line); + + wfprintf_np(f, 0, 4, _ttys->ts_cols, "USAGE:"); + crfprintf_np(f, ""); + wfprintf_np(f, 4, 4, _ttys->ts_cols, "%s", usage_line); + + if (osc->osc_long_desc) { + // The long description gets printed in its own paragraph. + _print_header(f, "DESCRIPTION", NULL); + wfprintf_np(f, 4, 4, _ttys->ts_cols, "%s", osc->osc_long_desc); + } else if (osc->osc_desc) { + // The short description gets printed on the same line. + crfprintf_np(f, ""); + wfprintf_np(f, 0, 4, _ttys->ts_cols, "DESCRIPTION: %s", + osc->osc_desc); + } if (osc->osc_required || osc->osc_positional || osc == _main_cmd) { i = 0; OS_SUBCOMMAND_OPTIONS_FOREACH(osco_i, osc, required, i) { - _print_header(f, "required", &header_printed); - - crfprintf_np(f, ""); + _print_header(f, "REQUIRED", &header_printed); _os_subcommand_print_option_usage(osc, osco_i, f); } i = 0; OS_SUBCOMMAND_OPTIONS_FOREACH(osco_i, osc, positional, i) { - _print_header(f, "required", &header_printed); + _print_header(f, "REQUIRED", &header_printed); if (osco_i->osco_flags & OS_SUBCOMMAND_OPTION_FLAG_OPTIONAL_POS) { continue; } - crfprintf_np(f, ""); _os_subcommand_print_option_usage(osc, osco_i, f); } if (osc == _main_cmd && osc != _internal_main_cmd) { // We do not expect the user's main command to specify that a // subcommand must follow, so always defer to ours. - _print_header(f, "required", &header_printed); - - crfprintf_np(f, ""); + _print_header(f, "REQUIRED", &header_printed); _os_subcommand_print_option_usage(osc, &_main_positional[0], f); } } @@ -458,18 +518,14 @@ _os_subcommand_print_usage(const os_subcommand_t *osc, FILE *f) if (osc->osc_optional || osc->osc_positional) { i = 0; OS_SUBCOMMAND_OPTIONS_FOREACH(osco_i, osc, optional, i) { - _print_header(f, "optional", &header_printed); - - crfprintf_np(f, ""); + _print_header(f, "OPTIONAL", &header_printed); _os_subcommand_print_option_usage(osc, osco_i, f); } i = 0; OS_SUBCOMMAND_OPTIONS_FOREACH(osco_i, osc, positional, i) { if (osco_i->osco_flags & OS_SUBCOMMAND_OPTION_FLAG_OPTIONAL_POS) { - _print_header(f, "optional", &header_printed); - - crfprintf_np(f, ""); + _print_header(f, "OPTIONAL", &header_printed); _os_subcommand_print_option_usage(osc, osco_i, f); } } @@ -481,6 +537,10 @@ _os_subcommand_find(const char *name) { const os_subcommand_t **oscip = NULL; + if (!name) { + return _main_cmd; + } + if (strcmp(_help_cmd->osc_name, name) == 0) { return &__help_cmd; } @@ -501,6 +561,59 @@ _os_subcommand_find(const char *name) return NULL; } +static int +_os_subcommand_be_helpful(const os_subcommand_t *osc, + int argc, const char *argv[]) +{ + int res = 0; + + if (osc->osc_flags & OS_SUBCOMMAND_FLAG_HELPFUL) { + if (argc == 1) { + _os_subcommand_print_usage(osc, stdout); + res = 1; + goto __out; + } + } + + if (osc->osc_flags & OS_SUBCOMMAND_FLAG_HELPFUL_FIRST_OPTION) { + if (argc == 2 && (strcmp(argv[1], "help") == 0 || + strcmp(argv[1], "-h") == 0 || + strcmp(argv[1], "-help") == 0 || + strcmp(argv[1], "--help") == 0)) { + _os_subcommand_print_usage(osc, stdout); + res = 1; + goto __out; + } + } + +__out: + return res; +} + +static void +_print_subcommand_list(const os_subcommand_t *osc, FILE *f) +{ + const os_subcommand_t **oscip = NULL; + bool header_printed = false; + + LINKER_SET_FOREACH(oscip, const os_subcommand_t **, + SUBCOMMAND_LINKER_SET) { + const os_subcommand_t *osci = *oscip; + + _print_header(f, "SUBCOMMANDS", &header_printed); + + if ((osci->osc_flags & OS_SUBCOMMAND_FLAG_MAIN) || + (osci->osc_flags & OS_SUBCOMMAND_FLAG_HIDDEN)) { + continue; + } + + _os_subcommand_print_help_line(osci, f); + } + + // Print the help subcommand last. + _os_subcommand_print_help_line(osc, f); +} + #pragma mark API int os_subcommand_main(int argc, const char *argv[], @@ -511,11 +624,7 @@ os_subcommand_main(int argc, const char *argv[], const os_subcommand_t *osc = NULL; const os_subcommand_t **oscip = NULL; - if (argc < 2) { - os_subcommand_fprintf(NULL, stderr, "please provide a subcommand"); - xit = EX_USAGE; - goto __out; - } + _init_column_count(); // Find the main subcommand if any exists. Otherwise we'll just use our pre- // canned main subcommand. @@ -529,6 +638,13 @@ os_subcommand_main(int argc, const char *argv[], osc = NULL; + // See if we just need to print help for the main command. + if (_os_subcommand_be_helpful(_main_cmd, argc, argv)) { + _print_subcommand_list(_help_cmd, stdout); + xit = 0; + goto __out; + } + // Invoke the main subcommand to snarf any global options. Our default // implementation does nothing and just returns 0. xit = _main_cmd->osc_invoke(_main_cmd, argc, argv); @@ -569,19 +685,26 @@ os_subcommand_main(int argc, const char *argv[], } } + if (_os_subcommand_be_helpful(osc, argc, argv)) { + xit = 0; + goto __out; + } + xit = osc->osc_invoke(osc, argc, argv); } else { - os_subcommand_fprintf(NULL, stderr, "unknonwn subcommand: %s", cmdname); + os_subcommand_fprintf(NULL, stderr, "unknown subcommand: %s", cmdname); xit = EX_USAGE; } __out: if (xit == EX_USAGE) { if (!osc) { - osc = _main_cmd; + // If we couldn't find the subcommand, then print the list of known + // subcommands. + _print_subcommand_list(_help_cmd, stderr); + } else { + _os_subcommand_print_usage(osc, stderr); } - - _os_subcommand_print_usage(osc, stderr); } return xit; @@ -605,7 +728,7 @@ os_subcommand_vfprintf(const os_subcommand_t *osc, FILE *f, if (!osc || (osc->osc_flags & OS_SUBCOMMAND_FLAG_MAIN)) { fprintf(f, "%s: ", getprogname()); } else { - fprintf(f, "%s::%s: ", getprogname(), osc->osc_name); + fprintf(f, "%s-%s: ", getprogname(), osc->osc_name); } vcrfprintf_np(f, fmt, ap);