Changeset 56344 in vbox for trunk/src/VBox/Frontends/VBoxManage/VBoxManageHelp.cpp
- Timestamp:
- Jun 10, 2015 10:58:10 PM (9 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Frontends/VBoxManage/VBoxManageHelp.cpp
r56195 r56344 30 30 #include "VBoxManage.h" 31 31 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 44 enum HELP_CMD_VBOXMANAGE g_enmCurCommand = HELP_CMD_VBOXMANAGE_INVALID; 45 /** The scope maskt for the current subcommand. */ 46 uint64_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 */ 56 void 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 */ 71 void 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 */ 84 static 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 */ 107 static 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 */ 191 DECLINLINE(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 */ 211 static 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 */ 255 static 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 */ 290 static 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 */ 315 RTEXITCODE 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 */ 332 RTEXITCODE 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 */ 356 RTEXITCODE 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 */ 387 static 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 */ 420 RTEXITCODE 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 32 456 33 457 … … 46 470 } 47 471 } 472 473 474 48 475 49 476 void printUsage(USAGECATEGORY fCategory, uint32_t fSubCategory, PRTSTREAM pStrm) … … 868 1295 "\n", SEP, SEP); 869 1296 } 1297 870 1298 #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 879 1312 #endif 880 1313 } … … 1008 1441 return RTEXITCODE_SYNTAX; 1009 1442 } 1443 1444 1445
Note:
See TracChangeset
for help on using the changeset viewer.