VirtualBox

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

Last change on this file was 103383, checked in by vboxsync, 3 months ago

scm: A couple of parfait things. bugref:3409

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

© 2023 Oracle
ContactPrivacy policyTerms of Use