VirtualBox

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

Last change on this file since 69169 was 69169, checked in by vboxsync, 8 years ago

scm: License and copyright updating. [missing file]

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette