VirtualBox

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

Last change on this file since 90330 was 86414, checked in by vboxsync, 4 years ago

IPRT,++: Adding a fLeaveOpen parameter to RTHandleGetStandard. Currently only honored/implemented for pipes. bugref:9841

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