VirtualBox

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

Last change on this file since 69222 was 69111, checked in by vboxsync, 7 years ago

(C) year

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