VirtualBox

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

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

fix

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 32.6 KB
Line 
1/* $Id: scmstream.cpp 41180 2012-05-06 23:51:14Z 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.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* 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/**
710 * Gets a character from the stream.
711 *
712 * @returns The next unsigned character in the stream.
713 * ~(unsigned)0 on failure.
714 * @param pStream The stream. Must be in read mode.
715 */
716unsigned ScmStreamGetCh(PSCMSTREAM pStream)
717{
718 /* Check stream state. */
719 AssertReturn(!pStream->fWriteOrRead, ~(unsigned)0);
720 if (RT_FAILURE(pStream->rc))
721 return ~(unsigned)0;
722 if (RT_UNLIKELY(!pStream->fFullyLineated))
723 {
724 int rc = scmStreamLineate(pStream);
725 if (RT_FAILURE(rc))
726 return ~(unsigned)0;
727 }
728
729 /* If there isn't enough stream left, fail already. */
730 if (RT_UNLIKELY(pStream->off >= pStream->cb))
731 return ~(unsigned)0;
732
733 /* Read a character. */
734 char ch = pStream->pch[pStream->off++];
735
736 /* Advance the line indicator. */
737 size_t iLine = pStream->iLine;
738 if (pStream->off >= pStream->paLines[iLine].off + pStream->paLines[iLine].cch + pStream->paLines[iLine].enmEol)
739 pStream->iLine++;
740
741 return (unsigned)ch;
742}
743
744
745/**
746 * Peeks at the next character from the stream.
747 *
748 * @returns The next unsigned character in the stream.
749 * ~(unsigned)0 on failure.
750 * @param pStream The stream. Must be in read mode.
751 */
752unsigned ScmStreamPeekCh(PSCMSTREAM pStream)
753{
754 /* Check stream state. */
755 AssertReturn(!pStream->fWriteOrRead, ~(unsigned)0);
756 if (RT_FAILURE(pStream->rc))
757 return ~(unsigned)0;
758 if (RT_UNLIKELY(!pStream->fFullyLineated))
759 {
760 int rc = scmStreamLineate(pStream);
761 if (RT_FAILURE(rc))
762 return ~(unsigned)0;
763 }
764
765 /* If there isn't enough stream left, fail already. */
766 if (RT_UNLIKELY(pStream->off >= pStream->cb))
767 return ~(unsigned)0;
768
769 /* Peek at the next character. */
770 char ch = pStream->pch[pStream->off];
771 return (unsigned)ch;
772}
773
774
775/**
776 * Reads @a cbToRead bytes into @a pvBuf.
777 *
778 * Will fail if end of stream is encountered before the entire read has been
779 * completed.
780 *
781 * @returns IPRT status code.
782 * @retval VERR_EOF if there isn't @a cbToRead bytes left to read. Stream
783 * position will be unchanged.
784 *
785 * @param pStream The stream. Must be in read mode.
786 * @param pvBuf The buffer to read into.
787 * @param cbToRead The number of bytes to read.
788 */
789int ScmStreamRead(PSCMSTREAM pStream, void *pvBuf, size_t cbToRead)
790{
791 AssertReturn(!pStream->fWriteOrRead, VERR_PERMISSION_DENIED);
792 if (RT_FAILURE(pStream->rc))
793 return pStream->rc;
794
795 /* If there isn't enough stream left, fail already. */
796 if (RT_UNLIKELY(pStream->cb - pStream->off < cbToRead))
797 return VERR_EOF;
798
799 /* Copy the data and simply seek to the new stream position. */
800 memcpy(pvBuf, &pStream->pch[pStream->off], cbToRead);
801 return ScmStreamSeekAbsolute(pStream, pStream->off + cbToRead);
802}
803
804
805/**
806 * Checks if the given line is empty or full of white space.
807 *
808 * @returns true if white space only, false if not (or if non-existant).
809 * @param pStream The stream. Must be in read mode.
810 * @param iLine The line in question.
811 */
812bool ScmStreamIsWhiteLine(PSCMSTREAM pStream, size_t iLine)
813{
814 SCMEOL enmEol;
815 size_t cchLine;
816 const char *pchLine = ScmStreamGetLineByNo(pStream, iLine, &cchLine, &enmEol);
817 if (!pchLine)
818 return false;
819 while (cchLine && RT_C_IS_SPACE(*pchLine))
820 pchLine++, cchLine--;
821 return cchLine == 0;
822}
823
824/**
825 * Try figure out the end of line style of the give stream.
826 *
827 * @returns Most likely end of line style.
828 * @param pStream The stream.
829 */
830SCMEOL ScmStreamGetEol(PSCMSTREAM pStream)
831{
832 SCMEOL enmEol;
833 if (pStream->cLines > 0)
834 enmEol = pStream->paLines[0].enmEol;
835 else if (pStream->cb == 0)
836 enmEol = SCMEOL_NONE;
837 else
838 {
839 const char *pchLF = (const char *)memchr(pStream->pch, '\n', pStream->cb);
840 if (pchLF && pchLF != pStream->pch && pchLF[-1] == '\r')
841 enmEol = SCMEOL_CRLF;
842 else
843 enmEol = SCMEOL_LF;
844 }
845
846 if (enmEol == SCMEOL_NONE)
847#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
848 enmEol = SCMEOL_CRLF;
849#else
850 enmEol = SCMEOL_LF;
851#endif
852 return enmEol;
853}
854
855/**
856 * Get the end of line indicator type for a line.
857 *
858 * @returns The EOL indicator. If the line isn't found, the default EOL
859 * indicator is return.
860 * @param pStream The stream.
861 * @param iLine The line (0-base).
862 */
863SCMEOL ScmStreamGetEolByLine(PSCMSTREAM pStream, size_t iLine)
864{
865 SCMEOL enmEol;
866 if (iLine < pStream->cLines)
867 enmEol = pStream->paLines[iLine].enmEol;
868 else
869#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
870 enmEol = SCMEOL_CRLF;
871#else
872 enmEol = SCMEOL_LF;
873#endif
874 return enmEol;
875}
876
877/**
878 * Appends a line to the stream.
879 *
880 * @returns IPRT status code.
881 * @param pStream The stream. Must be in write mode.
882 * @param pchLine Pointer to the line.
883 * @param cchLine Line length.
884 * @param enmEol Which end of line indicator to use.
885 */
886int ScmStreamPutLine(PSCMSTREAM pStream, const char *pchLine, size_t cchLine, SCMEOL enmEol)
887{
888 AssertReturn(pStream->fWriteOrRead, VERR_ACCESS_DENIED);
889 if (RT_FAILURE(pStream->rc))
890 return pStream->rc;
891
892 /*
893 * Make sure the previous line has a new-line indicator.
894 */
895 size_t off = pStream->off;
896 size_t iLine = pStream->iLine;
897 if (RT_UNLIKELY( iLine != 0
898 && pStream->paLines[iLine - 1].enmEol == SCMEOL_NONE))
899 {
900 AssertReturn(pStream->paLines[iLine].cch == 0, VERR_INTERNAL_ERROR_3);
901 SCMEOL enmEol2 = enmEol != SCMEOL_NONE ? enmEol : ScmStreamGetEol(pStream);
902 if (RT_UNLIKELY(off + cchLine + enmEol + enmEol2 > pStream->cbAllocated))
903 {
904 int rc = scmStreamGrowBuffer(pStream, cchLine + enmEol + enmEol2);
905 if (RT_FAILURE(rc))
906 return rc;
907 }
908 if (enmEol2 == SCMEOL_LF)
909 pStream->pch[off++] = '\n';
910 else
911 {
912 pStream->pch[off++] = '\r';
913 pStream->pch[off++] = '\n';
914 }
915 pStream->paLines[iLine - 1].enmEol = enmEol2;
916 pStream->paLines[iLine].off = off;
917 pStream->off = off;
918 pStream->cb = off;
919 }
920
921 /*
922 * Ensure we've got sufficient buffer space.
923 */
924 if (RT_UNLIKELY(off + cchLine + enmEol > pStream->cbAllocated))
925 {
926 int rc = scmStreamGrowBuffer(pStream, cchLine + enmEol);
927 if (RT_FAILURE(rc))
928 return rc;
929 }
930
931 /*
932 * Add a line record.
933 */
934 if (RT_UNLIKELY(iLine + 1 >= pStream->cLinesAllocated))
935 {
936 int rc = scmStreamGrowLines(pStream, iLine);
937 if (RT_FAILURE(rc))
938 return rc;
939 }
940
941 pStream->paLines[iLine].cch = off - pStream->paLines[iLine].off + cchLine;
942 pStream->paLines[iLine].enmEol = enmEol;
943
944 iLine++;
945 pStream->cLines = iLine;
946 pStream->iLine = iLine;
947
948 /*
949 * Copy the line
950 */
951 memcpy(&pStream->pch[off], pchLine, cchLine);
952 off += cchLine;
953 if (enmEol == SCMEOL_LF)
954 pStream->pch[off++] = '\n';
955 else if (enmEol == SCMEOL_CRLF)
956 {
957 pStream->pch[off++] = '\r';
958 pStream->pch[off++] = '\n';
959 }
960 pStream->off = off;
961 pStream->cb = off;
962
963 /*
964 * Start a new line.
965 */
966 pStream->paLines[iLine].off = off;
967 pStream->paLines[iLine].cch = 0;
968 pStream->paLines[iLine].enmEol = SCMEOL_NONE;
969
970 return VINF_SUCCESS;
971}
972
973/**
974 * Writes to the stream.
975 *
976 * @returns IPRT status code
977 * @param pStream The stream. Must be in write mode.
978 * @param pchBuf What to write.
979 * @param cchBuf How much to write.
980 */
981int ScmStreamWrite(PSCMSTREAM pStream, const char *pchBuf, size_t cchBuf)
982{
983 AssertReturn(pStream->fWriteOrRead, VERR_ACCESS_DENIED);
984 if (RT_FAILURE(pStream->rc))
985 return pStream->rc;
986
987 /*
988 * Ensure we've got sufficient buffer space.
989 */
990 size_t off = pStream->off;
991 if (RT_UNLIKELY(off + cchBuf > pStream->cbAllocated))
992 {
993 int rc = scmStreamGrowBuffer(pStream, cchBuf);
994 if (RT_FAILURE(rc))
995 return rc;
996 }
997
998 /*
999 * Deal with the odd case where we've already pushed a line with SCMEOL_NONE.
1000 */
1001 size_t iLine = pStream->iLine;
1002 if (RT_UNLIKELY( iLine > 0
1003 && pStream->paLines[iLine - 1].enmEol == SCMEOL_NONE))
1004 {
1005 iLine--;
1006 pStream->cLines = iLine;
1007 pStream->iLine = iLine;
1008 }
1009
1010 /*
1011 * Deal with lines.
1012 */
1013 const char *pchLF = (const char *)memchr(pchBuf, '\n', cchBuf);
1014 if (!pchLF)
1015 pStream->paLines[iLine].cch += cchBuf;
1016 else
1017 {
1018 const char *pchLine = pchBuf;
1019 for (;;)
1020 {
1021 if (RT_UNLIKELY(iLine + 1 >= pStream->cLinesAllocated))
1022 {
1023 int rc = scmStreamGrowLines(pStream, iLine);
1024 if (RT_FAILURE(rc))
1025 {
1026 iLine = pStream->iLine;
1027 pStream->paLines[iLine].cch = off - pStream->paLines[iLine].off;
1028 pStream->paLines[iLine].enmEol = SCMEOL_NONE;
1029 return rc;
1030 }
1031 }
1032
1033 size_t cchLine = pchLF - pchLine;
1034 if ( cchLine
1035 ? pchLF[-1] != '\r'
1036 : !pStream->paLines[iLine].cch
1037 || pStream->pch[pStream->paLines[iLine].off + pStream->paLines[iLine].cch - 1] != '\r')
1038 pStream->paLines[iLine].enmEol = SCMEOL_LF;
1039 else
1040 {
1041 pStream->paLines[iLine].enmEol = SCMEOL_CRLF;
1042 cchLine--;
1043 }
1044 pStream->paLines[iLine].cch += cchLine;
1045
1046 iLine++;
1047 size_t offBuf = pchLF + 1 - pchBuf;
1048 pStream->paLines[iLine].off = off + offBuf;
1049 pStream->paLines[iLine].cch = 0;
1050 pStream->paLines[iLine].enmEol = SCMEOL_NONE;
1051
1052 size_t cchLeft = cchBuf - offBuf;
1053 pchLine = pchLF + 1;
1054 pchLF = (const char *)memchr(pchLine, '\n', cchLeft);
1055 if (!pchLF)
1056 {
1057 pStream->paLines[iLine].cch = cchLeft;
1058 break;
1059 }
1060 }
1061
1062 pStream->iLine = iLine;
1063 pStream->cLines = iLine;
1064 }
1065
1066 /*
1067 * Copy the data and update position and size.
1068 */
1069 memcpy(&pStream->pch[off], pchBuf, cchBuf);
1070 off += cchBuf;
1071 pStream->off = off;
1072 pStream->cb = off;
1073
1074 return VINF_SUCCESS;
1075}
1076
1077/**
1078 * Write a character to the stream.
1079 *
1080 * @returns IPRT status code
1081 * @param pStream The stream. Must be in write mode.
1082 * @param pchBuf What to write.
1083 * @param cchBuf How much to write.
1084 */
1085int ScmStreamPutCh(PSCMSTREAM pStream, char ch)
1086{
1087 AssertReturn(pStream->fWriteOrRead, VERR_ACCESS_DENIED);
1088 if (RT_FAILURE(pStream->rc))
1089 return pStream->rc;
1090
1091 /*
1092 * Only deal with the simple cases here, use ScmStreamWrite for the
1093 * annoying stuff.
1094 */
1095 size_t off = pStream->off;
1096 if ( ch == '\n'
1097 || RT_UNLIKELY(off + 1 > pStream->cbAllocated))
1098 return ScmStreamWrite(pStream, &ch, 1);
1099
1100 /*
1101 * Just append it.
1102 */
1103 pStream->pch[off] = ch;
1104 pStream->off = off + 1;
1105 pStream->paLines[pStream->iLine].cch++;
1106
1107 return VINF_SUCCESS;
1108}
1109
1110/**
1111 * Copies @a cLines from the @a pSrc stream onto the @a pDst stream.
1112 *
1113 * The stream positions will be used and changed in both streams.
1114 *
1115 * @returns IPRT status code.
1116 * @param pDst The destination stream. Must be in write mode.
1117 * @param cLines The number of lines. (0 is accepted.)
1118 * @param pSrc The source stream. Must be in read mode.
1119 */
1120int ScmStreamCopyLines(PSCMSTREAM pDst, PSCMSTREAM pSrc, size_t cLines)
1121{
1122 AssertReturn(pDst->fWriteOrRead, VERR_ACCESS_DENIED);
1123 if (RT_FAILURE(pDst->rc))
1124 return pDst->rc;
1125
1126 AssertReturn(!pSrc->fWriteOrRead, VERR_ACCESS_DENIED);
1127 if (RT_FAILURE(pSrc->rc))
1128 return pSrc->rc;
1129
1130 while (cLines-- > 0)
1131 {
1132 SCMEOL enmEol;
1133 size_t cchLine;
1134 const char *pchLine = ScmStreamGetLine(pSrc, &cchLine, &enmEol);
1135 if (!pchLine)
1136 return pDst->rc = (RT_FAILURE(pSrc->rc) ? pSrc->rc : VERR_EOF);
1137
1138 int rc = ScmStreamPutLine(pDst, pchLine, cchLine, enmEol);
1139 if (RT_FAILURE(rc))
1140 return rc;
1141 }
1142
1143 return VINF_SUCCESS;
1144}
1145
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