VirtualBox

source: vbox/trunk/src/bldprogs/scmrw.cpp@ 67954

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

scm: cleaning up todos

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 28.3 KB
Line 
1/* $Id: scmrw.cpp 63568 2016-08-16 14:07:39Z vboxsync $ */
2/** @file
3 * IPRT Testcase / Tool - Source Code Massager.
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/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 * Worker for isBlankLine.
43 *
44 * @returns true if blank, false if not.
45 * @param pchLine Pointer to the start of the line.
46 * @param cchLine The (encoded) length of the line, excluding EOL char.
47 */
48static bool isBlankLineSlow(const char *pchLine, size_t cchLine)
49{
50 /*
51 * From the end, more likely to hit a non-blank char there.
52 */
53 while (cchLine-- > 0)
54 if (!RT_C_IS_BLANK(pchLine[cchLine]))
55 return false;
56 return true;
57}
58
59/**
60 * Helper for checking whether a line is blank.
61 *
62 * @returns true if blank, false if not.
63 * @param pchLine Pointer to the start of the line.
64 * @param cchLine The (encoded) length of the line, excluding EOL char.
65 */
66DECLINLINE(bool) isBlankLine(const char *pchLine, size_t cchLine)
67{
68 if (cchLine == 0)
69 return true;
70 /*
71 * We're more likely to fine a non-space char at the end of the line than
72 * at the start, due to source code indentation.
73 */
74 if (pchLine[cchLine - 1])
75 return false;
76
77 /*
78 * Don't bother inlining loop code.
79 */
80 return isBlankLineSlow(pchLine, cchLine);
81}
82
83
84/**
85 * Strip trailing blanks (space & tab).
86 *
87 * @returns True if modified, false if not.
88 * @param pIn The input stream.
89 * @param pOut The output stream.
90 * @param pSettings The settings.
91 */
92bool rewrite_StripTrailingBlanks(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings)
93{
94 if (!pSettings->fStripTrailingBlanks)
95 return false;
96
97 bool fModified = false;
98 SCMEOL enmEol;
99 size_t cchLine;
100 const char *pchLine;
101 while ((pchLine = ScmStreamGetLine(pIn, &cchLine, &enmEol)) != NULL)
102 {
103 int rc;
104 if ( cchLine == 0
105 || !RT_C_IS_BLANK(pchLine[cchLine - 1]) )
106 rc = ScmStreamPutLine(pOut, pchLine, cchLine, enmEol);
107 else
108 {
109 cchLine--;
110 while (cchLine > 0 && RT_C_IS_BLANK(pchLine[cchLine - 1]))
111 cchLine--;
112 rc = ScmStreamPutLine(pOut, pchLine, cchLine, enmEol);
113 fModified = true;
114 }
115 if (RT_FAILURE(rc))
116 return false;
117 }
118 if (fModified)
119 ScmVerbose(pState, 2, " * Stripped trailing blanks\n");
120 return fModified;
121}
122
123/**
124 * Expand tabs.
125 *
126 * @returns True if modified, false if not.
127 * @param pIn The input stream.
128 * @param pOut The output stream.
129 * @param pSettings The settings.
130 */
131bool rewrite_ExpandTabs(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings)
132{
133 if (!pSettings->fConvertTabs)
134 return false;
135
136 size_t const cchTab = pSettings->cchTab;
137 bool fModified = false;
138 SCMEOL enmEol;
139 size_t cchLine;
140 const char *pchLine;
141 while ((pchLine = ScmStreamGetLine(pIn, &cchLine, &enmEol)) != NULL)
142 {
143 int rc;
144 const char *pchTab = (const char *)memchr(pchLine, '\t', cchLine);
145 if (!pchTab)
146 rc = ScmStreamPutLine(pOut, pchLine, cchLine, enmEol);
147 else
148 {
149 size_t offTab = 0;
150 const char *pchChunk = pchLine;
151 for (;;)
152 {
153 size_t cchChunk = pchTab - pchChunk;
154 offTab += cchChunk;
155 ScmStreamWrite(pOut, pchChunk, cchChunk);
156
157 size_t cchToTab = cchTab - offTab % cchTab;
158 ScmStreamWrite(pOut, g_szTabSpaces, cchToTab);
159 offTab += cchToTab;
160
161 pchChunk = pchTab + 1;
162 size_t cchLeft = cchLine - (pchChunk - pchLine);
163 pchTab = (const char *)memchr(pchChunk, '\t', cchLeft);
164 if (!pchTab)
165 {
166 rc = ScmStreamPutLine(pOut, pchChunk, cchLeft, enmEol);
167 break;
168 }
169 }
170
171 fModified = true;
172 }
173 if (RT_FAILURE(rc))
174 return false;
175 }
176 if (fModified)
177 ScmVerbose(pState, 2, " * Expanded tabs\n");
178 return fModified;
179}
180
181/**
182 * Worker for rewrite_ForceNativeEol, rewrite_ForceLF and rewrite_ForceCRLF.
183 *
184 * @returns true if modifications were made, false if not.
185 * @param pIn The input stream.
186 * @param pOut The output stream.
187 * @param pSettings The settings.
188 * @param enmDesiredEol The desired end of line indicator type.
189 * @param pszDesiredSvnEol The desired svn:eol-style.
190 */
191static bool rewrite_ForceEol(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings,
192 SCMEOL enmDesiredEol, const char *pszDesiredSvnEol)
193{
194 if (!pSettings->fConvertEol)
195 return false;
196
197 bool fModified = false;
198 SCMEOL enmEol;
199 size_t cchLine;
200 const char *pchLine;
201 while ((pchLine = ScmStreamGetLine(pIn, &cchLine, &enmEol)) != NULL)
202 {
203 if ( enmEol != enmDesiredEol
204 && enmEol != SCMEOL_NONE)
205 {
206 fModified = true;
207 enmEol = enmDesiredEol;
208 }
209 int rc = ScmStreamPutLine(pOut, pchLine, cchLine, enmEol);
210 if (RT_FAILURE(rc))
211 return false;
212 }
213 if (fModified)
214 ScmVerbose(pState, 2, " * Converted EOL markers\n");
215
216 /* Check svn:eol-style if appropriate */
217 if ( pSettings->fSetSvnEol
218 && ScmSvnIsInWorkingCopy(pState))
219 {
220 char *pszEol;
221 int rc = ScmSvnQueryProperty(pState, "svn:eol-style", &pszEol);
222 if ( (RT_SUCCESS(rc) && strcmp(pszEol, pszDesiredSvnEol))
223 || rc == VERR_NOT_FOUND)
224 {
225 if (rc == VERR_NOT_FOUND)
226 ScmVerbose(pState, 2, " * Setting svn:eol-style to %s (missing)\n", pszDesiredSvnEol);
227 else
228 ScmVerbose(pState, 2, " * Setting svn:eol-style to %s (was: %s)\n", pszDesiredSvnEol, pszEol);
229 int rc2 = ScmSvnSetProperty(pState, "svn:eol-style", pszDesiredSvnEol);
230 if (RT_FAILURE(rc2))
231 RTMsgError("ScmSvnSetProperty: %Rrc\n", rc2); /** @todo propagate the error somehow... */
232 }
233 if (RT_SUCCESS(rc))
234 RTStrFree(pszEol);
235 }
236
237 /** @todo also check the subversion svn:eol-style state! */
238 return fModified;
239}
240
241/**
242 * Force native end of line indicator.
243 *
244 * @returns true if modifications were made, false if not.
245 * @param pIn The input stream.
246 * @param pOut The output stream.
247 * @param pSettings The settings.
248 */
249bool rewrite_ForceNativeEol(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings)
250{
251#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
252 return rewrite_ForceEol(pState, pIn, pOut, pSettings, SCMEOL_CRLF, "native");
253#else
254 return rewrite_ForceEol(pState, pIn, pOut, pSettings, SCMEOL_LF, "native");
255#endif
256}
257
258/**
259 * Force the stream to use LF as the end of line indicator.
260 *
261 * @returns true if modifications were made, false if not.
262 * @param pIn The input stream.
263 * @param pOut The output stream.
264 * @param pSettings The settings.
265 */
266bool rewrite_ForceLF(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings)
267{
268 return rewrite_ForceEol(pState, pIn, pOut, pSettings, SCMEOL_LF, "LF");
269}
270
271/**
272 * Force the stream to use CRLF as the end of line indicator.
273 *
274 * @returns true if modifications were made, false if not.
275 * @param pIn The input stream.
276 * @param pOut The output stream.
277 * @param pSettings The settings.
278 */
279bool rewrite_ForceCRLF(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings)
280{
281 return rewrite_ForceEol(pState, pIn, pOut, pSettings, SCMEOL_CRLF, "CRLF");
282}
283
284/**
285 * Strip trailing blank lines and/or make sure there is exactly one blank line
286 * at the end of the file.
287 *
288 * @returns true if modifications were made, false if not.
289 * @param pIn The input stream.
290 * @param pOut The output stream.
291 * @param pSettings The settings.
292 *
293 * @remarks ASSUMES trailing white space has been removed already.
294 */
295bool rewrite_AdjustTrailingLines(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings)
296{
297 if ( !pSettings->fStripTrailingLines
298 && !pSettings->fForceTrailingLine
299 && !pSettings->fForceFinalEol)
300 return false;
301
302 size_t const cLines = ScmStreamCountLines(pIn);
303
304 /* Empty files remains empty. */
305 if (cLines <= 1)
306 return false;
307
308 /* Figure out if we need to adjust the number of lines or not. */
309 size_t cLinesNew = cLines;
310
311 if ( pSettings->fStripTrailingLines
312 && ScmStreamIsWhiteLine(pIn, cLinesNew - 1))
313 {
314 while ( cLinesNew > 1
315 && ScmStreamIsWhiteLine(pIn, cLinesNew - 2))
316 cLinesNew--;
317 }
318
319 if ( pSettings->fForceTrailingLine
320 && !ScmStreamIsWhiteLine(pIn, cLinesNew - 1))
321 cLinesNew++;
322
323 bool fFixMissingEol = pSettings->fForceFinalEol
324 && ScmStreamGetEolByLine(pIn, cLinesNew - 1) == SCMEOL_NONE;
325
326 if ( !fFixMissingEol
327 && cLines == cLinesNew)
328 return false;
329
330 /* Copy the number of lines we've arrived at. */
331 ScmStreamRewindForReading(pIn);
332
333 size_t cCopied = RT_MIN(cLinesNew, cLines);
334 ScmStreamCopyLines(pOut, pIn, cCopied);
335
336 if (cCopied != cLinesNew)
337 {
338 while (cCopied++ < cLinesNew)
339 ScmStreamPutLine(pOut, "", 0, ScmStreamGetEol(pIn));
340 }
341 /* Fix missing EOL if required. */
342 else if (fFixMissingEol)
343 {
344 if (ScmStreamGetEol(pIn) == SCMEOL_LF)
345 ScmStreamWrite(pOut, "\n", 1);
346 else
347 ScmStreamWrite(pOut, "\r\n", 2);
348 }
349
350 ScmVerbose(pState, 2, " * Adjusted trailing blank lines\n");
351 return true;
352}
353
354/**
355 * Make sure there is no svn:executable keyword on the current file.
356 *
357 * @returns false - the state carries these kinds of changes.
358 * @param pState The rewriter state.
359 * @param pIn The input stream.
360 * @param pOut The output stream.
361 * @param pSettings The settings.
362 */
363bool rewrite_SvnNoExecutable(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings)
364{
365 RT_NOREF2(pIn, pOut);
366 if ( !pSettings->fSetSvnExecutable
367 || !ScmSvnIsInWorkingCopy(pState))
368 return false;
369
370 int rc = ScmSvnQueryProperty(pState, "svn:executable", NULL);
371 if (RT_SUCCESS(rc))
372 {
373 ScmVerbose(pState, 2, " * removing svn:executable\n");
374 rc = ScmSvnDelProperty(pState, "svn:executable");
375 if (RT_FAILURE(rc))
376 RTMsgError("ScmSvnSetProperty: %Rrc\n", rc); /** @todo error propagation here.. */
377 }
378 return false;
379}
380
381/**
382 * Make sure the Id and Revision keywords are expanded.
383 *
384 * @returns false - the state carries these kinds of changes.
385 * @param pState The rewriter state.
386 * @param pIn The input stream.
387 * @param pOut The output stream.
388 * @param pSettings The settings.
389 */
390bool rewrite_SvnKeywords(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings)
391{
392 RT_NOREF2(pIn, pOut);
393 if ( !pSettings->fSetSvnKeywords
394 || !ScmSvnIsInWorkingCopy(pState))
395 return false;
396
397 char *pszKeywords;
398 int rc = ScmSvnQueryProperty(pState, "svn:keywords", &pszKeywords);
399 if ( RT_SUCCESS(rc)
400 && ( !strstr(pszKeywords, "Id") /** @todo need some function for finding a word in a string. */
401 || !strstr(pszKeywords, "Revision")) )
402 {
403 if (!strstr(pszKeywords, "Id") && !strstr(pszKeywords, "Revision"))
404 rc = RTStrAAppend(&pszKeywords, " Id Revision");
405 else if (!strstr(pszKeywords, "Id"))
406 rc = RTStrAAppend(&pszKeywords, " Id");
407 else
408 rc = RTStrAAppend(&pszKeywords, " Revision");
409 if (RT_SUCCESS(rc))
410 {
411 ScmVerbose(pState, 2, " * changing svn:keywords to '%s'\n", pszKeywords);
412 rc = ScmSvnSetProperty(pState, "svn:keywords", pszKeywords);
413 if (RT_FAILURE(rc))
414 RTMsgError("ScmSvnSetProperty: %Rrc\n", rc); /** @todo error propagation here.. */
415 }
416 else
417 RTMsgError("RTStrAppend: %Rrc\n", rc); /** @todo error propagation here.. */
418 RTStrFree(pszKeywords);
419 }
420 else if (rc == VERR_NOT_FOUND)
421 {
422 ScmVerbose(pState, 2, " * setting svn:keywords to 'Id Revision'\n");
423 rc = ScmSvnSetProperty(pState, "svn:keywords", "Id Revision");
424 if (RT_FAILURE(rc))
425 RTMsgError("ScmSvnSetProperty: %Rrc\n", rc); /** @todo error propagation here.. */
426 }
427 else if (RT_SUCCESS(rc))
428 RTStrFree(pszKeywords);
429
430 return false;
431}
432
433/**
434 * Makefile.kup are empty files, enforce this.
435 *
436 * @returns true if modifications were made, false if not.
437 * @param pIn The input stream.
438 * @param pOut The output stream.
439 * @param pSettings The settings.
440 */
441bool rewrite_Makefile_kup(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings)
442{
443 RT_NOREF2(pOut, pSettings);
444
445 /* These files should be zero bytes. */
446 if (pIn->cb == 0)
447 return false;
448 ScmVerbose(pState, 2, " * Truncated file to zero bytes\n");
449 return true;
450}
451
452/**
453 * Rewrite a kBuild makefile.
454 *
455 * @returns true if modifications were made, false if not.
456 * @param pIn The input stream.
457 * @param pOut The output stream.
458 * @param pSettings The settings.
459 *
460 * @todo
461 *
462 * Ideas for Makefile.kmk and Config.kmk:
463 * - sort if1of/ifn1of sets.
464 * - line continuation slashes should only be preceded by one space.
465 */
466bool rewrite_Makefile_kmk(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings)
467{
468 RT_NOREF4(pState, pIn, pOut, pSettings);
469 return false;
470}
471
472
473static bool isFlowerBoxSectionMarker(PSCMSTREAM pIn, const char *pchLine, size_t cchLine,
474 const char **ppchText, size_t *pcchText)
475{
476 *ppchText = NULL;
477 *pcchText = 0;
478
479 /*
480 * The first line.
481 */
482 if (pchLine[0] != '/')
483 return false;
484 size_t offLine = 1;
485 while (offLine < cchLine && pchLine[offLine] == '*')
486 offLine++;
487 if (offLine < 20) /* (Code below depend on a reasonable minimum here.) */
488 return false;
489 while (offLine < cchLine && RT_C_IS_BLANK(pchLine[offLine]))
490 offLine++;
491 if (offLine != cchLine)
492 return false;
493
494 size_t const cchBox = cchLine;
495
496 /*
497 * The next line, extracting the text.
498 */
499 SCMEOL enmEol;
500 pchLine = ScmStreamGetLine(pIn, &cchLine, &enmEol);
501 if (cchLine < cchBox - 3)
502 return false;
503
504 offLine = 0;
505 if (RT_C_IS_BLANK(pchLine[0]))
506 offLine = RT_C_IS_BLANK(pchLine[1]) ? 2 : 1;
507
508 if (pchLine[offLine] != '*')
509 return false;
510 offLine++;
511
512 if (!RT_C_IS_BLANK(pchLine[offLine + 1]))
513 return false;
514 offLine++;
515
516 while (offLine < cchLine && RT_C_IS_BLANK(pchLine[offLine]))
517 offLine++;
518 if (offLine >= cchLine)
519 return false;
520 if (!RT_C_IS_UPPER(pchLine[offLine]))
521 return false;
522
523 *ppchText = &pchLine[offLine];
524 size_t const offText = offLine;
525
526 /* From the end now. */
527 offLine = cchLine - 1;
528 while (RT_C_IS_BLANK(pchLine[offLine]))
529 offLine--;
530
531 if (pchLine[offLine] != '*')
532 return false;
533 offLine--;
534 if (!RT_C_IS_BLANK(pchLine[offLine]))
535 return false;
536 offLine--;
537 while (RT_C_IS_BLANK(pchLine[offLine]))
538 offLine--;
539 *pcchText = offLine - offText + 1;
540
541 /*
542 * Third line closes the box.
543 */
544 pchLine = ScmStreamGetLine(pIn, &cchLine, &enmEol);
545 if (cchLine < cchBox - 3)
546 return false;
547
548 offLine = 0;
549 if (RT_C_IS_BLANK(pchLine[0]))
550 offLine = RT_C_IS_BLANK(pchLine[1]) ? 2 : 1;
551 while (offLine < cchLine && pchLine[offLine] == '*')
552 offLine++;
553 if (offLine < cchBox - 4)
554 return false;
555
556 if (pchLine[offLine] != '/')
557 return false;
558 offLine++;
559
560 while (offLine < cchLine && RT_C_IS_BLANK(pchLine[offLine]))
561 offLine++;
562 if (offLine != cchLine)
563 return false;
564
565 return true;
566}
567
568
569/**
570 * Flower box marker comments in C and C++ code.
571 *
572 * @returns true if modifications were made, false if not.
573 * @param pIn The input stream.
574 * @param pOut The output stream.
575 * @param pSettings The settings.
576 */
577bool rewrite_FixFlowerBoxMarkers(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings)
578{
579 if (!pSettings->fFixFlowerBoxMarkers)
580 return false;
581
582 /*
583 * Work thru the file line by line looking for flower box markers.
584 */
585 size_t cChanges = 0;
586 size_t cBlankLines = 0;
587 SCMEOL enmEol;
588 size_t cchLine;
589 const char *pchLine;
590 while ((pchLine = ScmStreamGetLine(pIn, &cchLine, &enmEol)) != NULL)
591 {
592 /*
593 * Get a likely match for a first line.
594 */
595 if ( pchLine[0] == '/'
596 && cchLine > 20
597 && pchLine[1] == '*'
598 && pchLine[2] == '*'
599 && pchLine[3] == '*')
600 {
601 size_t const offSaved = ScmStreamTell(pIn);
602 char const *pchText;
603 size_t cchText;
604 if (isFlowerBoxSectionMarker(pIn, pchLine, cchLine, &pchText, &cchText))
605 {
606 while (cBlankLines < pSettings->cMinBlankLinesBeforeFlowerBoxMakers)
607 {
608 ScmStreamPutEol(pOut, enmEol);
609 cBlankLines++;
610 }
611
612 ScmStreamPutCh(pOut, '/');
613 ScmStreamWrite(pOut, g_szAsterisks, pSettings->cchWidth - 1);
614 ScmStreamPutEol(pOut, enmEol);
615
616 static const char s_szLead[] = "* ";
617 ScmStreamWrite(pOut, s_szLead, sizeof(s_szLead) - 1);
618 ScmStreamWrite(pOut, pchText, cchText);
619 size_t offCurPlus1 = sizeof(s_szLead) - 1 + cchText + 1;
620 ScmStreamWrite(pOut, g_szSpaces, offCurPlus1 < pSettings->cchWidth ? pSettings->cchWidth - offCurPlus1 : 1);
621 ScmStreamPutCh(pOut, '*');
622 ScmStreamPutEol(pOut, enmEol);
623
624 ScmStreamWrite(pOut, g_szAsterisks, pSettings->cchWidth - 1);
625 ScmStreamPutCh(pOut, '/');
626 ScmStreamPutEol(pOut, enmEol);
627
628 cChanges++;
629 cBlankLines = 0;
630 continue;
631 }
632
633 int rc = ScmStreamSeekAbsolute(pIn, offSaved);
634 if (RT_FAILURE(rc))
635 return false;
636 }
637
638 int rc = ScmStreamPutLine(pOut, pchLine, cchLine, enmEol);
639 if (RT_FAILURE(rc))
640 return false;
641
642 /* Do blank line accounting so we can ensure at least two blank lines
643 before each section marker. */
644 if (!isBlankLine(pchLine, cchLine))
645 cBlankLines = 0;
646 else
647 cBlankLines++;
648 }
649 if (cChanges > 0)
650 ScmVerbose(pState, 2, " * Converted %zu flower boxer markers\n", cChanges);
651 return cChanges != 0;
652}
653
654
655/**
656 * Looks for the start of a todo comment.
657 *
658 * @returns Offset into the line of the comment start sequence.
659 * @param pchLine The line to search.
660 * @param cchLineBeforeTodo The length of the line before the todo.
661 * @param pfSameLine Indicates whether it's refering to a statemtn on
662 * the same line comment (true), or the next
663 * statement (false).
664 */
665static size_t findTodoCommentStart(char const *pchLine, size_t cchLineBeforeTodo, bool *pfSameLine)
666{
667 *pfSameLine = false;
668
669 /* Skip one '@' or '\\'. */
670 char ch;
671 if ( cchLineBeforeTodo > 2
672 && ( (ch = pchLine[cchLineBeforeTodo - 1] == '@')
673 || ch == '\\' ) )
674 cchLineBeforeTodo--;
675
676 /* Skip blanks. */
677 while ( cchLineBeforeTodo > 2
678 && RT_C_IS_BLANK(pchLine[cchLineBeforeTodo - 1]))
679 cchLineBeforeTodo--;
680
681 /* Look for same line indicator. */
682 if ( cchLineBeforeTodo > 0
683 && pchLine[cchLineBeforeTodo - 1] == '<')
684 {
685 *pfSameLine = true;
686 cchLineBeforeTodo--;
687 }
688
689 /* Skip *s */
690 while ( cchLineBeforeTodo > 1
691 && pchLine[cchLineBeforeTodo - 1] == '*')
692 cchLineBeforeTodo--;
693
694 /* Do we have a comment opening sequence. */
695 if ( cchLineBeforeTodo > 0
696 && pchLine[cchLineBeforeTodo - 1] == '/'
697 && ( ( cchLineBeforeTodo >= 2
698 && pchLine[cchLineBeforeTodo - 2] == '/')
699 || pchLine[cchLineBeforeTodo] == '*'))
700 {
701 /* Skip slashes at the start. */
702 while ( cchLineBeforeTodo > 0
703 && pchLine[cchLineBeforeTodo - 1] == '/')
704 cchLineBeforeTodo--;
705
706 return cchLineBeforeTodo;
707 }
708
709 return ~(size_t)0;
710}
711
712
713/**
714 * Looks for a TODO or todo in the given line.
715 *
716 * @returns Offset into the line of found, ~(size_t)0 if not.
717 * @param pchLine The line to search.
718 * @param cchLine The length of the line.
719 */
720static size_t findTodo(char const *pchLine, size_t cchLine)
721{
722 if (cchLine >= 4 + 2)
723 {
724 /* We don't search the first to chars because we need the start of a comment.
725 Also, skip the last three chars since we need at least four for a match. */
726 size_t const cchLineT = cchLine - 3;
727 if ( memchr(pchLine + 2, 't', cchLineT - 2) != NULL
728 || memchr(pchLine + 2, 'T', cchLineT - 2) != NULL)
729 {
730 for (size_t off = 2; off < cchLineT; off++)
731 {
732 char ch = pchLine[off];
733 if ( ( ch != 't'
734 && ch != 'T')
735 || ( (ch = pchLine[off + 1]) != 'o'
736 && ch != 'O')
737 || ( (ch = pchLine[off + 2]) != 'd'
738 && ch != 'D')
739 || ( (ch = pchLine[off + 3]) != 'o'
740 && ch != 'O')
741 || ( off + 4 != cchLine
742 && (ch = pchLine[off + 4]) != ' '
743 && ch != '\t'
744 && ch != ':' /** @todo */
745 && (ch != '*' || off + 5 > cchLine || pchLine[off + 5] != '/') /** @todo */
746 ) )
747 { /* not a hit - likely */ }
748 else
749 return off;
750 }
751 }
752 }
753 return ~(size_t)0;
754}
755
756
757/**
758 * Flower box marker comments in C and C++ code.
759 *
760 * @returns true if modifications were made, false if not.
761 * @param pIn The input stream.
762 * @param pOut The output stream.
763 * @param pSettings The settings.
764 */
765bool rewrite_Fix_C_and_CPP_Todos(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings)
766{
767 if (!pSettings->fFixTodos)
768 return false;
769
770 /*
771 * Work thru the file line by line looking for the start of todo comments.
772 */
773 size_t cChanges = 0;
774 SCMEOL enmEol;
775 size_t cchLine;
776 const char *pchLine;
777 while ((pchLine = ScmStreamGetLine(pIn, &cchLine, &enmEol)) != NULL)
778 {
779 /*
780 * Look for the word 'todo' in the line. We're currently only trying
781 * to catch comments starting with the word todo and adjust the start of
782 * the doxygen statement.
783 */
784 size_t offTodo = findTodo(pchLine, cchLine);
785 if ( offTodo != ~(size_t)0
786 && offTodo >= 2)
787 {
788 /* Work backwards to find the start of the comment. */
789 bool fSameLine = false;
790 size_t offCommentStart = findTodoCommentStart(pchLine, offTodo, &fSameLine);
791 if (offCommentStart != ~(size_t)0)
792 {
793 char szNew[64];
794 size_t cchNew = 0;
795 szNew[cchNew++] = '/';
796 szNew[cchNew++] = pchLine[offCommentStart + 1];
797 szNew[cchNew++] = pchLine[offCommentStart + 1];
798 if (fSameLine)
799 szNew[cchNew++] = '<';
800 szNew[cchNew++] = ' ';
801 szNew[cchNew++] = '@';
802 szNew[cchNew++] = 't';
803 szNew[cchNew++] = 'o';
804 szNew[cchNew++] = 'd';
805 szNew[cchNew++] = 'o';
806
807 /* Figure out wheter to continue after the @todo statement opening, we'll strip ':'
808 but need to take into account that we might be at the end of the line before
809 adding the space. */
810 size_t offTodoAfter = offTodo + 4;
811 if ( offTodoAfter < cchLine
812 && pchLine[offTodoAfter] == ':')
813 offTodoAfter++;
814 if ( offTodoAfter < cchLine
815 && RT_C_IS_BLANK(pchLine[offTodoAfter]))
816 offTodoAfter++;
817 if (offTodoAfter < cchLine)
818 szNew[cchNew++] = ' ';
819
820 /* Write it out. */
821 ScmStreamWrite(pOut, pchLine, offCommentStart);
822 ScmStreamWrite(pOut, szNew, cchNew);
823 if (offTodoAfter < cchLine)
824 ScmStreamWrite(pOut, &pchLine[offTodoAfter], cchLine - offTodoAfter);
825 ScmStreamPutEol(pOut, enmEol);
826
827 /* Check whether we actually made any changes. */
828 if ( cchNew != offTodoAfter - offCommentStart
829 || memcmp(szNew, &pchLine[offCommentStart], cchNew))
830 cChanges++;
831 continue;
832 }
833 }
834
835 int rc = ScmStreamPutLine(pOut, pchLine, cchLine, enmEol);
836 if (RT_FAILURE(rc))
837 return false;
838 }
839 if (cChanges > 0)
840 ScmVerbose(pState, 2, " * Converted %zu todo statements.\n", cChanges);
841 return cChanges != 0;
842}
843
844
845/**
846 * Rewrite a C/C++ source or header file.
847 *
848 * @returns true if modifications were made, false if not.
849 * @param pIn The input stream.
850 * @param pOut The output stream.
851 * @param pSettings The settings.
852 *
853 * @todo
854 *
855 * Ideas for C/C++:
856 * - space after if, while, for, switch
857 * - spaces in for (i=0;i<x;i++)
858 * - complex conditional, bird style.
859 * - remove unnecessary parentheses.
860 * - sort defined RT_OS_*|| and RT_ARCH
861 * - sizeof without parenthesis.
862 * - defined without parenthesis.
863 * - trailing spaces.
864 * - parameter indentation.
865 * - space after comma.
866 * - while (x--); -> multi line + comment.
867 * - else statement;
868 * - space between function and left parenthesis.
869 * - TODO, XXX, @todo cleanup.
870 * - Space before/after '*'.
871 * - ensure new line at end of file.
872 * - Indentation of precompiler statements (#ifdef, #defines).
873 * - space between functions.
874 * - string.h -> iprt/string.h, stdarg.h -> iprt/stdarg.h, etc.
875 */
876bool rewrite_C_and_CPP(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings)
877{
878
879 RT_NOREF4(pState, pIn, pOut, pSettings);
880 return false;
881}
882
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use