VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxManage/VBoxManageHelp.cpp@ 94887

Last change on this file since 94887 was 94290, checked in by vboxsync, 3 years ago

doc/manual,FE/VBoxManage: Make use of the general options from the refentry XML files when printing the help text and some more cleanups getting rid of all the legacy help code, bugref:9186

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.1 KB
Line 
1/* $Id: VBoxManageHelp.cpp 94290 2022-03-17 12:47:49Z vboxsync $ */
2/** @file
3 * VBoxManage - help and other message output.
4 */
5
6/*
7 * Copyright (C) 2006-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#include <VBox/version.h>
23
24#include <iprt/asm.h>
25#include <iprt/buildconfig.h>
26#include <iprt/ctype.h>
27#include <iprt/assert.h>
28#include <iprt/env.h>
29#include <iprt/err.h>
30#include <iprt/getopt.h>
31#include <iprt/stream.h>
32#include <iprt/message.h>
33#include <iprt/uni.h>
34
35#include "VBoxManage.h"
36
37
38/*********************************************************************************************************************************
39* Defined Constants And Macros *
40*********************************************************************************************************************************/
41/** If the usage is the given number of length long or longer, the error is
42 * repeated so the user can actually see it. */
43#define ERROR_REPEAT_AFTER_USAGE_LENGTH 16
44
45
46/*********************************************************************************************************************************
47* Global Variables *
48*********************************************************************************************************************************/
49DECLARE_TRANSLATION_CONTEXT(Help);
50
51static enum HELP_CMD_VBOXMANAGE g_enmCurCommand = HELP_CMD_COMMON;
52/** The scope mask for the current subcommand. */
53static uint64_t g_fCurSubcommandScope = RTMSGREFENTRYSTR_SCOPE_GLOBAL;
54
55/**
56 * Sets the current command.
57 *
58 * This affects future calls to error and help functions.
59 *
60 * @param enmCommand The command.
61 */
62void setCurrentCommand(enum HELP_CMD_VBOXMANAGE enmCommand)
63{
64 Assert(g_enmCurCommand == HELP_CMD_COMMON);
65 g_enmCurCommand = enmCommand;
66 g_fCurSubcommandScope = RTMSGREFENTRYSTR_SCOPE_GLOBAL;
67}
68
69
70/**
71 * Sets the current subcommand.
72 *
73 * This affects future calls to error and help functions.
74 *
75 * @param fSubcommandScope The subcommand scope.
76 */
77void setCurrentSubcommand(uint64_t fSubcommandScope)
78{
79 g_fCurSubcommandScope = fSubcommandScope;
80}
81
82
83/**
84 * Takes first char and make it uppercase.
85 *
86 * @returns pointer to string starting from next char.
87 * @param pszSrc Source string.
88 * @param pszDst Pointer to buffer to place first char uppercase.
89 */
90static const char *captialize(const char *pszSrc, char *pszDst)
91{
92 *RTStrPutCp(pszDst, RTUniCpToUpper(RTStrGetCp(pszSrc))) = '\0';
93 return RTStrNextCp(pszSrc);
94}
95
96
97/**
98 * Prints brief help for a command or subcommand.
99 *
100 * @returns Number of lines written.
101 * @param enmCommand The command.
102 * @param fSubcommandScope The subcommand scope, REFENTRYSTR_SCOPE_GLOBAL
103 * for all.
104 * @param pStrm The output stream.
105 */
106static uint32_t printBriefCommandOrSubcommandHelp(enum HELP_CMD_VBOXMANAGE enmCommand, uint64_t fSubcommandScope, PRTSTREAM pStrm)
107{
108 /*
109 * Try to find translated, falling back untranslated.
110 */
111 uint32_t cLinesWritten = 0;
112 uint32_t cPendingBlankLines = 0;
113 uint32_t cFound = 0;
114 PCHELP_LANG_ENTRY_T const apHelpLangEntries[] =
115 {
116 ASMAtomicUoReadPtrT(&g_pHelpLangEntry, PCHELP_LANG_ENTRY_T),
117#ifdef VBOX_WITH_VBOXMANAGE_NLS
118 &g_aHelpLangEntries[0]
119#endif
120 };
121 for (uint32_t k = 0; k < RT_ELEMENTS(apHelpLangEntries) && cFound == 0; k++)
122 {
123 /* skip if english is used */
124 if (k > 0 && apHelpLangEntries[k] == apHelpLangEntries[0])
125 break;
126 uint32_t const cHelpEntries = *apHelpLangEntries[k]->pcHelpEntries;
127 for (uint32_t i = 0; i < cHelpEntries; i++)
128 {
129 PCRTMSGREFENTRY pHelp = apHelpLangEntries[k]->papHelpEntries[i];
130 if ( pHelp->idInternal == (int64_t)enmCommand
131 || enmCommand == HELP_CMD_COMMON)
132 {
133 cFound++;
134 if (cFound == 1)
135 {
136 if (fSubcommandScope == RTMSGREFENTRYSTR_SCOPE_GLOBAL)
137 {
138 char szFirstChar[8];
139 RTStrmPrintf(pStrm, Help::tr("Usage - %s%s:\n"), szFirstChar, captialize(pHelp->pszBrief, szFirstChar));
140 }
141 else
142 RTStrmPrintf(pStrm, Help::tr("Usage:\n"));
143 }
144 RTMsgRefEntryPrintStringTable(pStrm, &pHelp->Synopsis, fSubcommandScope, &cPendingBlankLines, &cLinesWritten);
145 if (!cPendingBlankLines)
146 cPendingBlankLines = 1;
147 }
148 }
149 }
150 Assert(cFound > 0);
151 return cLinesWritten;
152}
153
154
155/**
156 * Prints the brief usage information for the current (sub)command.
157 *
158 * @param pStrm The output stream.
159 */
160void printUsage(PRTSTREAM pStrm)
161{
162 printBriefCommandOrSubcommandHelp(g_enmCurCommand, g_fCurSubcommandScope, pStrm);
163}
164
165
166/**
167 * Prints full help for a command or subcommand.
168 *
169 * @param enmCommand The command.
170 * @param fSubcommandScope The subcommand scope, REFENTRYSTR_SCOPE_GLOBAL
171 * for all.
172 * @param pStrm The output stream.
173 */
174static void printFullCommandOrSubcommandHelp(enum HELP_CMD_VBOXMANAGE enmCommand, uint64_t fSubcommandScope, PRTSTREAM pStrm)
175{
176 /* Try to find translated, then untranslated */
177 uint32_t cPendingBlankLines = 0;
178 uint32_t cFound = 0;
179 PCHELP_LANG_ENTRY_T const apHelpLangEntries[] =
180 {
181 ASMAtomicUoReadPtrT(&g_pHelpLangEntry, PCHELP_LANG_ENTRY_T),
182#ifdef VBOX_WITH_VBOXMANAGE_NLS
183 &g_aHelpLangEntries[0]
184#endif
185 };
186 for (uint32_t k = 0; k < RT_ELEMENTS(apHelpLangEntries) && cFound == 0; k++)
187 {
188 /* skip if english is used */
189 if (k > 0 && apHelpLangEntries[k] == apHelpLangEntries[0])
190 break;
191 uint32_t const cHelpEntries = *apHelpLangEntries[k]->pcHelpEntries;
192 for (uint32_t i = 0; i < cHelpEntries; i++)
193 {
194 PCRTMSGREFENTRY pHelp = apHelpLangEntries[k]->papHelpEntries[i];
195
196 if ( pHelp->idInternal == (int64_t)enmCommand
197 || enmCommand == HELP_CMD_COMMON)
198 {
199 cFound++;
200 RTMsgRefEntryPrintStringTable(pStrm, &pHelp->Help, fSubcommandScope, &cPendingBlankLines, NULL /*pcLinesWritten*/);
201 if (cPendingBlankLines < 2)
202 cPendingBlankLines = 2;
203 }
204 }
205 }
206 Assert(cFound > 0);
207}
208
209
210/**
211 * Prints the full help for the current (sub)command.
212 *
213 * @param pStrm The output stream.
214 */
215void printHelp(PRTSTREAM pStrm)
216{
217 printFullCommandOrSubcommandHelp(g_enmCurCommand, g_fCurSubcommandScope, pStrm);
218}
219
220
221/**
222 * Display no subcommand error message and current command usage.
223 *
224 * @returns RTEXITCODE_SYNTAX.
225 */
226RTEXITCODE errorNoSubcommand(void)
227{
228 Assert(g_enmCurCommand != HELP_CMD_VBOXMANAGE_INVALID);
229 Assert(g_fCurSubcommandScope == RTMSGREFENTRYSTR_SCOPE_GLOBAL);
230
231 return errorSyntax(Help::tr("No subcommand specified"));
232}
233
234
235/**
236 * Display unknown subcommand error message and current command usage.
237 *
238 * May show full command help instead if the subcommand is a common help option.
239 *
240 * @returns RTEXITCODE_SYNTAX, or RTEXITCODE_SUCCESS if common help option.
241 * @param pszSubcommand The name of the alleged subcommand.
242 */
243RTEXITCODE errorUnknownSubcommand(const char *pszSubcommand)
244{
245 Assert(g_enmCurCommand != HELP_CMD_VBOXMANAGE_INVALID);
246 Assert(g_fCurSubcommandScope == RTMSGREFENTRYSTR_SCOPE_GLOBAL);
247
248 /* check if help was requested. */
249 if ( strcmp(pszSubcommand, "--help") == 0
250 || strcmp(pszSubcommand, "-h") == 0
251 || strcmp(pszSubcommand, "-?") == 0)
252 {
253 printFullCommandOrSubcommandHelp(g_enmCurCommand, g_fCurSubcommandScope, g_pStdOut);
254 return RTEXITCODE_SUCCESS;
255 }
256
257 return errorSyntax(Help::tr("Unknown subcommand: %s"), pszSubcommand);
258}
259
260
261/**
262 * Display too many parameters error message and current command usage.
263 *
264 * May show full command help instead if the subcommand is a common help option.
265 *
266 * @returns RTEXITCODE_SYNTAX, or RTEXITCODE_SUCCESS if common help option.
267 * @param papszArgs The first unwanted parameter. Terminated by
268 * NULL entry.
269 */
270RTEXITCODE errorTooManyParameters(char **papszArgs)
271{
272 Assert(g_enmCurCommand != HELP_CMD_VBOXMANAGE_INVALID);
273 Assert(g_fCurSubcommandScope != RTMSGREFENTRYSTR_SCOPE_GLOBAL);
274
275 /* check if help was requested. */
276 if (papszArgs)
277 {
278 for (uint32_t i = 0; papszArgs[i]; i++)
279 if ( strcmp(papszArgs[i], "--help") == 0
280 || strcmp(papszArgs[i], "-h") == 0
281 || strcmp(papszArgs[i], "-?") == 0)
282 {
283 printFullCommandOrSubcommandHelp(g_enmCurCommand, g_fCurSubcommandScope, g_pStdOut);
284 return RTEXITCODE_SUCCESS;
285 }
286 else if (!strcmp(papszArgs[i], "--"))
287 break;
288 }
289
290 return errorSyntax(Help::tr("Too many parameters"));
291}
292
293
294/**
295 * Display current (sub)command usage and the custom error message.
296 *
297 * @returns RTEXITCODE_SYNTAX.
298 * @param pszFormat Custom error message format string.
299 * @param va Format arguments.
300 */
301RTEXITCODE errorSyntaxV(const char *pszFormat, va_list va)
302{
303 Assert(g_enmCurCommand != HELP_CMD_VBOXMANAGE_INVALID);
304
305 showLogo(g_pStdErr);
306
307 va_list vaCopy;
308 va_copy(vaCopy, va);
309 RTMsgErrorV(pszFormat, vaCopy);
310 va_end(vaCopy);
311
312 RTStrmPutCh(g_pStdErr, '\n');
313 if ( printBriefCommandOrSubcommandHelp(g_enmCurCommand, g_fCurSubcommandScope, g_pStdErr)
314 >= ERROR_REPEAT_AFTER_USAGE_LENGTH)
315 {
316 /* Usage was very long, repeat the error message. */
317 RTStrmPutCh(g_pStdErr, '\n');
318 RTMsgErrorV(pszFormat, va);
319 }
320 return RTEXITCODE_SYNTAX;
321}
322
323
324/**
325 * Display current (sub)command usage and the custom error message.
326 *
327 * @returns RTEXITCODE_SYNTAX.
328 * @param pszFormat Custom error message format string.
329 * @param ... Format arguments.
330 */
331RTEXITCODE errorSyntax(const char *pszFormat, ...)
332{
333 va_list va;
334 va_start(va, pszFormat);
335 RTEXITCODE rcExit = errorSyntaxV(pszFormat, va);
336 va_end(va);
337 return rcExit;
338}
339
340
341/**
342 * Display current (sub)command usage and the custom error message.
343 *
344 * @returns E_INVALIDARG
345 * @param pszFormat Custom error message format string.
346 * @param ... Format arguments.
347 */
348HRESULT errorSyntaxHr(const char *pszFormat, ...)
349{
350 va_list va;
351 va_start(va, pszFormat);
352 errorSyntaxV(pszFormat, va);
353 va_end(va);
354 return E_INVALIDARG;
355}
356
357
358/**
359 * Print an error message without the syntax stuff.
360 *
361 * @returns RTEXITCODE_SYNTAX.
362 */
363RTEXITCODE errorArgument(const char *pszFormat, ...)
364{
365 va_list args;
366 va_start(args, pszFormat);
367 RTMsgErrorV(pszFormat, args);
368 va_end(args);
369 return RTEXITCODE_SYNTAX;
370}
371
372
373/**
374 * Print an error message without the syntax stuff.
375 *
376 * @returns E_INVALIDARG.
377 */
378HRESULT errorArgumentHr(const char *pszFormat, ...)
379{
380 va_list args;
381 va_start(args, pszFormat);
382 RTMsgErrorV(pszFormat, args);
383 va_end(args);
384 return E_INVALIDARG;
385}
386
387
388/**
389 * Worker for errorGetOpt.
390 *
391 * @param rcGetOpt The RTGetOpt return value.
392 * @param pValueUnion The value union returned by RTGetOpt.
393 */
394static void errorGetOptWorker(int rcGetOpt, union RTGETOPTUNION const *pValueUnion)
395{
396 if (rcGetOpt == VINF_GETOPT_NOT_OPTION)
397 RTMsgError(Help::tr("Invalid parameter '%s'"), pValueUnion->psz);
398 else if (rcGetOpt > 0)
399 {
400 if (RT_C_IS_PRINT(rcGetOpt))
401 RTMsgError(Help::tr("Invalid option -%c"), rcGetOpt);
402 else
403 RTMsgError(Help::tr("Invalid option case %i"), rcGetOpt);
404 }
405 else if (rcGetOpt == VERR_GETOPT_UNKNOWN_OPTION)
406 RTMsgError(Help::tr("Unknown option: %s"), pValueUnion->psz);
407 else if (rcGetOpt == VERR_GETOPT_INVALID_ARGUMENT_FORMAT)
408 RTMsgError(Help::tr("Invalid argument format: %s"), pValueUnion->psz);
409 else if (pValueUnion->pDef)
410 RTMsgError("%s: %Rrs", pValueUnion->pDef->pszLong, rcGetOpt);
411 else
412 RTMsgError("%Rrs", rcGetOpt);
413}
414
415
416/**
417 * For use to deal with RTGetOptFetchValue failures.
418 *
419 * @retval RTEXITCODE_SYNTAX
420 * @param iValueNo The value number being fetched, counting the
421 * RTGetOpt value as zero and the first
422 * RTGetOptFetchValue call as one.
423 * @param pszOption The option being parsed.
424 * @param rcGetOptFetchValue The status returned by RTGetOptFetchValue.
425 * @param pValueUnion The value union returned by the fetch.
426 */
427RTEXITCODE errorFetchValue(int iValueNo, const char *pszOption, int rcGetOptFetchValue, union RTGETOPTUNION const *pValueUnion)
428{
429 Assert(g_enmCurCommand != HELP_CMD_VBOXMANAGE_INVALID);
430 showLogo(g_pStdErr);
431 if (rcGetOptFetchValue == VERR_GETOPT_REQUIRED_ARGUMENT_MISSING)
432 RTMsgError(Help::tr("Missing the %u%s value for option %s"),
433 iValueNo,
434 iValueNo == 1 ? Help::tr("st")
435 : iValueNo == 2 ? Help::tr("nd")
436 : iValueNo == 3 ? Help::tr("rd")
437 : Help::tr("th"),
438 pszOption);
439 else
440 errorGetOptWorker(rcGetOptFetchValue, pValueUnion);
441 return RTEXITCODE_SYNTAX;
442
443}
444
445
446/**
447 * Handled an RTGetOpt error or common option.
448 *
449 * This implements the 'V' and 'h' cases. It reports appropriate syntax errors
450 * for other @a rcGetOpt values.
451 *
452 * @retval RTEXITCODE_SUCCESS if help or version request.
453 * @retval RTEXITCODE_SYNTAX if not help or version request.
454 * @param rcGetOpt The RTGetOpt return value.
455 * @param pValueUnion The value union returned by RTGetOpt.
456 */
457RTEXITCODE errorGetOpt(int rcGetOpt, union RTGETOPTUNION const *pValueUnion)
458{
459 Assert(g_enmCurCommand != HELP_CMD_VBOXMANAGE_INVALID);
460
461 /*
462 * Check if it is an unhandled standard option.
463 */
464 if (rcGetOpt == 'V')
465 {
466 RTPrintf("%sr%d\n", VBOX_VERSION_STRING, RTBldCfgRevision());
467 return RTEXITCODE_SUCCESS;
468 }
469
470 if (rcGetOpt == 'h')
471 {
472 printFullCommandOrSubcommandHelp(g_enmCurCommand, g_fCurSubcommandScope, g_pStdOut);
473 return RTEXITCODE_SUCCESS;
474 }
475
476 /*
477 * We failed.
478 */
479 showLogo(g_pStdErr);
480 errorGetOptWorker(rcGetOpt, pValueUnion);
481 if ( printBriefCommandOrSubcommandHelp(g_enmCurCommand, g_fCurSubcommandScope, g_pStdErr)
482 >= ERROR_REPEAT_AFTER_USAGE_LENGTH)
483 {
484 /* Usage was very long, repeat the error message. */
485 RTStrmPutCh(g_pStdErr, '\n');
486 errorGetOptWorker(rcGetOpt, pValueUnion);
487 }
488 return RTEXITCODE_SYNTAX;
489}
490
491
492void showLogo(PRTSTREAM pStrm)
493{
494 static bool s_fShown; /* show only once */
495
496 if (!s_fShown)
497 {
498 RTStrmPrintf(pStrm, VBOX_PRODUCT " Command Line Management Interface Version "
499 VBOX_VERSION_STRING "\n"
500 "(C) 2005-" VBOX_C_YEAR " " VBOX_VENDOR "\n"
501 "All rights reserved.\n"
502 "\n");
503 s_fShown = true;
504 }
505}
Note: See TracBrowser for help on using the repository browser.

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