/** @file
 * IPRT - Command Line Parsing.
 */

/*
 * Copyright (C) 2007-2015 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_getopt_h
#define ___iprt_getopt_h


#include <iprt/cdefs.h>
#include <iprt/types.h>

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 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_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;
} 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);

/**
 * 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);

/**
 * 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 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);

/**
 * 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 and RTGetOptArgvToUtf16String flags
 * @{ */
/** Quote strings according to the Microsoft CRT rules. */
#define RTGETOPTARGV_CNV_QUOTE_MS_CRT       UINT32_C(0)
/** Quote strings according to the Unix Bourne Shell. */
#define RTGETOPTARGV_CNV_QUOTE_BOURNE_SH    UINT32_C(1)
/** Don't quote any strings at all. */
#define RTGETOPTARGV_CNV_UNQUOTED           UINT32_C(2)
/** Mask for the quoting style. */
#define RTGETOPTARGV_CNV_QUOTE_MASK         UINT32_C(3)
/** @} */

/**
 * 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

