VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/misc/messagerefentry.cpp

Last change on this file was 98103, checked in by vboxsync, 16 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: 11.6 KB
Line 
1/* $Id: messagerefentry.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * IPRT - Program usage and help formatting.
4 */
5
6/*
7 * Copyright (C) 2009-2023 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 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#include "internal/iprt.h"
42#include <iprt/message.h>
43
44#include <iprt/env.h>
45#include <iprt/errcore.h>
46#include <iprt/path.h>
47#include <iprt/string.h>
48#include <iprt/stream.h>
49#include "internal/process.h"
50
51
52/*********************************************************************************************************************************
53* Global Variables *
54*********************************************************************************************************************************/
55/** Spaces for intending. */
56static const char g_szSpaces[] = " ";
57
58
59/**
60 * Retruns the width for the given handle.
61 *
62 * @returns Screen width.
63 * @param pStrm The stream, g_pStdErr or g_pStdOut.
64 */
65static uint32_t getScreenWidth(PRTSTREAM pStrm)
66{
67 static uint32_t s_acch[2] = { 0, 0 };
68 uint32_t iWhich = pStrm == g_pStdErr ? 1 : 0;
69 uint32_t cch = s_acch[iWhich];
70 if (cch)
71 return cch;
72
73 const char *psz = RTEnvGet("IPRT_SCREEN_WIDTH");
74 if ( !psz
75 || RTStrToUInt32Full(psz, 0, &cch) != VINF_SUCCESS
76 || cch == 0)
77 {
78 int rc = RTStrmQueryTerminalWidth(pStrm, &cch);
79 if (rc == VERR_INVALID_FUNCTION)
80 {
81 /* It's not a console, but in case we're being piped to less/more/list
82 we look for a console handle on the other standard output handle
83 and standard input. (Latter doesn't work on windows.) */
84 rc = RTStrmQueryTerminalWidth(pStrm == g_pStdErr ? g_pStdOut : g_pStdErr, &cch);
85 if (rc == VERR_INVALID_FUNCTION || rc == VERR_INVALID_HANDLE)
86 rc = RTStrmQueryTerminalWidth(g_pStdIn, &cch);
87 if (RT_FAILURE(rc))
88 cch = 80;
89 }
90 }
91
92 s_acch[iWhich] = cch;
93 return cch;
94}
95
96
97/**
98 * Prints a string table string (paragraph), performing non-breaking-space
99 * replacement and wrapping.
100 *
101 * @returns IRPT status code.
102 * @param pStrm The output stream.
103 * @param psz The string table string to print.
104 * @param cchMaxWidth The maximum output width.
105 * @param fFlags String flags that may affect formatting.
106 * @param pcLinesWritten Pointer to variable to update with written lines.
107 */
108static int printString(PRTSTREAM pStrm, const char *psz, uint32_t cchMaxWidth, uint64_t fFlags, uint32_t *pcLinesWritten)
109{
110 uint32_t cLinesWritten;
111 size_t cch = strlen(psz);
112 const char *pszNbsp = strchr(psz, RTMSGREFENTRY_NBSP);
113 int rc;
114
115 /*
116 * No-wrap case is simpler, so handle that separately.
117 */
118 if (cch <= cchMaxWidth)
119 {
120 if (!pszNbsp)
121 rc = RTStrmWrite(pStrm, psz, cch);
122 else
123 {
124 do
125 {
126 rc = RTStrmWrite(pStrm, psz, pszNbsp - psz);
127 if (RT_SUCCESS(rc))
128 rc = RTStrmPutCh(pStrm, ' ');
129 psz = pszNbsp + 1;
130 pszNbsp = strchr(psz, RTMSGREFENTRY_NBSP);
131 } while (pszNbsp && RT_SUCCESS(rc));
132 if (RT_SUCCESS(rc))
133 rc = RTStrmWrite(pStrm, psz, strlen(psz));
134 }
135 if (RT_SUCCESS(rc))
136 rc = RTStrmPutCh(pStrm, '\n');
137 cLinesWritten = 1;
138 }
139 /*
140 * We need to wrap stuff, too bad.
141 */
142 else
143 {
144 /* Figure the paragraph indent level first. */
145 uint32_t cchIndent = 0;
146 while (*psz == ' ')
147 cchIndent++, psz++;
148 Assert(cchIndent + 4 + 1 <= RT_ELEMENTS(g_szSpaces));
149
150 if (cchIndent + 8 >= cchMaxWidth)
151 cchMaxWidth += cchIndent + 8;
152
153 /* Work our way thru the string, line by line. */
154 uint32_t cchHangingIndent = 0;
155 cLinesWritten = 0;
156 do
157 {
158 rc = RTStrmWrite(pStrm, g_szSpaces, cchIndent + cchHangingIndent);
159 if (RT_FAILURE(rc))
160 break;
161
162 size_t offLine = cchIndent + cchHangingIndent;
163 bool fPendingSpace = false;
164 do
165 {
166 const char *pszSpace = strchr(psz, ' ');
167 size_t cchWord = pszSpace ? pszSpace - psz : strlen(psz);
168 if ( offLine + cchWord + fPendingSpace > cchMaxWidth
169 && offLine != cchIndent
170 && fPendingSpace /* don't stop before first word */)
171 break;
172
173 pszNbsp = (const char *)memchr(psz, RTMSGREFENTRY_NBSP, cchWord);
174 while (pszNbsp)
175 {
176 size_t cchSubWord = pszNbsp - psz;
177 if (fPendingSpace)
178 {
179 rc = RTStrmPutCh(pStrm, ' ');
180 if (RT_FAILURE(rc))
181 break;
182 }
183 rc = RTStrmWrite(pStrm, psz, cchSubWord);
184 if (RT_FAILURE(rc))
185 break;
186 offLine += cchSubWord + fPendingSpace;
187 psz += cchSubWord + 1;
188 cchWord -= cchSubWord + 1;
189 pszNbsp = (const char *)memchr(psz, RTMSGREFENTRY_NBSP, cchWord);
190 fPendingSpace = true;
191 }
192 if (RT_FAILURE(rc))
193 break;
194
195 if (fPendingSpace)
196 {
197 rc = RTStrmPutCh(pStrm, ' ');
198 if (RT_FAILURE(rc))
199 break;
200 }
201 rc = RTStrmWrite(pStrm, psz, cchWord);
202 if (RT_FAILURE(rc))
203 break;
204
205 offLine += cchWord + fPendingSpace;
206 psz = pszSpace ? pszSpace + 1 : strchr(psz, '\0');
207 fPendingSpace = true;
208 } while (offLine < cchMaxWidth && *psz != '\0' && RT_SUCCESS(rc));
209
210 if (RT_SUCCESS(rc))
211 rc = RTStrmPutCh(pStrm, '\n');
212 if (RT_FAILURE(rc))
213 break;
214 cLinesWritten++;
215
216 /* Set up hanging indent if relevant. */
217 if (fFlags & RTMSGREFENTRYSTR_FLAGS_SYNOPSIS)
218 cchHangingIndent = 4;
219 } while (*psz != '\0');
220 }
221 *pcLinesWritten += cLinesWritten;
222 return rc;
223}
224
225
226/**
227 * Checks if the given string is empty (only spaces).
228 * @returns true if empty, false if not.
229 * @param psz The string to examine.
230 */
231DECLINLINE(bool) isEmptyString(const char *psz)
232{
233 char ch;
234 while ((ch = *psz) == ' ')
235 psz++;
236 return ch == '\0';
237}
238
239
240/**
241 * Prints a string table.
242 *
243 * @returns Current number of pending blank lines.
244 * @param pStrm The output stream.
245 * @param pStrTab The string table.
246 * @param fScope The selection scope.
247 * @param pcPendingBlankLines In: Pending blank lines from previous string
248 * table. Out: Pending blank lines.
249 * @param pcLinesWritten Pointer to variable that should be incremented
250 * by the number of lines written. Optional.
251 */
252RTDECL(int) RTMsgRefEntryPrintStringTable(PRTSTREAM pStrm, PCRTMSGREFENTRYSTRTAB pStrTab, uint64_t fScope,
253 uint32_t *pcPendingBlankLines, uint32_t *pcLinesWritten)
254{
255 uint32_t cPendingBlankLines = pcPendingBlankLines ? *pcPendingBlankLines : 0;
256 uint32_t cLinesWritten = 0;
257 uint32_t cchWidth = getScreenWidth(pStrm) - 1; /* (Seems a -1 here is prudent, at least on windows.) */
258 uint64_t fPrevScope = fScope;
259 int rc = VINF_SUCCESS;
260 for (uint32_t i = 0; i < pStrTab->cStrings; i++)
261 {
262 uint64_t fCurScope = pStrTab->paStrings[i].fScope;
263 if ((fCurScope & RTMSGREFENTRYSTR_SCOPE_MASK) == RTMSGREFENTRYSTR_SCOPE_SAME)
264 {
265 fCurScope &= ~RTMSGREFENTRYSTR_SCOPE_MASK;
266 fCurScope |= (fPrevScope & RTMSGREFENTRYSTR_SCOPE_MASK);
267 }
268 if (fCurScope & RTMSGREFENTRYSTR_SCOPE_MASK & fScope)
269 {
270 const char *psz = pStrTab->paStrings[i].psz;
271 if (psz && !isEmptyString(psz))
272 {
273 while (cPendingBlankLines > 0 && RT_SUCCESS(rc))
274 {
275 cPendingBlankLines--;
276 rc = RTStrmPutCh(pStrm, '\n');
277 cLinesWritten++;
278 }
279 if (RT_SUCCESS(rc))
280 rc = printString(pStrm, psz, cchWidth, fCurScope & RTMSGREFENTRYSTR_FLAGS_MASK, &cLinesWritten);
281 if (RT_FAILURE(rc))
282 break;
283 }
284 else
285 cPendingBlankLines++;
286 }
287 fPrevScope = fCurScope;
288 }
289
290 if (pcLinesWritten)
291 *pcLinesWritten += cLinesWritten;
292 if (pcPendingBlankLines)
293 *pcPendingBlankLines = cPendingBlankLines;
294 return rc;
295}
296
297
298RTDECL(int) RTMsgRefEntrySynopsisEx(PRTSTREAM pStrm, PCRTMSGREFENTRY pEntry, uint64_t fScope, uint32_t fFlags)
299{
300 AssertReturn(!(fFlags & ~RTMSGREFENTRY_SYNOPSIS_F_USAGE), VERR_INVALID_FLAGS);
301
302 if (!pStrm)
303 pStrm = g_pStdOut;
304 int rc = VINF_SUCCESS;
305 if (fFlags & RTMSGREFENTRY_SYNOPSIS_F_USAGE)
306 RTStrmPutStr(pStrm, "Usage: ");
307 if (RT_SUCCESS(rc))
308 rc = RTMsgRefEntryPrintStringTable(pStrm, &pEntry->Synopsis, fScope, NULL, NULL);
309 return rc;
310}
311
312
313RTDECL(int) RTMsgRefEntrySynopsis(PRTSTREAM pStrm, PCRTMSGREFENTRY pEntry)
314{
315 return RTMsgRefEntrySynopsisEx(pStrm, pEntry, UINT64_MAX, true /*fPrintUsage*/);
316}
317
318
319RTDECL(int) RTMsgRefEntryHelpEx(PRTSTREAM pStrm, PCRTMSGREFENTRY pEntry, uint64_t fScope, uint32_t fFlags)
320{
321 AssertReturn(!fFlags, VERR_INVALID_FLAGS);
322 if (!pStrm)
323 pStrm = g_pStdOut;
324 return RTMsgRefEntryPrintStringTable(pStrm, &pEntry->Help, fScope, NULL, NULL);
325}
326
327
328RTDECL(int) RTMsgRefEntryHelp(PRTSTREAM pStrm, PCRTMSGREFENTRY pEntry)
329{
330 return RTMsgRefEntryHelpEx(pStrm, pEntry, UINT64_MAX, 0 /*fFlags*/);
331}
332
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use