VirtualBox

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

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

scm: Splitting out the SCMSTREAM bits so it can be used by others.

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