/**
 * Print debug message depending on the m_cVerbosity level.
 *
 * @param   iMinLevel       The minimum m_cVerbosity level for this message.
 * @param   fMsg            Whether to dump parts for the current service message.
 * @param   pszFmt          The message format string.
 * @param   va              Optional arguments.
 */
void VBoxNetBaseService::debugPrintV(int iMinLevel, bool fMsg, const char *pszFmt, va_list va) const
{
    RT_NOREF(fMsg);
    if (iMinLevel <= m->m_cVerbosity)
    {
        va_list vaCopy;                 /* This dude is *very* special, thus the copy. */
        va_copy(vaCopy, va);
        RTStrmPrintf(g_pStdErr, "%s: %s: %N\n",
                     RTProcShortName(),
                     iMinLevel >= 2 ? "debug" : "info",
                     pszFmt,
                     &vaCopy);
        va_end(vaCopy);
    }
}
int main(int argc, char **argv)
{
    int rc = RTR3InitExe(argc, &argv, 0);
    if (RT_FAILURE(rc))
        return RTMsgInitFailure(rc);

    /*
     * Parse arguments.
     */
    static RTGETOPTDEF const s_aOptions[] =
    {
        { "--manifest",     'm', RTGETOPT_REQ_STRING  },
        { "--java",         'j', RTGETOPT_REQ_NOTHING },
        { "--chdir",        'C', RTGETOPT_REQ_STRING  },
        { "--attribute",    'a', RTGETOPT_REQ_STRING  },
        { "--verify",       'v', RTGETOPT_REQ_NOTHING },
    };

    bool            fVerify     = false;
    bool            fStdFormat  = true;
    const char     *pszManifest = NULL;
    const char     *pszChDir    = NULL;
    uint32_t        fAttr       = RTMANIFEST_ATTR_UNKNOWN;

    RTGETOPTSTATE GetState;
    rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
    if (RT_FAILURE(rc))
        return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOptInit failed: %Rrc", rc);

    RTGETOPTUNION ValueUnion;
    while (   (rc = RTGetOpt(&GetState, &ValueUnion)) != 0
           &&  rc != VINF_GETOPT_NOT_OPTION)
    {
        switch (rc)
        {
            case 'a':
            {
                static struct
                {
                    const char *pszAttr;
                    uint32_t    fAttr;
                } s_aAttributes[] =
                {
                    { "size",   RTMANIFEST_ATTR_SIZE    },
                    { "md5",    RTMANIFEST_ATTR_MD5     },
                    { "sha1",   RTMANIFEST_ATTR_SHA1    },
                    { "sha256", RTMANIFEST_ATTR_SHA256  },
                    { "sha512", RTMANIFEST_ATTR_SHA512  }
                };
                uint32_t fThisAttr = RTMANIFEST_ATTR_UNKNOWN;
                for (unsigned i = 0; i < RT_ELEMENTS(s_aAttributes); i++)
                    if (!RTStrICmp(s_aAttributes[i].pszAttr, ValueUnion.psz))
                    {
                        fThisAttr = s_aAttributes[i].fAttr;
                        break;
                    }
                if (fThisAttr == RTMANIFEST_ATTR_UNKNOWN)
                    return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown attribute type '%s'", ValueUnion.psz);

                if (fAttr == RTMANIFEST_ATTR_UNKNOWN)
                    fAttr = fThisAttr;
                else
                    fAttr |= fThisAttr;
                break;
            }

            case 'j':
                fStdFormat = false;
                break;

            case 'm':
                if (pszManifest)
                    return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Only one manifest can be specified");
                pszManifest = ValueUnion.psz;
                break;

            case 'v':
                fVerify = true;
                break;

            case 'C':
                if (pszChDir)
                    return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Only one directory change can be specified");
                pszChDir = ValueUnion.psz;
                break;

            case 'h':
                RTPrintf("Usage: %s [--manifest <file>] [--chdir <dir>] [--attribute <attrib-name> [..]] <files>\n"
                         "   or  %s --verify [--manifest <file>] [--chdir <dir>]\n"
                         "\n"
                         "attrib-name: size, md5, sha1, sha256 or sha512\n"
                         , RTProcShortName(), RTProcShortName());
                return RTEXITCODE_SUCCESS;

            case 'V':
                RTPrintf("%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision());
                return RTEXITCODE_SUCCESS;

            default:
                return RTGetOptPrintError(rc, &ValueUnion);
        }
    }

    /*
     * Take action.
     */
    RTEXITCODE rcExit;
    if (!fVerify)
    {
        if (rc != VINF_GETOPT_NOT_OPTION)
            RTMsgWarning("No files specified, the manifest will be empty.");
        if (fAttr == RTMANIFEST_ATTR_UNKNOWN)
            fAttr = RTMANIFEST_ATTR_SIZE | RTMANIFEST_ATTR_MD5
                  | RTMANIFEST_ATTR_SHA1 | RTMANIFEST_ATTR_SHA256 | RTMANIFEST_ATTR_SHA512;
        rcExit = rtManifestDoCreate(pszManifest, fStdFormat, pszChDir, fAttr, &GetState, &ValueUnion, rc);
    }
    else
    {
        if (rc == VINF_GETOPT_NOT_OPTION)
            return RTMsgErrorExit(RTEXITCODE_SYNTAX,
                                  "No files should be specified when verifying a manifest (--verfiy), "
                                  "only a manifest via the --manifest option");
        if (fAttr != RTMANIFEST_ATTR_UNKNOWN)
            return RTMsgErrorExit(RTEXITCODE_SYNTAX,
                                  "The --attribute (-a) option does not combine with --verify (-v)");


        rcExit = rtManifestDoVerify(pszManifest, fStdFormat, pszChDir);
    }

    return rcExit;
}
/**
 * Parse the arguments.
 *
 * @returns 0 on success, fully bitched exit code on failure.
 *
 * @param   argc    Argument count.
 * @param   argv    Argument vector.
 *
 * @todo r=bird: The --help and --version options shall not return a
 *               non-zero exit code.  So, this method need to grow some
 *               complexity.  I'm to blame for that blunder :/
 */
int VBoxNetBaseService::parseArgs(int argc, char **argv)
{

    RTGETOPTSTATE State;
    PRTGETOPTDEF paOptionArray = getOptionsPtr();
    int rc = RTGetOptInit(&State, argc, argv, paOptionArray, m->m_vecOptionDefs.size(), 0, 0 /*fFlags*/);
    AssertRCReturn(rc, 49);
#if 0
    /* default initialization */
    m_enmTrunkType = kIntNetTrunkType_WhateverNone;
#endif
    Log2(("BaseService: parseArgs enter\n"));

    for (;;)
    {
        RTGETOPTUNION Val;
        rc = RTGetOpt(&State, &Val);
        if (!rc)
            break;
        switch (rc)
        {
            case 'N': // --name
                m->m_ServiceName = Val.psz;
                break;

            case 'n': // --network
                m->m_NetworkName = Val.psz;
                break;

            case 't': //--trunk-name
                m->m_TrunkName = Val.psz;
                break;

            case 'T': //--trunk-type
                if (!strcmp(Val.psz, "none"))
                    m->m_enmTrunkType = kIntNetTrunkType_None;
                else if (!strcmp(Val.psz, "whatever"))
                    m->m_enmTrunkType = kIntNetTrunkType_WhateverNone;
                else if (!strcmp(Val.psz, "netflt"))
                    m->m_enmTrunkType = kIntNetTrunkType_NetFlt;
                else if (!strcmp(Val.psz, "netadp"))
                    m->m_enmTrunkType = kIntNetTrunkType_NetAdp;
                else if (!strcmp(Val.psz, "srvnat"))
                    m->m_enmTrunkType = kIntNetTrunkType_SrvNat;
                else
                {
                    RTStrmPrintf(g_pStdErr, "Invalid trunk type '%s'\n", Val.psz);
                    return RTEXITCODE_SYNTAX;
                }
                break;

            case 'a': // --mac-address
                m->m_MacAddress = Val.MacAddr;
                break;

            case 'i': // --ip-address
                m->m_Ipv4Address = Val.IPv4Addr;
                break;

            case 'm': // --netmask
                m->m_Ipv4Netmask = Val.IPv4Addr;
                break;

            case 'v': // --verbose
                m->m_cVerbosity++;
                break;

            case 'V': // --version (missed)
                RTPrintf("%sr%u\n", RTBldCfgVersion(), RTBldCfgRevision());
                return 1; /** @todo this exit code is wrong, of course. :/ */

            case 'M': // --need-main
                m->m_fNeedMain = true;
                break;

            case 'h': // --help (missed)
                RTPrintf("%s Version %sr%u\n"
                         "(C) 2009-" VBOX_C_YEAR " " VBOX_VENDOR "\n"
                         "All rights reserved.\n"
                         "\n"
                         "Usage: %s <options>\n"
                         "\n"
                         "Options:\n",
                         RTProcShortName(),
                         RTBldCfgVersion(),
                         RTBldCfgRevision(),
                         RTProcShortName());
                for (unsigned int i = 0; i < m->m_vecOptionDefs.size(); i++)
                    RTPrintf("    -%c, %s\n", m->m_vecOptionDefs[i]->iShort, m->m_vecOptionDefs[i]->pszLong);
                usage(); /* to print Service Specific usage */
                return 1; /** @todo this exit code is wrong, of course. :/ */

            default:
            {
                int rc1 = parseOpt(rc, Val);
                if (RT_FAILURE(rc1))
                {
                    RTEXITCODE rcExit = RTGetOptPrintError(rc, &Val);
                    RTPrintf("Use --help for more information.\n");
                    return rcExit;
                }
                break;
            }
        }
    }

    RTMemFree(paOptionArray);
    return RTEXITCODE_SUCCESS;
}