VirtualBox

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

Last change on this file since 67668 was 62537, checked in by vboxsync, 8 years ago

(C) 2016

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