VirtualBox

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

Last change on this file since 78052 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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