VirtualBox

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

Last change on this file since 57971 was 57353, checked in by vboxsync, 9 years ago

scm: Added fixing of flower boxes marking sections in C/C++ source file, like 'Header Files' and 'Defined Constants And Macros'. Applied it to the bldprogs.

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