VirtualBox

source: vbox/trunk/src/bldprogs/scmstream.cpp@ 99132

Last change on this file since 99132 was 98368, checked in by vboxsync, 22 months ago

scm: Changed the rewriter return code to include a lazy option which makes the caller check for modifications.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 43.0 KB
Line 
1/* $Id: scmstream.cpp 98368 2023-01-31 15:46:52Z vboxsync $ */
2/** @file
3 * IPRT Testcase / Tool - Source Code Massager Stream Code.
4 */
5
6/*
7 * Copyright (C) 2010-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 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#include <iprt/assert.h>
33#include <iprt/ctype.h>
34#include <iprt/err.h>
35#include <iprt/file.h>
36#include <iprt/handle.h>
37#include <iprt/mem.h>
38#include <iprt/pipe.h>
39#include <iprt/string.h>
40
41#include "scmstream.h"
42
43
44/**
45 * Initializes the stream structure.
46 *
47 * @param pStream The stream structure.
48 * @param fWriteOrRead The value of the fWriteOrRead stream member.
49 */
50static void scmStreamInitInternal(PSCMSTREAM pStream, bool fWriteOrRead)
51{
52 pStream->pch = NULL;
53 pStream->off = 0;
54 pStream->cb = 0;
55 pStream->cbAllocated = 0;
56
57 pStream->paLines = NULL;
58 pStream->iLine = 0;
59 pStream->cLines = 0;
60 pStream->cLinesAllocated = 0;
61
62 pStream->fWriteOrRead = fWriteOrRead;
63 pStream->fFileMemory = false;
64 pStream->fFullyLineated = false;
65
66 pStream->rc = VINF_SUCCESS;
67}
68
69/**
70 * Initialize an input stream.
71 *
72 * @returns IPRT status code.
73 * @param pStream The stream to initialize.
74 * @param pszFilename The file to take the stream content from.
75 */
76int ScmStreamInitForReading(PSCMSTREAM pStream, const char *pszFilename)
77{
78 scmStreamInitInternal(pStream, false /*fWriteOrRead*/);
79
80 void *pvFile;
81 size_t cbFile;
82 int rc = pStream->rc = RTFileReadAll(pszFilename, &pvFile, &cbFile);
83 if (RT_SUCCESS(rc))
84 {
85 pStream->pch = (char *)pvFile;
86 pStream->cb = cbFile;
87 pStream->cbAllocated = cbFile;
88 pStream->fFileMemory = true;
89 }
90 return rc;
91}
92
93/**
94 * Initialize an output stream.
95 *
96 * @returns IPRT status code
97 * @param pStream The stream to initialize.
98 * @param pRelatedStream Pointer to a related stream. NULL is fine.
99 */
100int ScmStreamInitForWriting(PSCMSTREAM pStream, PCSCMSTREAM pRelatedStream)
101{
102 scmStreamInitInternal(pStream, true /*fWriteOrRead*/);
103
104 /* allocate stuff */
105 size_t cbEstimate = !pRelatedStream ? _64K
106 : pRelatedStream->cb > 0 ? pRelatedStream->cb + pRelatedStream->cb / 10 : 64;
107 cbEstimate = RT_ALIGN(cbEstimate, _4K);
108 pStream->pch = (char *)RTMemAlloc(cbEstimate);
109 if (pStream->pch)
110 {
111 size_t cLinesEstimate = pRelatedStream && pRelatedStream->fFullyLineated
112 ? pRelatedStream->cLines + pRelatedStream->cLines / 10
113 : cbEstimate / 24;
114 cLinesEstimate = RT_ALIGN(cLinesEstimate, 512);
115 if (cLinesEstimate == 0)
116 cLinesEstimate = 16;
117 pStream->paLines = (PSCMSTREAMLINE)RTMemAlloc(cLinesEstimate * sizeof(SCMSTREAMLINE));
118 if (pStream->paLines)
119 {
120 pStream->paLines[0].off = 0;
121 pStream->paLines[0].cch = 0;
122 pStream->paLines[0].enmEol = SCMEOL_NONE;
123 pStream->cbAllocated = cbEstimate;
124 pStream->cLinesAllocated = cLinesEstimate;
125 return VINF_SUCCESS;
126 }
127
128 RTMemFree(pStream->pch);
129 pStream->pch = NULL;
130 }
131 return pStream->rc = VERR_NO_MEMORY;
132}
133
134/**
135 * Frees the resources associated with the stream.
136 *
137 * Nothing is happens to whatever the stream was initialized from or dumped to.
138 *
139 * @param pStream The stream to delete.
140 */
141void ScmStreamDelete(PSCMSTREAM pStream)
142{
143 if (pStream->pch)
144 {
145 if (pStream->fFileMemory)
146 RTFileReadAllFree(pStream->pch, pStream->cbAllocated);
147 else
148 RTMemFree(pStream->pch);
149 pStream->pch = NULL;
150 }
151 pStream->cbAllocated = 0;
152
153 if (pStream->paLines)
154 {
155 RTMemFree(pStream->paLines);
156 pStream->paLines = NULL;
157 }
158 pStream->cLinesAllocated = 0;
159}
160
161/**
162 * Get the stream status code.
163 *
164 * @returns IPRT status code.
165 * @param pStream The stream.
166 */
167int ScmStreamGetStatus(PCSCMSTREAM pStream)
168{
169 return pStream->rc;
170}
171
172/**
173 * Grows the buffer of a write stream.
174 *
175 * @returns IPRT status code.
176 * @param pStream The stream. Must be in write mode.
177 * @param cbAppending The minimum number of bytes to grow the buffer
178 * with.
179 */
180static int scmStreamGrowBuffer(PSCMSTREAM pStream, size_t cbAppending)
181{
182 size_t cbAllocated = pStream->cbAllocated;
183 cbAllocated += RT_MAX(0x1000 + cbAppending, cbAllocated);
184 cbAllocated = RT_ALIGN(cbAllocated, 0x1000);
185 void *pvNew;
186 if (!pStream->fFileMemory)
187 {
188 pvNew = RTMemRealloc(pStream->pch, cbAllocated);
189 if (!pvNew)
190 return pStream->rc = VERR_NO_MEMORY;
191 }
192 else
193 {
194 pvNew = RTMemDupEx(pStream->pch, pStream->off, cbAllocated - pStream->off);
195 if (!pvNew)
196 return pStream->rc = VERR_NO_MEMORY;
197 RTFileReadAllFree(pStream->pch, pStream->cbAllocated);
198 pStream->fFileMemory = false;
199 }
200 pStream->pch = (char *)pvNew;
201 pStream->cbAllocated = cbAllocated;
202
203 return VINF_SUCCESS;
204}
205
206/**
207 * Grows the line array of a stream.
208 *
209 * @returns IPRT status code.
210 * @param pStream The stream.
211 * @param iMinLine Minimum line number.
212 */
213static int scmStreamGrowLines(PSCMSTREAM pStream, size_t iMinLine)
214{
215 size_t cLinesAllocated = pStream->cLinesAllocated;
216 cLinesAllocated += RT_MAX(512 + iMinLine, cLinesAllocated);
217 cLinesAllocated = RT_ALIGN(cLinesAllocated, 512);
218 void *pvNew = RTMemRealloc(pStream->paLines, cLinesAllocated * sizeof(SCMSTREAMLINE));
219 if (!pvNew)
220 return pStream->rc = VERR_NO_MEMORY;
221
222 pStream->paLines = (PSCMSTREAMLINE)pvNew;
223 pStream->cLinesAllocated = cLinesAllocated;
224 return VINF_SUCCESS;
225}
226
227/**
228 * Rewinds the stream and sets the mode to read.
229 *
230 * @param pStream The stream.
231 */
232void ScmStreamRewindForReading(PSCMSTREAM pStream)
233{
234 pStream->off = 0;
235 pStream->iLine = 0;
236 pStream->fWriteOrRead = false;
237 pStream->rc = VINF_SUCCESS;
238}
239
240/**
241 * Rewinds the stream and sets the mode to write.
242 *
243 * @param pStream The stream.
244 */
245void ScmStreamRewindForWriting(PSCMSTREAM pStream)
246{
247 pStream->off = 0;
248 pStream->iLine = 0;
249 pStream->cLines = 0;
250 pStream->fWriteOrRead = true;
251 pStream->fFullyLineated = true;
252 pStream->rc = VINF_SUCCESS;
253
254 /* Initialize the first line with a zero length so ScmStreamWrite won't misbehave. */
255 if (pStream->cLinesAllocated == 0)
256 scmStreamGrowLines(pStream, 1);
257 if (pStream->cLinesAllocated > 0)
258 {
259 pStream->paLines[0].off = 0;
260 pStream->paLines[0].cch = 0;
261 pStream->paLines[0].enmEol = SCMEOL_NONE;
262 }
263}
264
265/**
266 * Checks if it's a text stream.
267 *
268 * Not 100% proof.
269 *
270 * @returns true if it probably is a text file, false if not.
271 * @param pStream The stream. Write or read, doesn't matter.
272 */
273bool ScmStreamIsText(PSCMSTREAM pStream)
274{
275 if (RTStrEnd(pStream->pch, pStream->cb))
276 return false;
277 if (!pStream->cb)
278 return true;
279 return true;
280}
281
282/**
283 * Performs an integrity check of the stream.
284 *
285 * @returns IPRT status code.
286 * @param pStream The stream.
287 */
288int ScmStreamCheckItegrity(PSCMSTREAM pStream)
289{
290 /*
291 * Perform sanity checks.
292 */
293 size_t const cbFile = pStream->cb;
294 for (size_t iLine = 0; iLine < pStream->cLines; iLine++)
295 {
296 size_t offEol = pStream->paLines[iLine].off + pStream->paLines[iLine].cch;
297 AssertReturn(offEol + pStream->paLines[iLine].enmEol <= cbFile, VERR_INTERNAL_ERROR_2);
298 switch (pStream->paLines[iLine].enmEol)
299 {
300 case SCMEOL_LF:
301 AssertReturn(pStream->pch[offEol] == '\n', VERR_INTERNAL_ERROR_3);
302 break;
303 case SCMEOL_CRLF:
304 AssertReturn(pStream->pch[offEol] == '\r', VERR_INTERNAL_ERROR_3);
305 AssertReturn(pStream->pch[offEol + 1] == '\n', VERR_INTERNAL_ERROR_3);
306 break;
307 case SCMEOL_NONE:
308 AssertReturn(iLine + 1 >= pStream->cLines, VERR_INTERNAL_ERROR_4);
309 break;
310 default:
311 AssertReturn(iLine + 1 >= pStream->cLines, VERR_INTERNAL_ERROR_5);
312 }
313 }
314 return VINF_SUCCESS;
315}
316
317/**
318 * Writes the stream to a file.
319 *
320 * @returns IPRT status code
321 * @param pStream The stream.
322 * @param pszFilenameFmt The filename format string.
323 * @param ... Format arguments.
324 */
325int ScmStreamWriteToFile(PSCMSTREAM pStream, const char *pszFilenameFmt, ...)
326{
327 int rc;
328
329#ifdef RT_STRICT
330 /*
331 * Check that what we're going to write makes sense first.
332 */
333 rc = ScmStreamCheckItegrity(pStream);
334 if (RT_FAILURE(rc))
335 return rc;
336#endif
337
338 /*
339 * Do the actual writing.
340 */
341 RTFILE hFile;
342 va_list va;
343 va_start(va, pszFilenameFmt);
344 rc = RTFileOpenV(&hFile, RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_WRITE, pszFilenameFmt, va);
345 if (RT_SUCCESS(rc))
346 {
347 rc = RTFileWrite(hFile, pStream->pch, pStream->cb, NULL);
348 RTFileClose(hFile);
349 }
350 va_end(va);
351 return rc;
352}
353
354/**
355 * Writes the stream to standard output.
356 *
357 * @returns IPRT status code
358 * @param pStream The stream.
359 */
360int ScmStreamWriteToStdOut(PSCMSTREAM pStream)
361{
362 int rc;
363
364#ifdef RT_STRICT
365 /*
366 * Check that what we're going to write makes sense first.
367 */
368 rc = ScmStreamCheckItegrity(pStream);
369 if (RT_FAILURE(rc))
370 return rc;
371#endif
372
373 /*
374 * Do the actual writing.
375 */
376 RTHANDLE h;
377 rc = RTHandleGetStandard(RTHANDLESTD_OUTPUT, true /*fLeaveOpen*/, &h);
378 if (RT_SUCCESS(rc))
379 {
380 switch (h.enmType)
381 {
382 case RTHANDLETYPE_FILE:
383 rc = RTFileWrite(h.u.hFile, pStream->pch, pStream->cb, NULL);
384 /** @todo RTFileClose */
385 break;
386 case RTHANDLETYPE_PIPE:
387 rc = RTPipeWriteBlocking(h.u.hPipe, pStream->pch, pStream->cb, NULL);
388 RTPipeClose(h.u.hPipe);
389 break;
390 default:
391 rc = VERR_INVALID_HANDLE;
392 break;
393 }
394 }
395 return rc;
396}
397
398/**
399 * Worker for ScmStreamGetLine that builds the line number index while parsing
400 * the stream.
401 *
402 * @returns Same as SCMStreamGetLine.
403 * @param pStream The stream. Must be in read mode.
404 * @param pcchLine Where to return the line length.
405 * @param penmEol Where to return the kind of end of line marker.
406 */
407static const char *scmStreamGetLineInternal(PSCMSTREAM pStream, size_t *pcchLine, PSCMEOL penmEol)
408{
409 AssertReturn(!pStream->fWriteOrRead, NULL);
410 if (RT_FAILURE(pStream->rc))
411 return NULL;
412
413 size_t off = pStream->off;
414 size_t cb = pStream->cb;
415 if (RT_UNLIKELY(off >= cb))
416 {
417 pStream->fFullyLineated = true;
418 return NULL;
419 }
420
421 size_t iLine = pStream->iLine;
422 if (RT_UNLIKELY(iLine >= pStream->cLinesAllocated))
423 {
424 int rc = scmStreamGrowLines(pStream, iLine);
425 if (RT_FAILURE(rc))
426 return NULL;
427 }
428 pStream->paLines[iLine].off = off;
429
430 cb -= off;
431 const char *pchRet = &pStream->pch[off];
432 const char *pch = (const char *)memchr(pchRet, '\n', cb);
433 if (RT_LIKELY(pch))
434 {
435 cb = pch - pchRet;
436 pStream->off = off + cb + 1;
437 if ( cb < 1
438 || pch[-1] != '\r')
439 pStream->paLines[iLine].enmEol = *penmEol = SCMEOL_LF;
440 else
441 {
442 pStream->paLines[iLine].enmEol = *penmEol = SCMEOL_CRLF;
443 cb--;
444 }
445 }
446 else
447 {
448 pStream->off = off + cb;
449 pStream->paLines[iLine].enmEol = *penmEol = SCMEOL_NONE;
450 }
451 *pcchLine = cb;
452 pStream->paLines[iLine].cch = cb;
453 pStream->cLines = pStream->iLine = ++iLine;
454
455 return pchRet;
456}
457
458/**
459 * Internal worker that delineates a stream.
460 *
461 * @returns IPRT status code.
462 * @param pStream The stream. Caller must check that it is in
463 * read mode.
464 */
465static int scmStreamLineate(PSCMSTREAM pStream)
466{
467 /* Save the stream position. */
468 size_t const offSaved = pStream->off;
469 size_t const iLineSaved = pStream->iLine;
470
471 /* Get each line. */
472 size_t cchLine;
473 SCMEOL enmEol;
474 while (scmStreamGetLineInternal(pStream, &cchLine, &enmEol))
475 /* nothing */;
476 Assert(RT_FAILURE(pStream->rc) || pStream->fFullyLineated);
477
478 /* Restore the position */
479 pStream->off = offSaved;
480 pStream->iLine = iLineSaved;
481
482 return pStream->rc;
483}
484
485/**
486 * Get the current stream position as an byte offset.
487 *
488 * @returns The current byte offset
489 * @param pStream The stream.
490 */
491size_t ScmStreamTell(PSCMSTREAM pStream)
492{
493 return pStream->off;
494}
495
496/**
497 * Get the current stream position as a line number.
498 *
499 * @returns The current line (0-based).
500 * @param pStream The stream.
501 */
502size_t ScmStreamTellLine(PSCMSTREAM pStream)
503{
504 return pStream->iLine;
505}
506
507
508/**
509 * Gets the stream offset of a given line.
510 *
511 * @returns The offset of the line, or the stream size if the line number is too
512 * high.
513 * @param pStream The stream. Must be in read mode.
514 * @param iLine The line we're asking about.
515 */
516size_t ScmStreamTellOffsetOfLine(PSCMSTREAM pStream, size_t iLine)
517{
518 AssertReturn(!pStream->fWriteOrRead, pStream->cb);
519 if (!pStream->fFullyLineated)
520 {
521 int rc = scmStreamLineate(pStream);
522 AssertRCReturn(rc, pStream->cb);
523 }
524 if (iLine >= pStream->cLines)
525 return pStream->cb;
526 return pStream->paLines[iLine].off;
527}
528
529
530/**
531 * Get the current stream size in bytes.
532 *
533 * @returns Count of bytes.
534 * @param pStream The stream.
535 */
536size_t ScmStreamSize(PSCMSTREAM pStream)
537{
538 return pStream->cb;
539}
540
541/**
542 * Gets the number of lines in the stream.
543 *
544 * @returns The number of lines.
545 * @param pStream The stream.
546 */
547size_t ScmStreamCountLines(PSCMSTREAM pStream)
548{
549 if (!pStream->fFullyLineated)
550 scmStreamLineate(pStream);
551 return pStream->cLines;
552}
553
554/**
555 * Seeks to a given byte offset in the stream.
556 *
557 * @returns IPRT status code.
558 * @retval VERR_SEEK if the new stream position is the middle of an EOL marker.
559 * This is a temporary restriction.
560 *
561 * @param pStream The stream. Must be in read mode.
562 * @param offAbsolute The offset to seek to. If this is beyond the
563 * end of the stream, the position is set to the
564 * end.
565 */
566int ScmStreamSeekAbsolute(PSCMSTREAM pStream, size_t offAbsolute)
567{
568 AssertReturn(!pStream->fWriteOrRead, VERR_ACCESS_DENIED);
569 if (RT_FAILURE(pStream->rc))
570 return pStream->rc;
571
572 /* Must be fully delineated. (lazy bird) */
573 if (RT_UNLIKELY(!pStream->fFullyLineated))
574 {
575 int rc = scmStreamLineate(pStream);
576 if (RT_FAILURE(rc))
577 return rc;
578 }
579
580 /* Ok, do the job. */
581 if (offAbsolute < pStream->cb)
582 {
583 /** @todo Should do a binary search here, but I'm too darn lazy tonight. */
584 pStream->off = ~(size_t)0;
585 for (size_t i = 0; i < pStream->cLines; i++)
586 {
587 if (offAbsolute < pStream->paLines[i].off + pStream->paLines[i].cch + pStream->paLines[i].enmEol)
588 {
589 pStream->off = offAbsolute;
590 pStream->iLine = i;
591 if (offAbsolute > pStream->paLines[i].off + pStream->paLines[i].cch)
592 return pStream->rc = VERR_SEEK;
593 break;
594 }
595 }
596 AssertReturn(pStream->off != ~(size_t)0, pStream->rc = VERR_INTERNAL_ERROR_3);
597 }
598 else
599 {
600 pStream->off = pStream->cb;
601 pStream->iLine = pStream->cLines;
602 }
603 return VINF_SUCCESS;
604}
605
606
607/**
608 * Seeks a number of bytes relative to the current stream position.
609 *
610 * @returns IPRT status code.
611 * @retval VERR_SEEK if the new stream position is the middle of an EOL marker.
612 * This is a temporary restriction.
613 *
614 * @param pStream The stream. Must be in read mode.
615 * @param offRelative The offset to seek to. A negative offset
616 * rewinds and positive one fast forwards the
617 * stream. Will quietly stop at the beginning and
618 * end of the stream.
619 */
620int ScmStreamSeekRelative(PSCMSTREAM pStream, ssize_t offRelative)
621{
622 size_t offAbsolute;
623 if (offRelative >= 0)
624 offAbsolute = pStream->off + offRelative;
625 else if ((size_t)-offRelative <= pStream->off)
626 offAbsolute = pStream->off + offRelative;
627 else
628 offAbsolute = 0;
629 return ScmStreamSeekAbsolute(pStream, offAbsolute);
630}
631
632/**
633 * Seeks to a given line in the stream.
634 *
635 * @returns IPRT status code.
636 *
637 * @param pStream The stream. Must be in read mode.
638 * @param iLine The line to seek to. If this is beyond the end
639 * of the stream, the position is set to the end.
640 */
641int ScmStreamSeekByLine(PSCMSTREAM pStream, size_t iLine)
642{
643 if (RT_FAILURE(pStream->rc))
644 return pStream->rc;
645
646 /* Must be fully delineated. (lazy bird) */
647 if (RT_UNLIKELY(!pStream->fFullyLineated))
648 {
649 AssertReturn(!pStream->fWriteOrRead, VERR_ACCESS_DENIED);
650 int rc = scmStreamLineate(pStream);
651 if (RT_FAILURE(rc))
652 return rc;
653 }
654
655 /* Ok, do the job. */
656 if (iLine < pStream->cLines)
657 {
658 pStream->iLine = iLine;
659 pStream->off = pStream->paLines[iLine].off;
660 if (pStream->fWriteOrRead)
661 {
662 pStream->cb = pStream->paLines[iLine].off;
663 pStream->cLines = iLine;
664 pStream->paLines[iLine].cch = 0;
665 pStream->paLines[iLine].enmEol = SCMEOL_NONE;
666 }
667 }
668 else
669 {
670 AssertReturn(!pStream->fWriteOrRead, VERR_ACCESS_DENIED);
671 pStream->off = pStream->cb;
672 pStream->iLine = pStream->cLines;
673 }
674 return VINF_SUCCESS;
675}
676
677/**
678 * Checks if the stream position is at the start of a line.
679 *
680 * @returns @c true if at the start, @c false if not.
681 * @param pStream The stream.
682 */
683bool ScmStreamIsAtStartOfLine(PSCMSTREAM pStream)
684{
685 if ( !pStream->fFullyLineated
686 && !pStream->fWriteOrRead)
687 {
688 int rc = scmStreamLineate(pStream);
689 if (RT_FAILURE(rc))
690 return false;
691 }
692 return pStream->off == pStream->paLines[pStream->iLine].off;
693}
694
695/**
696 * Compares the two streams from the start to end, binary fashion.
697 *
698 * The stream position does not change nor does it matter whether they are
699 * writable or readable.
700 *
701 * @returns true if identical, false if not.
702 * @param pStream1 The first stream.
703 * @param pStream2 The second stream.
704 */
705bool ScmStreamAreIdentical(PCSCMSTREAM pStream1, PCSCMSTREAM pStream2)
706{
707 return pStream1->cb == pStream2->cb
708 && memcmp(pStream1->pch, pStream2->pch, pStream1->cb) == 0;
709}
710
711
712/**
713 * Worker for ScmStreamGetLineByNo and ScmStreamGetLine.
714 *
715 * Works on a fully lineated stream.
716 *
717 * @returns Pointer to the first character in the line, not NULL terminated.
718 * NULL if the end of the stream has been reached or some problem
719 * occurred.
720 *
721 * @param pStream The stream. Must be in read mode.
722 * @param iLine The line to get (0-based).
723 * @param pcchLine The length.
724 * @param penmEol Where to return the end of line type indicator.
725 */
726DECLINLINE(const char *) scmStreamGetLineByNoCommon(PSCMSTREAM pStream, size_t iLine, size_t *pcchLine, PSCMEOL penmEol)
727{
728 Assert(!pStream->fWriteOrRead);
729 Assert(pStream->fFullyLineated);
730
731 /* Check stream status. */
732 if (RT_SUCCESS(pStream->rc))
733 {
734 /* Not at the end of the stream yet? */
735 if (RT_LIKELY(iLine < pStream->cLines))
736 {
737 /* Get the data. */
738 const char *pchRet = &pStream->pch[pStream->paLines[iLine].off];
739 *pcchLine = pStream->paLines[iLine].cch;
740 *penmEol = pStream->paLines[iLine].enmEol;
741
742 /* update the stream position. */
743 pStream->off = pStream->paLines[iLine].off + pStream->paLines[iLine].cch + pStream->paLines[iLine].enmEol;
744 pStream->iLine = iLine + 1;
745 return pchRet;
746 }
747 pStream->off = pStream->cb;
748 pStream->iLine = pStream->cLines;
749 }
750 *pcchLine = 0;
751 *penmEol = SCMEOL_NONE;
752 return NULL;
753}
754
755
756/**
757 * Get a numbered line from the stream (changes the position).
758 *
759 * A line is always delimited by a LF character or the end of the stream. The
760 * delimiter is not included in returned line length, but instead returned via
761 * the @a penmEol indicator.
762 *
763 * @returns Pointer to the first character in the line, not NULL terminated.
764 * NULL if the end of the stream has been reached or some problem
765 * occurred (*pcchLine set to zero and *penmEol to SCMEOL_NONE).
766 *
767 * @param pStream The stream. Must be in read mode.
768 * @param iLine The line to get (0-based).
769 * @param pcchLine The length.
770 * @param penmEol Where to return the end of line type indicator.
771 */
772const char *ScmStreamGetLineByNo(PSCMSTREAM pStream, size_t iLine, size_t *pcchLine, PSCMEOL penmEol)
773{
774 AssertReturn(!pStream->fWriteOrRead, NULL);
775
776 /* Make sure it's fully delineated so we can use the index. */
777 if (RT_LIKELY(pStream->fFullyLineated))
778 return scmStreamGetLineByNoCommon(pStream, iLine, pcchLine, penmEol);
779
780 int rc = pStream->rc;
781 if (RT_SUCCESS(rc))
782 {
783 rc = scmStreamLineate(pStream);
784 if (RT_SUCCESS(rc))
785 return scmStreamGetLineByNoCommon(pStream, iLine, pcchLine, penmEol);
786 }
787
788 *pcchLine = 0;
789 *penmEol = SCMEOL_NONE;
790 return NULL;
791}
792
793/**
794 * Get a line from the stream.
795 *
796 * A line is always delimited by a LF character or the end of the stream. The
797 * delimiter is not included in returned line length, but instead returned via
798 * the @a penmEol indicator.
799 *
800 * @returns Pointer to the first character in the line, not NULL terminated.
801 * NULL if the end of the stream has been reached or some problem
802 * occurred (*pcchLine set to zero and *penmEol to SCMEOL_NONE).
803 *
804 * @param pStream The stream. Must be in read mode.
805 * @param pcchLine The length.
806 * @param penmEol Where to return the end of line type indicator.
807 */
808const char *ScmStreamGetLine(PSCMSTREAM pStream, size_t *pcchLine, PSCMEOL penmEol)
809{
810 if (RT_LIKELY(pStream->fFullyLineated))
811 {
812 size_t offCur = pStream->off;
813 size_t iCurLine = pStream->iLine;
814 const char *pszLine = scmStreamGetLineByNoCommon(pStream, iCurLine, pcchLine, penmEol);
815 if ( pszLine
816 && offCur > pStream->paLines[iCurLine].off)
817 {
818 offCur -= pStream->paLines[iCurLine].off;
819 Assert(offCur <= pStream->paLines[iCurLine].cch + pStream->paLines[iCurLine].enmEol);
820 if (offCur < pStream->paLines[iCurLine].cch)
821 *pcchLine -= offCur;
822 else
823 *pcchLine = 0;
824 pszLine += offCur;
825 }
826 return pszLine;
827 }
828 return scmStreamGetLineInternal(pStream, pcchLine, penmEol);
829}
830
831/**
832 * Get the current buffer pointer.
833 *
834 * @returns Buffer pointer on success, NULL on failure (asserted).
835 * @param pStream The stream. Must be in read mode.
836 */
837const char *ScmStreamGetCur(PSCMSTREAM pStream)
838{
839 AssertReturn(!pStream->fWriteOrRead, NULL);
840 return pStream->pch + pStream->off;
841}
842
843/**
844 * Gets a character from the stream.
845 *
846 * @returns The next unsigned character in the stream.
847 * ~(unsigned)0 on failure.
848 * @param pStream The stream. Must be in read mode.
849 */
850unsigned ScmStreamGetCh(PSCMSTREAM pStream)
851{
852 /* Check stream state. */
853 AssertReturn(!pStream->fWriteOrRead, ~(unsigned)0);
854 if (RT_FAILURE(pStream->rc))
855 return ~(unsigned)0;
856 if (RT_UNLIKELY(!pStream->fFullyLineated))
857 {
858 int rc = scmStreamLineate(pStream);
859 if (RT_FAILURE(rc))
860 return ~(unsigned)0;
861 }
862
863 /* If there isn't enough stream left, fail already. */
864 if (RT_UNLIKELY(pStream->off >= pStream->cb))
865 return ~(unsigned)0;
866
867 /* Read a character. */
868 char ch = pStream->pch[pStream->off++];
869
870 /* Advance the line indicator. */
871 size_t iLine = pStream->iLine;
872 if (pStream->off >= pStream->paLines[iLine].off + pStream->paLines[iLine].cch + pStream->paLines[iLine].enmEol)
873 pStream->iLine++;
874
875 return (unsigned)ch;
876}
877
878
879/**
880 * Peeks at the next character from the stream.
881 *
882 * @returns The next unsigned character in the stream.
883 * ~(unsigned)0 on failure.
884 * @param pStream The stream. Must be in read mode.
885 */
886unsigned ScmStreamPeekCh(PSCMSTREAM pStream)
887{
888 /* Check stream state. */
889 AssertReturn(!pStream->fWriteOrRead, ~(unsigned)0);
890 if (RT_FAILURE(pStream->rc))
891 return ~(unsigned)0;
892 if (RT_UNLIKELY(!pStream->fFullyLineated))
893 {
894 int rc = scmStreamLineate(pStream);
895 if (RT_FAILURE(rc))
896 return ~(unsigned)0;
897 }
898
899 /* If there isn't enough stream left, fail already. */
900 if (RT_UNLIKELY(pStream->off >= pStream->cb))
901 return ~(unsigned)0;
902
903 /* Peek at the next character. */
904 char ch = pStream->pch[pStream->off];
905 return (unsigned)ch;
906}
907
908
909/**
910 * Reads @a cbToRead bytes into @a pvBuf.
911 *
912 * Will fail if end of stream is encountered before the entire read has been
913 * completed.
914 *
915 * @returns IPRT status code.
916 * @retval VERR_EOF if there isn't @a cbToRead bytes left to read. Stream
917 * position will be unchanged.
918 *
919 * @param pStream The stream. Must be in read mode.
920 * @param pvBuf The buffer to read into.
921 * @param cbToRead The number of bytes to read.
922 */
923int ScmStreamRead(PSCMSTREAM pStream, void *pvBuf, size_t cbToRead)
924{
925 AssertReturn(!pStream->fWriteOrRead, VERR_PERMISSION_DENIED);
926 if (RT_FAILURE(pStream->rc))
927 return pStream->rc;
928
929 /* If there isn't enough stream left, fail already. */
930 if (RT_UNLIKELY(pStream->cb - pStream->off < cbToRead))
931 return VERR_EOF;
932
933 /* Copy the data and simply seek to the new stream position. */
934 memcpy(pvBuf, &pStream->pch[pStream->off], cbToRead);
935 return ScmStreamSeekAbsolute(pStream, pStream->off + cbToRead);
936}
937
938
939/**
940 * Checks if we're at the end of the stream.
941 *
942 * @returns true if end of stream, false if not.
943 * @param pStream The stream. Must be in read mode.
944 */
945bool ScmStreamIsEndOfStream(PSCMSTREAM pStream)
946{
947 AssertReturn(!pStream->fWriteOrRead, false);
948 return pStream->off >= pStream->cb
949 || RT_FAILURE(pStream->rc);
950}
951
952
953/**
954 * Checks if the given line is empty or full of white space.
955 *
956 * @returns true if white space only, false if not (or if non-existant).
957 * @param pStream The stream. Must be in read mode.
958 * @param iLine The line in question.
959 */
960bool ScmStreamIsWhiteLine(PSCMSTREAM pStream, size_t iLine)
961{
962 SCMEOL enmEol;
963 size_t cchLine;
964 const char *pchLine = ScmStreamGetLineByNo(pStream, iLine, &cchLine, &enmEol);
965 if (!pchLine)
966 return false;
967 while (cchLine && RT_C_IS_SPACE(*pchLine))
968 pchLine++, cchLine--;
969 return cchLine == 0;
970}
971
972
973/**
974 * Try figure out the end of line style of the give stream.
975 *
976 * @returns Most likely end of line style.
977 * @param pStream The stream.
978 */
979SCMEOL ScmStreamGetEol(PSCMSTREAM pStream)
980{
981 SCMEOL enmEol;
982 if (pStream->cLines > 0)
983 enmEol = pStream->paLines[0].enmEol;
984 else if (pStream->cb == 0)
985 enmEol = SCMEOL_NONE;
986 else
987 {
988 const char *pchLF = (const char *)memchr(pStream->pch, '\n', pStream->cb);
989 if (pchLF && pchLF != pStream->pch && pchLF[-1] == '\r')
990 enmEol = SCMEOL_CRLF;
991 else
992 enmEol = SCMEOL_LF;
993 }
994
995 if (enmEol == SCMEOL_NONE)
996#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
997 enmEol = SCMEOL_CRLF;
998#else
999 enmEol = SCMEOL_LF;
1000#endif
1001 return enmEol;
1002}
1003
1004
1005/**
1006 * Get the end of line indicator type for a line.
1007 *
1008 * @returns The EOL indicator. If the line isn't found, the default EOL
1009 * indicator is return.
1010 * @param pStream The stream.
1011 * @param iLine The line (0-base).
1012 */
1013SCMEOL ScmStreamGetEolByLine(PSCMSTREAM pStream, size_t iLine)
1014{
1015 SCMEOL enmEol;
1016 if (iLine < pStream->cLines)
1017 enmEol = pStream->paLines[iLine].enmEol;
1018 else
1019#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
1020 enmEol = SCMEOL_CRLF;
1021#else
1022 enmEol = SCMEOL_LF;
1023#endif
1024 return enmEol;
1025}
1026
1027
1028/**
1029 * Appends a line to the stream.
1030 *
1031 * @returns IPRT status code.
1032 * @param pStream The stream. Must be in write mode.
1033 * @param pchLine Pointer to the line.
1034 * @param cchLine Line length.
1035 * @param enmEol Which end of line indicator to use.
1036 */
1037int ScmStreamPutLine(PSCMSTREAM pStream, const char *pchLine, size_t cchLine, SCMEOL enmEol)
1038{
1039 AssertReturn(pStream->fWriteOrRead, VERR_ACCESS_DENIED);
1040 if (RT_FAILURE(pStream->rc))
1041 return pStream->rc;
1042
1043 /*
1044 * Make sure the previous line has a new-line indicator.
1045 */
1046 size_t off = pStream->off;
1047 size_t iLine = pStream->iLine;
1048 if (RT_UNLIKELY( iLine != 0
1049 && pStream->paLines[iLine - 1].enmEol == SCMEOL_NONE))
1050 {
1051 AssertReturn(pStream->paLines[iLine].cch == 0, VERR_INTERNAL_ERROR_3);
1052 SCMEOL enmEol2 = enmEol != SCMEOL_NONE ? enmEol : ScmStreamGetEol(pStream);
1053 if (RT_UNLIKELY(off + cchLine + enmEol + enmEol2 > pStream->cbAllocated))
1054 {
1055 int rc = scmStreamGrowBuffer(pStream, cchLine + enmEol + enmEol2);
1056 if (RT_FAILURE(rc))
1057 return rc;
1058 }
1059 if (enmEol2 == SCMEOL_LF)
1060 pStream->pch[off++] = '\n';
1061 else
1062 {
1063 pStream->pch[off++] = '\r';
1064 pStream->pch[off++] = '\n';
1065 }
1066 pStream->paLines[iLine - 1].enmEol = enmEol2;
1067 pStream->paLines[iLine].off = off;
1068 pStream->off = off;
1069 pStream->cb = off;
1070 }
1071
1072 /*
1073 * Ensure we've got sufficient buffer space.
1074 */
1075 if (RT_UNLIKELY(off + cchLine + enmEol > pStream->cbAllocated))
1076 {
1077 int rc = scmStreamGrowBuffer(pStream, cchLine + enmEol);
1078 if (RT_FAILURE(rc))
1079 return rc;
1080 }
1081
1082 /*
1083 * Add a line record.
1084 */
1085 if (RT_UNLIKELY(iLine + 1 >= pStream->cLinesAllocated))
1086 {
1087 int rc = scmStreamGrowLines(pStream, iLine);
1088 if (RT_FAILURE(rc))
1089 return rc;
1090 }
1091
1092 pStream->paLines[iLine].cch = off - pStream->paLines[iLine].off + cchLine;
1093 pStream->paLines[iLine].enmEol = enmEol;
1094
1095 iLine++;
1096 pStream->cLines = iLine;
1097 pStream->iLine = iLine;
1098
1099 /*
1100 * Copy the line
1101 */
1102 memcpy(&pStream->pch[off], pchLine, cchLine);
1103 off += cchLine;
1104 if (enmEol == SCMEOL_LF)
1105 pStream->pch[off++] = '\n';
1106 else if (enmEol == SCMEOL_CRLF)
1107 {
1108 pStream->pch[off++] = '\r';
1109 pStream->pch[off++] = '\n';
1110 }
1111 pStream->off = off;
1112 pStream->cb = off;
1113
1114 /*
1115 * Start a new line.
1116 */
1117 pStream->paLines[iLine].off = off;
1118 pStream->paLines[iLine].cch = 0;
1119 pStream->paLines[iLine].enmEol = SCMEOL_NONE;
1120
1121 return VINF_SUCCESS;
1122}
1123
1124/**
1125 * Writes to the stream.
1126 *
1127 * @returns IPRT status code
1128 * @param pStream The stream. Must be in write mode.
1129 * @param pchBuf What to write.
1130 * @param cchBuf How much to write.
1131 */
1132int ScmStreamWrite(PSCMSTREAM pStream, const char *pchBuf, size_t cchBuf)
1133{
1134 AssertReturn(pStream->fWriteOrRead, VERR_ACCESS_DENIED);
1135 if (RT_FAILURE(pStream->rc))
1136 return pStream->rc;
1137
1138 /*
1139 * Ensure we've got sufficient buffer space.
1140 */
1141 size_t off = pStream->off;
1142 if (RT_UNLIKELY(off + cchBuf > pStream->cbAllocated))
1143 {
1144 int rc = scmStreamGrowBuffer(pStream, cchBuf);
1145 if (RT_FAILURE(rc))
1146 return rc;
1147 }
1148
1149 /*
1150 * Deal with the odd case where we've already pushed a line with SCMEOL_NONE.
1151 */
1152 size_t iLine = pStream->iLine;
1153 if (RT_UNLIKELY( iLine > 0
1154 && pStream->paLines[iLine - 1].enmEol == SCMEOL_NONE))
1155 {
1156 iLine--;
1157 pStream->cLines = iLine;
1158 pStream->iLine = iLine;
1159 }
1160
1161 /*
1162 * Deal with lines.
1163 */
1164 const char *pchLF = (const char *)memchr(pchBuf, '\n', cchBuf);
1165 if (!pchLF)
1166 pStream->paLines[iLine].cch += cchBuf;
1167 else
1168 {
1169 const char *pchLine = pchBuf;
1170 for (;;)
1171 {
1172 if (RT_UNLIKELY(iLine + 1 >= pStream->cLinesAllocated))
1173 {
1174 int rc = scmStreamGrowLines(pStream, iLine);
1175 if (RT_FAILURE(rc))
1176 {
1177 iLine = pStream->iLine;
1178 pStream->paLines[iLine].cch = off - pStream->paLines[iLine].off;
1179 pStream->paLines[iLine].enmEol = SCMEOL_NONE;
1180 return rc;
1181 }
1182 }
1183
1184 size_t cchLine = pchLF - pchLine;
1185 if ( cchLine
1186 ? pchLF[-1] != '\r'
1187 : !pStream->paLines[iLine].cch
1188 || pStream->pch[pStream->paLines[iLine].off + pStream->paLines[iLine].cch - 1] != '\r')
1189 pStream->paLines[iLine].enmEol = SCMEOL_LF;
1190 else
1191 {
1192 pStream->paLines[iLine].enmEol = SCMEOL_CRLF;
1193 cchLine--;
1194 }
1195 pStream->paLines[iLine].cch += cchLine;
1196
1197 iLine++;
1198 size_t offBuf = pchLF + 1 - pchBuf;
1199 pStream->paLines[iLine].off = off + offBuf;
1200 pStream->paLines[iLine].cch = 0;
1201 pStream->paLines[iLine].enmEol = SCMEOL_NONE;
1202
1203 size_t cchLeft = cchBuf - offBuf;
1204 pchLine = pchLF + 1;
1205 pchLF = (const char *)memchr(pchLine, '\n', cchLeft);
1206 if (!pchLF)
1207 {
1208 pStream->paLines[iLine].cch = cchLeft;
1209 break;
1210 }
1211 }
1212
1213 pStream->iLine = iLine;
1214 pStream->cLines = iLine;
1215 }
1216
1217 /*
1218 * Copy the data and update position and size.
1219 */
1220 memcpy(&pStream->pch[off], pchBuf, cchBuf);
1221 off += cchBuf;
1222 pStream->off = off;
1223 pStream->cb = off;
1224
1225 return VINF_SUCCESS;
1226}
1227
1228/**
1229 * Write a character to the stream.
1230 *
1231 * @returns IPRT status code
1232 * @param pStream The stream. Must be in write mode.
1233 * @param pchBuf What to write.
1234 * @param cchBuf How much to write.
1235 */
1236int ScmStreamPutCh(PSCMSTREAM pStream, char ch)
1237{
1238 AssertReturn(pStream->fWriteOrRead, VERR_ACCESS_DENIED);
1239 if (RT_FAILURE(pStream->rc))
1240 return pStream->rc;
1241
1242 /*
1243 * Only deal with the simple cases here, use ScmStreamWrite for the
1244 * annoying stuff.
1245 */
1246 size_t off = pStream->off;
1247 if ( ch == '\n'
1248 || RT_UNLIKELY(off + 1 > pStream->cbAllocated))
1249 return ScmStreamWrite(pStream, &ch, 1);
1250
1251 /*
1252 * Just append it.
1253 */
1254 pStream->pch[off] = ch;
1255 pStream->off = off + 1;
1256 pStream->paLines[pStream->iLine].cch++;
1257
1258 return VINF_SUCCESS;
1259}
1260
1261/**
1262 * Puts an EOL marker to the stream.
1263 *
1264 * @returns IPRt status code.
1265 * @param pStream The stream. Must be in write mode.
1266 * @param enmEol The end-of-line marker to write.
1267 */
1268int ScmStreamPutEol(PSCMSTREAM pStream, SCMEOL enmEol)
1269{
1270 if (enmEol == SCMEOL_LF)
1271 return ScmStreamWrite(pStream, "\n", 1);
1272 if (enmEol == SCMEOL_CRLF)
1273 return ScmStreamWrite(pStream, "\r\n", 2);
1274 if (enmEol == SCMEOL_NONE)
1275 return VINF_SUCCESS;
1276 AssertFailedReturn(VERR_INVALID_PARAMETER);
1277}
1278
1279/**
1280 * Formats a string and writes it to the SCM stream.
1281 *
1282 * @returns The number of bytes written (>= 0). Negative value are IPRT error
1283 * status codes.
1284 * @param pStream The stream to write to.
1285 * @param pszFormat The format string.
1286 * @param va The arguments to format.
1287 */
1288ssize_t ScmStreamPrintfV(PSCMSTREAM pStream, const char *pszFormat, va_list va)
1289{
1290 char *psz;
1291 ssize_t cch = RTStrAPrintfV(&psz, pszFormat, va);
1292 if (cch)
1293 {
1294 int rc = ScmStreamWrite(pStream, psz, cch);
1295 RTStrFree(psz);
1296 if (RT_FAILURE(rc))
1297 cch = rc;
1298 }
1299 return cch;
1300}
1301
1302/**
1303 * Formats a string and writes it to the SCM stream.
1304 *
1305 * @returns The number of bytes written (>= 0). Negative value are IPRT error
1306 * status codes.
1307 * @param pStream The stream to write to.
1308 * @param pszFormat The format string.
1309 * @param ... The arguments to format.
1310 */
1311ssize_t ScmStreamPrintf(PSCMSTREAM pStream, const char *pszFormat, ...)
1312{
1313 va_list va;
1314 va_start(va, pszFormat);
1315 ssize_t cch = ScmStreamPrintfV(pStream, pszFormat, va);
1316 va_end(va);
1317 return cch;
1318}
1319
1320/**
1321 * Copies @a cLines from the @a pSrc stream onto the @a pDst stream.
1322 *
1323 * The stream positions will be used and changed in both streams.
1324 *
1325 * @returns IPRT status code.
1326 * @param pDst The destination stream. Must be in write mode.
1327 * @param cLines The number of lines. (0 is accepted.)
1328 * @param pSrc The source stream. Must be in read mode.
1329 */
1330int ScmStreamCopyLines(PSCMSTREAM pDst, PSCMSTREAM pSrc, size_t cLines)
1331{
1332 AssertReturn(pDst->fWriteOrRead, VERR_ACCESS_DENIED);
1333 if (RT_FAILURE(pDst->rc))
1334 return pDst->rc;
1335
1336 AssertReturn(!pSrc->fWriteOrRead, VERR_ACCESS_DENIED);
1337 if (RT_FAILURE(pSrc->rc))
1338 return pSrc->rc;
1339
1340 while (cLines-- > 0)
1341 {
1342 SCMEOL enmEol;
1343 size_t cchLine;
1344 const char *pchLine = ScmStreamGetLine(pSrc, &cchLine, &enmEol);
1345 if (!pchLine)
1346 return pDst->rc = RT_FAILURE(pSrc->rc) ? pSrc->rc : VERR_EOF;
1347
1348 int rc = ScmStreamPutLine(pDst, pchLine, cchLine, enmEol);
1349 if (RT_FAILURE(rc))
1350 return rc;
1351 }
1352
1353 return VINF_SUCCESS;
1354}
1355
1356
1357/**
1358 * If the given C word is at off - 1, return @c true and skip beyond it,
1359 * otherwise return @c false.
1360 *
1361 * @retval true if the given C-word is at the current position minus one char.
1362 * The stream position changes.
1363 * @retval false if not. The stream position is unchanged.
1364 *
1365 * @param pStream The stream.
1366 * @param cchWord The length of the word.
1367 * @param pszWord The word.
1368 */
1369bool ScmStreamCMatchingWordM1(PSCMSTREAM pStream, const char *pszWord, size_t cchWord)
1370{
1371 /* Check stream state. */
1372 AssertReturn(!pStream->fWriteOrRead, false);
1373 AssertReturn(RT_SUCCESS(pStream->rc), false);
1374 AssertReturn(pStream->fFullyLineated, false);
1375
1376 /* Sufficient chars left on the line? */
1377 size_t const iLine = pStream->iLine;
1378 AssertReturn(pStream->off > pStream->paLines[iLine].off, false);
1379 size_t const cchLeft = pStream->paLines[iLine].cch + pStream->paLines[iLine].off - (pStream->off - 1);
1380 if (cchWord > cchLeft)
1381 return false;
1382
1383 /* Do they match? */
1384 const char *psz = &pStream->pch[pStream->off - 1];
1385 if (memcmp(psz, pszWord, cchWord))
1386 return false;
1387
1388 /* Is it the end of a C word? */
1389 if (cchWord < cchLeft)
1390 {
1391 psz += cchWord;
1392 if (RT_C_IS_ALNUM(*psz) || *psz == '_')
1393 return false;
1394 }
1395
1396 /* Skip ahead. */
1397 pStream->off += cchWord - 1;
1398 return true;
1399}
1400
1401
1402/**
1403 * Get's the C word starting at the current position.
1404 *
1405 * @returns Pointer to the word on success and the stream position advanced to
1406 * the end of it.
1407 * NULL on failure, stream position normally unchanged.
1408 * @param pStream The stream to get the C word from.
1409 * @param pcchWord Where to return the word length.
1410 */
1411const char *ScmStreamCGetWord(PSCMSTREAM pStream, size_t *pcchWord)
1412{
1413 /* Check stream state. */
1414 AssertReturn(!pStream->fWriteOrRead, NULL);
1415 AssertReturn(RT_SUCCESS(pStream->rc), NULL);
1416 AssertReturn(pStream->fFullyLineated, NULL);
1417
1418 /* Get the number of chars left on the line and locate the current char. */
1419 size_t const iLine = pStream->iLine;
1420 size_t const cchLeft = pStream->paLines[iLine].cch + pStream->paLines[iLine].off - pStream->off;
1421 const char *psz = &pStream->pch[pStream->off];
1422
1423 /* Is it a leading C character. */
1424 if (!RT_C_IS_ALPHA(*psz) && *psz != '_')
1425 return NULL;
1426
1427 /* Find the end of the word. */
1428 char ch;
1429 size_t off = 1;
1430 while ( off < cchLeft
1431 && ( (ch = psz[off]) == '_'
1432 || RT_C_IS_ALNUM(ch)))
1433 off++;
1434
1435 pStream->off += off;
1436 *pcchWord = off;
1437 return psz;
1438}
1439
1440
1441/**
1442 * Get's the C word starting at the current position minus one.
1443 *
1444 * @returns Pointer to the word on success and the stream position advanced to
1445 * the end of it.
1446 * NULL on failure, stream position normally unchanged.
1447 * @param pStream The stream to get the C word from.
1448 * @param pcchWord Where to return the word length.
1449 */
1450const char *ScmStreamCGetWordM1(PSCMSTREAM pStream, size_t *pcchWord)
1451{
1452 /* Check stream state. */
1453 AssertReturn(!pStream->fWriteOrRead, NULL);
1454 AssertReturn(RT_SUCCESS(pStream->rc), NULL);
1455 AssertReturn(pStream->fFullyLineated, NULL);
1456
1457 /* Get the number of chars left on the line and locate the current char. */
1458 size_t const iLine = pStream->iLine;
1459 size_t const cchLeft = pStream->paLines[iLine].cch + pStream->paLines[iLine].off - (pStream->off - 1);
1460 const char *psz = &pStream->pch[pStream->off - 1];
1461
1462 /* Is it a leading C character. */
1463 if (!RT_C_IS_ALPHA(*psz) && *psz != '_')
1464 return NULL;
1465
1466 /* Find the end of the word. */
1467 char ch;
1468 size_t off = 1;
1469 while ( off < cchLeft
1470 && ( (ch = psz[off]) == '_'
1471 || RT_C_IS_ALNUM(ch)))
1472 off++;
1473
1474 pStream->off += off - 1;
1475 *pcchWord = off;
1476 return psz;
1477}
1478
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