diff options
Diffstat (limited to 'include/iprt/getopt.h')
-rw-r--r-- | include/iprt/getopt.h | 538 |
1 files changed, 538 insertions, 0 deletions
diff --git a/include/iprt/getopt.h b/include/iprt/getopt.h new file mode 100644 index 00000000..b801f3cf --- /dev/null +++ b/include/iprt/getopt.h @@ -0,0 +1,538 @@ +/** @file + * IPRT - Command Line Parsing. + */ + +/* + * Copyright (C) 2007-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + +#ifndef IPRT_INCLUDED_getopt_h +#define IPRT_INCLUDED_getopt_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + + +#include <iprt/cdefs.h> +#include <iprt/types.h> +#include <iprt/errcore.h> /* for VINF_GETOPT_NOT_OPTION */ + +RT_C_DECLS_BEGIN + +/** @defgroup grp_rt_getopt RTGetOpt - Command Line Parsing + * @ingroup grp_rt + * @{ + */ + +/** @name Values for RTGETOPTDEF::fFlags and the fFlags parameter of + * RTGetOptFetchValue. + * + * @remarks When neither of the RTGETOPT_FLAG_HEX, RTGETOPT_FLAG_OCT and RTGETOPT_FLAG_DEC + * flags are specified with a integer value format, RTGetOpt will default to + * decimal but recognize the 0x prefix when present. RTGetOpt will not look for + * for the octal prefix (0). + * @{ */ +/** Requires no extra argument. + * (Can be assumed to be 0 for ever.) */ +#define RTGETOPT_REQ_NOTHING 0 +/** A value is required or error will be returned. */ +#define RTGETOPT_REQ_STRING 1 +/** The value must be a valid signed 8-bit integer or an error will be returned. */ +#define RTGETOPT_REQ_INT8 2 +/** The value must be a valid unsigned 8-bit integer or an error will be returned. */ +#define RTGETOPT_REQ_UINT8 3 +/** The value must be a valid signed 16-bit integer or an error will be returned. */ +#define RTGETOPT_REQ_INT16 4 +/** The value must be a valid unsigned 16-bit integer or an error will be returned. */ +#define RTGETOPT_REQ_UINT16 5 +/** The value must be a valid signed 32-bit integer or an error will be returned. */ +#define RTGETOPT_REQ_INT32 6 +/** The value must be a valid unsigned 32-bit integer or an error will be returned. */ +#define RTGETOPT_REQ_UINT32 7 +/** The value must be a valid signed 64-bit integer or an error will be returned. */ +#define RTGETOPT_REQ_INT64 8 +/** The value must be a valid unsigned 64-bit integer or an error will be returned. */ +#define RTGETOPT_REQ_UINT64 9 +/** The value must be a valid IPv4 address. + * (Not a name, but 4 values in the 0..255 range with dots separating them). */ +#define RTGETOPT_REQ_IPV4ADDR 10 +/** The value must be a valid IPv4 CIDR. + * As with RTGETOPT_REQ_IPV4ADDR, no name. + */ +#define RTGETOPT_REQ_IPV4CIDR 11 +#if 0 +/* take placers */ +/** The value must be a valid IPv6 addr + * @todo: Add types and parsing routines in (iprt/net.h) + */ +#define RTGETOPT_REQ_IPV6ADDR 12 +/** The value must be a valid IPv6 CIDR + * @todo: Add types and parsing routines in (iprt/net.h) + */ +#define RTGETOPT_REQ_IPV6CIDR 13 +#endif +/** The value must be a valid ethernet MAC address. */ +#define RTGETOPT_REQ_MACADDR 14 +/** The value must be a valid UUID. */ +#define RTGETOPT_REQ_UUID 15 +/** The value must be a string with value as "on" or "off". */ +#define RTGETOPT_REQ_BOOL_ONOFF 16 +/** Boolean option accepting a wide range of typical ways of + * expression true and false. */ +#define RTGETOPT_REQ_BOOL 17 +/** The value must two unsigned 32-bit integer values separated by a colon, + * slash, pipe or space(s). */ +#define RTGETOPT_REQ_UINT32_PAIR 18 +/** The value must two unsigned 64-bit integer values separated by a colon, + * slash, pipe or space(s). */ +#define RTGETOPT_REQ_UINT64_PAIR 19 +/** The value must at least unsigned 32-bit integer value, optionally + * followed by a second separated by a colon, slash, pipe or space(s). */ +#define RTGETOPT_REQ_UINT32_OPTIONAL_PAIR 20 +/** The value must at least unsigned 64-bit integer value, optionally + * followed by a second separated by a colon, slash, pipe or space(s). */ +#define RTGETOPT_REQ_UINT64_OPTIONAL_PAIR 21 +/** The mask of the valid required types. */ +#define RTGETOPT_REQ_MASK 31 +/** Treat the value as hexadecimal - only applicable with the RTGETOPT_REQ_*INT*. */ +#define RTGETOPT_FLAG_HEX RT_BIT(16) +/** Treat the value as octal - only applicable with the RTGETOPT_REQ_*INT*. */ +#define RTGETOPT_FLAG_OCT RT_BIT(17) +/** Treat the value as decimal - only applicable with the RTGETOPT_REQ_*INT*. */ +#define RTGETOPT_FLAG_DEC RT_BIT(18) +/** The index value is attached to the argument - only valid for long arguments. */ +#define RTGETOPT_FLAG_INDEX RT_BIT(19) +/** Treat the long option as case insensitive. */ +#define RTGETOPT_FLAG_ICASE RT_BIT(20) +/** Mask of valid bits - for validation. */ +#define RTGETOPT_VALID_MASK ( RTGETOPT_REQ_MASK \ + | RTGETOPT_FLAG_HEX \ + | RTGETOPT_FLAG_OCT \ + | RTGETOPT_FLAG_DEC \ + | RTGETOPT_FLAG_INDEX \ + | RTGETOPT_FLAG_ICASE) +/** @} */ + +/** + * An option definition. + */ +typedef struct RTGETOPTDEF +{ + /** The long option. + * This is optional */ + const char *pszLong; + /** The short option character. + * This doesn't have to be a character, it may also be a \#define or enum value if + * there isn't any short version of this option. Must be greater than 0. */ + int iShort; + /** The flags (RTGETOPT_*). */ + unsigned fFlags; +} RTGETOPTDEF; +/** Pointer to an option definition. */ +typedef RTGETOPTDEF *PRTGETOPTDEF; +/** Pointer to an const option definition. */ +typedef const RTGETOPTDEF *PCRTGETOPTDEF; + +/** + * Option argument union. + * + * What ends up here depends on argument format in the option definition. + * + * @remarks Integers will bet put in the \a i and \a u members and sign/zero extended + * according to the signedness indicated by the \a fFlags. So, you can choose + * use which ever of the integer members for accessing the value regardless + * of restrictions indicated in the \a fFlags. + */ +typedef union RTGETOPTUNION +{ + /** Pointer to the definition on failure or when the option doesn't take an argument. + * This can be NULL for some errors. */ + PCRTGETOPTDEF pDef; + /** A RTGETOPT_REQ_STRING option argument. */ + const char *psz; + + /** A RTGETOPT_REQ_INT8 option argument. */ + int8_t i8; + /** A RTGETOPT_REQ_UINT8 option argument . */ + uint8_t u8; + /** A RTGETOPT_REQ_INT16 option argument. */ + int16_t i16; + /** A RTGETOPT_REQ_UINT16 option argument . */ + uint16_t u16; + /** A RTGETOPT_REQ_INT16 option argument. */ + int32_t i32; + /** A RTGETOPT_REQ_UINT32 option argument . */ + uint32_t u32; + /** A RTGETOPT_REQ_INT64 option argument. */ + int64_t i64; + /** A RTGETOPT_REQ_UINT64 option argument. */ + uint64_t u64; +#ifdef IPRT_INCLUDED_net_h + /** A RTGETOPT_REQ_IPV4ADDR option argument. */ + RTNETADDRIPV4 IPv4Addr; + /** A RTGETOPT_REQ_IPV4CIDR option argument. */ + struct + { + RTNETADDRIPV4 IPv4Network; + RTNETADDRIPV4 IPv4Netmask; + } CidrIPv4; +#endif + /** A RTGETOPT_REQ_MACADDR option argument. */ + RTMAC MacAddr; + /** A RTGETOPT_REQ_UUID option argument. */ + RTUUID Uuid; + /** A boolean flag. */ + bool f; + /** A RTGETOPT_REQ_UINT32_PAIR or RTGETOPT_REQ_UINT32_OPTIONAL_PAIR option + * argument. */ + struct + { + uint32_t uFirst; + uint32_t uSecond; /**< Set to UINT32_MAX if optional and not present. */ + } PairU32; + /** A RTGETOPT_REQ_UINT64_COLON_PAIR option argument. */ + struct + { + uint64_t uFirst; + uint64_t uSecond; /**< Set to UINT64_MAX if optional and not present. */ + } PairU64; +} RTGETOPTUNION; +/** Pointer to an option argument union. */ +typedef RTGETOPTUNION *PRTGETOPTUNION; +/** Pointer to a const option argument union. */ +typedef RTGETOPTUNION const *PCRTGETOPTUNION; + + +/** + * RTGetOpt state. + */ +typedef struct RTGETOPTSTATE +{ + /** The next argument. */ + int iNext; + /** Argument array. */ + char **argv; + /** Number of items in argv. */ + int argc; + /** Option definition array. */ + PCRTGETOPTDEF paOptions; + /** Number of items in paOptions. */ + size_t cOptions; + /** The next short option. + * (For parsing ls -latrT4 kind of option lists.) */ + const char *pszNextShort; + /** The option definition which matched. NULL otherwise. */ + PCRTGETOPTDEF pDef; + /** The index of an index option, otherwise UINT32_MAX. */ + uint32_t uIndex; + /** The flags passed to RTGetOptInit. */ + uint32_t fFlags; + /** Number of non-options that we're skipping during a sorted get. The value + * INT32_MAX is used to indicate that there are no more options. This is used + * to implement '--'. */ + int32_t cNonOptions; + + /* More members may be added later for dealing with new features. */ +} RTGETOPTSTATE; +/** Pointer to RTGetOpt state. */ +typedef RTGETOPTSTATE *PRTGETOPTSTATE; + + +/** + * Initialize the RTGetOpt state. + * + * The passed in argument vector may be sorted if fFlags indicates that this is + * desired (to be implemented). + * + * @returns VINF_SUCCESS, VERR_INVALID_PARAMETER or VERR_INVALID_POINTER. + * @param pState The state. + * + * @param argc Argument count, to be copied from what comes in with + * main(). + * @param argv Argument array, to be copied from what comes in with + * main(). This may end up being modified by the + * option/argument sorting. + * @param paOptions Array of RTGETOPTDEF structures, which must specify what + * options are understood by the program. + * @param cOptions Number of array items passed in with paOptions. + * @param iFirst The argument to start with (in argv). + * @param fFlags The flags, see RTGETOPTINIT_FLAGS_XXX. + */ +RTDECL(int) RTGetOptInit(PRTGETOPTSTATE pState, int argc, char **argv, + PCRTGETOPTDEF paOptions, size_t cOptions, + int iFirst, uint32_t fFlags); + +/** @name RTGetOptInit flags. + * @{ */ +/** Sort the arguments so that options comes first, then non-options. */ +#define RTGETOPTINIT_FLAGS_OPTS_FIRST RT_BIT_32(0) +/** Prevent add the standard version and help options: + * - "--help", "-h" and "-?" returns 'h'. + * - "--version" and "-V" return 'V'. + */ +#define RTGETOPTINIT_FLAGS_NO_STD_OPTS RT_BIT_32(1) +/** @} */ + +/** + * Command line argument parser, handling both long and short options and checking + * argument formats, if desired. + * + * This is to be called in a loop until it returns 0 (meaning that all options + * were parsed) or a negative value (meaning that an error occurred). How non-option + * arguments are dealt with depends on the flags passed to RTGetOptInit. The default + * (fFlags = 0) is to return VINF_GETOPT_NOT_OPTION with pValueUnion->psz pointing to + * the argument string. + * + * For example, for a program which takes the following options: + * + * --optwithstring (or -s) and a string argument; + * --optwithint (or -i) and a 32-bit signed integer argument; + * --verbose (or -v) with no arguments, + * + * code would look something like this: + * + * @code +int main(int argc, char **argv) +{ + int rc = RTR3Init(); + if (RT_FAILURE(rc)) + return RTMsgInitFailure(rc); + + static const RTGETOPTDEF s_aOptions[] = + { + { "--optwithstring", 's', RTGETOPT_REQ_STRING }, + { "--optwithint", 'i', RTGETOPT_REQ_INT32 }, + { "--verbose", 'v', 0 }, + }; + + int ch; + RTGETOPTUNION ValueUnion; + RTGETOPTSTATE GetState; + RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0); + while ((ch = RTGetOpt(&GetState, &ValueUnion))) + { + // for options that require an argument, ValueUnion has received the value + switch (ch) + { + case 's': // --optwithstring or -s + // string argument, copy ValueUnion.psz + break; + + case 'i': // --optwithint or -i + // integer argument, copy ValueUnion.i32 + break; + + case 'v': // --verbose or -v + g_fOptVerbose = true; + break; + + case VINF_GETOPT_NOT_OPTION: + // handle non-option argument in ValueUnion.psz. + break; + + default: + return RTGetOptPrintError(ch, &ValueUnion); + } + } + + return RTEXITCODE_SUCCESS; +} + @endcode + * + * @returns 0 when done parsing. + * @returns the iShort value of the option. pState->pDef points to the option + * definition which matched. + * @returns IPRT error status on parse error. + * @returns VINF_GETOPT_NOT_OPTION when encountering a non-option argument and + * RTGETOPT_FLAG_SORT was not specified. pValueUnion->psz points to the + * argument string. + * @returns VERR_GETOPT_UNKNOWN_OPTION when encountering an unknown option. + * pValueUnion->psz points to the option string. + * @returns VERR_GETOPT_REQUIRED_ARGUMENT_MISSING and pValueUnion->pDef if + * a required argument (aka value) was missing for an option. + * @returns VERR_GETOPT_INVALID_ARGUMENT_FORMAT and pValueUnion->pDef if + * argument (aka value) conversion failed. + * + * @param pState The state previously initialized with RTGetOptInit. + * @param pValueUnion Union with value; in the event of an error, psz member + * points to erroneous parameter; otherwise, for options + * that require an argument, this contains the value of + * that argument, depending on the type that is required. + */ +RTDECL(int) RTGetOpt(PRTGETOPTSTATE pState, PRTGETOPTUNION pValueUnion); + +/** + * Fetch a value. + * + * Used to retrive a value argument in a manner similar to what RTGetOpt does + * (@a fFlags -> @a pValueUnion). This can be used when handling + * VINF_GETOPT_NOT_OPTION, but is equally useful for decoding options that + * takes more than one value. + * + * @returns VINF_SUCCESS on success. + * @returns IPRT error status on parse error. + * @returns VERR_INVALID_PARAMETER if the flags are wrong. + * @returns VERR_GETOPT_UNKNOWN_OPTION when pState->pDef is null. + * @returns VERR_GETOPT_REQUIRED_ARGUMENT_MISSING if there are no more + * available arguments. pValueUnion->pDef is NULL. + * @returns VERR_GETOPT_INVALID_ARGUMENT_FORMAT and pValueUnion->pDef is + * unchanged if value conversion failed. + * + * @param pState The state previously initialized with RTGetOptInit. + * @param pValueUnion Union with value; in the event of an error, psz member + * points to erroneous parameter; otherwise, for options + * that require an argument, this contains the value of + * that argument, depending on the type that is required. + * @param fFlags What to get, that is RTGETOPT_REQ_XXX. + */ +RTDECL(int) RTGetOptFetchValue(PRTGETOPTSTATE pState, PRTGETOPTUNION pValueUnion, uint32_t fFlags); + +/** + * Gets the pointer to the argv entry of the current non-option argument. + * + * This function ASSUMES the previous RTGetOpt() call returned + * VINF_GETOPT_NOT_OPTION and require RTGETOPTINIT_FLAGS_OPTS_FIRST to be + * specified to RTGetOptInit(). + * + * @returns Pointer to the argv entry of the current non-option. NULL if + * (detectable) precondition isn't fullfilled (asserted) + * @param pState The state previously initialized with RTGetOptInit. + */ +RTDECL(char **) RTGetOptNonOptionArrayPtr(PRTGETOPTSTATE pState); + +/** + * Print error messages for a RTGetOpt default case. + * + * Uses RTMsgError. + * + * @returns Suitable exit code. + * + * @param ch The RTGetOpt return value. + * @param pValueUnion The value union returned by RTGetOpt. + */ +RTDECL(RTEXITCODE) RTGetOptPrintError(int ch, PCRTGETOPTUNION pValueUnion); + +/** + * Formats error messages for a RTGetOpt default case. + * + * @returns On success, positive count of formatted character excluding the + * terminator. On buffer overflow, negative number giving the required + * buffer size (including terminator char). (RTStrPrintf2 style.) + * + * @param pszBuf The buffer to format into. + * @param cbBuf The size of the buffer @a pszBuf points to. + * @param ch The RTGetOpt return value. + * @param pValueUnion The value union returned by RTGetOpt. + */ +RTDECL(ssize_t) RTGetOptFormatError(char *pszBuf, size_t cbBuf, int ch, PCRTGETOPTUNION pValueUnion); + +/** + * Parses the @a pszCmdLine string into an argv array. + * + * This is useful for converting a response file or similar to an argument + * vector that can be used with RTGetOptInit(). + * + * This function aims at following the bourne shell string quoting rules. + * + * @returns IPRT status code. + * + * @param ppapszArgv Where to return the argument vector. This must be + * freed by calling RTGetOptArgvFreeEx or + * RTGetOptArgvFree. + * @param pcArgs Where to return the argument count. + * @param pszCmdLine The string to parse. + * @param fFlags A combination of the RTGETOPTARGV_CNV_XXX flags, + * except RTGETOPTARGV_CNV_UNQUOTED is not supported. + * @param pszSeparators String containing the argument separators. If NULL, + * then space, tab, line feed (\\n) and return (\\r) + * are used. + */ +RTDECL(int) RTGetOptArgvFromString(char ***ppapszArgv, int *pcArgs, const char *pszCmdLine, uint32_t fFlags, + const char *pszSeparators); + +/** + * Frees and argument vector returned by RTGetOptStringToArgv. + * + * @param papszArgv Argument vector. NULL is fine. + */ +RTDECL(void) RTGetOptArgvFree(char **papszArgv); + +/** + * Frees and argument vector returned by RTGetOptStringToArgv, taking + * RTGETOPTARGV_CNV_MODIFY_INPUT into account. + * + * @param papszArgv Argument vector. NULL is fine. + * @param fFlags The flags passed to RTGetOptStringToArgv. + */ +RTDECL(void) RTGetOptArgvFreeEx(char **papszArgv, uint32_t fFlags); + +/** + * Turns an argv array into a command line string. + * + * This is useful for calling CreateProcess on Windows, but can also be used for + * displaying an argv array. + * + * This function aims at following the bourn shell string quoting rules. + * + * @returns IPRT status code. + * + * @param ppszCmdLine Where to return the command line string. This must + * be freed by calling RTStrFree. + * @param papszArgv The argument vector to convert. + * @param fFlags A combination of the RTGETOPTARGV_CNV_XXX flags. + */ +RTDECL(int) RTGetOptArgvToString(char **ppszCmdLine, const char * const *papszArgv, uint32_t fFlags); + +/** @name RTGetOptArgvToString, RTGetOptArgvToUtf16String and + * RTGetOptArgvFromString flags + * @{ */ +/** Quote strings according to the Microsoft CRT rules. */ +#define RTGETOPTARGV_CNV_QUOTE_MS_CRT UINT32_C(0x00000000) +/** Quote strings according to the Unix Bourne Shell. */ +#define RTGETOPTARGV_CNV_QUOTE_BOURNE_SH UINT32_C(0x00000001) +/** Don't quote any strings at all. */ +#define RTGETOPTARGV_CNV_UNQUOTED UINT32_C(0x00000002) +/** Mask for the quoting style. */ +#define RTGETOPTARGV_CNV_QUOTE_MASK UINT32_C(0x00000003) +/** Allow RTGetOptArgvFromString to modifying the command line input string. + * @note Must use RTGetOptArgvFreeEx to free. */ +#define RTGETOPTARGV_CNV_MODIFY_INPUT UINT32_C(0x00000004) +/** Valid bits. */ +#define RTGETOPTARGV_CNV_VALID_MASK UINT32_C(0x00000007) +/** @} */ + +/** + * Convenience wrapper around RTGetOpArgvToString and RTStrToUtf16. + * + * @returns IPRT status code. + * + * @param ppwszCmdLine Where to return the command line string. This must + * be freed by calling RTUtf16Free. + * @param papszArgv The argument vector to convert. + * @param fFlags A combination of the RTGETOPTARGV_CNV_XXX flags. + */ +RTDECL(int) RTGetOptArgvToUtf16String(PRTUTF16 *ppwszCmdLine, const char * const *papszArgv, uint32_t fFlags); + +/** @} */ + +RT_C_DECLS_END + +#endif /* !IPRT_INCLUDED_getopt_h */ + |