VirtualBox

Ignore:
Timestamp:
Jun 10, 2015 10:58:10 PM (9 years ago)
Author:
vboxsync
Message:

VBoxManage,manual: Working help code generator.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Frontends/VBoxManage/VBoxManageHelp.cpp

    r56195 r56344  
    3030#include "VBoxManage.h"
    3131
     32/*******************************************************************************
     33*   Defined Constants And Macros                                               *
     34*******************************************************************************/
     35/** If the usage is the given number of length long or longer, the error is
     36 * repeated so the user can actually see it. */
     37#define ERROR_REPEAT_AFTER_USAGE_LENGTH 16
     38
     39
     40/*******************************************************************************
     41*   Global Variables                                                           *
     42*******************************************************************************/
     43#ifndef VBOX_ONLY_DOCS
     44enum HELP_CMD_VBOXMANAGE    g_enmCurCommand = HELP_CMD_VBOXMANAGE_INVALID;
     45/** The scope maskt for the current subcommand. */
     46uint64_t                    g_fCurSubcommandScope = UINT64_MAX;
     47
     48
     49/**
     50 * Sets the current command.
     51 *
     52 * This affects future calls to error and help functions.
     53 *
     54 * @param   enmCommand          The command.
     55 */
     56void setCurrentCommand(enum HELP_CMD_VBOXMANAGE enmCommand)
     57{
     58    Assert(g_enmCurCommand == HELP_CMD_VBOXMANAGE_INVALID);
     59    g_enmCurCommand       = enmCommand;
     60    g_fCurSubcommandScope = UINT64_MAX;
     61}
     62
     63
     64/**
     65 * Sets the current subcommand.
     66 *
     67 * This affects future calls to error and help functions.
     68 *
     69 * @param   fSubcommandScope    The subcommand scope.
     70 */
     71void setCurrentSubcommand(uint64_t fSubcommandScope)
     72{
     73    g_fCurSubcommandScope = fSubcommandScope;
     74}
     75
     76
     77
     78/**
     79 * Retruns the width for the given handle.
     80 *
     81 * @returns Screen width.
     82 * @param   pStrm           The stream, g_pStdErr or g_pStdOut.
     83 */
     84static uint32_t getScreenWidth(PRTSTREAM pStrm)
     85{
     86    static uint32_t s_acch[2] = { 0, 0};
     87    uint32_t        iWhich    = pStrm == g_pStdErr ? 1 : 0;
     88    uint32_t        cch       = s_acch[iWhich];
     89    if (cch)
     90        return cch;
     91
     92    cch = 80; /** @todo screen width IPRT API. */
     93    s_acch[iWhich] = cch;
     94    return cch;
     95}
     96
     97
     98/**
     99 * Prints a string table string (paragraph), performing non-breaking-space
     100 * replacement and wrapping.
     101 *
     102 * @returns Number of lines written.
     103 * @param   pStrm           The output stream.
     104 * @param   psz             The string table string to print.
     105 * @param   cchMaxWidth     The maximum output width.
     106 */
     107static uint32_t printString(PRTSTREAM pStrm, const char *psz, uint32_t cchMaxWidth)
     108{
     109    uint32_t    cLinesWritten;
     110    size_t      cch     = strlen(psz);
     111    const char *pszNbsp = strchr(psz, REFENTRY_NBSP);
     112
     113    /*
     114     * No-wrap case is simpler, so handle that separately.
     115     */
     116    if (cch <= cchMaxWidth)
     117    {
     118        if (!pszNbsp)
     119            RTStrmWrite(pStrm, psz, cch);
     120        else
     121        {
     122            do
     123            {
     124                RTStrmWrite(pStrm, psz, pszNbsp - psz);
     125                RTStrmWrite(pStrm, " ", 1);
     126                psz = pszNbsp + 1;
     127                pszNbsp = strchr(psz, REFENTRY_NBSP);
     128            } while (pszNbsp);
     129            RTStrmWrite(pStrm, psz, strlen(psz));
     130        }
     131        RTStrmWrite(pStrm, "\n", 1);
     132        cLinesWritten = 1;
     133    }
     134    /*
     135     * We need to wrap stuff, too bad.
     136     */
     137    else
     138    {
     139        /* Figure the paragraph indent level first. */
     140        const char * const pszIndent = psz;
     141        uint32_t cchIndent = 0;
     142        while (*psz == ' ')
     143            cchIndent++, psz++;
     144        if (cchIndent + 8 >= cchMaxWidth)
     145            cchMaxWidth += cchIndent + 8;
     146
     147        /* Work our way thru the string, line by line. */
     148        cLinesWritten = 0;
     149        do
     150        {
     151            RTStrmWrite(pStrm, pszIndent, cchIndent);
     152            size_t offLine = cchIndent;
     153            do
     154            {
     155                const char *pszSpace = strchr(psz, ' ');
     156                size_t      cchWord  = pszSpace ? pszSpace + 1 - psz : strlen(psz);
     157                if (   offLine + cchWord > cchMaxWidth
     158                    && offLine != cchIndent)
     159                    break;
     160
     161                pszNbsp = (const char *)memchr(psz, REFENTRY_NBSP, cchWord);
     162                while (pszNbsp)
     163                {
     164                    size_t cchSubWord = pszNbsp - psz;
     165                    RTStrmWrite(pStrm, psz, cchSubWord);
     166                    RTStrmWrite(pStrm, " ", 1);
     167                    psz     += cchSubWord + 1;
     168                    offLine += cchSubWord + 1;
     169                    cchWord -= cchSubWord + 1;
     170                    pszNbsp = (const char *)memchr(psz, REFENTRY_NBSP, cchWord);
     171                }
     172
     173                RTStrmWrite(pStrm, psz, cchWord);
     174                psz     += cchWord;
     175                offLine += cchWord;
     176
     177            } while (offLine < cchMaxWidth && *psz != '\0');
     178            RTStrmWrite(pStrm, "\n", 1);
     179            cLinesWritten++;
     180        } while (*psz != '\0');
     181    }
     182    return cLinesWritten;
     183}
     184
     185
     186/**
     187 * Checks if the given string is empty (only spaces).
     188 * @returns true if empty, false if not.
     189 * @param   psz                 The string to examine.
     190 */
     191DECLINLINE(bool) isEmptyString(const char *psz)
     192{
     193    char ch;
     194    while ((ch = *psz) == ' ')
     195        psz++;
     196    return ch == '\0';
     197}
     198
     199
     200/**
     201 * Prints a string table.
     202 *
     203 * @returns Current number of pending blank lines.
     204 * @param   pStrm               The output stream.
     205 * @param   pStrTab             The string table.
     206 * @param   fScope              The selection scope.
     207 * @param   cPendingBlankLines  Pending blank lines from previous string table.
     208 * @param   pcLinesWritten      Pointer to variable that should be incremented
     209 *                              by the number of lines written.  Optional.
     210 */
     211static uint32_t printStringTable(PRTSTREAM pStrm, PCREFENTRYSTRTAB pStrTab, uint64_t fScope, uint32_t cPendingBlankLines,
     212                                 uint32_t *pcLinesWritten = NULL)
     213{
     214    uint32_t cLinesWritten = 0;
     215    uint32_t cchWidth      = getScreenWidth(pStrm);
     216    uint64_t fPrevScope    = fScope;
     217    for (uint32_t i = 0; i < pStrTab->cStrings; i++)
     218    {
     219        uint64_t fCurScope = pStrTab->paStrings[i].fScope;
     220        if (fCurScope == REFENTRYSTR_SCOPE_SAME)
     221            fCurScope = fPrevScope;
     222        if (fCurScope & fScope)
     223        {
     224            const char *psz = pStrTab->paStrings[i].psz;
     225            if (psz && !isEmptyString(psz))
     226            {
     227                while (cPendingBlankLines > 0)
     228                {
     229                    cPendingBlankLines--;
     230                    RTStrmWrite(pStrm, "\n", 3);
     231                    cLinesWritten++;
     232                }
     233                cLinesWritten += printString(pStrm, psz, cchWidth);
     234            }
     235            else
     236                cPendingBlankLines++;
     237        }
     238        fPrevScope = fCurScope;
     239    }
     240
     241    if (cLinesWritten)
     242        *pcLinesWritten += cLinesWritten;
     243    return cPendingBlankLines;
     244}
     245
     246
     247/**
     248 * Prints brief help for a command or subcommand.
     249 *
     250 * @returns Number of lines written.
     251 * @param   enmCommand          The command.
     252 * @param   fSubcommandScope    The subcommand scope, UINT64_MAX for all.
     253 * @param   pStrm               The output stream.
     254 */
     255static uint32_t printBriefCommandOrSubcommandHelp(enum HELP_CMD_VBOXMANAGE enmCommand, uint64_t fSubcommandScope, PRTSTREAM pStrm)
     256{
     257    uint32_t cLinesWritten = 0;
     258    uint32_t cPendingBlankLines = 0;
     259    uint32_t cFound = 0;
     260    for (uint32_t i = 0; i < g_cHelpEntries; i++)
     261    {
     262        PCREFENTRY pHelp = g_apHelpEntries[i];
     263        if (pHelp->idInternal == (int64_t)enmCommand)
     264        {
     265            cFound++;
     266            if (cFound == 1)
     267            {
     268                if (fSubcommandScope == REFENTRYSTR_SCOPE_GLOBAL)
     269                    RTStrmPrintf(pStrm, "Usage - %c%s:\n", RT_C_TO_UPPER(pHelp->pszBrief[0]), pHelp->pszBrief + 1);
     270                else
     271                    RTStrmPrintf(pStrm, "Usage:\n");
     272            }
     273            cPendingBlankLines = printStringTable(pStrm, &pHelp->Synopsis, fSubcommandScope, cPendingBlankLines, &cLinesWritten);
     274            if (!cPendingBlankLines)
     275                cPendingBlankLines = 1;
     276        }
     277    }
     278    Assert(cFound > 0);
     279    return cLinesWritten;
     280}
     281
     282
     283/**
     284 * Prints full help for a command or subcommand.
     285 *
     286 * @param   enmCommand          The command.
     287 * @param   fSubcommandScope    The subcommand scope, UINT64_MAX for all.
     288 * @param   pStrm               The output stream.
     289 */
     290static void printFullCommandOrSubcommandHelp(enum HELP_CMD_VBOXMANAGE enmCommand, uint64_t fSubcommandScope, PRTSTREAM pStrm)
     291{
     292    uint32_t cPendingBlankLines = 0;
     293    uint32_t cFound = 0;
     294    for (uint32_t i = 0; i < g_cHelpEntries; i++)
     295    {
     296        PCREFENTRY pHelp = g_apHelpEntries[i];
     297        if (   pHelp->idInternal == (int64_t)enmCommand
     298            || enmCommand == HELP_CMD_VBOXMANAGE_INVALID)
     299        {
     300            cFound++;
     301            cPendingBlankLines = printStringTable(pStrm, &pHelp->Help, fSubcommandScope, cPendingBlankLines);
     302            if (cPendingBlankLines < 2)
     303                cPendingBlankLines = 2;
     304        }
     305    }
     306    Assert(cFound > 0);
     307}
     308
     309
     310/**
     311 * Display no subcommand error message and current command usage.
     312 *
     313 * @returns RTEXITCODE_SYNTAX.
     314 */
     315RTEXITCODE errorNoSubcommand(void)
     316{
     317    Assert(g_enmCurCommand != HELP_CMD_VBOXMANAGE_INVALID);
     318    Assert(g_fCurSubcommandScope == UINT64_MAX);
     319
     320    return errorSyntax("No subcommand specified");
     321}
     322
     323
     324/**
     325 * Display unknown subcommand error message and current command usage.
     326 *
     327 * May show full command help instead if the subcommand is a common help option.
     328 *
     329 * @returns RTEXITCODE_SYNTAX, or RTEXITCODE_SUCCESS if common help option.
     330 * @param   pszSubcommand       The name of the alleged subcommand.
     331 */
     332RTEXITCODE errorUnknownSubcommand(const char *pszSubcommand)
     333{
     334    Assert(g_enmCurCommand != HELP_CMD_VBOXMANAGE_INVALID);
     335    Assert(g_fCurSubcommandScope == UINT64_MAX);
     336
     337    /* check if help was requested. */
     338    if (   strcmp(pszSubcommand, "--help") == 0
     339        || strcmp(pszSubcommand, "-h") == 0
     340        || strcmp(pszSubcommand, "-?") == 0)
     341    {
     342        printFullCommandOrSubcommandHelp(g_enmCurCommand, g_fCurSubcommandScope, g_pStdOut);
     343        return RTEXITCODE_SUCCESS;
     344    }
     345
     346    return errorSyntax("Unknown subcommand: %s", pszSubcommand);
     347}
     348
     349/**
     350 * Display current (sub)command usage and the custom error message.
     351 *
     352 * @returns RTEXITCODE_SYNTAX.
     353 * @param   pszFormat           Custom error message format string.
     354 * @param   ...                 Format arguments.
     355 */
     356RTEXITCODE errorSyntax(const char *pszFormat, ...)
     357{
     358    Assert(g_enmCurCommand != HELP_CMD_VBOXMANAGE_INVALID);
     359
     360    showLogo(g_pStdErr);
     361
     362    va_list va;
     363    va_start(va, pszFormat);
     364    RTMsgErrorV(pszFormat, va);
     365    va_end(va);
     366
     367    RTStrmWrite(g_pStdErr, "\n", 1);
     368    if (   printBriefCommandOrSubcommandHelp(g_enmCurCommand, g_fCurSubcommandScope, g_pStdErr)
     369        >= ERROR_REPEAT_AFTER_USAGE_LENGTH)
     370    {
     371        /* Usage was very long, repeat the error message. */
     372        RTStrmWrite(g_pStdErr, "\n", 1);
     373        va_start(va, pszFormat);
     374        RTMsgErrorV(pszFormat, va);
     375        va_end(va);
     376    }
     377    return RTEXITCODE_SYNTAX;
     378}
     379
     380
     381/**
     382 * Worker for errorGetOpt.
     383 *
     384 * @param   rcGetOpt            The RTGetOpt return value.
     385 * @param   pValueUnion         The value union returned by RTGetOpt.
     386 */
     387static void errorGetOptWorker(int rcGetOpt, union RTGETOPTUNION const *pValueUnion)
     388{
     389    if (rcGetOpt == VINF_GETOPT_NOT_OPTION)
     390        RTMsgError("Invalid parameter '%s'", pValueUnion->psz);
     391    else if (rcGetOpt > 0)
     392    {
     393        if (RT_C_IS_PRINT(rcGetOpt))
     394            RTMsgError("Invalid option -%c", rcGetOpt);
     395        else
     396            RTMsgError("Invalid option case %i", rcGetOpt);
     397    }
     398    else if (rcGetOpt == VERR_GETOPT_UNKNOWN_OPTION)
     399        RTMsgError("Unknown option: %s", pValueUnion->psz);
     400    else if (rcGetOpt == VERR_GETOPT_INVALID_ARGUMENT_FORMAT)
     401        RTMsgError("Invalid argument format: %s", pValueUnion->psz);
     402    else if (pValueUnion->pDef)
     403        RTMsgError("%s: %Rrs", pValueUnion->pDef->pszLong, rcGetOpt);
     404    else
     405        RTMsgError("%Rrs", rcGetOpt);
     406}
     407
     408
     409/**
     410 * Handled an RTGetOpt error or common option.
     411 *
     412 * This implements the 'V' and 'h' cases.  It reports appropriate syntax errors
     413 * for other @a rcGetOpt values.
     414 *
     415 * @retval  RTEXITCODE_SUCCESS if help or version request.
     416 * @retval  RTEXITCODE_SYNTAX if not help or version request.
     417 * @param   rcGetOpt            The RTGetOpt return value.
     418 * @param   pValueUnion         The value union returned by RTGetOpt.
     419 */
     420RTEXITCODE errorGetOpt(int rcGetOpt, union RTGETOPTUNION const *pValueUnion)
     421{
     422    Assert(g_enmCurCommand != HELP_CMD_VBOXMANAGE_INVALID);
     423
     424    /*
     425     * Check if it is an unhandled standard option.
     426     */
     427    if (rcGetOpt == 'V')
     428    {
     429        RTPrintf("%sr%d\n", VBOX_VERSION_STRING, RTBldCfgRevision());
     430        return RTEXITCODE_SUCCESS;
     431    }
     432
     433    if (rcGetOpt == 'h')
     434    {
     435        printFullCommandOrSubcommandHelp(g_enmCurCommand, g_fCurSubcommandScope, g_pStdOut);
     436        return RTEXITCODE_SUCCESS;
     437    }
     438
     439    /*
     440     * We failed.
     441     */
     442    showLogo(g_pStdErr);
     443    errorGetOptWorker(rcGetOpt, pValueUnion);
     444    if (   printBriefCommandOrSubcommandHelp(g_enmCurCommand, g_fCurSubcommandScope, g_pStdErr)
     445        >= ERROR_REPEAT_AFTER_USAGE_LENGTH)
     446    {
     447        /* Usage was very long, repeat the error message. */
     448        RTStrmWrite(g_pStdErr, "\n", 1);
     449        errorGetOptWorker(rcGetOpt, pValueUnion);
     450    }
     451    return RTEXITCODE_SYNTAX;
     452}
     453
     454#endif /* VBOX_ONLY_DOCS */
     455
    32456
    33457
     
    46470    }
    47471}
     472
     473
     474
    48475
    49476void printUsage(USAGECATEGORY fCategory, uint32_t fSubCategory, PRTSTREAM pStrm)
     
    8681295                     "\n", SEP, SEP);
    8691296    }
     1297
    8701298#ifndef VBOX_ONLY_DOCS /* Converted to man page, not needed. */
    871     if (fCategory & USAGE_EXTPACK)
    872     {
    873         RTStrmPrintf(pStrm,
    874                            "%s extpack %s         install [--replace] <tarball> |\n"
    875                      "                            uninstall [--force] <name> |\n"
    876                      "                            cleanup\n"
    877                      "\n", SEP);
    878     }
     1299    if (fCategory == USAGE_ALL)
     1300    {
     1301        uint32_t cPendingBlankLines = 0;
     1302        for (uint32_t i = 0; i < g_cHelpEntries; i++)
     1303        {
     1304            PCREFENTRY pHelp = g_apHelpEntries[i];
     1305            RTStrmPrintf(pStrm, "  %c%s:\n", RT_C_TO_UPPER(pHelp->pszBrief[0]), pHelp->pszBrief + 1);
     1306            cPendingBlankLines = printStringTable(pStrm, &pHelp->Synopsis, REFENTRYSTR_SCOPE_GLOBAL, cPendingBlankLines);
     1307            if (!cPendingBlankLines)
     1308                cPendingBlankLines = 1;
     1309        }
     1310    }
     1311
    8791312#endif
    8801313}
     
    10081441    return RTEXITCODE_SYNTAX;
    10091442}
     1443
     1444
     1445
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette