VirtualBox

source: vbox/trunk/src/bldprogs/scmparser.cpp@ 69311

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

scm: *.vbs and *.vb aren't batch files

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 34.0 KB
Line 
1/* $Id: scmparser.cpp 69258 2017-10-25 00:11:43Z vboxsync $ */
2/** @file
3 * IPRT Testcase / Tool - Source Code Massager, Code Parsers.
4 */
5
6/*
7 * Copyright (C) 2010-2017 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/dir.h>
25#include <iprt/env.h>
26#include <iprt/file.h>
27#include <iprt/err.h>
28#include <iprt/getopt.h>
29#include <iprt/initterm.h>
30#include <iprt/mem.h>
31#include <iprt/message.h>
32#include <iprt/param.h>
33#include <iprt/path.h>
34#include <iprt/process.h>
35#include <iprt/stream.h>
36#include <iprt/string.h>
37
38#include "scm.h"
39
40
41/*********************************************************************************************************************************
42* Structures and Typedefs *
43*********************************************************************************************************************************/
44typedef size_t (*PFNISCOMMENT)(const char *pchLine, size_t cchLine, bool fSecond);
45
46
47/**
48 * Callback for checking if C++ line comment.
49 */
50static size_t isCppLineComment(const char *pchLine, size_t cchLine, bool fSecond)
51{
52 if ( cchLine >= 2
53 && pchLine[0] == '/'
54 && pchLine[1] == '/')
55 {
56 if (!fSecond)
57 return 2;
58 if (cchLine >= 3 && pchLine[2] == '/')
59 return 3;
60 }
61 return 0;
62}
63
64
65/**
66 * Callback for checking if hash comment.
67 */
68static size_t isHashComment(const char *pchLine, size_t cchLine, bool fSecond)
69{
70 if (cchLine >= 1 && *pchLine == '#')
71 {
72 if (!fSecond)
73 return 1;
74 if (cchLine >= 2 && pchLine[1] == '#')
75 return 2;
76 }
77 return 0;
78}
79
80
81/**
82 * Callback for checking if semicolon comment.
83 */
84static size_t isSemicolonComment(const char *pchLine, size_t cchLine, bool fSecond)
85{
86 if (cchLine >= 1 && *pchLine == ';')
87 {
88 if (!fSecond)
89 return 1;
90 if (cchLine >= 2 && pchLine[1] == ';')
91 return 2;
92 }
93 return 0;
94}
95
96
97/** Macro for checking for a batch file comment prefix. */
98#define IS_REM(a_pch, a_off, a_cch) \
99 ( (a_off) + 3 <= (a_cch) \
100 && ((a_pch)[(a_off) ] == 'R' || (a_pch)[(a_off) ] == 'r') \
101 && ((a_pch)[(a_off) + 1] == 'E' || (a_pch)[(a_off) + 1] == 'e') \
102 && ((a_pch)[(a_off) + 2] == 'M' || (a_pch)[(a_off) + 2] == 'm') \
103 && ((a_off) + 3 == (a_cch) || RT_C_IS_SPACE((a_pch)[(a_off) + 3])) )
104
105
106/**
107 * Callback for checking if comment.
108 */
109static size_t isBatchComment(const char *pchLine, size_t cchLine, bool fSecond)
110{
111 if (!fSecond)
112 {
113 if (IS_REM(pchLine, 0, cchLine))
114 return 3;
115 }
116 else
117 {
118 /* Check for the 2nd in "rem rem" lines. */
119 if ( cchLine >= 4
120 && RT_C_IS_SPACE(*pchLine)
121 && IS_REM(pchLine, 1, cchLine))
122 return 4;
123 }
124 return 0;
125}
126
127/**
128 * Callback for checking if tick comment.
129 */
130static size_t isTickComment(const char *pchLine, size_t cchLine, bool fSecond)
131{
132 if (cchLine >= 1 && *pchLine == '\'')
133 {
134 if (!fSecond)
135 return 1;
136 if (cchLine >= 2 && pchLine[1] == '\'')
137 return 2;
138 }
139 return 0;
140}
141
142
143/**
144 * Common worker for enumeratePythonComments and enumerateSimpleLineComments.
145 *
146 * @returns IPRT status code.
147 * @param pIn The input stream.
148 * @param pfnIsComment Comment tester function.
149 * @param pfnCallback The callback.
150 * @param pvUser The user argument for the callback.
151 * @param ppchLine Pointer to the line variable.
152 * @param pcchLine Pointer to the line length variable.
153 * @param penmEol Pointer to the line ending type variable.
154 * @param piLine Pointer to the line number variable.
155 * @param poff Pointer to the line offset variable. On input this
156 * is positioned at the start of the comment.
157 */
158static int handleLineComment(PSCMSTREAM pIn, PFNISCOMMENT pfnIsComment,
159 PFNSCMCOMMENTENUMERATOR pfnCallback, void *pvUser,
160 const char **ppchLine, size_t *pcchLine, PSCMEOL penmEol,
161 uint32_t *piLine, size_t *poff)
162{
163 /* Unpack input/output variables. */
164 uint32_t iLine = *piLine;
165 const char *pchLine = *ppchLine;
166 size_t cchLine = *pcchLine;
167 size_t off = *poff;
168 SCMEOL enmEol = *penmEol;
169
170 /*
171 * Take down the basic info about the comment.
172 */
173 SCMCOMMENTINFO Info;
174 Info.iLineStart = iLine;
175 Info.iLineEnd = iLine;
176 Info.offStart = (uint32_t)off;
177 Info.offEnd = (uint32_t)cchLine;
178
179 size_t cchSkip = pfnIsComment(&pchLine[off], cchLine - off, false);
180 Assert(cchSkip > 0);
181 off += cchSkip;
182
183 /* Determin comment type. */
184 Info.enmType = kScmCommentType_Line;
185 char ch;
186 cchSkip = 1;
187 if ( off < cchLine
188 && ( (ch = pchLine[off]) == '!'
189 || (cchSkip = pfnIsComment(&pchLine[off], cchLine - off, true)) > 0) )
190 {
191 unsigned ch2;
192 if ( off + cchSkip == cchLine
193 || RT_C_IS_SPACE(ch2 = pchLine[off + cchSkip]) )
194 {
195 Info.enmType = ch != '!' ? kScmCommentType_Line_JavaDoc : kScmCommentType_Line_Qt;
196 off += cchSkip;
197 }
198 else if ( ch2 == '<'
199 && ( off + cchSkip + 1 == cchLine
200 || RT_C_IS_SPACE(pchLine[off + cchSkip + 1]) ))
201 {
202 Info.enmType = ch == '!' ? kScmCommentType_Line_JavaDoc_After : kScmCommentType_Line_Qt_After;
203 off += cchSkip + 1;
204 }
205 }
206
207 /*
208 * Copy body of the first line. Like for C, we ignore a single space in the first comment line.
209 */
210 if (off < cchLine && RT_C_IS_SPACE(pchLine[off]))
211 off++;
212 size_t cchBody = cchLine;
213 while (cchBody >= off && RT_C_IS_SPACE(pchLine[cchBody - 1]))
214 cchBody--;
215 cchBody -= off;
216 size_t cbBodyAlloc = RT_MAX(_1K, RT_ALIGN_Z(cchBody + 64, 128));
217 char *pszBody = (char *)RTMemAlloc(cbBodyAlloc);
218 if (!pszBody)
219 return VERR_NO_MEMORY;
220 memcpy(pszBody, &pchLine[off], cchBody);
221 pszBody[cchBody] = '\0';
222
223 Info.cBlankLinesBefore = cchBody == 0;
224
225 /*
226 * Look for more comment lines and append them to the body.
227 */
228 while ((pchLine = ScmStreamGetLine(pIn, &cchLine, &enmEol)) != NULL)
229 {
230 iLine++;
231
232 /* Skip leading spaces. */
233 off = 0;
234 while (off < cchLine && RT_C_IS_SPACE(pchLine[off]))
235 off++;
236
237 /* Check if it's a comment. */
238 if ( off >= cchLine
239 || (cchSkip = pfnIsComment(&pchLine[off], cchLine - off, false)) == 0)
240 break;
241 off += cchSkip;
242
243 /* Split on doxygen comment start (if not already in one). */
244 if ( Info.enmType == kScmCommentType_Line
245 && off + 1 < cchLine
246 && ( pfnIsComment(&pchLine[off], cchLine - off, true) > 0
247 || ( pchLine[off + 1] == '!'
248 && ( off + 2 == cchLine
249 || pchLine[off + 2] != '!') ) ) )
250 {
251 off -= cchSkip;
252 break;
253 }
254
255 /* Append the body w/o trailing spaces and some leading ones. */
256 if (off < cchLine && RT_C_IS_SPACE(pchLine[off]))
257 off++;
258 while (off < cchLine && off < Info.offStart + 3 && RT_C_IS_SPACE(pchLine[off]))
259 off++;
260 size_t cchAppend = cchLine;
261 while (cchAppend > off && RT_C_IS_SPACE(pchLine[cchAppend - 1]))
262 cchAppend--;
263 cchAppend -= off;
264
265 size_t cchNewBody = cchBody + 1 + cchAppend;
266 if (cchNewBody >= cbBodyAlloc)
267 {
268 cbBodyAlloc = RT_MAX(cbBodyAlloc ? cbBodyAlloc * 2 : _1K, RT_ALIGN_Z(cchNewBody + 64, 128));
269 void *pvNew = RTMemRealloc(pszBody, cbBodyAlloc);
270 if (pvNew)
271 pszBody = (char *)pvNew;
272 else
273 {
274 RTMemFree(pszBody);
275 return VERR_NO_MEMORY;
276 }
277 }
278
279 if ( cchBody > 0
280 || cchAppend > 0)
281 {
282 if (cchBody > 0)
283 pszBody[cchBody++] = '\n';
284 memcpy(&pszBody[cchBody], &pchLine[off], cchAppend);
285 cchBody += cchAppend;
286 pszBody[cchBody] = '\0';
287 }
288 else
289 Info.cBlankLinesBefore++;
290
291 /* Advance. */
292 Info.offEnd = (uint32_t)cchLine;
293 Info.iLineEnd = iLine;
294 }
295
296 /*
297 * Strip trailing empty lines in the body.
298 */
299 Info.cBlankLinesAfter = 0;
300 while (cchBody >= 1 && pszBody[cchBody - 1] == '\n')
301 {
302 Info.cBlankLinesAfter++;
303 pszBody[--cchBody] = '\0';
304 }
305
306 /*
307 * Do the callback and return.
308 */
309 int rc = pfnCallback(&Info, pszBody, cchBody, pvUser);
310
311 RTMemFree(pszBody);
312
313 *piLine = iLine;
314 *ppchLine = pchLine;
315 *pcchLine = cchLine;
316 *poff = off;
317 *penmEol = enmEol;
318 return rc;
319}
320
321
322
323/**
324 * Common string litteral handler.
325 *
326 * @returns new pchLine value.
327 * @param pIn The input string.
328 * @param chType The quotation type.
329 * @param pchLine The current line.
330 * @param ppchLine Pointer to the line variable.
331 * @param pcchLine Pointer to the line length variable.
332 * @param penmEol Pointer to the line ending type variable.
333 * @param piLine Pointer to the line number variable.
334 * @param poff Pointer to the line offset variable.
335 */
336static const char *handleStringLitteral(PSCMSTREAM pIn, char chType, const char *pchLine, size_t *pcchLine, PSCMEOL penmEol,
337 uint32_t *piLine, size_t *poff)
338{
339 size_t off = *poff;
340 for (;;)
341 {
342 bool fEnd = false;
343 bool fEscaped = false;
344 size_t const cchLine = *pcchLine;
345 while (off < cchLine)
346 {
347 char ch = pchLine[off++];
348 if (!fEscaped)
349 {
350 if (ch != chType)
351 {
352 if (ch != '\\')
353 { /* likely */ }
354 else
355 fEscaped = true;
356 }
357 else
358 {
359 fEnd = true;
360 break;
361 }
362 }
363 else
364 fEscaped = false;
365 }
366 if (fEnd)
367 break;
368
369 /* next line */
370 pchLine = ScmStreamGetLine(pIn, pcchLine, penmEol);
371 if (!pchLine)
372 break;
373 *piLine += 1;
374 off = 0;
375 }
376
377 *poff = off;
378 return pchLine;
379}
380
381
382/**
383 * Deals with comments in C and C++ code.
384 *
385 * @returns VBox status code / callback return code.
386 * @param pIn The stream to parse.
387 * @param pfnCallback The callback.
388 * @param pvUser The user parameter for the callback.
389 */
390static int enumerateCStyleComments(PSCMSTREAM pIn, PFNSCMCOMMENTENUMERATOR pfnCallback, void *pvUser)
391{
392 AssertCompile('\'' < '/');
393 AssertCompile('"' < '/');
394
395 int rcRet = VINF_SUCCESS;
396 uint32_t iLine = 0;
397 SCMEOL enmEol;
398 size_t cchLine;
399 const char *pchLine;
400 while ((pchLine = ScmStreamGetLine(pIn, &cchLine, &enmEol)) != NULL)
401 {
402 size_t off = 0;
403 while (off < cchLine)
404 {
405 unsigned ch = pchLine[off++];
406 if (ch > (unsigned)'/')
407 { /* not interesting */ }
408 else if (ch == '/')
409 {
410 if (off < cchLine)
411 {
412 ch = pchLine[off++];
413 if (ch == '*')
414 {
415 /*
416 * Multiline comment. Find the end.
417 *
418 * Note! This is very similar to the python doc string handling further down.
419 */
420 SCMCOMMENTINFO Info;
421 Info.iLineStart = iLine;
422 Info.offStart = (uint32_t)off - 2;
423 Info.iLineEnd = UINT32_MAX;
424 Info.offEnd = UINT32_MAX;
425 Info.cBlankLinesBefore = 0;
426
427 /* Determin comment type (same as for line-comments). */
428 Info.enmType = kScmCommentType_MultiLine;
429 if ( off < cchLine
430 && ( (ch = pchLine[off]) == '*'
431 || ch == '!') )
432 {
433 unsigned ch2;
434 if ( off + 1 == cchLine
435 || RT_C_IS_SPACE(ch2 = pchLine[off + 1]) )
436 {
437 Info.enmType = ch == '*' ? kScmCommentType_MultiLine_JavaDoc : kScmCommentType_MultiLine_Qt;
438 off += 1;
439 }
440 else if ( ch2 == '<'
441 && ( off + 2 == cchLine
442 || RT_C_IS_SPACE(pchLine[off + 2]) ))
443 {
444 Info.enmType = ch == '*' ? kScmCommentType_MultiLine_JavaDoc_After
445 : kScmCommentType_MultiLine_Qt_After;
446 off += 2;
447 }
448 }
449
450 /*
451 * Copy the body and find the end of the multiline comment.
452 */
453 size_t cbBodyAlloc = 0;
454 size_t cchBody = 0;
455 char *pszBody = NULL;
456 for (;;)
457 {
458 /* Parse the line up to the end-of-comment or end-of-line. */
459 size_t offLineStart = off;
460 size_t offLastNonBlank = off;
461 size_t offFirstNonBlank = ~(size_t)0;
462 while (off < cchLine)
463 {
464 ch = pchLine[off++];
465 if (ch != '*' || off >= cchLine || pchLine[off] != '/')
466 {
467 if (RT_C_IS_BLANK(ch))
468 {/* kind of likely */}
469 else
470 {
471 offLastNonBlank = off - 1;
472 if (offFirstNonBlank != ~(size_t)0)
473 {/* likely */}
474 else if ( ch != '*' /* ignore continuation-asterisks */
475 || off > Info.offStart + 1 + 1
476 || off > cchLine
477 || ( off < cchLine
478 && !RT_C_IS_SPACE(pchLine[off]))
479 || pszBody == NULL)
480 offFirstNonBlank = off - 1;
481 }
482 }
483 else
484 {
485 Info.offEnd = (uint32_t)++off;
486 Info.iLineEnd = iLine;
487 break;
488 }
489 }
490
491 /* Append line content to the comment body string. */
492 size_t cchAppend;
493 if (offFirstNonBlank == ~(size_t)0)
494 cchAppend = 0; /* empty line */
495 else
496 {
497 if (pszBody)
498 offLineStart = RT_MIN(Info.offStart + 3, offFirstNonBlank);
499 else if (offFirstNonBlank > Info.offStart + 2) /* Skip one leading blank at the start of the comment. */
500 offLineStart++;
501 cchAppend = offLastNonBlank + 1 - offLineStart;
502 Assert(cchAppend <= cchLine);
503 }
504
505 size_t cchNewBody = cchBody + (cchBody > 0) + cchAppend;
506 if (cchNewBody >= cbBodyAlloc)
507 {
508 cbBodyAlloc = RT_MAX(cbBodyAlloc ? cbBodyAlloc * 2 : _1K, RT_ALIGN_Z(cchNewBody + 64, 128));
509 void *pvNew = RTMemRealloc(pszBody, cbBodyAlloc);
510 if (pvNew)
511 pszBody = (char *)pvNew;
512 else
513 {
514 RTMemFree(pszBody);
515 return VERR_NO_MEMORY;
516 }
517 }
518
519 if (cchBody > 0) /* no leading blank lines */
520 pszBody[cchBody++] = '\n';
521 else if (cchAppend == 0)
522 Info.cBlankLinesBefore++;
523 memcpy(&pszBody[cchBody], &pchLine[offLineStart], cchAppend);
524 cchBody += cchAppend;
525 pszBody[cchBody] = '\0';
526
527 /* Advance to the next line, if we haven't yet seen the end of this comment. */
528 if (Info.iLineEnd != UINT32_MAX)
529 break;
530 pchLine = ScmStreamGetLine(pIn, &cchLine, &enmEol);
531 if (!pchLine)
532 {
533 Info.offEnd = (uint32_t)cchLine;
534 Info.iLineEnd = iLine;
535 break;
536 }
537 iLine++;
538 off = 0;
539 }
540
541 /* Strip trailing empty lines in the body. */
542 Info.cBlankLinesAfter = 0;
543 while (cchBody >= 1 && pszBody[cchBody - 1] == '\n')
544 {
545 Info.cBlankLinesAfter++;
546 pszBody[--cchBody] = '\0';
547 }
548
549 /* Do the callback. */
550 int rc = pfnCallback(&Info, pszBody, cchBody, pvUser);
551 RTMemFree(pszBody);
552 if (RT_FAILURE(rc))
553 return rc;
554 if (rc > VINF_SUCCESS && rcRet == VINF_SUCCESS)
555 rcRet = rc;
556 }
557 else if (ch == '/')
558 {
559 /*
560 * Line comment. Join the other line comment guys.
561 */
562 off -= 2;
563 int rc = handleLineComment(pIn, isCppLineComment, pfnCallback, pvUser,
564 &pchLine, &cchLine, &enmEol, &iLine, &off);
565 if (RT_FAILURE(rc))
566 return rc;
567 if (rcRet == VINF_SUCCESS)
568 rcRet = rc;
569 }
570 }
571 }
572 else if (ch == '"')
573 {
574 /*
575 * String litterals may include sequences that looks like comments. So,
576 * they needs special handling to avoid confusion.
577 */
578 pchLine = handleStringLitteral(pIn, '"', pchLine, &cchLine, &enmEol, &iLine, &off);
579 }
580 /* else: We don't have to deal with character litterals as these shouldn't
581 include comment-like sequences. */
582 } /* for each character in the line */
583
584 iLine++;
585 } /* for each line in the stream */
586
587 int rcStream = ScmStreamGetStatus(pIn);
588 if (RT_SUCCESS(rcStream))
589 return rcRet;
590 return rcStream;
591}
592
593
594/**
595 * Deals with comments in Python code.
596 *
597 * @returns VBox status code / callback return code.
598 * @param pIn The stream to parse.
599 * @param pfnCallback The callback.
600 * @param pvUser The user parameter for the callback.
601 */
602static int enumeratePythonComments(PSCMSTREAM pIn, PFNSCMCOMMENTENUMERATOR pfnCallback, void *pvUser)
603{
604 AssertCompile('#' < '\'');
605 AssertCompile('"' < '\'');
606
607 int rcRet = VINF_SUCCESS;
608 uint32_t iLine = 0;
609 SCMEOL enmEol;
610 size_t cchLine;
611 const char *pchLine;
612 SCMCOMMENTINFO Info;
613 while ((pchLine = ScmStreamGetLine(pIn, &cchLine, &enmEol)) != NULL)
614 {
615 size_t off = 0;
616 while (off < cchLine)
617 {
618 char ch = pchLine[off++];
619 if ((unsigned char)ch > (unsigned char)'\'')
620 { /* not interesting */ }
621 else if (ch == '#')
622 {
623 /*
624 * Line comment. Join paths with the others.
625 */
626 off -= 1;
627 int rc = handleLineComment(pIn, isHashComment, pfnCallback, pvUser,
628 &pchLine, &cchLine, &enmEol, &iLine, &off);
629 if (RT_FAILURE(rc))
630 return rc;
631 if (rcRet == VINF_SUCCESS)
632 rcRet = rc;
633 }
634 else if (ch == '"' || ch == '\'')
635 {
636 /*
637 * String litterals may be doc strings and they may legally include hashes.
638 */
639 const char chType = ch;
640 if ( off + 1 >= cchLine
641 || pchLine[off] != chType
642 || pchLine[off + 1] != chType)
643 pchLine = handleStringLitteral(pIn, chType, pchLine, &cchLine, &enmEol, &iLine, &off);
644 else
645 {
646 /*
647 * Doc string (/ long string).
648 *
649 * Note! This is very similar to the multiline C comment handling above.
650 */
651 Info.iLineStart = iLine;
652 Info.offStart = (uint32_t)off - 1;
653 Info.iLineEnd = UINT32_MAX;
654 Info.offEnd = UINT32_MAX;
655 Info.cBlankLinesBefore = 0;
656 Info.enmType = kScmCommentType_DocString;
657
658 off += 2;
659
660 /* Copy the body and find the end of the doc string comment. */
661 size_t cbBodyAlloc = 0;
662 size_t cchBody = 0;
663 char *pszBody = NULL;
664 for (;;)
665 {
666 /* Parse the line up to the end-of-comment or end-of-line. */
667 size_t offLineStart = off;
668 size_t offLastNonBlank = off;
669 size_t offFirstNonBlank = ~(size_t)0;
670 bool fEscaped = false;
671 while (off < cchLine)
672 {
673 ch = pchLine[off++];
674 if (!fEscaped)
675 {
676 if ( off + 1 >= cchLine
677 || ch != chType
678 || pchLine[off] != chType
679 || pchLine[off + 1] != chType)
680 {
681 if (RT_C_IS_BLANK(ch))
682 {/* kind of likely */}
683 else
684 {
685 offLastNonBlank = off - 1;
686 if (offFirstNonBlank != ~(size_t)0)
687 {/* likely */}
688 else if ( ch != '*' /* ignore continuation-asterisks */
689 || off > Info.offStart + 1 + 1
690 || off > cchLine
691 || ( off < cchLine
692 && !RT_C_IS_SPACE(pchLine[off]))
693 || pszBody == NULL)
694 offFirstNonBlank = off - 1;
695
696 if (ch != '\\')
697 {/* likely */ }
698 else
699 fEscaped = true;
700 }
701 }
702 else
703 {
704 off += 2;
705 Info.offEnd = (uint32_t)off;
706 Info.iLineEnd = iLine;
707 break;
708 }
709 }
710 else
711 fEscaped = false;
712 }
713
714 /* Append line content to the comment body string. */
715 size_t cchAppend;
716 if (offFirstNonBlank == ~(size_t)0)
717 cchAppend = 0; /* empty line */
718 else
719 {
720 if (pszBody)
721 offLineStart = RT_MIN(Info.offStart + 3, offFirstNonBlank);
722 else if (offFirstNonBlank > Info.offStart + 2) /* Skip one leading blank at the start of the comment. */
723 offLineStart++;
724 cchAppend = offLastNonBlank + 1 - offLineStart;
725 Assert(cchAppend <= cchLine);
726 }
727
728 size_t cchNewBody = cchBody + (cchBody > 0) + cchAppend;
729 if (cchNewBody >= cbBodyAlloc)
730 {
731 cbBodyAlloc = RT_MAX(cbBodyAlloc ? cbBodyAlloc * 2 : _1K, RT_ALIGN_Z(cchNewBody + 64, 128));
732 void *pvNew = RTMemRealloc(pszBody, cbBodyAlloc);
733 if (pvNew)
734 pszBody = (char *)pvNew;
735 else
736 {
737 RTMemFree(pszBody);
738 return VERR_NO_MEMORY;
739 }
740 }
741
742 if (cchBody > 0) /* no leading blank lines */
743 pszBody[cchBody++] = '\n';
744 else if (cchAppend == 0)
745 Info.cBlankLinesBefore++;
746 memcpy(&pszBody[cchBody], &pchLine[offLineStart], cchAppend);
747 cchBody += cchAppend;
748 pszBody[cchBody] = '\0';
749
750 /* Advance to the next line, if we haven't yet seen the end of this comment. */
751 if (Info.iLineEnd != UINT32_MAX)
752 break;
753 pchLine = ScmStreamGetLine(pIn, &cchLine, &enmEol);
754 if (!pchLine)
755 {
756 Info.offEnd = (uint32_t)cchLine;
757 Info.iLineEnd = iLine;
758 break;
759 }
760 iLine++;
761 off = 0;
762 }
763
764 /* Strip trailing empty lines in the body. */
765 Info.cBlankLinesAfter = 0;
766 while (cchBody >= 1 && pszBody[cchBody - 1] == '\n')
767 {
768 Info.cBlankLinesAfter++;
769 pszBody[--cchBody] = '\0';
770 }
771
772 /* Do the callback. */
773 int rc = pfnCallback(&Info, pszBody, cchBody, pvUser);
774 RTMemFree(pszBody);
775 if (RT_FAILURE(rc))
776 return rc;
777 if (rc > VINF_SUCCESS && rcRet == VINF_SUCCESS)
778 rcRet = rc;
779 }
780 }
781 /* else: We don't have to deal with character litterals as these shouldn't
782 include comment-like sequences. */
783 } /* for each character in the line */
784
785 iLine++;
786 } /* for each line in the stream */
787
788 int rcStream = ScmStreamGetStatus(pIn);
789 if (RT_SUCCESS(rcStream))
790 return rcRet;
791 return rcStream;
792}
793
794
795/**
796 * Deals with comments in DOS batch files.
797 *
798 * @returns VBox status code / callback return code.
799 * @param pIn The stream to parse.
800 * @param pfnCallback The callback.
801 * @param pvUser The user parameter for the callback.
802 */
803static int enumerateBatchComments(PSCMSTREAM pIn, PFNSCMCOMMENTENUMERATOR pfnCallback, void *pvUser)
804{
805 int rcRet = VINF_SUCCESS;
806 uint32_t iLine = 0;
807 SCMEOL enmEol;
808 size_t cchLine;
809 const char *pchLine = ScmStreamGetLine(pIn, &cchLine, &enmEol);
810 while (pchLine != NULL)
811 {
812 /*
813 * Skip leading blanks and check for 'rem'.
814 * At the moment we do not parse '::lable-comments'.
815 */
816 size_t off = 0;
817 while (off + 3 < cchLine && RT_C_IS_SPACE(pchLine[off]))
818 off++;
819 if (!IS_REM(pchLine, off, cchLine))
820 {
821 iLine++;
822 pchLine = ScmStreamGetLine(pIn, &cchLine, &enmEol);
823 }
824 else
825 {
826 int rc = handleLineComment(pIn, isBatchComment, pfnCallback, pvUser,
827 &pchLine, &cchLine, &enmEol, &iLine, &off);
828 if (RT_FAILURE(rc))
829 return rc;
830 if (rcRet == VINF_SUCCESS)
831 rcRet = rc;
832 }
833 }
834
835 int rcStream = ScmStreamGetStatus(pIn);
836 if (RT_SUCCESS(rcStream))
837 return rcRet;
838 return rcStream;
839}
840
841
842/**
843 * Deals with simple line comments.
844 *
845 * @returns VBox status code / callback return code.
846 * @param pIn The stream to parse.
847 * @param chStart The start of comment character.
848 * @param pfnIsComment Comment tester function.
849 * @param pfnCallback The callback.
850 * @param pvUser The user parameter for the callback.
851 */
852static int enumerateSimpleLineComments(PSCMSTREAM pIn, char chStart, PFNISCOMMENT pfnIsComment,
853 PFNSCMCOMMENTENUMERATOR pfnCallback, void *pvUser)
854{
855 int rcRet = VINF_SUCCESS;
856 uint32_t iLine = 0;
857 SCMEOL enmEol;
858 size_t cchLine;
859 const char *pchLine;
860 while ((pchLine = ScmStreamGetLine(pIn, &cchLine, &enmEol)) != NULL)
861 {
862 size_t off = 0;
863 while (off < cchLine)
864 {
865 char ch = pchLine[off++];
866 if (ch != chStart)
867 { /* not interesting */ }
868 else
869 {
870 off -= 1;
871 int rc = handleLineComment(pIn, pfnIsComment, pfnCallback, pvUser,
872 &pchLine, &cchLine, &enmEol, &iLine, &off);
873 if (RT_FAILURE(rc))
874 return rc;
875 if (rcRet == VINF_SUCCESS)
876 rcRet = rc;
877 }
878 } /* for each character in the line */
879
880 iLine++;
881 } /* for each line in the stream */
882
883 int rcStream = ScmStreamGetStatus(pIn);
884 if (RT_SUCCESS(rcStream))
885 return rcRet;
886 return rcStream;
887}
888
889
890/**
891 * Enumerates the comments in the given stream, calling @a pfnCallback for each.
892 *
893 * @returns IPRT status code.
894 * @param pIn The stream to parse.
895 * @param enmCommentStyle The comment style of the source stream.
896 * @param pfnCallback The function to call.
897 * @param pvUser User argument to the callback.
898 */
899int ScmEnumerateComments(PSCMSTREAM pIn, SCMCOMMENTSTYLE enmCommentStyle, PFNSCMCOMMENTENUMERATOR pfnCallback, void *pvUser)
900{
901 switch (enmCommentStyle)
902 {
903 case kScmCommentStyle_C:
904 return enumerateCStyleComments(pIn, pfnCallback, pvUser);
905
906 case kScmCommentStyle_Python:
907 return enumeratePythonComments(pIn, pfnCallback, pvUser);
908
909 case kScmCommentStyle_Semicolon:
910 return enumerateSimpleLineComments(pIn, ';', isSemicolonComment, pfnCallback, pvUser);
911
912 case kScmCommentStyle_Hash:
913 return enumerateSimpleLineComments(pIn, '#', isHashComment, pfnCallback, pvUser);
914
915 case kScmCommentStyle_Rem_Upper:
916 case kScmCommentStyle_Rem_Lower:
917 case kScmCommentStyle_Rem_Camel:
918 return enumerateBatchComments(pIn, pfnCallback, pvUser);
919
920 case kScmCommentStyle_Tick:
921 return enumerateSimpleLineComments(pIn, '\'', isTickComment, pfnCallback, pvUser);
922
923 default:
924 AssertFailedReturn(VERR_INVALID_PARAMETER);
925 }
926}
927
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