summaryrefslogtreecommitdiffstats
path: root/src/bldprogs/scmstream.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/bldprogs/scmstream.cpp')
-rw-r--r--src/bldprogs/scmstream.cpp1447
1 files changed, 1447 insertions, 0 deletions
diff --git a/src/bldprogs/scmstream.cpp b/src/bldprogs/scmstream.cpp
new file mode 100644
index 00000000..8fa37da5
--- /dev/null
+++ b/src/bldprogs/scmstream.cpp
@@ -0,0 +1,1447 @@
+/* $Id: scmstream.cpp $ */
+/** @file
+ * IPRT Testcase / Tool - Source Code Massager Stream Code.
+ */
+
+/*
+ * Copyright (C) 2010-2022 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/assert.h>
+#include <iprt/ctype.h>
+#include <iprt/err.h>
+#include <iprt/file.h>
+#include <iprt/handle.h>
+#include <iprt/mem.h>
+#include <iprt/pipe.h>
+#include <iprt/string.h>
+
+#include "scmstream.h"
+
+
+/**
+ * Initializes the stream structure.
+ *
+ * @param pStream The stream structure.
+ * @param fWriteOrRead The value of the fWriteOrRead stream member.
+ */
+static void scmStreamInitInternal(PSCMSTREAM pStream, bool fWriteOrRead)
+{
+ pStream->pch = NULL;
+ pStream->off = 0;
+ pStream->cb = 0;
+ pStream->cbAllocated = 0;
+
+ pStream->paLines = NULL;
+ pStream->iLine = 0;
+ pStream->cLines = 0;
+ pStream->cLinesAllocated = 0;
+
+ pStream->fWriteOrRead = fWriteOrRead;
+ pStream->fFileMemory = false;
+ pStream->fFullyLineated = false;
+
+ pStream->rc = VINF_SUCCESS;
+}
+
+/**
+ * Initialize an input stream.
+ *
+ * @returns IPRT status code.
+ * @param pStream The stream to initialize.
+ * @param pszFilename The file to take the stream content from.
+ */
+int ScmStreamInitForReading(PSCMSTREAM pStream, const char *pszFilename)
+{
+ scmStreamInitInternal(pStream, false /*fWriteOrRead*/);
+
+ void *pvFile;
+ size_t cbFile;
+ int rc = pStream->rc = RTFileReadAll(pszFilename, &pvFile, &cbFile);
+ if (RT_SUCCESS(rc))
+ {
+ pStream->pch = (char *)pvFile;
+ pStream->cb = cbFile;
+ pStream->cbAllocated = cbFile;
+ pStream->fFileMemory = true;
+ }
+ return rc;
+}
+
+/**
+ * Initialize an output stream.
+ *
+ * @returns IPRT status code
+ * @param pStream The stream to initialize.
+ * @param pRelatedStream Pointer to a related stream. NULL is fine.
+ */
+int ScmStreamInitForWriting(PSCMSTREAM pStream, PCSCMSTREAM pRelatedStream)
+{
+ scmStreamInitInternal(pStream, true /*fWriteOrRead*/);
+
+ /* allocate stuff */
+ size_t cbEstimate = !pRelatedStream ? _64K
+ : pRelatedStream->cb > 0 ? pRelatedStream->cb + pRelatedStream->cb / 10 : 64;
+ cbEstimate = RT_ALIGN(cbEstimate, _4K);
+ pStream->pch = (char *)RTMemAlloc(cbEstimate);
+ if (pStream->pch)
+ {
+ size_t cLinesEstimate = pRelatedStream && pRelatedStream->fFullyLineated
+ ? pRelatedStream->cLines + pRelatedStream->cLines / 10
+ : cbEstimate / 24;
+ cLinesEstimate = RT_ALIGN(cLinesEstimate, 512);
+ if (cLinesEstimate == 0)
+ cLinesEstimate = 16;
+ pStream->paLines = (PSCMSTREAMLINE)RTMemAlloc(cLinesEstimate * sizeof(SCMSTREAMLINE));
+ if (pStream->paLines)
+ {
+ pStream->paLines[0].off = 0;
+ pStream->paLines[0].cch = 0;
+ pStream->paLines[0].enmEol = SCMEOL_NONE;
+ pStream->cbAllocated = cbEstimate;
+ pStream->cLinesAllocated = cLinesEstimate;
+ return VINF_SUCCESS;
+ }
+
+ RTMemFree(pStream->pch);
+ pStream->pch = NULL;
+ }
+ return pStream->rc = VERR_NO_MEMORY;
+}
+
+/**
+ * Frees the resources associated with the stream.
+ *
+ * Nothing is happens to whatever the stream was initialized from or dumped to.
+ *
+ * @param pStream The stream to delete.
+ */
+void ScmStreamDelete(PSCMSTREAM pStream)
+{
+ if (pStream->pch)
+ {
+ if (pStream->fFileMemory)
+ RTFileReadAllFree(pStream->pch, pStream->cbAllocated);
+ else
+ RTMemFree(pStream->pch);
+ pStream->pch = NULL;
+ }
+ pStream->cbAllocated = 0;
+
+ if (pStream->paLines)
+ {
+ RTMemFree(pStream->paLines);
+ pStream->paLines = NULL;
+ }
+ pStream->cLinesAllocated = 0;
+}
+
+/**
+ * Get the stream status code.
+ *
+ * @returns IPRT status code.
+ * @param pStream The stream.
+ */
+int ScmStreamGetStatus(PCSCMSTREAM pStream)
+{
+ return pStream->rc;
+}
+
+/**
+ * Grows the buffer of a write stream.
+ *
+ * @returns IPRT status code.
+ * @param pStream The stream. Must be in write mode.
+ * @param cbAppending The minimum number of bytes to grow the buffer
+ * with.
+ */
+static int scmStreamGrowBuffer(PSCMSTREAM pStream, size_t cbAppending)
+{
+ size_t cbAllocated = pStream->cbAllocated;
+ cbAllocated += RT_MAX(0x1000 + cbAppending, cbAllocated);
+ cbAllocated = RT_ALIGN(cbAllocated, 0x1000);
+ void *pvNew;
+ if (!pStream->fFileMemory)
+ {
+ pvNew = RTMemRealloc(pStream->pch, cbAllocated);
+ if (!pvNew)
+ return pStream->rc = VERR_NO_MEMORY;
+ }
+ else
+ {
+ pvNew = RTMemDupEx(pStream->pch, pStream->off, cbAllocated - pStream->off);
+ if (!pvNew)
+ return pStream->rc = VERR_NO_MEMORY;
+ RTFileReadAllFree(pStream->pch, pStream->cbAllocated);
+ pStream->fFileMemory = false;
+ }
+ pStream->pch = (char *)pvNew;
+ pStream->cbAllocated = cbAllocated;
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Grows the line array of a stream.
+ *
+ * @returns IPRT status code.
+ * @param pStream The stream.
+ * @param iMinLine Minimum line number.
+ */
+static int scmStreamGrowLines(PSCMSTREAM pStream, size_t iMinLine)
+{
+ size_t cLinesAllocated = pStream->cLinesAllocated;
+ cLinesAllocated += RT_MAX(512 + iMinLine, cLinesAllocated);
+ cLinesAllocated = RT_ALIGN(cLinesAllocated, 512);
+ void *pvNew = RTMemRealloc(pStream->paLines, cLinesAllocated * sizeof(SCMSTREAMLINE));
+ if (!pvNew)
+ return pStream->rc = VERR_NO_MEMORY;
+
+ pStream->paLines = (PSCMSTREAMLINE)pvNew;
+ pStream->cLinesAllocated = cLinesAllocated;
+ return VINF_SUCCESS;
+}
+
+/**
+ * Rewinds the stream and sets the mode to read.
+ *
+ * @param pStream The stream.
+ */
+void ScmStreamRewindForReading(PSCMSTREAM pStream)
+{
+ pStream->off = 0;
+ pStream->iLine = 0;
+ pStream->fWriteOrRead = false;
+ pStream->rc = VINF_SUCCESS;
+}
+
+/**
+ * Rewinds the stream and sets the mode to write.
+ *
+ * @param pStream The stream.
+ */
+void ScmStreamRewindForWriting(PSCMSTREAM pStream)
+{
+ pStream->off = 0;
+ pStream->iLine = 0;
+ pStream->cLines = 0;
+ pStream->fWriteOrRead = true;
+ pStream->fFullyLineated = true;
+ pStream->rc = VINF_SUCCESS;
+
+ /* Initialize the first line with a zero length so ScmStreamWrite won't misbehave. */
+ if (pStream->cLinesAllocated == 0)
+ scmStreamGrowLines(pStream, 1);
+ if (pStream->cLinesAllocated > 0)
+ {
+ pStream->paLines[0].off = 0;
+ pStream->paLines[0].cch = 0;
+ pStream->paLines[0].enmEol = SCMEOL_NONE;
+ }
+}
+
+/**
+ * Checks if it's a text stream.
+ *
+ * Not 100% proof.
+ *
+ * @returns true if it probably is a text file, false if not.
+ * @param pStream The stream. Write or read, doesn't matter.
+ */
+bool ScmStreamIsText(PSCMSTREAM pStream)
+{
+ if (RTStrEnd(pStream->pch, pStream->cb))
+ return false;
+ if (!pStream->cb)
+ return true;
+ return true;
+}
+
+/**
+ * Performs an integrity check of the stream.
+ *
+ * @returns IPRT status code.
+ * @param pStream The stream.
+ */
+int ScmStreamCheckItegrity(PSCMSTREAM pStream)
+{
+ /*
+ * Perform sanity checks.
+ */
+ size_t const cbFile = pStream->cb;
+ for (size_t iLine = 0; iLine < pStream->cLines; iLine++)
+ {
+ size_t offEol = pStream->paLines[iLine].off + pStream->paLines[iLine].cch;
+ AssertReturn(offEol + pStream->paLines[iLine].enmEol <= cbFile, VERR_INTERNAL_ERROR_2);
+ switch (pStream->paLines[iLine].enmEol)
+ {
+ case SCMEOL_LF:
+ AssertReturn(pStream->pch[offEol] == '\n', VERR_INTERNAL_ERROR_3);
+ break;
+ case SCMEOL_CRLF:
+ AssertReturn(pStream->pch[offEol] == '\r', VERR_INTERNAL_ERROR_3);
+ AssertReturn(pStream->pch[offEol + 1] == '\n', VERR_INTERNAL_ERROR_3);
+ break;
+ case SCMEOL_NONE:
+ AssertReturn(iLine + 1 >= pStream->cLines, VERR_INTERNAL_ERROR_4);
+ break;
+ default:
+ AssertReturn(iLine + 1 >= pStream->cLines, VERR_INTERNAL_ERROR_5);
+ }
+ }
+ return VINF_SUCCESS;
+}
+
+/**
+ * Writes the stream to a file.
+ *
+ * @returns IPRT status code
+ * @param pStream The stream.
+ * @param pszFilenameFmt The filename format string.
+ * @param ... Format arguments.
+ */
+int ScmStreamWriteToFile(PSCMSTREAM pStream, const char *pszFilenameFmt, ...)
+{
+ int rc;
+
+#ifdef RT_STRICT
+ /*
+ * Check that what we're going to write makes sense first.
+ */
+ rc = ScmStreamCheckItegrity(pStream);
+ if (RT_FAILURE(rc))
+ return rc;
+#endif
+
+ /*
+ * Do the actual writing.
+ */
+ RTFILE hFile;
+ va_list va;
+ va_start(va, pszFilenameFmt);
+ rc = RTFileOpenV(&hFile, RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_WRITE, pszFilenameFmt, va);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFileWrite(hFile, pStream->pch, pStream->cb, NULL);
+ RTFileClose(hFile);
+ }
+ va_end(va);
+ return rc;
+}
+
+/**
+ * Writes the stream to standard output.
+ *
+ * @returns IPRT status code
+ * @param pStream The stream.
+ */
+int ScmStreamWriteToStdOut(PSCMSTREAM pStream)
+{
+ int rc;
+
+#ifdef RT_STRICT
+ /*
+ * Check that what we're going to write makes sense first.
+ */
+ rc = ScmStreamCheckItegrity(pStream);
+ if (RT_FAILURE(rc))
+ return rc;
+#endif
+
+ /*
+ * Do the actual writing.
+ */
+ RTHANDLE h;
+ rc = RTHandleGetStandard(RTHANDLESTD_OUTPUT, true /*fLeaveOpen*/, &h);
+ if (RT_SUCCESS(rc))
+ {
+ switch (h.enmType)
+ {
+ case RTHANDLETYPE_FILE:
+ rc = RTFileWrite(h.u.hFile, pStream->pch, pStream->cb, NULL);
+ /** @todo RTFileClose */
+ break;
+ case RTHANDLETYPE_PIPE:
+ rc = RTPipeWriteBlocking(h.u.hPipe, pStream->pch, pStream->cb, NULL);
+ RTPipeClose(h.u.hPipe);
+ break;
+ default:
+ rc = VERR_INVALID_HANDLE;
+ break;
+ }
+ }
+ return rc;
+}
+
+/**
+ * Worker for ScmStreamGetLine that builds the line number index while parsing
+ * the stream.
+ *
+ * @returns Same as SCMStreamGetLine.
+ * @param pStream The stream. Must be in read mode.
+ * @param pcchLine Where to return the line length.
+ * @param penmEol Where to return the kind of end of line marker.
+ */
+static const char *scmStreamGetLineInternal(PSCMSTREAM pStream, size_t *pcchLine, PSCMEOL penmEol)
+{
+ AssertReturn(!pStream->fWriteOrRead, NULL);
+ if (RT_FAILURE(pStream->rc))
+ return NULL;
+
+ size_t off = pStream->off;
+ size_t cb = pStream->cb;
+ if (RT_UNLIKELY(off >= cb))
+ {
+ pStream->fFullyLineated = true;
+ return NULL;
+ }
+
+ size_t iLine = pStream->iLine;
+ if (RT_UNLIKELY(iLine >= pStream->cLinesAllocated))
+ {
+ int rc = scmStreamGrowLines(pStream, iLine);
+ if (RT_FAILURE(rc))
+ return NULL;
+ }
+ pStream->paLines[iLine].off = off;
+
+ cb -= off;
+ const char *pchRet = &pStream->pch[off];
+ const char *pch = (const char *)memchr(pchRet, '\n', cb);
+ if (RT_LIKELY(pch))
+ {
+ cb = pch - pchRet;
+ pStream->off = off + cb + 1;
+ if ( cb < 1
+ || pch[-1] != '\r')
+ pStream->paLines[iLine].enmEol = *penmEol = SCMEOL_LF;
+ else
+ {
+ pStream->paLines[iLine].enmEol = *penmEol = SCMEOL_CRLF;
+ cb--;
+ }
+ }
+ else
+ {
+ pStream->off = off + cb;
+ pStream->paLines[iLine].enmEol = *penmEol = SCMEOL_NONE;
+ }
+ *pcchLine = cb;
+ pStream->paLines[iLine].cch = cb;
+ pStream->cLines = pStream->iLine = ++iLine;
+
+ return pchRet;
+}
+
+/**
+ * Internal worker that delineates a stream.
+ *
+ * @returns IPRT status code.
+ * @param pStream The stream. Caller must check that it is in
+ * read mode.
+ */
+static int scmStreamLineate(PSCMSTREAM pStream)
+{
+ /* Save the stream position. */
+ size_t const offSaved = pStream->off;
+ size_t const iLineSaved = pStream->iLine;
+
+ /* Get each line. */
+ size_t cchLine;
+ SCMEOL enmEol;
+ while (scmStreamGetLineInternal(pStream, &cchLine, &enmEol))
+ /* nothing */;
+ Assert(RT_FAILURE(pStream->rc) || pStream->fFullyLineated);
+
+ /* Restore the position */
+ pStream->off = offSaved;
+ pStream->iLine = iLineSaved;
+
+ return pStream->rc;
+}
+
+/**
+ * Get the current stream position as an byte offset.
+ *
+ * @returns The current byte offset
+ * @param pStream The stream.
+ */
+size_t ScmStreamTell(PSCMSTREAM pStream)
+{
+ return pStream->off;
+}
+
+/**
+ * Get the current stream position as a line number.
+ *
+ * @returns The current line (0-based).
+ * @param pStream The stream.
+ */
+size_t ScmStreamTellLine(PSCMSTREAM pStream)
+{
+ return pStream->iLine;
+}
+
+
+/**
+ * Gets the stream offset of a given line.
+ *
+ * @returns The offset of the line, or the stream size if the line number is too
+ * high.
+ * @param pStream The stream. Must be in read mode.
+ * @param iLine The line we're asking about.
+ */
+size_t ScmStreamTellOffsetOfLine(PSCMSTREAM pStream, size_t iLine)
+{
+ AssertReturn(!pStream->fWriteOrRead, pStream->cb);
+ if (!pStream->fFullyLineated)
+ {
+ int rc = scmStreamLineate(pStream);
+ AssertRCReturn(rc, pStream->cb);
+ }
+ if (iLine >= pStream->cLines)
+ return pStream->cb;
+ return pStream->paLines[iLine].off;
+}
+
+
+/**
+ * Get the current stream size in bytes.
+ *
+ * @returns Count of bytes.
+ * @param pStream The stream.
+ */
+size_t ScmStreamSize(PSCMSTREAM pStream)
+{
+ return pStream->cb;
+}
+
+/**
+ * Gets the number of lines in the stream.
+ *
+ * @returns The number of lines.
+ * @param pStream The stream.
+ */
+size_t ScmStreamCountLines(PSCMSTREAM pStream)
+{
+ if (!pStream->fFullyLineated)
+ scmStreamLineate(pStream);
+ return pStream->cLines;
+}
+
+/**
+ * Seeks to a given byte offset in the stream.
+ *
+ * @returns IPRT status code.
+ * @retval VERR_SEEK if the new stream position is the middle of an EOL marker.
+ * This is a temporary restriction.
+ *
+ * @param pStream The stream. Must be in read mode.
+ * @param offAbsolute The offset to seek to. If this is beyond the
+ * end of the stream, the position is set to the
+ * end.
+ */
+int ScmStreamSeekAbsolute(PSCMSTREAM pStream, size_t offAbsolute)
+{
+ AssertReturn(!pStream->fWriteOrRead, VERR_ACCESS_DENIED);
+ if (RT_FAILURE(pStream->rc))
+ return pStream->rc;
+
+ /* Must be fully delineated. (lazy bird) */
+ if (RT_UNLIKELY(!pStream->fFullyLineated))
+ {
+ int rc = scmStreamLineate(pStream);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ /* Ok, do the job. */
+ if (offAbsolute < pStream->cb)
+ {
+ /** @todo Should do a binary search here, but I'm too darn lazy tonight. */
+ pStream->off = ~(size_t)0;
+ for (size_t i = 0; i < pStream->cLines; i++)
+ {
+ if (offAbsolute < pStream->paLines[i].off + pStream->paLines[i].cch + pStream->paLines[i].enmEol)
+ {
+ pStream->off = offAbsolute;
+ pStream->iLine = i;
+ if (offAbsolute > pStream->paLines[i].off + pStream->paLines[i].cch)
+ return pStream->rc = VERR_SEEK;
+ break;
+ }
+ }
+ AssertReturn(pStream->off != ~(size_t)0, pStream->rc = VERR_INTERNAL_ERROR_3);
+ }
+ else
+ {
+ pStream->off = pStream->cb;
+ pStream->iLine = pStream->cLines;
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Seeks a number of bytes relative to the current stream position.
+ *
+ * @returns IPRT status code.
+ * @retval VERR_SEEK if the new stream position is the middle of an EOL marker.
+ * This is a temporary restriction.
+ *
+ * @param pStream The stream. Must be in read mode.
+ * @param offRelative The offset to seek to. A negative offset
+ * rewinds and positive one fast forwards the
+ * stream. Will quietly stop at the beginning and
+ * end of the stream.
+ */
+int ScmStreamSeekRelative(PSCMSTREAM pStream, ssize_t offRelative)
+{
+ size_t offAbsolute;
+ if (offRelative >= 0)
+ offAbsolute = pStream->off + offRelative;
+ else if ((size_t)-offRelative <= pStream->off)
+ offAbsolute = pStream->off + offRelative;
+ else
+ offAbsolute = 0;
+ return ScmStreamSeekAbsolute(pStream, offAbsolute);
+}
+
+/**
+ * Seeks to a given line in the stream.
+ *
+ * @returns IPRT status code.
+ *
+ * @param pStream The stream. Must be in read mode.
+ * @param iLine The line to seek to. If this is beyond the end
+ * of the stream, the position is set to the end.
+ */
+int ScmStreamSeekByLine(PSCMSTREAM pStream, size_t iLine)
+{
+ if (RT_FAILURE(pStream->rc))
+ return pStream->rc;
+
+ /* Must be fully delineated. (lazy bird) */
+ if (RT_UNLIKELY(!pStream->fFullyLineated))
+ {
+ AssertReturn(!pStream->fWriteOrRead, VERR_ACCESS_DENIED);
+ int rc = scmStreamLineate(pStream);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ /* Ok, do the job. */
+ if (iLine < pStream->cLines)
+ {
+ pStream->iLine = iLine;
+ pStream->off = pStream->paLines[iLine].off;
+ if (pStream->fWriteOrRead)
+ {
+ pStream->cb = pStream->paLines[iLine].off;
+ pStream->cLines = iLine;
+ pStream->paLines[iLine].cch = 0;
+ pStream->paLines[iLine].enmEol = SCMEOL_NONE;
+ }
+ }
+ else
+ {
+ AssertReturn(!pStream->fWriteOrRead, VERR_ACCESS_DENIED);
+ pStream->off = pStream->cb;
+ pStream->iLine = pStream->cLines;
+ }
+ return VINF_SUCCESS;
+}
+
+/**
+ * Checks if the stream position is at the start of a line.
+ *
+ * @returns @c true if at the start, @c false if not.
+ * @param pStream The stream.
+ */
+bool ScmStreamIsAtStartOfLine(PSCMSTREAM pStream)
+{
+ if ( !pStream->fFullyLineated
+ && !pStream->fWriteOrRead)
+ {
+ int rc = scmStreamLineate(pStream);
+ if (RT_FAILURE(rc))
+ return false;
+ }
+ return pStream->off == pStream->paLines[pStream->iLine].off;
+}
+
+/**
+ * Worker for ScmStreamGetLineByNo and ScmStreamGetLine.
+ *
+ * Works on a fully lineated stream.
+ *
+ * @returns Pointer to the first character in the line, not NULL terminated.
+ * NULL if the end of the stream has been reached or some problem
+ * occurred.
+ *
+ * @param pStream The stream. Must be in read mode.
+ * @param iLine The line to get (0-based).
+ * @param pcchLine The length.
+ * @param penmEol Where to return the end of line type indicator.
+ */
+DECLINLINE(const char *) scmStreamGetLineByNoCommon(PSCMSTREAM pStream, size_t iLine, size_t *pcchLine, PSCMEOL penmEol)
+{
+ Assert(!pStream->fWriteOrRead);
+ Assert(pStream->fFullyLineated);
+
+ /* Check stream status. */
+ if (RT_SUCCESS(pStream->rc))
+ {
+ /* Not at the end of the stream yet? */
+ if (RT_LIKELY(iLine < pStream->cLines))
+ {
+ /* Get the data. */
+ const char *pchRet = &pStream->pch[pStream->paLines[iLine].off];
+ *pcchLine = pStream->paLines[iLine].cch;
+ *penmEol = pStream->paLines[iLine].enmEol;
+
+ /* update the stream position. */
+ pStream->off = pStream->paLines[iLine].off + pStream->paLines[iLine].cch + pStream->paLines[iLine].enmEol;
+ pStream->iLine = iLine + 1;
+ return pchRet;
+ }
+ pStream->off = pStream->cb;
+ pStream->iLine = pStream->cLines;
+ }
+ *pcchLine = 0;
+ *penmEol = SCMEOL_NONE;
+ return NULL;
+}
+
+
+/**
+ * Get a numbered line from the stream (changes the position).
+ *
+ * A line is always delimited by a LF character or the end of the stream. The
+ * delimiter is not included in returned line length, but instead returned via
+ * the @a penmEol indicator.
+ *
+ * @returns Pointer to the first character in the line, not NULL terminated.
+ * NULL if the end of the stream has been reached or some problem
+ * occurred (*pcchLine set to zero and *penmEol to SCMEOL_NONE).
+ *
+ * @param pStream The stream. Must be in read mode.
+ * @param iLine The line to get (0-based).
+ * @param pcchLine The length.
+ * @param penmEol Where to return the end of line type indicator.
+ */
+const char *ScmStreamGetLineByNo(PSCMSTREAM pStream, size_t iLine, size_t *pcchLine, PSCMEOL penmEol)
+{
+ AssertReturn(!pStream->fWriteOrRead, NULL);
+
+ /* Make sure it's fully delineated so we can use the index. */
+ if (RT_LIKELY(pStream->fFullyLineated))
+ return scmStreamGetLineByNoCommon(pStream, iLine, pcchLine, penmEol);
+
+ int rc = pStream->rc;
+ if (RT_SUCCESS(rc))
+ {
+ rc = scmStreamLineate(pStream);
+ if (RT_SUCCESS(rc))
+ return scmStreamGetLineByNoCommon(pStream, iLine, pcchLine, penmEol);
+ }
+
+ *pcchLine = 0;
+ *penmEol = SCMEOL_NONE;
+ return NULL;
+}
+
+/**
+ * Get a line from the stream.
+ *
+ * A line is always delimited by a LF character or the end of the stream. The
+ * delimiter is not included in returned line length, but instead returned via
+ * the @a penmEol indicator.
+ *
+ * @returns Pointer to the first character in the line, not NULL terminated.
+ * NULL if the end of the stream has been reached or some problem
+ * occurred (*pcchLine set to zero and *penmEol to SCMEOL_NONE).
+ *
+ * @param pStream The stream. Must be in read mode.
+ * @param pcchLine The length.
+ * @param penmEol Where to return the end of line type indicator.
+ */
+const char *ScmStreamGetLine(PSCMSTREAM pStream, size_t *pcchLine, PSCMEOL penmEol)
+{
+ if (RT_LIKELY(pStream->fFullyLineated))
+ {
+ size_t offCur = pStream->off;
+ size_t iCurLine = pStream->iLine;
+ const char *pszLine = scmStreamGetLineByNoCommon(pStream, iCurLine, pcchLine, penmEol);
+ if ( pszLine
+ && offCur > pStream->paLines[iCurLine].off)
+ {
+ offCur -= pStream->paLines[iCurLine].off;
+ Assert(offCur <= pStream->paLines[iCurLine].cch + pStream->paLines[iCurLine].enmEol);
+ if (offCur < pStream->paLines[iCurLine].cch)
+ *pcchLine -= offCur;
+ else
+ *pcchLine = 0;
+ pszLine += offCur;
+ }
+ return pszLine;
+ }
+ return scmStreamGetLineInternal(pStream, pcchLine, penmEol);
+}
+
+/**
+ * Get the current buffer pointer.
+ *
+ * @returns Buffer pointer on success, NULL on failure (asserted).
+ * @param pStream The stream. Must be in read mode.
+ */
+const char *ScmStreamGetCur(PSCMSTREAM pStream)
+{
+ AssertReturn(!pStream->fWriteOrRead, NULL);
+ return pStream->pch + pStream->off;
+}
+
+/**
+ * Gets a character from the stream.
+ *
+ * @returns The next unsigned character in the stream.
+ * ~(unsigned)0 on failure.
+ * @param pStream The stream. Must be in read mode.
+ */
+unsigned ScmStreamGetCh(PSCMSTREAM pStream)
+{
+ /* Check stream state. */
+ AssertReturn(!pStream->fWriteOrRead, ~(unsigned)0);
+ if (RT_FAILURE(pStream->rc))
+ return ~(unsigned)0;
+ if (RT_UNLIKELY(!pStream->fFullyLineated))
+ {
+ int rc = scmStreamLineate(pStream);
+ if (RT_FAILURE(rc))
+ return ~(unsigned)0;
+ }
+
+ /* If there isn't enough stream left, fail already. */
+ if (RT_UNLIKELY(pStream->off >= pStream->cb))
+ return ~(unsigned)0;
+
+ /* Read a character. */
+ char ch = pStream->pch[pStream->off++];
+
+ /* Advance the line indicator. */
+ size_t iLine = pStream->iLine;
+ if (pStream->off >= pStream->paLines[iLine].off + pStream->paLines[iLine].cch + pStream->paLines[iLine].enmEol)
+ pStream->iLine++;
+
+ return (unsigned)ch;
+}
+
+
+/**
+ * Peeks at the next character from the stream.
+ *
+ * @returns The next unsigned character in the stream.
+ * ~(unsigned)0 on failure.
+ * @param pStream The stream. Must be in read mode.
+ */
+unsigned ScmStreamPeekCh(PSCMSTREAM pStream)
+{
+ /* Check stream state. */
+ AssertReturn(!pStream->fWriteOrRead, ~(unsigned)0);
+ if (RT_FAILURE(pStream->rc))
+ return ~(unsigned)0;
+ if (RT_UNLIKELY(!pStream->fFullyLineated))
+ {
+ int rc = scmStreamLineate(pStream);
+ if (RT_FAILURE(rc))
+ return ~(unsigned)0;
+ }
+
+ /* If there isn't enough stream left, fail already. */
+ if (RT_UNLIKELY(pStream->off >= pStream->cb))
+ return ~(unsigned)0;
+
+ /* Peek at the next character. */
+ char ch = pStream->pch[pStream->off];
+ return (unsigned)ch;
+}
+
+
+/**
+ * Reads @a cbToRead bytes into @a pvBuf.
+ *
+ * Will fail if end of stream is encountered before the entire read has been
+ * completed.
+ *
+ * @returns IPRT status code.
+ * @retval VERR_EOF if there isn't @a cbToRead bytes left to read. Stream
+ * position will be unchanged.
+ *
+ * @param pStream The stream. Must be in read mode.
+ * @param pvBuf The buffer to read into.
+ * @param cbToRead The number of bytes to read.
+ */
+int ScmStreamRead(PSCMSTREAM pStream, void *pvBuf, size_t cbToRead)
+{
+ AssertReturn(!pStream->fWriteOrRead, VERR_PERMISSION_DENIED);
+ if (RT_FAILURE(pStream->rc))
+ return pStream->rc;
+
+ /* If there isn't enough stream left, fail already. */
+ if (RT_UNLIKELY(pStream->cb - pStream->off < cbToRead))
+ return VERR_EOF;
+
+ /* Copy the data and simply seek to the new stream position. */
+ memcpy(pvBuf, &pStream->pch[pStream->off], cbToRead);
+ return ScmStreamSeekAbsolute(pStream, pStream->off + cbToRead);
+}
+
+
+/**
+ * Checks if the given line is empty or full of white space.
+ *
+ * @returns true if white space only, false if not (or if non-existant).
+ * @param pStream The stream. Must be in read mode.
+ * @param iLine The line in question.
+ */
+bool ScmStreamIsWhiteLine(PSCMSTREAM pStream, size_t iLine)
+{
+ SCMEOL enmEol;
+ size_t cchLine;
+ const char *pchLine = ScmStreamGetLineByNo(pStream, iLine, &cchLine, &enmEol);
+ if (!pchLine)
+ return false;
+ while (cchLine && RT_C_IS_SPACE(*pchLine))
+ pchLine++, cchLine--;
+ return cchLine == 0;
+}
+
+
+/**
+ * Try figure out the end of line style of the give stream.
+ *
+ * @returns Most likely end of line style.
+ * @param pStream The stream.
+ */
+SCMEOL ScmStreamGetEol(PSCMSTREAM pStream)
+{
+ SCMEOL enmEol;
+ if (pStream->cLines > 0)
+ enmEol = pStream->paLines[0].enmEol;
+ else if (pStream->cb == 0)
+ enmEol = SCMEOL_NONE;
+ else
+ {
+ const char *pchLF = (const char *)memchr(pStream->pch, '\n', pStream->cb);
+ if (pchLF && pchLF != pStream->pch && pchLF[-1] == '\r')
+ enmEol = SCMEOL_CRLF;
+ else
+ enmEol = SCMEOL_LF;
+ }
+
+ if (enmEol == SCMEOL_NONE)
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+ enmEol = SCMEOL_CRLF;
+#else
+ enmEol = SCMEOL_LF;
+#endif
+ return enmEol;
+}
+
+
+/**
+ * Get the end of line indicator type for a line.
+ *
+ * @returns The EOL indicator. If the line isn't found, the default EOL
+ * indicator is return.
+ * @param pStream The stream.
+ * @param iLine The line (0-base).
+ */
+SCMEOL ScmStreamGetEolByLine(PSCMSTREAM pStream, size_t iLine)
+{
+ SCMEOL enmEol;
+ if (iLine < pStream->cLines)
+ enmEol = pStream->paLines[iLine].enmEol;
+ else
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+ enmEol = SCMEOL_CRLF;
+#else
+ enmEol = SCMEOL_LF;
+#endif
+ return enmEol;
+}
+
+
+/**
+ * Appends a line to the stream.
+ *
+ * @returns IPRT status code.
+ * @param pStream The stream. Must be in write mode.
+ * @param pchLine Pointer to the line.
+ * @param cchLine Line length.
+ * @param enmEol Which end of line indicator to use.
+ */
+int ScmStreamPutLine(PSCMSTREAM pStream, const char *pchLine, size_t cchLine, SCMEOL enmEol)
+{
+ AssertReturn(pStream->fWriteOrRead, VERR_ACCESS_DENIED);
+ if (RT_FAILURE(pStream->rc))
+ return pStream->rc;
+
+ /*
+ * Make sure the previous line has a new-line indicator.
+ */
+ size_t off = pStream->off;
+ size_t iLine = pStream->iLine;
+ if (RT_UNLIKELY( iLine != 0
+ && pStream->paLines[iLine - 1].enmEol == SCMEOL_NONE))
+ {
+ AssertReturn(pStream->paLines[iLine].cch == 0, VERR_INTERNAL_ERROR_3);
+ SCMEOL enmEol2 = enmEol != SCMEOL_NONE ? enmEol : ScmStreamGetEol(pStream);
+ if (RT_UNLIKELY(off + cchLine + enmEol + enmEol2 > pStream->cbAllocated))
+ {
+ int rc = scmStreamGrowBuffer(pStream, cchLine + enmEol + enmEol2);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ if (enmEol2 == SCMEOL_LF)
+ pStream->pch[off++] = '\n';
+ else
+ {
+ pStream->pch[off++] = '\r';
+ pStream->pch[off++] = '\n';
+ }
+ pStream->paLines[iLine - 1].enmEol = enmEol2;
+ pStream->paLines[iLine].off = off;
+ pStream->off = off;
+ pStream->cb = off;
+ }
+
+ /*
+ * Ensure we've got sufficient buffer space.
+ */
+ if (RT_UNLIKELY(off + cchLine + enmEol > pStream->cbAllocated))
+ {
+ int rc = scmStreamGrowBuffer(pStream, cchLine + enmEol);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ /*
+ * Add a line record.
+ */
+ if (RT_UNLIKELY(iLine + 1 >= pStream->cLinesAllocated))
+ {
+ int rc = scmStreamGrowLines(pStream, iLine);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ pStream->paLines[iLine].cch = off - pStream->paLines[iLine].off + cchLine;
+ pStream->paLines[iLine].enmEol = enmEol;
+
+ iLine++;
+ pStream->cLines = iLine;
+ pStream->iLine = iLine;
+
+ /*
+ * Copy the line
+ */
+ memcpy(&pStream->pch[off], pchLine, cchLine);
+ off += cchLine;
+ if (enmEol == SCMEOL_LF)
+ pStream->pch[off++] = '\n';
+ else if (enmEol == SCMEOL_CRLF)
+ {
+ pStream->pch[off++] = '\r';
+ pStream->pch[off++] = '\n';
+ }
+ pStream->off = off;
+ pStream->cb = off;
+
+ /*
+ * Start a new line.
+ */
+ pStream->paLines[iLine].off = off;
+ pStream->paLines[iLine].cch = 0;
+ pStream->paLines[iLine].enmEol = SCMEOL_NONE;
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Writes to the stream.
+ *
+ * @returns IPRT status code
+ * @param pStream The stream. Must be in write mode.
+ * @param pchBuf What to write.
+ * @param cchBuf How much to write.
+ */
+int ScmStreamWrite(PSCMSTREAM pStream, const char *pchBuf, size_t cchBuf)
+{
+ AssertReturn(pStream->fWriteOrRead, VERR_ACCESS_DENIED);
+ if (RT_FAILURE(pStream->rc))
+ return pStream->rc;
+
+ /*
+ * Ensure we've got sufficient buffer space.
+ */
+ size_t off = pStream->off;
+ if (RT_UNLIKELY(off + cchBuf > pStream->cbAllocated))
+ {
+ int rc = scmStreamGrowBuffer(pStream, cchBuf);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ /*
+ * Deal with the odd case where we've already pushed a line with SCMEOL_NONE.
+ */
+ size_t iLine = pStream->iLine;
+ if (RT_UNLIKELY( iLine > 0
+ && pStream->paLines[iLine - 1].enmEol == SCMEOL_NONE))
+ {
+ iLine--;
+ pStream->cLines = iLine;
+ pStream->iLine = iLine;
+ }
+
+ /*
+ * Deal with lines.
+ */
+ const char *pchLF = (const char *)memchr(pchBuf, '\n', cchBuf);
+ if (!pchLF)
+ pStream->paLines[iLine].cch += cchBuf;
+ else
+ {
+ const char *pchLine = pchBuf;
+ for (;;)
+ {
+ if (RT_UNLIKELY(iLine + 1 >= pStream->cLinesAllocated))
+ {
+ int rc = scmStreamGrowLines(pStream, iLine);
+ if (RT_FAILURE(rc))
+ {
+ iLine = pStream->iLine;
+ pStream->paLines[iLine].cch = off - pStream->paLines[iLine].off;
+ pStream->paLines[iLine].enmEol = SCMEOL_NONE;
+ return rc;
+ }
+ }
+
+ size_t cchLine = pchLF - pchLine;
+ if ( cchLine
+ ? pchLF[-1] != '\r'
+ : !pStream->paLines[iLine].cch
+ || pStream->pch[pStream->paLines[iLine].off + pStream->paLines[iLine].cch - 1] != '\r')
+ pStream->paLines[iLine].enmEol = SCMEOL_LF;
+ else
+ {
+ pStream->paLines[iLine].enmEol = SCMEOL_CRLF;
+ cchLine--;
+ }
+ pStream->paLines[iLine].cch += cchLine;
+
+ iLine++;
+ size_t offBuf = pchLF + 1 - pchBuf;
+ pStream->paLines[iLine].off = off + offBuf;
+ pStream->paLines[iLine].cch = 0;
+ pStream->paLines[iLine].enmEol = SCMEOL_NONE;
+
+ size_t cchLeft = cchBuf - offBuf;
+ pchLine = pchLF + 1;
+ pchLF = (const char *)memchr(pchLine, '\n', cchLeft);
+ if (!pchLF)
+ {
+ pStream->paLines[iLine].cch = cchLeft;
+ break;
+ }
+ }
+
+ pStream->iLine = iLine;
+ pStream->cLines = iLine;
+ }
+
+ /*
+ * Copy the data and update position and size.
+ */
+ memcpy(&pStream->pch[off], pchBuf, cchBuf);
+ off += cchBuf;
+ pStream->off = off;
+ pStream->cb = off;
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Write a character to the stream.
+ *
+ * @returns IPRT status code
+ * @param pStream The stream. Must be in write mode.
+ * @param pchBuf What to write.
+ * @param cchBuf How much to write.
+ */
+int ScmStreamPutCh(PSCMSTREAM pStream, char ch)
+{
+ AssertReturn(pStream->fWriteOrRead, VERR_ACCESS_DENIED);
+ if (RT_FAILURE(pStream->rc))
+ return pStream->rc;
+
+ /*
+ * Only deal with the simple cases here, use ScmStreamWrite for the
+ * annoying stuff.
+ */
+ size_t off = pStream->off;
+ if ( ch == '\n'
+ || RT_UNLIKELY(off + 1 > pStream->cbAllocated))
+ return ScmStreamWrite(pStream, &ch, 1);
+
+ /*
+ * Just append it.
+ */
+ pStream->pch[off] = ch;
+ pStream->off = off + 1;
+ pStream->paLines[pStream->iLine].cch++;
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Puts an EOL marker to the stream.
+ *
+ * @returns IPRt status code.
+ * @param pStream The stream. Must be in write mode.
+ * @param enmEol The end-of-line marker to write.
+ */
+int ScmStreamPutEol(PSCMSTREAM pStream, SCMEOL enmEol)
+{
+ if (enmEol == SCMEOL_LF)
+ return ScmStreamWrite(pStream, "\n", 1);
+ if (enmEol == SCMEOL_CRLF)
+ return ScmStreamWrite(pStream, "\r\n", 2);
+ if (enmEol == SCMEOL_NONE)
+ return VINF_SUCCESS;
+ AssertFailedReturn(VERR_INVALID_PARAMETER);
+}
+
+/**
+ * Formats a string and writes it to the SCM stream.
+ *
+ * @returns The number of bytes written (>= 0). Negative value are IPRT error
+ * status codes.
+ * @param pStream The stream to write to.
+ * @param pszFormat The format string.
+ * @param va The arguments to format.
+ */
+ssize_t ScmStreamPrintfV(PSCMSTREAM pStream, const char *pszFormat, va_list va)
+{
+ char *psz;
+ ssize_t cch = RTStrAPrintfV(&psz, pszFormat, va);
+ if (cch)
+ {
+ int rc = ScmStreamWrite(pStream, psz, cch);
+ RTStrFree(psz);
+ if (RT_FAILURE(rc))
+ cch = rc;
+ }
+ return cch;
+}
+
+/**
+ * Formats a string and writes it to the SCM stream.
+ *
+ * @returns The number of bytes written (>= 0). Negative value are IPRT error
+ * status codes.
+ * @param pStream The stream to write to.
+ * @param pszFormat The format string.
+ * @param ... The arguments to format.
+ */
+ssize_t ScmStreamPrintf(PSCMSTREAM pStream, const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ ssize_t cch = ScmStreamPrintfV(pStream, pszFormat, va);
+ va_end(va);
+ return cch;
+}
+
+/**
+ * Copies @a cLines from the @a pSrc stream onto the @a pDst stream.
+ *
+ * The stream positions will be used and changed in both streams.
+ *
+ * @returns IPRT status code.
+ * @param pDst The destination stream. Must be in write mode.
+ * @param cLines The number of lines. (0 is accepted.)
+ * @param pSrc The source stream. Must be in read mode.
+ */
+int ScmStreamCopyLines(PSCMSTREAM pDst, PSCMSTREAM pSrc, size_t cLines)
+{
+ AssertReturn(pDst->fWriteOrRead, VERR_ACCESS_DENIED);
+ if (RT_FAILURE(pDst->rc))
+ return pDst->rc;
+
+ AssertReturn(!pSrc->fWriteOrRead, VERR_ACCESS_DENIED);
+ if (RT_FAILURE(pSrc->rc))
+ return pSrc->rc;
+
+ while (cLines-- > 0)
+ {
+ SCMEOL enmEol;
+ size_t cchLine;
+ const char *pchLine = ScmStreamGetLine(pSrc, &cchLine, &enmEol);
+ if (!pchLine)
+ return pDst->rc = RT_FAILURE(pSrc->rc) ? pSrc->rc : VERR_EOF;
+
+ int rc = ScmStreamPutLine(pDst, pchLine, cchLine, enmEol);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * If the given C word is at off - 1, return @c true and skip beyond it,
+ * otherwise return @c false.
+ *
+ * @retval true if the given C-word is at the current position minus one char.
+ * The stream position changes.
+ * @retval false if not. The stream position is unchanged.
+ *
+ * @param pStream The stream.
+ * @param cchWord The length of the word.
+ * @param pszWord The word.
+ */
+bool ScmStreamCMatchingWordM1(PSCMSTREAM pStream, const char *pszWord, size_t cchWord)
+{
+ /* Check stream state. */
+ AssertReturn(!pStream->fWriteOrRead, false);
+ AssertReturn(RT_SUCCESS(pStream->rc), false);
+ AssertReturn(pStream->fFullyLineated, false);
+
+ /* Sufficient chars left on the line? */
+ size_t const iLine = pStream->iLine;
+ AssertReturn(pStream->off > pStream->paLines[iLine].off, false);
+ size_t const cchLeft = pStream->paLines[iLine].cch + pStream->paLines[iLine].off - (pStream->off - 1);
+ if (cchWord > cchLeft)
+ return false;
+
+ /* Do they match? */
+ const char *psz = &pStream->pch[pStream->off - 1];
+ if (memcmp(psz, pszWord, cchWord))
+ return false;
+
+ /* Is it the end of a C word? */
+ if (cchWord < cchLeft)
+ {
+ psz += cchWord;
+ if (RT_C_IS_ALNUM(*psz) || *psz == '_')
+ return false;
+ }
+
+ /* Skip ahead. */
+ pStream->off += cchWord - 1;
+ return true;
+}
+
+
+/**
+ * Get's the C word starting at the current position.
+ *
+ * @returns Pointer to the word on success and the stream position advanced to
+ * the end of it.
+ * NULL on failure, stream position normally unchanged.
+ * @param pStream The stream to get the C word from.
+ * @param pcchWord Where to return the word length.
+ */
+const char *ScmStreamCGetWord(PSCMSTREAM pStream, size_t *pcchWord)
+{
+ /* Check stream state. */
+ AssertReturn(!pStream->fWriteOrRead, NULL);
+ AssertReturn(RT_SUCCESS(pStream->rc), NULL);
+ AssertReturn(pStream->fFullyLineated, NULL);
+
+ /* Get the number of chars left on the line and locate the current char. */
+ size_t const iLine = pStream->iLine;
+ size_t const cchLeft = pStream->paLines[iLine].cch + pStream->paLines[iLine].off - pStream->off;
+ const char *psz = &pStream->pch[pStream->off];
+
+ /* Is it a leading C character. */
+ if (!RT_C_IS_ALPHA(*psz) && *psz != '_')
+ return NULL;
+
+ /* Find the end of the word. */
+ char ch;
+ size_t off = 1;
+ while ( off < cchLeft
+ && ( (ch = psz[off]) == '_'
+ || RT_C_IS_ALNUM(ch)))
+ off++;
+
+ pStream->off += off;
+ *pcchWord = off;
+ return psz;
+}
+
+
+/**
+ * Get's the C word starting at the current position minus one.
+ *
+ * @returns Pointer to the word on success and the stream position advanced to
+ * the end of it.
+ * NULL on failure, stream position normally unchanged.
+ * @param pStream The stream to get the C word from.
+ * @param pcchWord Where to return the word length.
+ */
+const char *ScmStreamCGetWordM1(PSCMSTREAM pStream, size_t *pcchWord)
+{
+ /* Check stream state. */
+ AssertReturn(!pStream->fWriteOrRead, NULL);
+ AssertReturn(RT_SUCCESS(pStream->rc), NULL);
+ AssertReturn(pStream->fFullyLineated, NULL);
+
+ /* Get the number of chars left on the line and locate the current char. */
+ size_t const iLine = pStream->iLine;
+ size_t const cchLeft = pStream->paLines[iLine].cch + pStream->paLines[iLine].off - (pStream->off - 1);
+ const char *psz = &pStream->pch[pStream->off - 1];
+
+ /* Is it a leading C character. */
+ if (!RT_C_IS_ALPHA(*psz) && *psz != '_')
+ return NULL;
+
+ /* Find the end of the word. */
+ char ch;
+ size_t off = 1;
+ while ( off < cchLeft
+ && ( (ch = psz[off]) == '_'
+ || RT_C_IS_ALNUM(ch)))
+ off++;
+
+ pStream->off += off - 1;
+ *pcchWord = off;
+ return psz;
+}
+