VirtualBox

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

Last change on this file since 106842 was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

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