VirtualBox

Changeset 95586 in vbox for trunk/src/VBox/Runtime/r3


Ignore:
Timestamp:
Jul 11, 2022 9:51:30 AM (3 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
152218
Message:

IPRT: Added RTStrmWrappedPrintf and RTStrmWrappedPrintfV for some simple output wrapping accoring to the terminal size (80 columns if not terminal).

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Runtime/r3/stream.cpp

    r93115 r95586  
    4545#include <iprt/assert.h>
    4646#include <iprt/alloc.h>
     47#include <iprt/ctype.h>
    4748#include <iprt/err.h>
    4849#include <iprt/param.h>
     
    9899#endif
    99100} RTSTREAM;
     101
     102
     103/**
     104 * State for wrapped output (RTStrmWrappedPrintf, RTStrmWrappedPrintfV).
     105 */
     106typedef struct RTSTRMWRAPPEDSTATE
     107{
     108    PRTSTREAM   pStream;            /**< The output stream. */
     109    uint32_t    cchWidth;           /**< The line width. */
     110    uint32_t    cchLine;            /**< The current line length (valid chars in szLine). */
     111    uint32_t    cLines;             /**< Number of lines written. */
     112    uint32_t    cchIndent;          /**< The indent (determined from the first line). */
     113    int         rcStatus;           /**< The output status. */
     114    uint8_t     cchHangingIndent;   /**< Hanging indent (from fFlags). */
     115    char        szLine[0x1000+1];   /**< We must buffer output so we can do proper word splitting. */
     116} RTSTRMWRAPPEDSTATE;
    100117
    101118
     
    12911308}
    12921309
     1310
     1311
     1312#define RTSTRMWRAPPED_F_LINE_OFFSET_MASK            UINT32_C(0x00000fff)
     1313#define RTSTRMWRAPPED_F_NON_TERMINAL_WIDTH_MASK     UINT32_C(0x000ff000)
     1314#define RTSTRMWRAPPED_F_NON_TERMINAL_WIDTH_SHIFT    12
     1315#define RTSTRMWRAPPED_F_HANGING_INDENT_MASK         UINT32_C(0x01f00000)
     1316#define RTSTRMWRAPPED_F_HANGING_INDENT_SHIFT        20
     1317#define RTSTRMWRAPPED_F_HANGING_INDENT              UINT32_C(0x80000000)
     1318
     1319/**
     1320 * Outputs @a cchIndent spaces.
     1321 */
     1322static void rtStrmWrapppedIndent(RTSTRMWRAPPEDSTATE *pState, uint32_t cchIndent)
     1323{
     1324    static const char s_szSpaces[] = "                                                ";
     1325    while (cchIndent)
     1326    {
     1327        uint32_t cchToWrite = RT_MIN(cchIndent, sizeof(s_szSpaces) - 1);
     1328        int rc = RTStrmWrite(pState->pStream, s_szSpaces, cchToWrite);
     1329        if (RT_SUCCESS(rc))
     1330            cchIndent -= cchToWrite;
     1331        else
     1332        {
     1333            pState->rcStatus = rc;
     1334            break;
     1335        }
     1336    }
     1337}
     1338
     1339
     1340/**
     1341 * Flushes the current line.
     1342 *
     1343 * @param   pState      The wrapped output state.
     1344 * @param   fPartial    Set if partial flush due to buffer overflow, clear when
     1345 *                      flushing due to '\n'.
     1346 */
     1347static void rtStrmWrappedFlushLine(RTSTRMWRAPPEDSTATE *pState, bool fPartial)
     1348{
     1349    /*
     1350     * Check indentation in case we need to split the line later.
     1351     */
     1352    uint32_t cchIndent = pState->cchIndent;
     1353    if (cchIndent == UINT32_MAX)
     1354    {
     1355        pState->cchIndent = 0;
     1356        cchIndent = pState->cchHangingIndent;
     1357        while (RT_C_IS_BLANK(pState->szLine[cchIndent]))
     1358            cchIndent++;
     1359    }
     1360
     1361    /*
     1362     * Do the flushing.
     1363     */
     1364    uint32_t cchLine = pState->cchLine;
     1365    Assert(cchLine < sizeof(pState->szLine));
     1366    while (cchLine >= pState->cchWidth || !fPartial)
     1367    {
     1368        /*
     1369         * Hopefully we don't need to do any wrapping ...
     1370         */
     1371        uint32_t offSplit;
     1372        if (pState->cchIndent + cchLine <= pState->cchWidth)
     1373        {
     1374            if (!fPartial)
     1375            {
     1376                rtStrmWrapppedIndent(pState, pState->cchIndent);
     1377                pState->szLine[cchLine] = '\n';
     1378                int rc = RTStrmWrite(pState->pStream, pState->szLine, cchLine + 1);
     1379                if (RT_FAILURE(rc))
     1380                    pState->rcStatus = rc;
     1381                pState->cLines   += 1;
     1382                pState->cchLine   = 0;
     1383                pState->cchIndent = UINT32_MAX;
     1384                return;
     1385            }
     1386
     1387            /*
     1388             * ... no such luck.
     1389             */
     1390            offSplit = cchLine;
     1391        }
     1392        else
     1393            offSplit = pState->cchWidth - pState->cchIndent;
     1394
     1395        /* Find the start of the current word: */
     1396        while (offSplit > 0 && !RT_C_IS_BLANK(pState->szLine[offSplit - 1]))
     1397            offSplit--;
     1398
     1399        /* Skip spaces. */
     1400        while (offSplit > 0 && RT_C_IS_BLANK(pState->szLine[offSplit - 1]))
     1401            offSplit--;
     1402        uint32_t offNextLine = offSplit;
     1403
     1404        /* If the first word + indent is wider than the screen width, so just output it in full. */
     1405        if (offSplit == 0) /** @todo Split words, look for hyphen...  This code is currently a bit crude. */
     1406        {
     1407            while (offSplit < cchLine && !RT_C_IS_BLANK(pState->szLine[offSplit]))
     1408                offSplit++;
     1409            offNextLine = offSplit;
     1410        }
     1411
     1412        while (offNextLine < cchLine && RT_C_IS_BLANK(pState->szLine[offNextLine]))
     1413            offNextLine++;
     1414
     1415        /*
     1416         * Output and advance.
     1417         */
     1418        rtStrmWrapppedIndent(pState, pState->cchIndent);
     1419        int rc = RTStrmWrite(pState->pStream, pState->szLine, offSplit);
     1420        if (RT_SUCCESS(rc))
     1421            rc = RTStrmPutCh(pState->pStream, '\n');
     1422        if (RT_FAILURE(rc))
     1423            pState->rcStatus = rc;
     1424
     1425        cchLine -= offNextLine;
     1426        pState->cchLine   = cchLine;
     1427        pState->cLines   += 1;
     1428        pState->cchIndent = cchIndent;
     1429        memmove(&pState->szLine[0], &pState->szLine[offNextLine], cchLine);
     1430    }
     1431
     1432    /* The indentation level is reset for each '\n' we process, so only save cchIndent if partial. */
     1433    pState->cchIndent = fPartial ? cchIndent : UINT32_MAX;
     1434}
     1435
     1436
     1437/**
     1438 * @callback_method_impl{FNRTSTROUTPUT}
     1439 */
     1440static DECLCALLBACK(size_t) rtStrmWrappedOutput(void *pvArg, const char *pachChars, size_t cbChars)
     1441{
     1442    RTSTRMWRAPPEDSTATE *pState = (RTSTRMWRAPPEDSTATE *)pvArg;
     1443    size_t const cchRet = cbChars;
     1444    while (cbChars > 0)
     1445    {
     1446        if (*pachChars == '\n')
     1447        {
     1448            rtStrmWrappedFlushLine(pState, false /*fPartial*/);
     1449            pachChars++;
     1450            cbChars--;
     1451        }
     1452        else
     1453        {
     1454            const char *pszEol      = (const char *)memchr(pachChars, '\n', cbChars);
     1455            size_t      cchToCopy   = pszEol ? (size_t)(pszEol - pachChars) : cbChars;
     1456            uint32_t    cchLine     = pState->cchLine;
     1457            Assert(cchLine < sizeof(pState->szLine));
     1458            bool const  fFlush      = cchLine + cchToCopy >= sizeof(pState->szLine);
     1459            if (fFlush)
     1460                cchToCopy = cchToCopy - sizeof(pState->szLine) - 1;
     1461
     1462            pState->cchLine = cchLine + (uint32_t)cchToCopy;
     1463            memcpy(&pState->szLine[cchLine], pachChars, cchToCopy);
     1464
     1465            pachChars += cchToCopy;
     1466            cbChars   -= cchToCopy;
     1467
     1468            if (fFlush)
     1469                rtStrmWrappedFlushLine(pState, true /*fPartial*/);
     1470        }
     1471    }
     1472    return cchRet;
     1473}
     1474
     1475
     1476RTDECL(int32_t) RTStrmWrappedPrintfV(PRTSTREAM pStream, uint32_t fFlags, const char *pszFormat, va_list va)
     1477{
     1478    /*
     1479     * Figure the output width and set up the rest of the output state.
     1480     */
     1481    RTSTRMWRAPPEDSTATE State;
     1482    State.pStream           = pStream;
     1483    State.cchLine           = fFlags & RTSTRMWRAPPED_F_LINE_OFFSET_MASK;
     1484    State.cLines            = 0;
     1485    State.rcStatus          = VINF_SUCCESS;
     1486    State.cchIndent         = UINT32_MAX;
     1487    State.cchHangingIndent  = 0;
     1488    if (fFlags & RTSTRMWRAPPED_F_HANGING_INDENT)
     1489    {
     1490        State.cchHangingIndent = (fFlags & RTSTRMWRAPPED_F_HANGING_INDENT_MASK) >> RTSTRMWRAPPED_F_HANGING_INDENT_SHIFT;
     1491        if (!State.cchHangingIndent)
     1492            State.cchHangingIndent = 4;
     1493    }
     1494
     1495    int rc = RTStrmQueryTerminalWidth(pStream, &State.cchWidth);
     1496    if (RT_SUCCESS(rc))
     1497        State.cchWidth = RT_MIN(State.cchWidth, RTSTRMWRAPPED_F_LINE_OFFSET_MASK + 1);
     1498    else
     1499    {
     1500        State.cchWidth = (uint32_t)fFlags & RTSTRMWRAPPED_F_NON_TERMINAL_WIDTH_MASK;
     1501        if (!State.cchWidth)
     1502            State.cchWidth = 80;
     1503    }
     1504    if (State.cchWidth < 32)
     1505        State.cchWidth = 32;
     1506    //State.cchWidth         -= 1; /* necessary here? */
     1507
     1508    /*
     1509     * Do the formatting.
     1510     */
     1511    RTStrFormatV(rtStrmWrappedOutput, &State, NULL, NULL, pszFormat, va);
     1512
     1513    /*
     1514     * Returning is simple if the buffer is empty.  Otherwise we'll have to
     1515     * perform a partial flush and write out whatever is left ourselves.
     1516     */
     1517    if (RT_SUCCESS(State.rcStatus))
     1518    {
     1519        if (State.cchLine == 0)
     1520            return State.cLines << 16;
     1521
     1522        rtStrmWrappedFlushLine(&State, true /*fPartial*/);
     1523        if (RT_SUCCESS(State.rcStatus) && State.cchLine > 0)
     1524        {
     1525            rtStrmWrapppedIndent(&State, State.cchIndent);
     1526            State.rcStatus = RTStrmWrite(State.pStream, State.szLine, State.cchLine);
     1527        }
     1528        if (RT_SUCCESS(State.rcStatus))
     1529            return RT_MIN(State.cchIndent + State.cchLine, RTSTRMWRAPPED_F_LINE_OFFSET_MASK) | (State.cLines << 16);
     1530    }
     1531    return State.rcStatus;
     1532}
     1533
     1534
     1535RTDECL(int32_t) RTStrmWrappedPrintf(PRTSTREAM pStream, uint32_t fFlags, const char *pszFormat, ...)
     1536{
     1537    va_list va;
     1538    va_start(va, pszFormat);
     1539    int32_t rcRet = RTStrmWrappedPrintfV(pStream, fFlags, pszFormat, va);
     1540    va_end(va);
     1541    return rcRet;
     1542}
     1543
Note: See TracChangeset for help on using the changeset viewer.

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