X-Git-Url: https://git.saurik.com/apple/libc.git/blobdiff_plain/507116e319a1470bb0a5040d4d23e5c76955ef97..refs/heads/master:/libdarwin/h/ctl.h?ds=inline diff --git a/libdarwin/h/ctl.h b/libdarwin/h/ctl.h index 450f097..0953d49 100644 --- a/libdarwin/h/ctl.h +++ b/libdarwin/h/ctl.h @@ -32,118 +32,110 @@ * * The user may define each subcommand taken by the utility as: * - * static const os_subcommand_t _foo_cmd = { - * .osc_version = OS_SUBCOMMAND_VERSION, - * .osc_flags = 0, - * .osc_name = "foo", - * .osc_desc = "does a foo", - * .osc_optstring = NULL, - * .osc_options = NULL, - * .osc_info = NULL, - * .osc_invoke = &_foo_invoke, + * static const struct option _template_opts[] = { + * [0] = { + * .name = "bar", + * .has_arg = required_argument, + * .flag = NULL, + * .val = 'f', + * }, { + * .name = "baz", + * .has_arg = optional_argument, + * .flag = NULL, + * .val = 'b', + * }, { + * .name = NULL, + * .has_arg = 0, + * .flag = NULL, + * .val = 0, + * }, * }; - * OS_SUBCOMMAND_REGISTER(_foo_cmd); * - * static const os_subcommand_t _bar_cmd = { - * .osc_version = OS_SUBCOMMAND_VERSION, - * .osc_flags = 0, - * .osc_name = "bar", - * .osc_desc = "bars a foo", - * .osc_optstring = "x:q", - * .osc_options = _bar_opts, - * .osc_info = &_bar_optinfo, - * .osc_invoke = &_bar_invoke, + * static const os_subcommand_option_t _template_required[] = { + * [0] = { + * .osco_template = OS_SUBCOMMAND_OPTION_VERSION, + * .osco_flags = 0, + * .osco_option = &_template_opts[0], + * .osco_argument_usage = "thing-to-bar", + * .osco_argument_human = "The thing to bar. May be specified as a " + * "bar that has a baz. This baz should have a shnaz.", + * }, + * OS_SUBCOMMAND_OPTION_TERMINATOR, * }; - * OS_SUBCOMMAND_REGISTER(_bar_cmd); - * }; * - * Where the "bar" subcommand's option information is returned by the routine: - * - * static void - * _bar_optinfo(const os_subcommand_t *osc, - * os_subcommand_optarg_format_t format, const struct option *opt, - * os_subcommand_option_t *scopt) - * { - * switch (format) { - * case OS_SUBCOMMAND_OPTARG_USAGE: - * switch (opt->val) { - * case 'x': - * scopt->osco_flags |= OS_SUBCOMMAND_OPTION_FLAG_OPTIONAL; - * scopt->osco_argdesc = "x-argument"; - * break; - * case 'q': - * scopt->osco_argdesc = "q-argument"; - * break; - * default: - * __builtin_unreachable(); - * } - * break; - * case OS_SUBCOMMAND_OPTARG_HUMAN: - * switch (opt->val) { - * case 'x': - * scopt->osco_flags |= OS_SUBCOMMAND_OPTION_FLAG_OPTIONAL; - * scopt->osco_argdesc = "argument describing x"; - * break; - * case 'q': - * scopt->osco_argdesc = "Lorem ipsum dolor sit amet, consectetur " - * "adipiscing elit. Nullam a maximus lectus. Curabitur ornare " - * "convallis turpis, in porttitor augue tempus laoreet. Maecenas " - * "auctor mauris tortor, et tempor libero maximus id. Donec ac " - * "nunc et elit sagittis commodo. Donec tincidunt libero vehicula " - * "ex eleifend sagittis. Suspendisse consectetur cursus elit. " - * "Proin neque metus, commodo id rhoncus eu, cursus hendrerit ex. " - * "Etiam in fringilla nulla, vitae mollis eros."; - * break; - * default: - * __builtin_unreachable(); - * } - * break; + * static const os_subcommand_option_t _template_optional[] = { + * [0] = { + * .osco_template = OS_SUBCOMMAND_OPTION_VERSION, + * .osco_flags = 0, + * .osco_option = &_template_opts[1], + * .osco_argument_usage = "thing-to-baz", + * .osco_argument_human = "The baz of which to propagate a foo.", + * }, + * OS_SUBCOMMAND_OPTION_TERMINATOR, + * }; + * + * static const os_subcommand_option_t _template_positional[] = { + * [0] = { + * .osco_template = OS_SUBCOMMAND_OPTION_VERSION, + * .osco_flags = 0, + * .osco_option = NULL, + * .osco_argument_usage = "positional-baz", + * .osco_argument_human = "A baz specified by position.", + * }, + * OS_SUBCOMMAND_OPTION_TERMINATOR, + * }; + * + * static const os_subcommand_t _template_cmd = { + * .osc_template = OS_SUBCOMMAND_VERSION, + * .osc_flags = 0, + * .osc_name = "foo", + * .osc_desc = "foo a bar or maybe baz", + * .osc_optstring = "f:b:", + * .osc_options = _foo_opts, + * .osc_required = _foo_required, + * .osc_optional = _foo_optional, + * .osc_positional = _template_positional, + * .osc_invoke = &_foo_invoke, * } - * } + * OS_SUBCOMMAND_REGISTER(_foo_cmd); + * }; * * When {@link os_subcommand_main} is called, the tool's "help" subcommand will * display approximately the following: * * $ tool help - * usage: playground [...] | help [subcommand] + * usage: tool * * subcommands: - * foo does a foo - * bar bars a foo + * foo foo a bar or maybe baz + * help Prints helpful information * * $ tool help foo - * usage: tool foo - * - * $ tool help bar - * usage: tool bar [--xarg] --qarg[=q-argument] - * --xarg argument describing x - * --qarg[=q-argument] Lorem ipsum dolor sit amet, consectetur - * adipiscing elit. Nullam a maximus lectus. - * Curabitur ornare convallis turpis, in porttitor - * augue tempus laoreet. Maecenas auctor mauris - * tortor, et tempor libero maximus id. Donec ac - * nunc et elit sagittis commodo. Donec tincidunt - * libero vehicula ex eleifend sagittis. Suspendisse - * consectetur cursus elit. Proin neque metus, - * commodo id rhoncus eu, cursus hendrerit ex. Etiam - * in fringilla nulla, vitae mollis eros. + * usage: tool foo [options] --bar= + * + * required options: + * --bar= The thing to bar. May be specified as a bar that + * has a baz. This baz should have a shnaz. + * + * positional-baz A baz specified by position. + * + * optional options: + * --baz[=thing-to-baz] The baz of which to propagate a foo. */ #ifndef __DARWIN_CTL_H #define __DARWIN_CTL_H #include #include - -#if DARWIN_TAPI -#define LINKER_SET_ENTRY(_x, _y) -#else #include -#endif - #include #include #include +#if DARWIN_TAPI +#include "tapi.h" +#endif + __BEGIN_DECLS; /*! @@ -169,31 +161,6 @@ typedef struct _os_subcommand os_subcommand_t; */ #define OS_SUBCOMMAND_OPTION_VERSION ((os_struct_version_t)0) -/*! - * @typedef os_subcommand_optarg_format_t - * A type describing a usage format for the argument taken by an option. - * - * @const OS_SUBCOMMAND_OPTION_USAGE - * The short-form name of the argument given to the option. For example, if the - * subcommand takes a "--file" option with a required argument, this might be - * "file-path" and will be displayed as - * - * --file= - * - * @const OS_SUBCOMMAND_OPTION_HUMAN - * The long-form description of the argument given to the option. Extending the - * above example, this might be "The path to a file to take as input. This path - * must be absolute; relative paths are not supported." and will be displayed as - * - * --file The path to a file to take as input. This path must be - * absolute; relative paths are not supported. - */ -DARWIN_API_AVAILABLE_20181020 -OS_CLOSED_ENUM(os_subcommand_optarg_format, uint64_t, - OS_SUBCOMMAND_OPTARG_USAGE, - OS_SUBCOMMAND_OPTARG_HUMAN, -); - /*! * @typedef os_subcommand_option_flags_t * Flags describing an option for a subcommand. @@ -201,66 +168,108 @@ OS_CLOSED_ENUM(os_subcommand_optarg_format, uint64_t, * @const OS_SUBCOMMAND_OPTION_FLAG_INIT * No flags set. This value is suitable for initialization purposes. * - * @const OS_SUBCOMMAND_OPTION_FLAG_OPTIONAL - * The option does not need to be present in the subcommand invocation. By - * default, options are considered required. + * @const OS_SUBCOMMAND_OPTION_FLAG_TERMINATOR + * The option terminates an array of {@link os_subcommand_option_t} structures + * and does not contain any useful information. + * + * @const OS_SUBCOMMAND_OPTION_FLAG_OPTIONAL_POS + * The option is a positional option (that is, not identified by a long or short + * flag) and is not required for the subcommand to execute successfully. + * + * @const OS_SUBCOMMAND_OPTION_FLAG_ENUM + * The option has an explicitly-defined list of valid inputs that are enumerated + * in the option's {@link osco_argument_usage} field. When printing usage + * information for this option, the implementation will not transform the string + * in this field in any way. + * + * For example, an option named "--stream" might have three valid inputs: + * "stdin", "stdout", and "stderr", and the usage string may be specified as + * + * "stdin|stdout|stderr" + * + * Without this flag, the implementation would print this string as a parameter + * name, i.e. in all caps: + * + * "" + * + * With this flag, the string will be printed as it is given. */ DARWIN_API_AVAILABLE_20181020 OS_CLOSED_ENUM(os_subcommand_option_flags, uint64_t, OS_SUBCOMMAND_OPTION_FLAG_INIT = 0, - OS_SUBCOMMAND_OPTION_FLAG_OPTIONAL = (1 << 0), + OS_SUBCOMMAND_OPTION_FLAG_TERMINATOR = (1 << 0), + OS_SUBCOMMAND_OPTION_FLAG_OPTIONAL_POS = (1 << 1), + OS_SUBCOMMAND_OPTION_FLAG_ENUM = (1 << 2), ); /*! * @typedef os_subcommand_option_t * A structure describing human-readable information about a particular option * taken by a subcommand. This structure is to be returned when the - * implementation queries about a command's options individually. This is done - * when the implementation is synthesizing a usage string. + * implementation invokes a {@link os_subcommand_option_info_t} function to + * query about a command's options individually. This is done when the + * implementation is synthesizing a usage string. * * @field osco_version * The version of the structure. Initialize to * {@link OS_SUBCOMMAND_OPTION_VERSION}. * * @field osco_flags - * On return from a {@link os_subcommand_option_info_t} function, a set of flags - * describing information about the option. + * A set of flags describing information about the option. + * + * @field osco_option + * A pointer to the option structure ingested by getopt_long(3) which + * corresponds to this option. + * + * @field osco_argument_usage + * The short-form name of the argument given to the option, appropriate for + * display in a usage specifier. For example, if the subcommand takes a "--file" + * option with a required argument, this might be the string "FILE-PATH", and + * the resulting usage specifier would be + * + * --file= * - * @field osco_argdesc - * On return from a {@link os_subcommand_option_info_t} function, this should - * point to a constant string describing the argument to the option. + * @field osco_argument_human + * The long-form description of the argument given to the option. Extending the + * above example, this might be the string "The path to a file to take as input. + * This path must be absolute; relative paths are not supported." and the + * resulting usage specifier would be + * + * --file= The path to a file to take as input. This path must be + * absolute; relative paths are not supported. */ -DARWIN_API_AVAILABLE_20181020 +DARWIN_API_AVAILABLE_20191015 typedef struct _os_subcommand_option { const os_struct_version_t osco_version; os_subcommand_option_flags_t osco_flags; - const char *osco_argdesc; + const struct option *osco_option; + const char *osco_argument_usage; + const char *osco_argument_human; } os_subcommand_option_t; /*! - * @typedef os_subcommand_option_info_t - * A type describing a function which returns option information. - * - * @param osc - * The subcommand to which the option belongs. - * - * @param format - * The format of usage information required. - * - * @param opt - * A pointer to the option structure for which to retrieve information. - * - * @param scopt - * A pointer to a subcommand option structure to be populated with information - * pertaining to the option. When passed to the callee, this structure is zero- - * filled. + * @const OS_SUBCOMMAND_OPTION_TERMINATOR + * A convenience terminator for an array of {@link os_subcommand_option_t} + * structures. */ -DARWIN_API_AVAILABLE_20181020 -typedef void (*os_subcommand_option_info_t)( - const os_subcommand_t *osc, - os_subcommand_optarg_format_t format, - const struct option *opt, - os_subcommand_option_t *scopt); +#define OS_SUBCOMMAND_OPTION_TERMINATOR (os_subcommand_option_t){ \ + .osco_version = OS_SUBCOMMAND_OPTION_VERSION, \ + .osco_flags = OS_SUBCOMMAND_OPTION_FLAG_TERMINATOR, \ + .osco_option = NULL, \ + .osco_argument_usage = NULL, \ + .osco_argument_human = NULL, \ +} + +/*! +* @const OS_SUBCOMMAND_GETOPT_TERMINATOR +* A convenience terminator for an array of getopt(3) option structures. +*/ +#define OS_SUBCOMMAND_GETOPT_TERMINATOR (struct option){ \ + .name = NULL, \ + .has_arg = 0, \ + .flag = NULL, \ + .val = 0, \ +} /*! * @const OS_SUBCOMMAND_VERSION @@ -288,6 +297,42 @@ typedef void (*os_subcommand_option_info_t)( * * @const OS_SUBCOMMAND_FLAG_HIDDEN * This subcommand should not be displayed in the list of subcommands. + * + * @const OS_SUBCOMMAND_FLAG_MAIN + * This subcommand is the "main" subcommand. Designating a main subcommand + * allows the program to specify and parse global options using an + * {@link os_subcommand_t} object and {@link os_subcommand_main}. + * + * This flag implies the behavior of {@link OS_SUBCOMMAND_FLAG_HIDDEN}. + * + * If the program specifies a main subcommand, that subcommand's invocation + * routine is unconditionally called before calling the subcommand invocation, + * if the user provided a subcommand. The invocation function for the main + * subcommand should not exit on success and should instead return 0. + * + * If multiple subcommands in the same program set + * {@link OS_SUBCOMMAND_FLAG_MAIN}, the implementation's behavior is undefined. + * + * @const OS_SUBCOMMAND_FLAG_HELPFUL + * When invoked with no arguments, this subcommand will print usage information + * to stdout and exit with status zero. + * + * @const OS_SUBCOMMAND_FLAG_HELPFUL_FIRST_OPTION + * Allow the implementation to detect whether the user is attempting to print + * usage information for the given subcommand. If the implementation concludes + * that the user is seeking help, it will print the subcommand's usage + * information to stdout and exit with status 0. + * + * The implementation will conclude that the user is seeking help if + * + * - only one argument is provided to the subcommand, and + * + * any of the following + * + * - the argument is "-h" + * - the argument is "-help" + * - the argument is "--help" + * - the argument is "help" */ DARWIN_API_AVAILABLE_20181020 OS_CLOSED_OPTIONS(os_subcommand_flags, uint64_t, @@ -295,6 +340,9 @@ OS_CLOSED_OPTIONS(os_subcommand_flags, uint64_t, OS_SUBCOMMAND_FLAG_REQUIRE_ROOT = (1 << 0), OS_SUBCOMMAND_FLAG_TTYONLY = (1 << 1), OS_SUBCOMMAND_FLAG_HIDDEN = (1 << 2), + OS_SUBCOMMAND_FLAG_MAIN = (1 << 3), + OS_SUBCOMMAND_FLAG_HELPFUL = (1 << 4), + OS_SUBCOMMAND_FLAG_HELPFUL_FIRST_OPTION = (1 << 5), ); /*! @@ -312,17 +360,21 @@ OS_CLOSED_OPTIONS(os_subcommand_flags, uint64_t, * subcommand. * * @result - * An exit code, preferably from sysexits(3). Note that exit codes should not - * intersect with POSIX error codes from errno.h (cf. intro(2)). + * An exit code, preferably from sysexits(3). Do not return a POSIX error code + * directly from this routine. * * @discussion - * You may exit directly from within the routine if you wish. + * You may exit directly on success or failure from this routine if desired. If + * the routine is the invocation for a main subcommand, then on success, the + * routine should return zero to the caller rather than exiting so that the + * implementation may continue parsing the command line arguments. */ DARWIN_API_AVAILABLE_20181020 typedef int (*os_subcommand_invoke_t)( - const os_subcommand_t *osc, - int argc, - const char *argv[]); + const os_subcommand_t *osc, + int argc, + const char *argv[] +); /*! * @struct os_subcommand_t @@ -342,6 +394,11 @@ typedef int (*os_subcommand_invoke_t)( * A brief description of the subcommand. This description will be displayed * next to the subcommand when the user lists all subcommands. * + * @field osc_long_desc + * A long description of the subcommand. This description will be displayed + * when the user invokes help on the subcommand. If it's NULL the brief + * description from osc_desc will be displayed. + * * @field osc_optstring * The option string associated with the subcommand. The implementation does not * recognize this string directly; the intent of storing it here is to provide a @@ -361,7 +418,34 @@ typedef int (*os_subcommand_invoke_t)( * * @field osc_options * A pointer to an array of option structures describing the long options - * recognized by the subcommand (cf. getopt_long(3)). + * recognized by the subcommand. This array must be terminated by a NULL entry + * as expected by getopt_long(3). + * + * @field osc_required + * A pointer to an array of subcommand option descriptors. The options described + * in this array are required for the subcommand to execute successfully. This + * array should be terminated with {@link OS_SUBCOMMAND_OPTION_TERMINATOR}. + * + * This array is consulted when printing usage information. + * + * @field osc_optional + * A pointer to an array of subcommand option descriptors. The options described + * in this array are parsed by the subcommand but not required for it to execute + * successfully. This array should be terminated with + * {@link OS_SUBCOMMAND_OPTION_TERMINATOR}. + * + * This array is consulted when printing usage information. + * + * @field osc_positional + * A pointer to an array of subcommand option descriptors. The options described + * in this array are expected to follow the required and optional arguments in + * the command line invocation, in the order given in this array. This array + * should be terminated with {@link OS_SUBCOMMAND_OPTION_TERMINATOR}. + * + * These options are expected to have their {@link osco_option} fields set to + * NULL. + * + * This array is consulted when printing usage information. * * @field osc_info * A pointer to a function which returns information about the subcommand's @@ -370,7 +454,7 @@ typedef int (*os_subcommand_invoke_t)( * @field osc_invoke * The implementation for the subcommand. */ -DARWIN_API_AVAILABLE_20181020 +DARWIN_API_AVAILABLE_20200401 struct _os_subcommand { const os_struct_version_t osc_version; const os_subcommand_flags_t osc_flags; @@ -378,10 +462,24 @@ struct _os_subcommand { const char *const osc_desc; const char *osc_optstring; const struct option *osc_options; - const os_subcommand_option_info_t osc_info; + const os_subcommand_option_t *osc_required; + const os_subcommand_option_t *osc_optional; + const os_subcommand_option_t *osc_positional; const os_subcommand_invoke_t osc_invoke; + const char *const osc_long_desc; }; +/*! + * @typedef os_subcommand_main_flags_t + * Flags modifying the behavior of {@link os_subcommand_main}. + * + * @const OS_SUBCOMMAND_MAIN_FLAG_INIT + * No flags set. This value is suitable for initialization purposes. + */ +OS_CLOSED_OPTIONS(os_subcommand_main_flags, uint64_t, + OS_SUBCOMMAND_MAIN_FLAG_INIT, +); + /*! * @function os_subcommand_main * Dispatches the subcommand appropriate for the given arguments. All @@ -394,6 +492,9 @@ struct _os_subcommand { * @param argv * The argument vector supplied to main(). * + * @param flags + * Flags modifying the behavior of the implementation. + * * @result * The exit status from the subcommand's invocation function or an exit status * from the implementation indicating that the subcommand could not be @@ -420,10 +521,70 @@ struct _os_subcommand { * * This routine implicitly implements a "help" subcommand. */ -DARWIN_API_AVAILABLE_20181020 +DARWIN_API_AVAILABLE_20191015 OS_EXPORT OS_WARN_RESULT OS_NONNULL2 int -os_subcommand_main(int argc, const char *argv[]); +os_subcommand_main(int argc, const char *argv[], + os_subcommand_main_flags_t flags); + +/*! + * @function os_subcommand_fprintf + * Prints a message in the context of a subcommand to the given output stream. + * + * @param osc + * The subcommand which represents the context of the message. + * + * @param f + * The stream to which the message shall be printed. If NULL, will be printed to + * stderr(4). + * + * @param fmt + * A printf(3)-style format string. + * + * @param ... + * The arguments corresponding to {@link fmt}. + * + * @discussion + * This routine provides a uniform method for printing messages in the context + * of a subcommand. It will ensure that the message is prefixed appropriately + * (e.g. with the program name and/or subcommand name). + * + * This routine should be used for printing messages intended for humans to + * read; the implementation makes no guarantees about the output format's + * stability. If any output is intended to be machine-parseable, it should be + * written with fprintf(3) et al. + */ +DARWIN_API_AVAILABLE_20191015 +OS_EXPORT OS_NONNULL3 OS_FORMAT_PRINTF(3, 4) +void +os_subcommand_fprintf(const os_subcommand_t *osc, FILE *f, + const char *fmt, ...); + +/*! + * @function os_subcommand_vfprintf + * Prints a message in the context of a subcommand to the given output stream. + * + * @param osc + * The subcommand which represents the context of the message. + * + * @param f + * The stream to which the message shall be printed. If NULL, will be printed to + * stderr(4). + * + * @param fmt + * A printf(3)-style format string. + * + * @param ap + * The argument list corresponding to {@link fmt}. + * + * @discussion + * See discussion for {@link os_subcommand_fprintf}. + */ +DARWIN_API_AVAILABLE_20191015 +OS_EXPORT OS_NONNULL3 +void +os_subcommand_vfprintf(const os_subcommand_t *osc, FILE *f, + const char *fmt, va_list ap); __END_DECLS;