VirtualBox

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

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

RT_STR_TUPLE: Switch the length and string so it'll work with functions like RTStrAAppendN. VBoxTpG: Parser works.

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