VirtualBox

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

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

Working on parsing defines.

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