VirtualBox

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

Last change on this file since 69166 was 69166, checked in by vboxsync, 7 years ago

scm: License and copyright updating.

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