VirtualBox

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

Last change on this file since 76551 was 76551, checked in by vboxsync, 6 years ago

scm: More work on header guard massaging (not active yet).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 41.8 KB
Line 
1/* $Id: scmstream.cpp 76551 2018-12-31 05:09:06Z vboxsync $ */
2/** @file
3 * IPRT Testcase / Tool - Source Code Massager Stream Code.
4 */
5
6/*
7 * Copyright (C) 2010-2017 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, &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 break;
375 case RTHANDLETYPE_PIPE:
376 rc = RTPipeWriteBlocking(h.u.hPipe, pStream->pch, pStream->cb, NULL);
377 break;
378 default:
379 rc = VERR_INVALID_HANDLE;
380 break;
381 }
382 }
383 return rc;
384}
385
386/**
387 * Worker for ScmStreamGetLine that builds the line number index while parsing
388 * the stream.
389 *
390 * @returns Same as SCMStreamGetLine.
391 * @param pStream The stream. Must be in read mode.
392 * @param pcchLine Where to return the line length.
393 * @param penmEol Where to return the kind of end of line marker.
394 */
395static const char *scmStreamGetLineInternal(PSCMSTREAM pStream, size_t *pcchLine, PSCMEOL penmEol)
396{
397 AssertReturn(!pStream->fWriteOrRead, NULL);
398 if (RT_FAILURE(pStream->rc))
399 return NULL;
400
401 size_t off = pStream->off;
402 size_t cb = pStream->cb;
403 if (RT_UNLIKELY(off >= cb))
404 {
405 pStream->fFullyLineated = true;
406 return NULL;
407 }
408
409 size_t iLine = pStream->iLine;
410 if (RT_UNLIKELY(iLine >= pStream->cLinesAllocated))
411 {
412 int rc = scmStreamGrowLines(pStream, iLine);
413 if (RT_FAILURE(rc))
414 return NULL;
415 }
416 pStream->paLines[iLine].off = off;
417
418 cb -= off;
419 const char *pchRet = &pStream->pch[off];
420 const char *pch = (const char *)memchr(pchRet, '\n', cb);
421 if (RT_LIKELY(pch))
422 {
423 cb = pch - pchRet;
424 pStream->off = off + cb + 1;
425 if ( cb < 1
426 || pch[-1] != '\r')
427 pStream->paLines[iLine].enmEol = *penmEol = SCMEOL_LF;
428 else
429 {
430 pStream->paLines[iLine].enmEol = *penmEol = SCMEOL_CRLF;
431 cb--;
432 }
433 }
434 else
435 {
436 pStream->off = off + cb;
437 pStream->paLines[iLine].enmEol = *penmEol = SCMEOL_NONE;
438 }
439 *pcchLine = cb;
440 pStream->paLines[iLine].cch = cb;
441 pStream->cLines = pStream->iLine = ++iLine;
442
443 return pchRet;
444}
445
446/**
447 * Internal worker that delineates a stream.
448 *
449 * @returns IPRT status code.
450 * @param pStream The stream. Caller must check that it is in
451 * read mode.
452 */
453static int scmStreamLineate(PSCMSTREAM pStream)
454{
455 /* Save the stream position. */
456 size_t const offSaved = pStream->off;
457 size_t const iLineSaved = pStream->iLine;
458
459 /* Get each line. */
460 size_t cchLine;
461 SCMEOL enmEol;
462 while (scmStreamGetLineInternal(pStream, &cchLine, &enmEol))
463 /* nothing */;
464 Assert(RT_FAILURE(pStream->rc) || pStream->fFullyLineated);
465
466 /* Restore the position */
467 pStream->off = offSaved;
468 pStream->iLine = iLineSaved;
469
470 return pStream->rc;
471}
472
473/**
474 * Get the current stream position as an byte offset.
475 *
476 * @returns The current byte offset
477 * @param pStream The stream.
478 */
479size_t ScmStreamTell(PSCMSTREAM pStream)
480{
481 return pStream->off;
482}
483
484/**
485 * Get the current stream position as a line number.
486 *
487 * @returns The current line (0-based).
488 * @param pStream The stream.
489 */
490size_t ScmStreamTellLine(PSCMSTREAM pStream)
491{
492 return pStream->iLine;
493}
494
495
496/**
497 * Gets the stream offset of a given line.
498 *
499 * @returns The offset of the line, or the stream size if the line number is too
500 * high.
501 * @param pStream The stream. Must be in read mode.
502 * @param iLine The line we're asking about.
503 */
504size_t ScmStreamTellOffsetOfLine(PSCMSTREAM pStream, size_t iLine)
505{
506 AssertReturn(!pStream->fWriteOrRead, pStream->cb);
507 if (!pStream->fFullyLineated)
508 {
509 int rc = scmStreamLineate(pStream);
510 AssertRCReturn(rc, pStream->cb);
511 }
512 if (iLine >= pStream->cLines)
513 return pStream->cb;
514 return pStream->paLines[iLine].off;
515}
516
517
518/**
519 * Get the current stream size in bytes.
520 *
521 * @returns Count of bytes.
522 * @param pStream The stream.
523 */
524size_t ScmStreamSize(PSCMSTREAM pStream)
525{
526 return pStream->cb;
527}
528
529/**
530 * Gets the number of lines in the stream.
531 *
532 * @returns The number of lines.
533 * @param pStream The stream.
534 */
535size_t ScmStreamCountLines(PSCMSTREAM pStream)
536{
537 if (!pStream->fFullyLineated)
538 scmStreamLineate(pStream);
539 return pStream->cLines;
540}
541
542/**
543 * Seeks to a given byte offset in the stream.
544 *
545 * @returns IPRT status code.
546 * @retval VERR_SEEK if the new stream position is the middle of an EOL marker.
547 * This is a temporary restriction.
548 *
549 * @param pStream The stream. Must be in read mode.
550 * @param offAbsolute The offset to seek to. If this is beyond the
551 * end of the stream, the position is set to the
552 * end.
553 */
554int ScmStreamSeekAbsolute(PSCMSTREAM pStream, size_t offAbsolute)
555{
556 AssertReturn(!pStream->fWriteOrRead, VERR_ACCESS_DENIED);
557 if (RT_FAILURE(pStream->rc))
558 return pStream->rc;
559
560 /* Must be fully delineated. (lazy bird) */
561 if (RT_UNLIKELY(!pStream->fFullyLineated))
562 {
563 int rc = scmStreamLineate(pStream);
564 if (RT_FAILURE(rc))
565 return rc;
566 }
567
568 /* Ok, do the job. */
569 if (offAbsolute < pStream->cb)
570 {
571 /** @todo Should do a binary search here, but I'm too darn lazy tonight. */
572 pStream->off = ~(size_t)0;
573 for (size_t i = 0; i < pStream->cLines; i++)
574 {
575 if (offAbsolute < pStream->paLines[i].off + pStream->paLines[i].cch + pStream->paLines[i].enmEol)
576 {
577 pStream->off = offAbsolute;
578 pStream->iLine = i;
579 if (offAbsolute > pStream->paLines[i].off + pStream->paLines[i].cch)
580 return pStream->rc = VERR_SEEK;
581 break;
582 }
583 }
584 AssertReturn(pStream->off != ~(size_t)0, pStream->rc = VERR_INTERNAL_ERROR_3);
585 }
586 else
587 {
588 pStream->off = pStream->cb;
589 pStream->iLine = pStream->cLines;
590 }
591 return VINF_SUCCESS;
592}
593
594
595/**
596 * Seeks a number of bytes relative to the current stream position.
597 *
598 * @returns IPRT status code.
599 * @retval VERR_SEEK if the new stream position is the middle of an EOL marker.
600 * This is a temporary restriction.
601 *
602 * @param pStream The stream. Must be in read mode.
603 * @param offRelative The offset to seek to. A negative offset
604 * rewinds and positive one fast forwards the
605 * stream. Will quietly stop at the beginning and
606 * end of the stream.
607 */
608int ScmStreamSeekRelative(PSCMSTREAM pStream, ssize_t offRelative)
609{
610 size_t offAbsolute;
611 if (offRelative >= 0)
612 offAbsolute = pStream->off + offRelative;
613 else if ((size_t)-offRelative <= pStream->off)
614 offAbsolute = pStream->off + offRelative;
615 else
616 offAbsolute = 0;
617 return ScmStreamSeekAbsolute(pStream, offAbsolute);
618}
619
620/**
621 * Seeks to a given line in the stream.
622 *
623 * @returns IPRT status code.
624 *
625 * @param pStream The stream. Must be in read mode.
626 * @param iLine The line to seek to. If this is beyond the end
627 * of the stream, the position is set to the end.
628 */
629int ScmStreamSeekByLine(PSCMSTREAM pStream, size_t iLine)
630{
631 if (RT_FAILURE(pStream->rc))
632 return pStream->rc;
633
634 /* Must be fully delineated. (lazy bird) */
635 if (RT_UNLIKELY(!pStream->fFullyLineated))
636 {
637 AssertReturn(!pStream->fWriteOrRead, VERR_ACCESS_DENIED);
638 int rc = scmStreamLineate(pStream);
639 if (RT_FAILURE(rc))
640 return rc;
641 }
642
643 /* Ok, do the job. */
644 if (iLine < pStream->cLines)
645 {
646 pStream->iLine = iLine;
647 pStream->off = pStream->paLines[iLine].off;
648 if (pStream->fWriteOrRead)
649 {
650 pStream->cb = pStream->paLines[iLine].off;
651 pStream->cLines = iLine;
652 pStream->paLines[iLine].cch = 0;
653 pStream->paLines[iLine].enmEol = SCMEOL_NONE;
654 }
655 }
656 else
657 {
658 AssertReturn(!pStream->fWriteOrRead, VERR_ACCESS_DENIED);
659 pStream->off = pStream->cb;
660 pStream->iLine = pStream->cLines;
661 }
662 return VINF_SUCCESS;
663}
664
665/**
666 * Checks if the stream position is at the start of a line.
667 *
668 * @returns @c true if at the start, @c false if not.
669 * @param pStream The stream.
670 */
671bool ScmStreamIsAtStartOfLine(PSCMSTREAM pStream)
672{
673 if ( !pStream->fFullyLineated
674 && !pStream->fWriteOrRead)
675 {
676 int rc = scmStreamLineate(pStream);
677 if (RT_FAILURE(rc))
678 return false;
679 }
680 return pStream->off == pStream->paLines[pStream->iLine].off;
681}
682
683/**
684 * Worker for ScmStreamGetLineByNo and ScmStreamGetLine.
685 *
686 * Works on a fully lineated stream.
687 *
688 * @returns Pointer to the first character in the line, not NULL terminated.
689 * NULL if the end of the stream has been reached or some problem
690 * occurred.
691 *
692 * @param pStream The stream. Must be in read mode.
693 * @param iLine The line to get (0-based).
694 * @param pcchLine The length.
695 * @param penmEol Where to return the end of line type indicator.
696 */
697DECLINLINE(const char *) scmStreamGetLineByNoCommon(PSCMSTREAM pStream, size_t iLine, size_t *pcchLine, PSCMEOL penmEol)
698{
699 Assert(!pStream->fWriteOrRead);
700 Assert(pStream->fFullyLineated);
701
702 /* Check stream status. */
703 if (RT_SUCCESS(pStream->rc))
704 {
705 /* Not at the end of the stream yet? */
706 if (RT_LIKELY(iLine < pStream->cLines))
707 {
708 /* Get the data. */
709 const char *pchRet = &pStream->pch[pStream->paLines[iLine].off];
710 *pcchLine = pStream->paLines[iLine].cch;
711 *penmEol = pStream->paLines[iLine].enmEol;
712
713 /* update the stream position. */
714 pStream->off = pStream->paLines[iLine].off + pStream->paLines[iLine].cch + pStream->paLines[iLine].enmEol;
715 pStream->iLine = iLine + 1;
716 return pchRet;
717 }
718 pStream->off = pStream->cb;
719 pStream->iLine = pStream->cLines;
720 }
721 *pcchLine = 0;
722 *penmEol = SCMEOL_NONE;
723 return NULL;
724}
725
726
727/**
728 * Get a numbered line from the stream (changes the position).
729 *
730 * A line is always delimited by a LF character or the end of the stream. The
731 * delimiter is not included in returned line length, but instead returned via
732 * the @a penmEol indicator.
733 *
734 * @returns Pointer to the first character in the line, not NULL terminated.
735 * NULL if the end of the stream has been reached or some problem
736 * occurred (*pcchLine set to zero and *penmEol to SCMEOL_NONE).
737 *
738 * @param pStream The stream. Must be in read mode.
739 * @param iLine The line to get (0-based).
740 * @param pcchLine The length.
741 * @param penmEol Where to return the end of line type indicator.
742 */
743const char *ScmStreamGetLineByNo(PSCMSTREAM pStream, size_t iLine, size_t *pcchLine, PSCMEOL penmEol)
744{
745 AssertReturn(!pStream->fWriteOrRead, NULL);
746
747 /* Make sure it's fully delineated so we can use the index. */
748 if (RT_LIKELY(pStream->fFullyLineated))
749 return scmStreamGetLineByNoCommon(pStream, iLine, pcchLine, penmEol);
750
751 int rc = pStream->rc;
752 if (RT_SUCCESS(rc))
753 {
754 rc = scmStreamLineate(pStream);
755 if (RT_SUCCESS(rc))
756 return scmStreamGetLineByNoCommon(pStream, iLine, pcchLine, penmEol);
757 }
758
759 *pcchLine = 0;
760 *penmEol = SCMEOL_NONE;
761 return NULL;
762}
763
764/**
765 * Get a line from the stream.
766 *
767 * A line is always delimited by a LF character or the end of the stream. The
768 * delimiter is not included in returned line length, but instead returned via
769 * the @a penmEol indicator.
770 *
771 * @returns Pointer to the first character in the line, not NULL terminated.
772 * NULL if the end of the stream has been reached or some problem
773 * occurred (*pcchLine set to zero and *penmEol to SCMEOL_NONE).
774 *
775 * @param pStream The stream. Must be in read mode.
776 * @param pcchLine The length.
777 * @param penmEol Where to return the end of line type indicator.
778 */
779const char *ScmStreamGetLine(PSCMSTREAM pStream, size_t *pcchLine, PSCMEOL penmEol)
780{
781 if (RT_LIKELY(pStream->fFullyLineated))
782 {
783 size_t offCur = pStream->off;
784 size_t iCurLine = pStream->iLine;
785 const char *pszLine = scmStreamGetLineByNoCommon(pStream, iCurLine, pcchLine, penmEol);
786 if ( pszLine
787 && offCur > pStream->paLines[iCurLine].off)
788 {
789 offCur -= pStream->paLines[iCurLine].off;
790 Assert(offCur <= pStream->paLines[iCurLine].cch + pStream->paLines[iCurLine].enmEol);
791 if (offCur < pStream->paLines[iCurLine].cch)
792 *pcchLine -= offCur;
793 else
794 *pcchLine = 0;
795 pszLine += offCur;
796 }
797 return pszLine;
798 }
799 return scmStreamGetLineInternal(pStream, pcchLine, penmEol);
800}
801
802/**
803 * Get the current buffer pointer.
804 *
805 * @returns Buffer pointer on success, NULL on failure (asserted).
806 * @param pStream The stream. Must be in read mode.
807 */
808const char *ScmStreamGetCur(PSCMSTREAM pStream)
809{
810 AssertReturn(!pStream->fWriteOrRead, NULL);
811 return pStream->pch + pStream->off;
812}
813
814/**
815 * Gets a character from the stream.
816 *
817 * @returns The next unsigned character in the stream.
818 * ~(unsigned)0 on failure.
819 * @param pStream The stream. Must be in read mode.
820 */
821unsigned ScmStreamGetCh(PSCMSTREAM pStream)
822{
823 /* Check stream state. */
824 AssertReturn(!pStream->fWriteOrRead, ~(unsigned)0);
825 if (RT_FAILURE(pStream->rc))
826 return ~(unsigned)0;
827 if (RT_UNLIKELY(!pStream->fFullyLineated))
828 {
829 int rc = scmStreamLineate(pStream);
830 if (RT_FAILURE(rc))
831 return ~(unsigned)0;
832 }
833
834 /* If there isn't enough stream left, fail already. */
835 if (RT_UNLIKELY(pStream->off >= pStream->cb))
836 return ~(unsigned)0;
837
838 /* Read a character. */
839 char ch = pStream->pch[pStream->off++];
840
841 /* Advance the line indicator. */
842 size_t iLine = pStream->iLine;
843 if (pStream->off >= pStream->paLines[iLine].off + pStream->paLines[iLine].cch + pStream->paLines[iLine].enmEol)
844 pStream->iLine++;
845
846 return (unsigned)ch;
847}
848
849
850/**
851 * Peeks at the next character from the stream.
852 *
853 * @returns The next unsigned character in the stream.
854 * ~(unsigned)0 on failure.
855 * @param pStream The stream. Must be in read mode.
856 */
857unsigned ScmStreamPeekCh(PSCMSTREAM pStream)
858{
859 /* Check stream state. */
860 AssertReturn(!pStream->fWriteOrRead, ~(unsigned)0);
861 if (RT_FAILURE(pStream->rc))
862 return ~(unsigned)0;
863 if (RT_UNLIKELY(!pStream->fFullyLineated))
864 {
865 int rc = scmStreamLineate(pStream);
866 if (RT_FAILURE(rc))
867 return ~(unsigned)0;
868 }
869
870 /* If there isn't enough stream left, fail already. */
871 if (RT_UNLIKELY(pStream->off >= pStream->cb))
872 return ~(unsigned)0;
873
874 /* Peek at the next character. */
875 char ch = pStream->pch[pStream->off];
876 return (unsigned)ch;
877}
878
879
880/**
881 * Reads @a cbToRead bytes into @a pvBuf.
882 *
883 * Will fail if end of stream is encountered before the entire read has been
884 * completed.
885 *
886 * @returns IPRT status code.
887 * @retval VERR_EOF if there isn't @a cbToRead bytes left to read. Stream
888 * position will be unchanged.
889 *
890 * @param pStream The stream. Must be in read mode.
891 * @param pvBuf The buffer to read into.
892 * @param cbToRead The number of bytes to read.
893 */
894int ScmStreamRead(PSCMSTREAM pStream, void *pvBuf, size_t cbToRead)
895{
896 AssertReturn(!pStream->fWriteOrRead, VERR_PERMISSION_DENIED);
897 if (RT_FAILURE(pStream->rc))
898 return pStream->rc;
899
900 /* If there isn't enough stream left, fail already. */
901 if (RT_UNLIKELY(pStream->cb - pStream->off < cbToRead))
902 return VERR_EOF;
903
904 /* Copy the data and simply seek to the new stream position. */
905 memcpy(pvBuf, &pStream->pch[pStream->off], cbToRead);
906 return ScmStreamSeekAbsolute(pStream, pStream->off + cbToRead);
907}
908
909
910/**
911 * Checks if the given line is empty or full of white space.
912 *
913 * @returns true if white space only, false if not (or if non-existant).
914 * @param pStream The stream. Must be in read mode.
915 * @param iLine The line in question.
916 */
917bool ScmStreamIsWhiteLine(PSCMSTREAM pStream, size_t iLine)
918{
919 SCMEOL enmEol;
920 size_t cchLine;
921 const char *pchLine = ScmStreamGetLineByNo(pStream, iLine, &cchLine, &enmEol);
922 if (!pchLine)
923 return false;
924 while (cchLine && RT_C_IS_SPACE(*pchLine))
925 pchLine++, cchLine--;
926 return cchLine == 0;
927}
928
929/**
930 * Try figure out the end of line style of the give stream.
931 *
932 * @returns Most likely end of line style.
933 * @param pStream The stream.
934 */
935SCMEOL ScmStreamGetEol(PSCMSTREAM pStream)
936{
937 SCMEOL enmEol;
938 if (pStream->cLines > 0)
939 enmEol = pStream->paLines[0].enmEol;
940 else if (pStream->cb == 0)
941 enmEol = SCMEOL_NONE;
942 else
943 {
944 const char *pchLF = (const char *)memchr(pStream->pch, '\n', pStream->cb);
945 if (pchLF && pchLF != pStream->pch && pchLF[-1] == '\r')
946 enmEol = SCMEOL_CRLF;
947 else
948 enmEol = SCMEOL_LF;
949 }
950
951 if (enmEol == SCMEOL_NONE)
952#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
953 enmEol = SCMEOL_CRLF;
954#else
955 enmEol = SCMEOL_LF;
956#endif
957 return enmEol;
958}
959
960/**
961 * Get the end of line indicator type for a line.
962 *
963 * @returns The EOL indicator. If the line isn't found, the default EOL
964 * indicator is return.
965 * @param pStream The stream.
966 * @param iLine The line (0-base).
967 */
968SCMEOL ScmStreamGetEolByLine(PSCMSTREAM pStream, size_t iLine)
969{
970 SCMEOL enmEol;
971 if (iLine < pStream->cLines)
972 enmEol = pStream->paLines[iLine].enmEol;
973 else
974#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
975 enmEol = SCMEOL_CRLF;
976#else
977 enmEol = SCMEOL_LF;
978#endif
979 return enmEol;
980}
981
982/**
983 * Appends a line to the stream.
984 *
985 * @returns IPRT status code.
986 * @param pStream The stream. Must be in write mode.
987 * @param pchLine Pointer to the line.
988 * @param cchLine Line length.
989 * @param enmEol Which end of line indicator to use.
990 */
991int ScmStreamPutLine(PSCMSTREAM pStream, const char *pchLine, size_t cchLine, SCMEOL enmEol)
992{
993 AssertReturn(pStream->fWriteOrRead, VERR_ACCESS_DENIED);
994 if (RT_FAILURE(pStream->rc))
995 return pStream->rc;
996
997 /*
998 * Make sure the previous line has a new-line indicator.
999 */
1000 size_t off = pStream->off;
1001 size_t iLine = pStream->iLine;
1002 if (RT_UNLIKELY( iLine != 0
1003 && pStream->paLines[iLine - 1].enmEol == SCMEOL_NONE))
1004 {
1005 AssertReturn(pStream->paLines[iLine].cch == 0, VERR_INTERNAL_ERROR_3);
1006 SCMEOL enmEol2 = enmEol != SCMEOL_NONE ? enmEol : ScmStreamGetEol(pStream);
1007 if (RT_UNLIKELY(off + cchLine + enmEol + enmEol2 > pStream->cbAllocated))
1008 {
1009 int rc = scmStreamGrowBuffer(pStream, cchLine + enmEol + enmEol2);
1010 if (RT_FAILURE(rc))
1011 return rc;
1012 }
1013 if (enmEol2 == SCMEOL_LF)
1014 pStream->pch[off++] = '\n';
1015 else
1016 {
1017 pStream->pch[off++] = '\r';
1018 pStream->pch[off++] = '\n';
1019 }
1020 pStream->paLines[iLine - 1].enmEol = enmEol2;
1021 pStream->paLines[iLine].off = off;
1022 pStream->off = off;
1023 pStream->cb = off;
1024 }
1025
1026 /*
1027 * Ensure we've got sufficient buffer space.
1028 */
1029 if (RT_UNLIKELY(off + cchLine + enmEol > pStream->cbAllocated))
1030 {
1031 int rc = scmStreamGrowBuffer(pStream, cchLine + enmEol);
1032 if (RT_FAILURE(rc))
1033 return rc;
1034 }
1035
1036 /*
1037 * Add a line record.
1038 */
1039 if (RT_UNLIKELY(iLine + 1 >= pStream->cLinesAllocated))
1040 {
1041 int rc = scmStreamGrowLines(pStream, iLine);
1042 if (RT_FAILURE(rc))
1043 return rc;
1044 }
1045
1046 pStream->paLines[iLine].cch = off - pStream->paLines[iLine].off + cchLine;
1047 pStream->paLines[iLine].enmEol = enmEol;
1048
1049 iLine++;
1050 pStream->cLines = iLine;
1051 pStream->iLine = iLine;
1052
1053 /*
1054 * Copy the line
1055 */
1056 memcpy(&pStream->pch[off], pchLine, cchLine);
1057 off += cchLine;
1058 if (enmEol == SCMEOL_LF)
1059 pStream->pch[off++] = '\n';
1060 else if (enmEol == SCMEOL_CRLF)
1061 {
1062 pStream->pch[off++] = '\r';
1063 pStream->pch[off++] = '\n';
1064 }
1065 pStream->off = off;
1066 pStream->cb = off;
1067
1068 /*
1069 * Start a new line.
1070 */
1071 pStream->paLines[iLine].off = off;
1072 pStream->paLines[iLine].cch = 0;
1073 pStream->paLines[iLine].enmEol = SCMEOL_NONE;
1074
1075 return VINF_SUCCESS;
1076}
1077
1078/**
1079 * Writes to the stream.
1080 *
1081 * @returns IPRT status code
1082 * @param pStream The stream. Must be in write mode.
1083 * @param pchBuf What to write.
1084 * @param cchBuf How much to write.
1085 */
1086int ScmStreamWrite(PSCMSTREAM pStream, const char *pchBuf, size_t cchBuf)
1087{
1088 AssertReturn(pStream->fWriteOrRead, VERR_ACCESS_DENIED);
1089 if (RT_FAILURE(pStream->rc))
1090 return pStream->rc;
1091
1092 /*
1093 * Ensure we've got sufficient buffer space.
1094 */
1095 size_t off = pStream->off;
1096 if (RT_UNLIKELY(off + cchBuf > pStream->cbAllocated))
1097 {
1098 int rc = scmStreamGrowBuffer(pStream, cchBuf);
1099 if (RT_FAILURE(rc))
1100 return rc;
1101 }
1102
1103 /*
1104 * Deal with the odd case where we've already pushed a line with SCMEOL_NONE.
1105 */
1106 size_t iLine = pStream->iLine;
1107 if (RT_UNLIKELY( iLine > 0
1108 && pStream->paLines[iLine - 1].enmEol == SCMEOL_NONE))
1109 {
1110 iLine--;
1111 pStream->cLines = iLine;
1112 pStream->iLine = iLine;
1113 }
1114
1115 /*
1116 * Deal with lines.
1117 */
1118 const char *pchLF = (const char *)memchr(pchBuf, '\n', cchBuf);
1119 if (!pchLF)
1120 pStream->paLines[iLine].cch += cchBuf;
1121 else
1122 {
1123 const char *pchLine = pchBuf;
1124 for (;;)
1125 {
1126 if (RT_UNLIKELY(iLine + 1 >= pStream->cLinesAllocated))
1127 {
1128 int rc = scmStreamGrowLines(pStream, iLine);
1129 if (RT_FAILURE(rc))
1130 {
1131 iLine = pStream->iLine;
1132 pStream->paLines[iLine].cch = off - pStream->paLines[iLine].off;
1133 pStream->paLines[iLine].enmEol = SCMEOL_NONE;
1134 return rc;
1135 }
1136 }
1137
1138 size_t cchLine = pchLF - pchLine;
1139 if ( cchLine
1140 ? pchLF[-1] != '\r'
1141 : !pStream->paLines[iLine].cch
1142 || pStream->pch[pStream->paLines[iLine].off + pStream->paLines[iLine].cch - 1] != '\r')
1143 pStream->paLines[iLine].enmEol = SCMEOL_LF;
1144 else
1145 {
1146 pStream->paLines[iLine].enmEol = SCMEOL_CRLF;
1147 cchLine--;
1148 }
1149 pStream->paLines[iLine].cch += cchLine;
1150
1151 iLine++;
1152 size_t offBuf = pchLF + 1 - pchBuf;
1153 pStream->paLines[iLine].off = off + offBuf;
1154 pStream->paLines[iLine].cch = 0;
1155 pStream->paLines[iLine].enmEol = SCMEOL_NONE;
1156
1157 size_t cchLeft = cchBuf - offBuf;
1158 pchLine = pchLF + 1;
1159 pchLF = (const char *)memchr(pchLine, '\n', cchLeft);
1160 if (!pchLF)
1161 {
1162 pStream->paLines[iLine].cch = cchLeft;
1163 break;
1164 }
1165 }
1166
1167 pStream->iLine = iLine;
1168 pStream->cLines = iLine;
1169 }
1170
1171 /*
1172 * Copy the data and update position and size.
1173 */
1174 memcpy(&pStream->pch[off], pchBuf, cchBuf);
1175 off += cchBuf;
1176 pStream->off = off;
1177 pStream->cb = off;
1178
1179 return VINF_SUCCESS;
1180}
1181
1182/**
1183 * Write a character to the stream.
1184 *
1185 * @returns IPRT status code
1186 * @param pStream The stream. Must be in write mode.
1187 * @param pchBuf What to write.
1188 * @param cchBuf How much to write.
1189 */
1190int ScmStreamPutCh(PSCMSTREAM pStream, char ch)
1191{
1192 AssertReturn(pStream->fWriteOrRead, VERR_ACCESS_DENIED);
1193 if (RT_FAILURE(pStream->rc))
1194 return pStream->rc;
1195
1196 /*
1197 * Only deal with the simple cases here, use ScmStreamWrite for the
1198 * annoying stuff.
1199 */
1200 size_t off = pStream->off;
1201 if ( ch == '\n'
1202 || RT_UNLIKELY(off + 1 > pStream->cbAllocated))
1203 return ScmStreamWrite(pStream, &ch, 1);
1204
1205 /*
1206 * Just append it.
1207 */
1208 pStream->pch[off] = ch;
1209 pStream->off = off + 1;
1210 pStream->paLines[pStream->iLine].cch++;
1211
1212 return VINF_SUCCESS;
1213}
1214
1215/**
1216 * Puts an EOL marker to the stream.
1217 *
1218 * @returns IPRt status code.
1219 * @param pStream The stream. Must be in write mode.
1220 * @param enmEol The end-of-line marker to write.
1221 */
1222int ScmStreamPutEol(PSCMSTREAM pStream, SCMEOL enmEol)
1223{
1224 if (enmEol == SCMEOL_LF)
1225 return ScmStreamWrite(pStream, "\n", 1);
1226 if (enmEol == SCMEOL_CRLF)
1227 return ScmStreamWrite(pStream, "\r\n", 2);
1228 if (enmEol == SCMEOL_NONE)
1229 return VINF_SUCCESS;
1230 AssertFailedReturn(VERR_INVALID_PARAMETER);
1231}
1232
1233/**
1234 * Formats a string and writes it to the SCM stream.
1235 *
1236 * @returns The number of bytes written (>= 0). Negative value are IPRT error
1237 * status codes.
1238 * @param pStream The stream to write to.
1239 * @param pszFormat The format string.
1240 * @param va The arguments to format.
1241 */
1242ssize_t ScmStreamPrintfV(PSCMSTREAM pStream, const char *pszFormat, va_list va)
1243{
1244 char *psz;
1245 ssize_t cch = RTStrAPrintfV(&psz, pszFormat, va);
1246 if (cch)
1247 {
1248 int rc = ScmStreamWrite(pStream, psz, cch);
1249 RTStrFree(psz);
1250 if (RT_FAILURE(rc))
1251 cch = rc;
1252 }
1253 return cch;
1254}
1255
1256/**
1257 * Formats a string and writes it to the SCM stream.
1258 *
1259 * @returns The number of bytes written (>= 0). Negative value are IPRT error
1260 * status codes.
1261 * @param pStream The stream to write to.
1262 * @param pszFormat The format string.
1263 * @param ... The arguments to format.
1264 */
1265ssize_t ScmStreamPrintf(PSCMSTREAM pStream, const char *pszFormat, ...)
1266{
1267 va_list va;
1268 va_start(va, pszFormat);
1269 ssize_t cch = ScmStreamPrintfV(pStream, pszFormat, va);
1270 va_end(va);
1271 return cch;
1272}
1273
1274/**
1275 * Copies @a cLines from the @a pSrc stream onto the @a pDst stream.
1276 *
1277 * The stream positions will be used and changed in both streams.
1278 *
1279 * @returns IPRT status code.
1280 * @param pDst The destination stream. Must be in write mode.
1281 * @param cLines The number of lines. (0 is accepted.)
1282 * @param pSrc The source stream. Must be in read mode.
1283 */
1284int ScmStreamCopyLines(PSCMSTREAM pDst, PSCMSTREAM pSrc, size_t cLines)
1285{
1286 AssertReturn(pDst->fWriteOrRead, VERR_ACCESS_DENIED);
1287 if (RT_FAILURE(pDst->rc))
1288 return pDst->rc;
1289
1290 AssertReturn(!pSrc->fWriteOrRead, VERR_ACCESS_DENIED);
1291 if (RT_FAILURE(pSrc->rc))
1292 return pSrc->rc;
1293
1294 while (cLines-- > 0)
1295 {
1296 SCMEOL enmEol;
1297 size_t cchLine;
1298 const char *pchLine = ScmStreamGetLine(pSrc, &cchLine, &enmEol);
1299 if (!pchLine)
1300 return pDst->rc = RT_FAILURE(pSrc->rc) ? pSrc->rc : VERR_EOF;
1301
1302 int rc = ScmStreamPutLine(pDst, pchLine, cchLine, enmEol);
1303 if (RT_FAILURE(rc))
1304 return rc;
1305 }
1306
1307 return VINF_SUCCESS;
1308}
1309
1310
1311/**
1312 * If the given C word is at off - 1, return @c true and skip beyond it,
1313 * otherwise return @c false.
1314 *
1315 * @retval true if the given C-word is at the current position minus one char.
1316 * The stream position changes.
1317 * @retval false if not. The stream position is unchanged.
1318 *
1319 * @param pStream The stream.
1320 * @param cchWord The length of the word.
1321 * @param pszWord The word.
1322 */
1323bool ScmStreamCMatchingWordM1(PSCMSTREAM pStream, const char *pszWord, size_t cchWord)
1324{
1325 /* Check stream state. */
1326 AssertReturn(!pStream->fWriteOrRead, false);
1327 AssertReturn(RT_SUCCESS(pStream->rc), false);
1328 AssertReturn(pStream->fFullyLineated, false);
1329
1330 /* Sufficient chars left on the line? */
1331 size_t const iLine = pStream->iLine;
1332 AssertReturn(pStream->off > pStream->paLines[iLine].off, false);
1333 size_t const cchLeft = pStream->paLines[iLine].cch + pStream->paLines[iLine].off - (pStream->off - 1);
1334 if (cchWord > cchLeft)
1335 return false;
1336
1337 /* Do they match? */
1338 const char *psz = &pStream->pch[pStream->off - 1];
1339 if (memcmp(psz, pszWord, cchWord))
1340 return false;
1341
1342 /* Is it the end of a C word? */
1343 if (cchWord < cchLeft)
1344 {
1345 psz += cchWord;
1346 if (RT_C_IS_ALNUM(*psz) || *psz == '_')
1347 return false;
1348 }
1349
1350 /* Skip ahead. */
1351 pStream->off += cchWord - 1;
1352 return true;
1353}
1354
1355/**
1356 * Get's the C word starting at the current position.
1357 *
1358 * @returns Pointer to the word on success and the stream position advanced to
1359 * the end of it.
1360 * NULL on failure, stream position normally unchanged.
1361 * @param pStream The stream to get the C word from.
1362 * @param pcchWord Where to return the word length.
1363 */
1364const char *ScmStreamCGetWord(PSCMSTREAM pStream, size_t *pcchWord)
1365{
1366 /* Check stream state. */
1367 AssertReturn(!pStream->fWriteOrRead, NULL);
1368 AssertReturn(RT_SUCCESS(pStream->rc), NULL);
1369 AssertReturn(pStream->fFullyLineated, NULL);
1370
1371 /* Get the number of chars left on the line and locate the current char. */
1372 size_t const iLine = pStream->iLine;
1373 size_t const cchLeft = pStream->paLines[iLine].cch + pStream->paLines[iLine].off - pStream->off;
1374 const char *psz = &pStream->pch[pStream->off];
1375
1376 /* Is it a leading C character. */
1377 if (!RT_C_IS_ALPHA(*psz) && *psz != '_')
1378 return NULL;
1379
1380 /* Find the end of the word. */
1381 char ch;
1382 size_t off = 1;
1383 while ( off < cchLeft
1384 && ( (ch = psz[off]) == '_'
1385 || RT_C_IS_ALNUM(ch)))
1386 off++;
1387
1388 pStream->off += off;
1389 *pcchWord = off;
1390 return psz;
1391}
1392
1393
1394/**
1395 * Get's the C word starting at the current position minus one.
1396 *
1397 * @returns Pointer to the word on success and the stream position advanced to
1398 * the end of it.
1399 * NULL on failure, stream position normally unchanged.
1400 * @param pStream The stream to get the C word from.
1401 * @param pcchWord Where to return the word length.
1402 */
1403const char *ScmStreamCGetWordM1(PSCMSTREAM pStream, size_t *pcchWord)
1404{
1405 /* Check stream state. */
1406 AssertReturn(!pStream->fWriteOrRead, NULL);
1407 AssertReturn(RT_SUCCESS(pStream->rc), NULL);
1408 AssertReturn(pStream->fFullyLineated, NULL);
1409
1410 /* Get the number of chars left on the line and locate the current char. */
1411 size_t const iLine = pStream->iLine;
1412 size_t const cchLeft = pStream->paLines[iLine].cch + pStream->paLines[iLine].off - (pStream->off - 1);
1413 const char *psz = &pStream->pch[pStream->off - 1];
1414
1415 /* Is it a leading C character. */
1416 if (!RT_C_IS_ALPHA(*psz) && *psz != '_')
1417 return NULL;
1418
1419 /* Find the end of the word. */
1420 char ch;
1421 size_t off = 1;
1422 while ( off < cchLeft
1423 && ( (ch = psz[off]) == '_'
1424 || RT_C_IS_ALNUM(ch)))
1425 off++;
1426
1427 pStream->off += off - 1;
1428 *pcchWord = off;
1429 return psz;
1430}
1431
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