VirtualBox

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

Last change on this file since 50653 was 48959, checked in by vboxsync, 11 years ago

bldprogs: Whitespace and svn:keywords cleanups by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use