VirtualBox

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

Last change on this file since 41186 was 41186, checked in by vboxsync, 13 years ago

More preprocessing (disabled).

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette