diff options
author | Rich Salz <rsalz@akamai.com> | 2021-05-01 13:11:49 +0200 |
---|---|---|
committer | Dr. David von Oheimb <dev@ddvo.net> | 2021-05-01 13:11:49 +0200 |
commit | f4407385f58242dcc6ae95a60c2a3dc8782bee42 (patch) | |
tree | 7b89ff8ead447d4d0bb3812e5d9c1fc36f32d526 | |
parent | c0f4400c4051cc26fbe385b6af9fc67e7c66dbdd (diff) |
APPS: Document the core of the opt_ API
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: David von Oheimb <david.von.oheimb@siemens.com>
(Merged from https://github.com/openssl/openssl/pull/14995)
-rw-r--r-- | apps/include/fmt.h | 3 | ||||
-rw-r--r-- | apps/include/opt.h | 3 | ||||
-rw-r--r-- | apps/lib/opt.c | 6 | ||||
-rw-r--r-- | doc/internal/man3/OPTIONS.pod | 301 |
4 files changed, 306 insertions, 7 deletions
diff --git a/apps/include/fmt.h b/apps/include/fmt.h index c9edd4707e..f235899bf8 100644 --- a/apps/include/fmt.h +++ b/apps/include/fmt.h @@ -17,7 +17,8 @@ #ifndef OSSL_APPS_FMT_H #define OSSL_APPS_FMT_H -/* On some platforms, it's important to distinguish between text and binary +/* + * On some platforms, it's important to distinguish between text and binary * files. On some, there might even be specific file formats for different * contents. The FORMAT_xxx macros are meant to express an intent with the * file being read or created. diff --git a/apps/include/opt.h b/apps/include/opt.h index f9ac5accae..f22e9af05e 100644 --- a/apps/include/opt.h +++ b/apps/include/opt.h @@ -349,7 +349,6 @@ char *opt_init(int ac, char **av, const OPTIONS * o); int opt_next(void); void opt_begin(void); int opt_format(const char *s, unsigned long flags, int *result); -const char *format2str(int format); int opt_int(const char *arg, int *result); int opt_int_arg(void); int opt_ulong(const char *arg, unsigned long *result); @@ -381,8 +380,6 @@ int opt_verify(int i, X509_VERIFY_PARAM *vpm); int opt_rand(int i); int opt_provider(int i); void opt_help(const OPTIONS * list); -void opt_print(const OPTIONS * opt, int doingparams, int width); -int opt_format_error(const char *s, unsigned long flags); void print_format_error(int format, unsigned long flags); int opt_isdir(const char *name); int opt_printf_stderr(const char *fmt, ...); diff --git a/apps/lib/opt.c b/apps/lib/opt.c index 83ae28cdc1..a6b6f7ce4f 100644 --- a/apps/lib/opt.c +++ b/apps/lib/opt.c @@ -227,7 +227,7 @@ static OPT_PAIR formats[] = { }; /* Print an error message about a failed format parse. */ -int opt_format_error(const char *s, unsigned long flags) +static int opt_format_error(const char *s, unsigned long flags) { OPT_PAIR *ap; @@ -325,7 +325,7 @@ int opt_format(const char *s, unsigned long flags, int *result) } /* Return string representing the given format. */ -const char *format2str(int format) +static const char *format2str(int format) { switch (format) { default: @@ -973,7 +973,7 @@ static const char *valtype2param(const OPTIONS *o) return "parm"; } -void opt_print(const OPTIONS *o, int doingparams, int width) +static void opt_print(const OPTIONS *o, int doingparams, int width) { const char* help; char start[80 + 1]; diff --git a/doc/internal/man3/OPTIONS.pod b/doc/internal/man3/OPTIONS.pod new file mode 100644 index 0000000000..3c0fcdaf80 --- /dev/null +++ b/doc/internal/man3/OPTIONS.pod @@ -0,0 +1,301 @@ +=pod + +=head1 NAME + +OPTIONS, OPT_PAIR, +opt_progname, opt_appname, opt_getprog, opt_init, opt_format, +opt_int, opt_long, opt_imax, opt_umax, opt_ulong, opt_pair, +opt_string, opt_cipher, opt_md, opt_next, opt_arg, opt_flag, opt_unknown, +opt_num_rest, opt_rest, opt_help, opt_isdir +- Option parsing for commands and tests + +=head1 SYNOPSIS + + #include "opt.h" + + typedef struct { ... } OPTIONS; + typedef struct { ... } OPT_PAIR; + + char *opt_progname(const char *argv0); + char *opt_appname(const char *arg0); + char *opt_getprog(void); + char *opt_init(int argc, char **argv, const OPTIONS *o); + + int opt_next(void); + void opt_help(const OPTIONS *list); + char *opt_arg(void); + char *opt_flag(void); + char *opt_unknown(void); + int opt_cipher(const char *name, EVP_CIPHER **cipherp); + int opt_md(const char *name, EVP_MD **mdp); + + int opt_int(const char *value, int *result); + int opt_long(const char *value, long *result); + int opt_imax(const char *value, intmax_t *result); + int opt_umax(const char *value, uintmax_t *result); + int opt_ulong(const char *value, unsigned long *result); + + int opt_isdir(const char *name); + + int opt_format(const char *s, unsigned long flags, int *result); + int opt_string(const char *name, const char **options); + int opt_pair(const char *name, const OPT_PAIR* pairs, int *result); + + int opt_num_rest(void); + char **opt_rest(void); + +=head1 DESCRIPTION + +The functions on this page provide a common set of option-parsing for +the OpenSSL command and the internal test programs. +It is intended to be used like the standard getopt(3) routine, except +that multi-character flag names are supported, and a variety of parsing +and other utility functions are also provided. + +Programs that use this should make sure to set the appropriate C<-I> +flag. + +These routines expect a global B<BIO> named B<bio_err> to point to +the equivalent of B<stderr>. This is already done in the OpenSSL +application. + +=head2 Data Types + +Each program should define, near the main() routine, an enumeration +that is the set of options the program accepts. For example: + + typedef enum OPTION_choice { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, + OPT_YES, OPT_NAME, OPT_COUNT, OPT_OFILE, + ... + } OPTION_CHOICE; + +The first two lines must appear exactly as shown. In addition to +defining symbolic names for the constants that opt_next() returns, +it also helps guarantee that every command has a C<-help> option. +The third line is a sample +set of flags, and the closing C<typedef> name is used for error-checking +as discussed below. +By declaring the variable as an C<OPTION_CHOICE>, with the right warning +flags, the compiler could check that all specified options are handled. + +The B<OPTIONS> C<typedef> specifies an option: what type of argument +it takes (if any), and an optional "help" string. It is a C<struct> +containing these fields: + + const char *name; + int retval; + int valtype; + const char *helpstr; + +The B<name> is the name of the option that the user would type. Options +are words prefaced with a minus sign. If the user uses two minus signs, +this is also accepted for compatibility with other GNU software. Some +names are special, and are described below. + +The B<retval> is the value to return if the option is found. It should be +one of the choices in the enumeration above. + +The B<valtype> defines what the option's parameter must be. It should +be chosen from the following set: + + \0 No value + '-' No value + 's' A text string + '/' A directory + '<' Name of file to open for input + '>' Name of file to open for output + 'n' A signed number that fits in the C<int> type + 'p' A positive number that fits in the C<int> type + 'N' A nonnegative number that fits in the C<int> type + 'M' A signed number that fits in the C<intmax_t> type + 'U' An unsigned number that fits in the C<uintmax_t> type + 'l' A signed number that fits in the C<long> type + 'u' An unsigned number that fits in the C<unsigned long> type + 'c' File in PEM, DER, or S/MIME format + 'F' A file in PEM or DER format + 'E' Like 'F' but also allows ENGINE + 'f' Any file format + +The B<helpstr> is what to display when the user uses the help option, +which should be C<"help">. + +A program should declare its options right after the enumeration, +and should follow the ordering of the enumeration as this helps +readability and maintainability: + + static OPTIONS my_options[] = { + {"help", OPT_HELP, '-', "Display this summary"}, + {"yes", OPT_YES, '-', "Print an affirmative reply"}, + {"count", OPT_COUNT, 'p', "Repeat count"}, + {"output" OPT_OFILE, '>', "Output file; default is stdout"}, + {NULL} + }; + +Note that the B<OPT_HELP> option is explicitly listed, and the list ends with +an entry of all-null's. The other two special options, B<OPT_ERR> and B<OPT_EOF> +should not appear in the array. + +If the help string is too long to fit into one line, it may be continued +on multiple lines; each entry should use B<OPT_MORE_STR>, like this: + + {"output" OPT_OFILE, '>', "Output file; default is stdout"}, + {OPT_MORE_STR, 0, 0, + "This flag is not really needed on Unix systems"}, + {OPT_MORE_STR, 0, 0, + "(Unix and descendents for ths win!)"} + +Each subsequent line will be indented the correct amount. + +By default, the help display will include a standard prolog: + + Usage: PROGRAM [options] + Valid options are: + ...detailed list of options... + +Sometimes there are parameters that should appear in the synopsis. +Use B<OPT_HELP_STR> as the first entry in your array: + + {OPT_HELP_STR, 1, '-', Usage: %s [options] [text...]\n"} + +The B<retval> and B<valtype> are ignored, and the B<helpstr> should +follow the general construction as shown. The C<%s> will get the program +name. + +If a command has a large set of options, it can be useful to break them +into sections. Use the macro B<OPT_SECTION> or B<OPT_SECTION_STR> +to indicate this. The two lines below are equivalent: + + OPT_SECTION("Validation"), + {OPT_SECTION_STR, 1, '-', "Validation options:\n"}, + +In addition to providing help about options, you can provide a description +of the parameters a command takes. These should appear at the end of +the options and are indicated by using B<OPT_PARAM_STR> or the +B<OPT_PARAMETERS> macro: + + OPT_PARAMETERS() + {OPT_PARAM_STR, 1, '-', "Parameters:\n"} + +Every "option" after after this should contain the parameter and +the help string: + + {"text", 0, 0, "Words to display (optional)"}, + +=head2 Functions + +The opt_init() function takes the "argc, argv" arguments given to main() and +a pointer to the list of options. It returns the simple program +name, as defined by opt_progname(). + +The opt_progname() function takes the full pathname, C<argv[0]>, and returns +the simple short name of the executable, to be used for error messages and +the like. The opt_appname() functions takes the "application" name (such +as the specific command from L<openssl(1)> and appends it to the program +name. This function should only be called once. Once set, opt_getprog() +also returns the value. + +Once opt_init() has been called, opt_next() can be called in a loop to +fetch each option in turn. It returns -1, or OPT_EOF when the +end of arguments has been reached. This is typically done like this: + + prog = opt_init(argc, argv, my_options); + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_EOF: + case OPT_ERR: + opthelp: + fprintf(stderr, "%s: Use -help for summary\n", prog); + exit(1); + case OPT_HELP: + opt_help(my_options); + exit(0); + ...other options... + } + } + +The opt_help() function takes a list of option definitions and prints a +nicely-formatted output. + +Within the option parsing loop, opt_flag() returns the option, +without any leading hyphens. The opt_arg() function returns +the option's value, if there is one. + +In an option list, there can be at most one option with the empty string. +This is a "wildcard" or "unknown" option. For example, it allows an +option to be be taken as digest algorithm, like C<-sha1>. The +function opt_cipher() takes the specified I<name> and fills in +the cipher into I<cipherp>. The function opt_md() does the same +thing for message digest. + +There are a several useful functions for parsing numbers. These are +opt_int(), opt_long(), opt_ulong(), opt_imax(), and opt_umax(). They all +take C<0x> to mean hexadecimal and C<0> to mean octal, and will do the +necessary range-checking. They return 1 if successful and fill in the +C<result> pointer with the value, or 0 on error. Note that opt_next() +will also do range-check on the argument if the appropriate B<valtype> +field is specified for the option. This means that error-checking inside +the C<switch> C<case> can often be elided. + +The opt_isdir() function returns 1 if the specified I<name> is +a directory, or 0 if not. The opt_format() function takes a string value, +such as used with the B<-informat> or similar option, and fills +the value from the constants in F<fmt.h> file. + +The opt_string() function checks that I<name> appears in the +NULL-terminated array of strings. It returns 1 if found, +or prints a diagnostic and returns 0 if not. + +The opt_pair() function takes a list of I<pairs>, each of which +has a text name and an integer. The specified I<name> is +found on the list, it puts the index in I<*result>, and returns +1. If not found, it returns 0. + +After processing all the options, the opt_num_rest() returns what is +left, and opt_rest() returns a pointer to the first non-option. +If there were no parameters, it will point to the NULL that is +at the end of the standard B<argv> array. + +=head2 Common Options + +There are a few groups of options that are common to many OpenSSL programs. +These are handled with sets of macros that define common option names +and common code to handle them. The categories are identified by a +letter: + + V Validation + X Extended certificate + S TLS/SSL + R Random state + +The B<OPT_x_ENUM> macro is used to define the numeration values, where B<x> +is one of the letters above. The B<OPT_x_OPTIONS> macro is used to +list the set of common options, and the B<OPT_x_CASES> is used in +the C<switch> statement. + +The common options are used throughout the sources for the OpenSSL commands. +They are also used with common descriptions when generating the +manpages, in the file F<doc/perlvars.pm>, which follow a similar naming +convention. + +=head1 RETURN VALUES + +Detailed above. + +=head1 EXAMPLES + +The best examples can be found in sources for the commands in the F<apps> +directory of the source tree. +A notable exception is F<apps/cmp.c> which uses this API, but does +things very differently. + +=head1 COPYRIGHT + +Copyright 2021 The OpenSSL Project Authors. All Rights Reserved. + +Licensed under the Apache License 2.0 (the "License"). You may not use this +file except in compliance with the License. You can obtain a copy in the file +LICENSE in the source distribution or at +L<https://www.openssl.org/source/license.html>. + +=cut |