VirtualBox

source: vbox/trunk/src/bldprogs/scm.cpp@ 69349

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

scm: perl files

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 83.9 KB
Line 
1/* $Id: scm.cpp 69349 2017-10-26 13:47: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#include "scmdiff.h"
40
41
42/*********************************************************************************************************************************
43* Defined Constants And Macros *
44*********************************************************************************************************************************/
45/** The name of the settings files. */
46#define SCM_SETTINGS_FILENAME ".scm-settings"
47
48
49/*********************************************************************************************************************************
50* Structures and Typedefs *
51*********************************************************************************************************************************/
52
53/**
54 * Option identifiers.
55 *
56 * @note The first chunk, down to SCMOPT_TAB_SIZE, are alternately set &
57 * clear. So, the option setting a flag (boolean) will have an even
58 * number and the one clearing it will have an odd number.
59 * @note Down to SCMOPT_LAST_SETTINGS corresponds exactly to SCMSETTINGSBASE.
60 */
61typedef enum SCMOPT
62{
63 SCMOPT_CONVERT_EOL = 10000,
64 SCMOPT_NO_CONVERT_EOL,
65 SCMOPT_CONVERT_TABS,
66 SCMOPT_NO_CONVERT_TABS,
67 SCMOPT_FORCE_FINAL_EOL,
68 SCMOPT_NO_FORCE_FINAL_EOL,
69 SCMOPT_FORCE_TRAILING_LINE,
70 SCMOPT_NO_FORCE_TRAILING_LINE,
71 SCMOPT_STRIP_TRAILING_BLANKS,
72 SCMOPT_NO_STRIP_TRAILING_BLANKS,
73 SCMOPT_STRIP_TRAILING_LINES,
74 SCMOPT_NO_STRIP_TRAILING_LINES,
75 SCMOPT_FIX_FLOWER_BOX_MARKERS,
76 SCMOPT_NO_FIX_FLOWER_BOX_MARKERS,
77 SCMOPT_FIX_TODOS,
78 SCMOPT_NO_FIX_TODOS,
79 SCMOPT_UPDATE_COPYRIGHT_YEAR,
80 SCMOPT_NO_UPDATE_COPYRIGHT_YEAR,
81 SCMOPT_EXTERNAL_COPYRIGHT,
82 SCMOPT_NO_EXTERNAL_COPYRIGHT,
83 SCMOPT_NO_UPDATE_LICENSE,
84 SCMOPT_LICENSE_OSE_GPL,
85 SCMOPT_LICENSE_OSE_DUAL_GPL_CDDL,
86 SCMOPT_LICENSE_OSE_CDDL,
87 SCMOPT_LICENSE_LGPL,
88 SCMOPT_LICENSE_MIT,
89 SCMOPT_LICENSE_BASED_ON_MIT,
90 SCMOPT_LGPL_DISCLAIMER,
91 SCMOPT_NO_LGPL_DISCLAIMER,
92 SCMOPT_MIN_BLANK_LINES_BEFORE_FLOWER_BOX_MARKERS,
93 SCMOPT_ONLY_SVN_DIRS,
94 SCMOPT_NOT_ONLY_SVN_DIRS,
95 SCMOPT_ONLY_SVN_FILES,
96 SCMOPT_NOT_ONLY_SVN_FILES,
97 SCMOPT_SET_SVN_EOL,
98 SCMOPT_DONT_SET_SVN_EOL,
99 SCMOPT_SET_SVN_EXECUTABLE,
100 SCMOPT_DONT_SET_SVN_EXECUTABLE,
101 SCMOPT_SET_SVN_KEYWORDS,
102 SCMOPT_DONT_SET_SVN_KEYWORDS,
103 SCMOPT_TAB_SIZE,
104 SCMOPT_WIDTH,
105 SCMOPT_TREAT_AS,
106 SCMOPT_FILTER_OUT_DIRS,
107 SCMOPT_FILTER_FILES,
108 SCMOPT_FILTER_OUT_FILES,
109 SCMOPT_LAST_SETTINGS = SCMOPT_FILTER_OUT_FILES,
110 //
111 SCMOPT_DIFF_IGNORE_EOL,
112 SCMOPT_DIFF_NO_IGNORE_EOL,
113 SCMOPT_DIFF_IGNORE_SPACE,
114 SCMOPT_DIFF_NO_IGNORE_SPACE,
115 SCMOPT_DIFF_IGNORE_LEADING_SPACE,
116 SCMOPT_DIFF_NO_IGNORE_LEADING_SPACE,
117 SCMOPT_DIFF_IGNORE_TRAILING_SPACE,
118 SCMOPT_DIFF_NO_IGNORE_TRAILING_SPACE,
119 SCMOPT_DIFF_SPECIAL_CHARS,
120 SCMOPT_DIFF_NO_SPECIAL_CHARS,
121 SCMOPT_END
122} SCMOPT;
123
124
125/*********************************************************************************************************************************
126* Global Variables *
127*********************************************************************************************************************************/
128const char g_szTabSpaces[16+1] = " ";
129const char g_szAsterisks[255+1] =
130"****************************************************************************************************"
131"****************************************************************************************************"
132"*******************************************************";
133const char g_szSpaces[255+1] =
134" "
135" "
136" ";
137static const char g_szProgName[] = "scm";
138static const char *g_pszChangedSuff = "";
139static bool g_fDryRun = true;
140static bool g_fDiffSpecialChars = true;
141static bool g_fDiffIgnoreEol = false;
142static bool g_fDiffIgnoreLeadingWS = false;
143static bool g_fDiffIgnoreTrailingWS = false;
144static int g_iVerbosity = 2;//99; //0;
145uint32_t g_uYear = 0; /**< The current year. */
146/** @name Statistics
147 * @{ */
148static uint32_t g_cDirsProcessed = 0;
149static uint32_t g_cFilesProcessed = 0;
150static uint32_t g_cFilesModified = 0;
151static uint32_t g_cFilesSkipped = 0;
152static uint32_t g_cFilesNotInSvn = 0;
153static uint32_t g_cFilesNoRewriters = 0;
154static uint32_t g_cFilesBinaries = 0;
155/** @} */
156
157/** The global settings. */
158static SCMSETTINGSBASE const g_Defaults =
159{
160 /* .fConvertEol = */ true,
161 /* .fConvertTabs = */ true,
162 /* .fForceFinalEol = */ true,
163 /* .fForceTrailingLine = */ false,
164 /* .fStripTrailingBlanks = */ true,
165 /* .fStripTrailingLines = */ true,
166 /* .fFixFlowerBoxMarkers = */ true,
167 /* .cMinBlankLinesBeforeFlowerBoxMakers = */ 2,
168 /* .fFixTodos = */ true,
169 /* .fUpdateCopyrightYear = */ false,
170 /* .fExternalCopyright = */ false,
171 /* .fLgplDisclaimer = */ false,
172 /* .enmUpdateLicense = */ kScmLicense_OseGpl,
173 /* .fOnlySvnFiles = */ false,
174 /* .fOnlySvnDirs = */ false,
175 /* .fSetSvnEol = */ false,
176 /* .fSetSvnExecutable = */ false,
177 /* .fSetSvnKeywords = */ false,
178 /* .cchTab = */ 8,
179 /* .cchWidth = */ 130,
180 /* .pszTreatAsName = */ NULL,
181 /* .pszFilterFiles = */ (char *)"",
182 /* .pszFilterOutFiles = */ (char *)"*.exe|*.com|20*-*-*.log",
183 /* .pszFilterOutDirs = */ (char *)".svn|.hg|.git|CVS",
184};
185
186/** Option definitions for the base settings. */
187static RTGETOPTDEF g_aScmOpts[] =
188{
189 { "--convert-eol", SCMOPT_CONVERT_EOL, RTGETOPT_REQ_NOTHING },
190 { "--no-convert-eol", SCMOPT_NO_CONVERT_EOL, RTGETOPT_REQ_NOTHING },
191 { "--convert-tabs", SCMOPT_CONVERT_TABS, RTGETOPT_REQ_NOTHING },
192 { "--no-convert-tabs", SCMOPT_NO_CONVERT_TABS, RTGETOPT_REQ_NOTHING },
193 { "--force-final-eol", SCMOPT_FORCE_FINAL_EOL, RTGETOPT_REQ_NOTHING },
194 { "--no-force-final-eol", SCMOPT_NO_FORCE_FINAL_EOL, RTGETOPT_REQ_NOTHING },
195 { "--force-trailing-line", SCMOPT_FORCE_TRAILING_LINE, RTGETOPT_REQ_NOTHING },
196 { "--no-force-trailing-line", SCMOPT_NO_FORCE_TRAILING_LINE, RTGETOPT_REQ_NOTHING },
197 { "--strip-trailing-blanks", SCMOPT_STRIP_TRAILING_BLANKS, RTGETOPT_REQ_NOTHING },
198 { "--no-strip-trailing-blanks", SCMOPT_NO_STRIP_TRAILING_BLANKS, RTGETOPT_REQ_NOTHING },
199 { "--strip-trailing-lines", SCMOPT_STRIP_TRAILING_LINES, RTGETOPT_REQ_NOTHING },
200 { "--strip-no-trailing-lines", SCMOPT_NO_STRIP_TRAILING_LINES, RTGETOPT_REQ_NOTHING },
201 { "--min-blank-lines-before-flower-box-makers", SCMOPT_MIN_BLANK_LINES_BEFORE_FLOWER_BOX_MARKERS, RTGETOPT_REQ_UINT8 },
202 { "--fix-flower-box-markers", SCMOPT_FIX_FLOWER_BOX_MARKERS, RTGETOPT_REQ_NOTHING },
203 { "--no-fix-flower-box-markers", SCMOPT_NO_FIX_FLOWER_BOX_MARKERS, RTGETOPT_REQ_NOTHING },
204 { "--fix-todos", SCMOPT_FIX_TODOS, RTGETOPT_REQ_NOTHING },
205 { "--no-fix-todos", SCMOPT_NO_FIX_TODOS, RTGETOPT_REQ_NOTHING },
206 { "--update-copyright-year", SCMOPT_UPDATE_COPYRIGHT_YEAR, RTGETOPT_REQ_NOTHING },
207 { "--no-update-copyright-year", SCMOPT_NO_UPDATE_COPYRIGHT_YEAR, RTGETOPT_REQ_NOTHING },
208 { "--external-copyright", SCMOPT_EXTERNAL_COPYRIGHT, RTGETOPT_REQ_NOTHING },
209 { "--no-external-copyright", SCMOPT_NO_EXTERNAL_COPYRIGHT, RTGETOPT_REQ_NOTHING },
210 { "--no-update-license", SCMOPT_NO_UPDATE_LICENSE, RTGETOPT_REQ_NOTHING },
211 { "--license-ose-gpl", SCMOPT_LICENSE_OSE_GPL, RTGETOPT_REQ_NOTHING },
212 { "--license-ose-dual", SCMOPT_LICENSE_OSE_DUAL_GPL_CDDL, RTGETOPT_REQ_NOTHING },
213 { "--license-ose-cddl", SCMOPT_LICENSE_OSE_CDDL, RTGETOPT_REQ_NOTHING },
214 { "--license-lgpl", SCMOPT_LICENSE_LGPL, RTGETOPT_REQ_NOTHING },
215 { "--license-mit", SCMOPT_LICENSE_MIT, RTGETOPT_REQ_NOTHING },
216 { "--license-based-on-mit", SCMOPT_LICENSE_BASED_ON_MIT, RTGETOPT_REQ_NOTHING },
217 { "--lgpl-disclaimer", SCMOPT_LGPL_DISCLAIMER, RTGETOPT_REQ_NOTHING },
218 { "--no-lgpl-disclaimer", SCMOPT_NO_LGPL_DISCLAIMER, RTGETOPT_REQ_NOTHING },
219 { "--only-svn-dirs", SCMOPT_ONLY_SVN_DIRS, RTGETOPT_REQ_NOTHING },
220 { "--not-only-svn-dirs", SCMOPT_NOT_ONLY_SVN_DIRS, RTGETOPT_REQ_NOTHING },
221 { "--only-svn-files", SCMOPT_ONLY_SVN_FILES, RTGETOPT_REQ_NOTHING },
222 { "--not-only-svn-files", SCMOPT_NOT_ONLY_SVN_FILES, RTGETOPT_REQ_NOTHING },
223 { "--set-svn-eol", SCMOPT_SET_SVN_EOL, RTGETOPT_REQ_NOTHING },
224 { "--dont-set-svn-eol", SCMOPT_DONT_SET_SVN_EOL, RTGETOPT_REQ_NOTHING },
225 { "--set-svn-executable", SCMOPT_SET_SVN_EXECUTABLE, RTGETOPT_REQ_NOTHING },
226 { "--dont-set-svn-executable", SCMOPT_DONT_SET_SVN_EXECUTABLE, RTGETOPT_REQ_NOTHING },
227 { "--set-svn-keywords", SCMOPT_SET_SVN_KEYWORDS, RTGETOPT_REQ_NOTHING },
228 { "--dont-set-svn-keywords", SCMOPT_DONT_SET_SVN_KEYWORDS, RTGETOPT_REQ_NOTHING },
229 { "--tab-size", SCMOPT_TAB_SIZE, RTGETOPT_REQ_UINT8 },
230 { "--width", SCMOPT_WIDTH, RTGETOPT_REQ_UINT8 },
231 { "--treat-as", SCMOPT_TREAT_AS, RTGETOPT_REQ_STRING },
232 { "--filter-out-dirs", SCMOPT_FILTER_OUT_DIRS, RTGETOPT_REQ_STRING },
233 { "--filter-files", SCMOPT_FILTER_FILES, RTGETOPT_REQ_STRING },
234 { "--filter-out-files", SCMOPT_FILTER_OUT_FILES, RTGETOPT_REQ_STRING },
235};
236
237/** Consider files matching the following patterns (base names only). */
238static const char *g_pszFileFilter = NULL;
239
240static PFNSCMREWRITER const g_aRewritersFor_Makefile_kup[] =
241{
242 rewrite_SvnNoExecutable,
243 rewrite_Makefile_kup
244};
245
246static PFNSCMREWRITER const g_aRewritersFor_Makefile_kmk[] =
247{
248 rewrite_ForceNativeEol,
249 rewrite_StripTrailingBlanks,
250 rewrite_AdjustTrailingLines,
251 rewrite_SvnNoExecutable,
252 rewrite_SvnKeywords,
253 rewrite_Copyright_HashComment,
254 rewrite_Makefile_kmk
255};
256
257static PFNSCMREWRITER const g_aRewritersFor_OtherMakefiles[] =
258{
259 rewrite_ForceNativeEol,
260 rewrite_StripTrailingBlanks,
261 rewrite_AdjustTrailingLines,
262 rewrite_SvnNoExecutable,
263 rewrite_SvnKeywords,
264 rewrite_Copyright_HashComment,
265};
266
267static PFNSCMREWRITER const g_aRewritersFor_C_and_CPP[] =
268{
269 rewrite_ForceNativeEol,
270 rewrite_ExpandTabs,
271 rewrite_StripTrailingBlanks,
272 rewrite_AdjustTrailingLines,
273 rewrite_SvnNoExecutable,
274 rewrite_SvnKeywords,
275 rewrite_Copyright_CstyleComment,
276 rewrite_FixFlowerBoxMarkers,
277 rewrite_Fix_C_and_CPP_Todos,
278 rewrite_C_and_CPP
279};
280
281static PFNSCMREWRITER const g_aRewritersFor_H_and_HPP[] =
282{
283 rewrite_ForceNativeEol,
284 rewrite_ExpandTabs,
285 rewrite_StripTrailingBlanks,
286 rewrite_AdjustTrailingLines,
287 rewrite_SvnNoExecutable,
288 rewrite_Copyright_CstyleComment,
289 rewrite_C_and_CPP
290};
291
292static PFNSCMREWRITER const g_aRewritersFor_RC[] =
293{
294 rewrite_ForceNativeEol,
295 rewrite_ExpandTabs,
296 rewrite_StripTrailingBlanks,
297 rewrite_AdjustTrailingLines,
298 rewrite_SvnNoExecutable,
299 rewrite_SvnKeywords,
300 rewrite_Copyright_CstyleComment,
301};
302
303static PFNSCMREWRITER const g_aRewritersFor_DTrace[] =
304{
305 rewrite_ForceNativeEol,
306 rewrite_ExpandTabs,
307 rewrite_StripTrailingBlanks,
308 rewrite_AdjustTrailingLines,
309 rewrite_SvnKeywords,
310 rewrite_Copyright_CstyleComment,
311};
312
313static PFNSCMREWRITER const g_aRewritersFor_DSL[] =
314{
315 rewrite_ForceNativeEol,
316 rewrite_ExpandTabs,
317 rewrite_StripTrailingBlanks,
318 rewrite_AdjustTrailingLines,
319 rewrite_SvnNoExecutable,
320 rewrite_SvnKeywords,
321 rewrite_Copyright_CstyleComment,
322};
323
324static PFNSCMREWRITER const g_aRewritersFor_ASM[] =
325{
326 rewrite_ForceNativeEol,
327 rewrite_ExpandTabs,
328 rewrite_StripTrailingBlanks,
329 rewrite_AdjustTrailingLines,
330 rewrite_SvnNoExecutable,
331 rewrite_SvnKeywords,
332 rewrite_Copyright_SemicolonComment,
333};
334
335static PFNSCMREWRITER const g_aRewritersFor_DEF[] =
336{
337 rewrite_ForceNativeEol,
338 rewrite_ExpandTabs,
339 rewrite_StripTrailingBlanks,
340 rewrite_AdjustTrailingLines,
341 rewrite_SvnNoExecutable,
342 rewrite_SvnKeywords,
343 rewrite_Copyright_SemicolonComment,
344};
345
346static PFNSCMREWRITER const g_aRewritersFor_ShellScripts[] =
347{
348 rewrite_ForceLF,
349 rewrite_ExpandTabs,
350 rewrite_StripTrailingBlanks,
351 rewrite_Copyright_HashComment,
352};
353
354static PFNSCMREWRITER const g_aRewritersFor_BatchFiles[] =
355{
356 rewrite_ForceCRLF,
357 rewrite_ExpandTabs,
358 rewrite_StripTrailingBlanks,
359 rewrite_Copyright_RemComment,
360};
361
362static PFNSCMREWRITER const g_aRewritersFor_BasicScripts[] =
363{
364 rewrite_ForceCRLF,
365 rewrite_ExpandTabs,
366 rewrite_StripTrailingBlanks,
367 rewrite_Copyright_TickComment,
368};
369
370static PFNSCMREWRITER const g_aRewritersFor_SedScripts[] =
371{
372 rewrite_ForceLF,
373 rewrite_ExpandTabs,
374 rewrite_StripTrailingBlanks,
375 rewrite_Copyright_HashComment,
376};
377
378static PFNSCMREWRITER const g_aRewritersFor_Python[] =
379{
380 /** @todo rewrite_ForceLFIfExecutable */
381 rewrite_ExpandTabs,
382 rewrite_StripTrailingBlanks,
383 rewrite_AdjustTrailingLines,
384 rewrite_SvnKeywords,
385 rewrite_Copyright_PythonComment,
386};
387
388static PFNSCMREWRITER const g_aRewritersFor_Perl[] =
389{
390 /** @todo rewrite_ForceLFIfExecutable */
391 rewrite_ExpandTabs,
392 rewrite_StripTrailingBlanks,
393 rewrite_AdjustTrailingLines,
394 rewrite_SvnKeywords,
395 rewrite_Copyright_HashComment,
396};
397
398static PFNSCMREWRITER const g_aRewritersFor_ScmSettings[] =
399{
400 rewrite_ForceNativeEol,
401 rewrite_ExpandTabs,
402 rewrite_StripTrailingBlanks,
403 rewrite_AdjustTrailingLines,
404 rewrite_SvnNoExecutable,
405 rewrite_SvnKeywords,
406 rewrite_Copyright_HashComment,
407};
408
409static PFNSCMREWRITER const g_aRewritersFor_Images[] =
410{
411 rewrite_SvnNoExecutable,
412 rewrite_SvnBinary,
413};
414
415static PFNSCMREWRITER const g_aRewritersFor_Xslt[] =
416{
417 rewrite_ForceNativeEol,
418 rewrite_ExpandTabs,
419 rewrite_StripTrailingBlanks,
420 rewrite_AdjustTrailingLines,
421 rewrite_SvnNoExecutable,
422 rewrite_SvnKeywords,
423 /** @todo copyright is in an XML comment. */
424};
425
426static PFNSCMREWRITER const g_aRewritersFor_Xml[] =
427{
428 rewrite_ForceNativeEol,
429 rewrite_ExpandTabs,
430 rewrite_StripTrailingBlanks,
431 rewrite_AdjustTrailingLines,
432 rewrite_SvnNoExecutable,
433 rewrite_SvnKeywords,
434 /** @todo copyright is in an XML comment. */
435};
436
437static PFNSCMREWRITER const g_aRewritersFor_QtProject[] =
438{
439 rewrite_ForceNativeEol,
440 rewrite_StripTrailingBlanks,
441 rewrite_AdjustTrailingLines,
442 rewrite_SvnNoExecutable,
443 rewrite_SvnKeywords,
444 rewrite_Copyright_HashComment,
445};
446
447static PFNSCMREWRITER const g_aRewritersFor_QtResourceFiles[] =
448{
449 rewrite_ForceNativeEol,
450 rewrite_SvnNoExecutable,
451 rewrite_SvnKeywords,
452 /** @todo figure out copyright for Qt resource XML files. */
453};
454
455static PFNSCMREWRITER const g_aRewritersFor_QtTranslations[] =
456{
457 rewrite_ForceNativeEol,
458 rewrite_SvnNoExecutable,
459};
460
461static PFNSCMREWRITER const g_aRewritersFor_QtUiFiles[] =
462{
463 rewrite_ForceNativeEol,
464 rewrite_SvnNoExecutable,
465 rewrite_SvnKeywords,
466 /** @todo copyright is in an XML 'comment' element. */
467};
468
469static PFNSCMREWRITER const g_aRewritersFor_FileLists[] = /* both makefile and shell script */
470{
471 rewrite_ForceLF,
472 rewrite_ExpandTabs,
473 rewrite_StripTrailingBlanks,
474 rewrite_AdjustTrailingLines,
475 rewrite_Copyright_HashComment,
476};
477
478
479
480#define SCM_CFG_ENTRY(a_aRewriters, a_fBinary, a_szFilePatterns) \
481 { RT_ELEMENTS(a_aRewriters), &a_aRewriters[0], a_fBinary, a_szFilePatterns }
482static SCMCFGENTRY const g_aConfigs[] =
483{
484 SCM_CFG_ENTRY(g_aRewritersFor_Makefile_kup, false, "Makefile.kup" ),
485 SCM_CFG_ENTRY(g_aRewritersFor_Makefile_kmk, false, "*.kmk" ),
486 SCM_CFG_ENTRY(g_aRewritersFor_OtherMakefiles, false, "Makefile" ),
487 SCM_CFG_ENTRY(g_aRewritersFor_C_and_CPP, false, "*.c|*.cpp|*.C|*.CPP|*.cxx|*.cc|*.m|*.mm" ),
488 SCM_CFG_ENTRY(g_aRewritersFor_H_and_HPP, false, "*.h|*.hpp" ),
489 SCM_CFG_ENTRY(g_aRewritersFor_RC, false, "*.rc" ),
490 SCM_CFG_ENTRY(g_aRewritersFor_ASM, false, "*.asm|*.mac|*.inc" ),
491 SCM_CFG_ENTRY(g_aRewritersFor_DTrace, false, "*.d" ),
492 SCM_CFG_ENTRY(g_aRewritersFor_DEF, false, "*.def" ),
493 SCM_CFG_ENTRY(g_aRewritersFor_DSL, false, "*.dsl" ),
494 SCM_CFG_ENTRY(g_aRewritersFor_ShellScripts, false, "*.sh|configure" ),
495 SCM_CFG_ENTRY(g_aRewritersFor_BatchFiles, false, "*.bat|*.cmd|*.btm" ),
496 SCM_CFG_ENTRY(g_aRewritersFor_BasicScripts, false, "*.vbs|*.vb" ),
497 SCM_CFG_ENTRY(g_aRewritersFor_SedScripts, false, "*.sed" ),
498 SCM_CFG_ENTRY(g_aRewritersFor_Python, false, "*.py" ),
499 SCM_CFG_ENTRY(g_aRewritersFor_Perl, false, "*.pl" ),
500 SCM_CFG_ENTRY(g_aRewritersFor_ScmSettings, false, "*.scm-settings" ),
501 SCM_CFG_ENTRY(g_aRewritersFor_Images, true, "*.png|*.bmp|*.jpg|*.pnm|*.ico|*.icns" ),
502 SCM_CFG_ENTRY(g_aRewritersFor_Xslt, false, "*.xsl" ),
503 SCM_CFG_ENTRY(g_aRewritersFor_Xml, false, "*.xml" ),
504 SCM_CFG_ENTRY(g_aRewritersFor_QtProject, false, "*.pro" ),
505 SCM_CFG_ENTRY(g_aRewritersFor_QtResourceFiles, false, "*.qrc" ),
506 SCM_CFG_ENTRY(g_aRewritersFor_QtTranslations, false, "*.ts" ),
507 SCM_CFG_ENTRY(g_aRewritersFor_QtUiFiles, false, "*.ui" ),
508 /* Must be be last: */
509 SCM_CFG_ENTRY(g_aRewritersFor_FileLists, false, "files_*" ),
510};
511
512
513
514/* -=-=-=-=-=- settings -=-=-=-=-=- */
515
516
517/**
518 * Init a settings structure with settings from @a pSrc.
519 *
520 * @returns IPRT status code
521 * @param pSettings The settings.
522 * @param pSrc The source settings.
523 */
524static int scmSettingsBaseInitAndCopy(PSCMSETTINGSBASE pSettings, PCSCMSETTINGSBASE pSrc)
525{
526 *pSettings = *pSrc;
527
528 int rc = RTStrDupEx(&pSettings->pszFilterFiles, pSrc->pszFilterFiles);
529 if (RT_SUCCESS(rc))
530 {
531 rc = RTStrDupEx(&pSettings->pszFilterOutFiles, pSrc->pszFilterOutFiles);
532 if (RT_SUCCESS(rc))
533 {
534 rc = RTStrDupEx(&pSettings->pszFilterOutDirs, pSrc->pszFilterOutDirs);
535 if (RT_SUCCESS(rc))
536 {
537 if (pSrc->pszTreatAsName)
538 rc = RTStrDupEx(&pSettings->pszTreatAsName, pSrc->pszTreatAsName);
539 if (RT_SUCCESS(rc))
540 return VINF_SUCCESS;
541
542 RTStrFree(pSettings->pszFilterOutDirs);
543 }
544 RTStrFree(pSettings->pszFilterOutFiles);
545 }
546 RTStrFree(pSettings->pszFilterFiles);
547 }
548
549 pSettings->pszFilterFiles = NULL;
550 pSettings->pszFilterOutFiles = NULL;
551 pSettings->pszFilterOutDirs = NULL;
552 pSettings->pszTreatAsName = NULL;
553 return rc;
554}
555
556/**
557 * Init a settings structure.
558 *
559 * @returns IPRT status code
560 * @param pSettings The settings.
561 */
562static int scmSettingsBaseInit(PSCMSETTINGSBASE pSettings)
563{
564 return scmSettingsBaseInitAndCopy(pSettings, &g_Defaults);
565}
566
567/**
568 * Deletes the settings, i.e. free any dynamically allocated content.
569 *
570 * @param pSettings The settings.
571 */
572static void scmSettingsBaseDelete(PSCMSETTINGSBASE pSettings)
573{
574 if (pSettings)
575 {
576 Assert(pSettings->cchTab != UINT8_MAX);
577 pSettings->cchTab = UINT8_MAX;
578
579 RTStrFree(pSettings->pszFilterFiles);
580 pSettings->pszFilterFiles = NULL;
581
582 RTStrFree(pSettings->pszFilterOutFiles);
583 pSettings->pszFilterOutFiles = NULL;
584
585 RTStrFree(pSettings->pszFilterOutDirs);
586 pSettings->pszFilterOutDirs = NULL;
587
588 RTStrFree(pSettings->pszTreatAsName);
589 pSettings->pszTreatAsName = NULL;
590 }
591}
592
593
594/**
595 * Processes a RTGetOpt result.
596 *
597 * @retval VINF_SUCCESS if handled.
598 * @retval VERR_OUT_OF_RANGE if the option value was out of range.
599 * @retval VERR_GETOPT_UNKNOWN_OPTION if the option was not recognized.
600 *
601 * @param pSettings The settings to change.
602 * @param rc The RTGetOpt return value.
603 * @param pValueUnion The RTGetOpt value union.
604 * @param pchDir The absolute path to the directory relative
605 * components in pchLine should be relative to.
606 * @param cchDir The length of the @a pchDir string.
607 */
608static int scmSettingsBaseHandleOpt(PSCMSETTINGSBASE pSettings, int rc, PRTGETOPTUNION pValueUnion,
609 const char *pchDir, size_t cchDir)
610{
611 Assert(pchDir[cchDir - 1] == '/');
612
613 switch (rc)
614 {
615 case SCMOPT_CONVERT_EOL:
616 pSettings->fConvertEol = true;
617 return VINF_SUCCESS;
618 case SCMOPT_NO_CONVERT_EOL:
619 pSettings->fConvertEol = false;
620 return VINF_SUCCESS;
621
622 case SCMOPT_CONVERT_TABS:
623 pSettings->fConvertTabs = true;
624 return VINF_SUCCESS;
625 case SCMOPT_NO_CONVERT_TABS:
626 pSettings->fConvertTabs = false;
627 return VINF_SUCCESS;
628
629 case SCMOPT_FORCE_FINAL_EOL:
630 pSettings->fForceFinalEol = true;
631 return VINF_SUCCESS;
632 case SCMOPT_NO_FORCE_FINAL_EOL:
633 pSettings->fForceFinalEol = false;
634 return VINF_SUCCESS;
635
636 case SCMOPT_FORCE_TRAILING_LINE:
637 pSettings->fForceTrailingLine = true;
638 return VINF_SUCCESS;
639 case SCMOPT_NO_FORCE_TRAILING_LINE:
640 pSettings->fForceTrailingLine = false;
641 return VINF_SUCCESS;
642
643
644 case SCMOPT_STRIP_TRAILING_BLANKS:
645 pSettings->fStripTrailingBlanks = true;
646 return VINF_SUCCESS;
647 case SCMOPT_NO_STRIP_TRAILING_BLANKS:
648 pSettings->fStripTrailingBlanks = false;
649 return VINF_SUCCESS;
650
651 case SCMOPT_MIN_BLANK_LINES_BEFORE_FLOWER_BOX_MARKERS:
652 pSettings->cMinBlankLinesBeforeFlowerBoxMakers = pValueUnion->u8;
653 return VINF_SUCCESS;
654
655
656 case SCMOPT_STRIP_TRAILING_LINES:
657 pSettings->fStripTrailingLines = true;
658 return VINF_SUCCESS;
659 case SCMOPT_NO_STRIP_TRAILING_LINES:
660 pSettings->fStripTrailingLines = false;
661 return VINF_SUCCESS;
662
663 case SCMOPT_FIX_FLOWER_BOX_MARKERS:
664 pSettings->fFixFlowerBoxMarkers = true;
665 return VINF_SUCCESS;
666 case SCMOPT_NO_FIX_FLOWER_BOX_MARKERS:
667 pSettings->fFixFlowerBoxMarkers = false;
668 return VINF_SUCCESS;
669
670 case SCMOPT_UPDATE_COPYRIGHT_YEAR:
671 pSettings->fUpdateCopyrightYear = true;
672 return VINF_SUCCESS;
673 case SCMOPT_NO_UPDATE_COPYRIGHT_YEAR:
674 pSettings->fUpdateCopyrightYear = false;
675 return VINF_SUCCESS;
676
677 case SCMOPT_EXTERNAL_COPYRIGHT:
678 pSettings->fExternalCopyright = true;
679 return VINF_SUCCESS;
680 case SCMOPT_NO_EXTERNAL_COPYRIGHT:
681 pSettings->fExternalCopyright = false;
682 return VINF_SUCCESS;
683
684 case SCMOPT_NO_UPDATE_LICENSE:
685 pSettings->enmUpdateLicense = kScmLicense_LeaveAlone;
686 return VINF_SUCCESS;
687 case SCMOPT_LICENSE_OSE_GPL:
688 pSettings->enmUpdateLicense = kScmLicense_OseGpl;
689 return VINF_SUCCESS;
690 case SCMOPT_LICENSE_OSE_DUAL_GPL_CDDL:
691 pSettings->enmUpdateLicense = kScmLicense_OseDualGplCddl;
692 return VINF_SUCCESS;
693 case SCMOPT_LICENSE_OSE_CDDL:
694 pSettings->enmUpdateLicense = kScmLicense_OseCddl;
695 return VINF_SUCCESS;
696 case SCMOPT_LICENSE_LGPL:
697 pSettings->enmUpdateLicense = kScmLicense_Lgpl;
698 return VINF_SUCCESS;
699 case SCMOPT_LICENSE_MIT:
700 pSettings->enmUpdateLicense = kScmLicense_Mit;
701 return VINF_SUCCESS;
702 case SCMOPT_LICENSE_BASED_ON_MIT:
703 pSettings->enmUpdateLicense = kScmLicense_BasedOnMit;
704 return VINF_SUCCESS;
705
706 case SCMOPT_LGPL_DISCLAIMER:
707 pSettings->fLgplDisclaimer = true;
708 return VINF_SUCCESS;
709 case SCMOPT_NO_LGPL_DISCLAIMER:
710 pSettings->fLgplDisclaimer = false;
711 return VINF_SUCCESS;
712
713 case SCMOPT_ONLY_SVN_DIRS:
714 pSettings->fOnlySvnDirs = true;
715 return VINF_SUCCESS;
716 case SCMOPT_NOT_ONLY_SVN_DIRS:
717 pSettings->fOnlySvnDirs = false;
718 return VINF_SUCCESS;
719
720 case SCMOPT_ONLY_SVN_FILES:
721 pSettings->fOnlySvnFiles = true;
722 return VINF_SUCCESS;
723 case SCMOPT_NOT_ONLY_SVN_FILES:
724 pSettings->fOnlySvnFiles = false;
725 return VINF_SUCCESS;
726
727 case SCMOPT_SET_SVN_EOL:
728 pSettings->fSetSvnEol = true;
729 return VINF_SUCCESS;
730 case SCMOPT_DONT_SET_SVN_EOL:
731 pSettings->fSetSvnEol = false;
732 return VINF_SUCCESS;
733
734 case SCMOPT_SET_SVN_EXECUTABLE:
735 pSettings->fSetSvnExecutable = true;
736 return VINF_SUCCESS;
737 case SCMOPT_DONT_SET_SVN_EXECUTABLE:
738 pSettings->fSetSvnExecutable = false;
739 return VINF_SUCCESS;
740
741 case SCMOPT_SET_SVN_KEYWORDS:
742 pSettings->fSetSvnKeywords = true;
743 return VINF_SUCCESS;
744 case SCMOPT_DONT_SET_SVN_KEYWORDS:
745 pSettings->fSetSvnKeywords = false;
746 return VINF_SUCCESS;
747
748 case SCMOPT_TAB_SIZE:
749 if ( pValueUnion->u8 < 1
750 || pValueUnion->u8 >= RT_ELEMENTS(g_szTabSpaces))
751 {
752 RTMsgError("Invalid tab size: %u - must be in {1..%u}\n",
753 pValueUnion->u8, RT_ELEMENTS(g_szTabSpaces) - 1);
754 return VERR_OUT_OF_RANGE;
755 }
756 pSettings->cchTab = pValueUnion->u8;
757 return VINF_SUCCESS;
758
759 case SCMOPT_WIDTH:
760 if (pValueUnion->u8 < 20 || pValueUnion->u8 > 200)
761 {
762 RTMsgError("Invalid width size: %u - must be in {20..200} range\n", pValueUnion->u8);
763 return VERR_OUT_OF_RANGE;
764 }
765 pSettings->cchWidth = pValueUnion->u8;
766 return VINF_SUCCESS;
767
768 case SCMOPT_TREAT_AS:
769 if (pSettings->pszTreatAsName)
770 {
771 RTStrFree(pSettings->pszTreatAsName);
772 pSettings->pszTreatAsName = NULL;
773 }
774 if (*pValueUnion->psz)
775 {
776 pSettings->pszTreatAsName = RTStrDup(pValueUnion->psz);
777 if (!pSettings->pszTreatAsName)
778 return VERR_NO_MEMORY;
779 }
780 return VINF_SUCCESS;
781
782 case SCMOPT_FILTER_OUT_DIRS:
783 case SCMOPT_FILTER_FILES:
784 case SCMOPT_FILTER_OUT_FILES:
785 {
786 char **ppsz = NULL;
787 switch (rc)
788 {
789 case SCMOPT_FILTER_OUT_DIRS: ppsz = &pSettings->pszFilterOutDirs; break;
790 case SCMOPT_FILTER_FILES: ppsz = &pSettings->pszFilterFiles; break;
791 case SCMOPT_FILTER_OUT_FILES: ppsz = &pSettings->pszFilterOutFiles; break;
792 }
793
794 /*
795 * An empty string zaps the current list.
796 */
797 if (!*pValueUnion->psz)
798 return RTStrATruncate(ppsz, 0);
799
800 /*
801 * Non-empty strings are appended to the pattern list.
802 *
803 * Strip leading and trailing pattern separators before attempting
804 * to append it. If it's just separators, don't do anything.
805 */
806 const char *pszSrc = pValueUnion->psz;
807 while (*pszSrc == '|')
808 pszSrc++;
809 size_t cchSrc = strlen(pszSrc);
810 while (cchSrc > 0 && pszSrc[cchSrc - 1] == '|')
811 cchSrc--;
812 if (!cchSrc)
813 return VINF_SUCCESS;
814
815 /* Append it pattern by pattern, turning settings-relative paths into absolute ones. */
816 for (;;)
817 {
818 const char *pszEnd = (const char *)memchr(pszSrc, '|', cchSrc);
819 size_t cchPattern = pszEnd ? pszEnd - pszSrc : cchSrc;
820 int rc2;
821 if (*pszSrc == '/')
822 rc2 = RTStrAAppendExN(ppsz, 3,
823 "|", *ppsz && **ppsz != '\0' ? (size_t)1 : (size_t)0,
824 pchDir, cchDir - 1,
825 pszSrc, cchPattern);
826 else
827 rc2 = RTStrAAppendExN(ppsz, 2,
828 "|", *ppsz && **ppsz != '\0' ? (size_t)1 : (size_t)0,
829 pszSrc, cchPattern);
830 if (RT_FAILURE(rc2))
831 return rc2;
832
833 /* next */
834 cchSrc -= cchPattern;
835 if (!cchSrc)
836 return VINF_SUCCESS;
837 cchSrc -= 1;
838 pszSrc += cchPattern + 1;
839 }
840 /* not reached */
841 }
842
843 default:
844 return VERR_GETOPT_UNKNOWN_OPTION;
845 }
846}
847
848/**
849 * Parses an option string.
850 *
851 * @returns IPRT status code.
852 * @param pBase The base settings structure to apply the options
853 * to.
854 * @param pszOptions The options to parse.
855 * @param pchDir The absolute path to the directory relative
856 * components in pchLine should be relative to.
857 * @param cchDir The length of the @a pchDir string.
858 */
859static int scmSettingsBaseParseString(PSCMSETTINGSBASE pBase, const char *pszLine, const char *pchDir, size_t cchDir)
860{
861 int cArgs;
862 char **papszArgs;
863 int rc = RTGetOptArgvFromString(&papszArgs, &cArgs, pszLine, RTGETOPTARGV_CNV_QUOTE_BOURNE_SH, NULL);
864 if (RT_SUCCESS(rc))
865 {
866 RTGETOPTUNION ValueUnion;
867 RTGETOPTSTATE GetOptState;
868 rc = RTGetOptInit(&GetOptState, cArgs, papszArgs, &g_aScmOpts[0], RT_ELEMENTS(g_aScmOpts), 0, 0 /*fFlags*/);
869 if (RT_SUCCESS(rc))
870 {
871 while ((rc = RTGetOpt(&GetOptState, &ValueUnion)) != 0)
872 {
873 rc = scmSettingsBaseHandleOpt(pBase, rc, &ValueUnion, pchDir, cchDir);
874 if (RT_FAILURE(rc))
875 break;
876 }
877 }
878 RTGetOptArgvFree(papszArgs);
879 }
880
881 return rc;
882}
883
884/**
885 * Parses an unterminated option string.
886 *
887 * @returns IPRT status code.
888 * @param pBase The base settings structure to apply the options
889 * to.
890 * @param pchLine The line.
891 * @param cchLine The line length.
892 * @param pchDir The absolute path to the directory relative
893 * components in pchLine should be relative to.
894 * @param cchDir The length of the @a pchDir string.
895 */
896static int scmSettingsBaseParseStringN(PSCMSETTINGSBASE pBase, const char *pchLine, size_t cchLine,
897 const char *pchDir, size_t cchDir)
898{
899 char *pszLine = RTStrDupN(pchLine, cchLine);
900 if (!pszLine)
901 return VERR_NO_MEMORY;
902 int rc = scmSettingsBaseParseString(pBase, pszLine, pchDir, cchDir);
903 RTStrFree(pszLine);
904 return rc;
905}
906
907/**
908 * Verifies the options string.
909 *
910 * @returns IPRT status code.
911 * @param pszOptions The options to verify .
912 */
913static int scmSettingsBaseVerifyString(const char *pszOptions)
914{
915 SCMSETTINGSBASE Base;
916 int rc = scmSettingsBaseInit(&Base);
917 if (RT_SUCCESS(rc))
918 {
919 rc = scmSettingsBaseParseString(&Base, pszOptions, "/", 1);
920 scmSettingsBaseDelete(&Base);
921 }
922 return rc;
923}
924
925/**
926 * Loads settings found in editor and SCM settings directives within the
927 * document (@a pStream).
928 *
929 * @returns IPRT status code.
930 * @param pBase The settings base to load settings into.
931 * @param pStream The stream to scan for settings directives.
932 */
933static int scmSettingsBaseLoadFromDocument(PSCMSETTINGSBASE pBase, PSCMSTREAM pStream)
934{
935 /** @todo Editor and SCM settings directives in documents. */
936 RT_NOREF2(pBase, pStream);
937 return VINF_SUCCESS;
938}
939
940/**
941 * Creates a new settings file struct, cloning @a pSettings.
942 *
943 * @returns IPRT status code.
944 * @param ppSettings Where to return the new struct.
945 * @param pSettingsBase The settings to inherit from.
946 */
947static int scmSettingsCreate(PSCMSETTINGS *ppSettings, PCSCMSETTINGSBASE pSettingsBase)
948{
949 PSCMSETTINGS pSettings = (PSCMSETTINGS)RTMemAlloc(sizeof(*pSettings));
950 if (!pSettings)
951 return VERR_NO_MEMORY;
952 int rc = scmSettingsBaseInitAndCopy(&pSettings->Base, pSettingsBase);
953 if (RT_SUCCESS(rc))
954 {
955 pSettings->pDown = NULL;
956 pSettings->pUp = NULL;
957 pSettings->paPairs = NULL;
958 pSettings->cPairs = 0;
959 *ppSettings = pSettings;
960 return VINF_SUCCESS;
961 }
962 RTMemFree(pSettings);
963 return rc;
964}
965
966/**
967 * Destroys a settings structure.
968 *
969 * @param pSettings The settings structure to destroy. NULL is OK.
970 */
971static void scmSettingsDestroy(PSCMSETTINGS pSettings)
972{
973 if (pSettings)
974 {
975 scmSettingsBaseDelete(&pSettings->Base);
976 for (size_t i = 0; i < pSettings->cPairs; i++)
977 {
978 RTStrFree(pSettings->paPairs[i].pszPattern);
979 RTStrFree(pSettings->paPairs[i].pszOptions);
980 RTStrFree(pSettings->paPairs[i].pszRelativeTo);
981 pSettings->paPairs[i].pszPattern = NULL;
982 pSettings->paPairs[i].pszOptions = NULL;
983 pSettings->paPairs[i].pszRelativeTo = NULL;
984 }
985 RTMemFree(pSettings->paPairs);
986 pSettings->paPairs = NULL;
987 RTMemFree(pSettings);
988 }
989}
990
991/**
992 * Adds a pattern/options pair to the settings structure.
993 *
994 * @returns IPRT status code.
995 * @param pSettings The settings.
996 * @param pchLine The line containing the unparsed pair.
997 * @param cchLine The length of the line.
998 * @param offColon The offset of the colon into the line.
999 * @param pchDir The absolute path to the directory relative
1000 * components in pchLine should be relative to.
1001 * @param cchDir The length of the @a pchDir string.
1002 */
1003static int scmSettingsAddPair(PSCMSETTINGS pSettings, const char *pchLine, size_t cchLine, size_t offColon,
1004 const char *pchDir, size_t cchDir)
1005{
1006 Assert(pchLine[offColon] == ':' && offColon < cchLine);
1007 Assert(pchDir[cchDir - 1] == '/');
1008
1009 /*
1010 * Split the string.
1011 */
1012 size_t cchPattern = offColon;
1013 size_t cchOptions = cchLine - cchPattern - 1;
1014
1015 /* strip spaces everywhere */
1016 while (cchPattern > 0 && RT_C_IS_SPACE(pchLine[cchPattern - 1]))
1017 cchPattern--;
1018 while (cchPattern > 0 && RT_C_IS_SPACE(*pchLine))
1019 cchPattern--, pchLine++;
1020
1021 const char *pchOptions = &pchLine[offColon + 1];
1022 while (cchOptions > 0 && RT_C_IS_SPACE(pchOptions[cchOptions - 1]))
1023 cchOptions--;
1024 while (cchOptions > 0 && RT_C_IS_SPACE(*pchOptions))
1025 cchOptions--, pchOptions++;
1026
1027 /* Quietly ignore empty patterns and empty options. */
1028 if (!cchOptions || !cchPattern)
1029 return VINF_SUCCESS;
1030
1031 /*
1032 * Prepair the pair and verify the option string.
1033 */
1034 uint32_t iPair = pSettings->cPairs;
1035 if ((iPair % 32) == 0)
1036 {
1037 void *pvNew = RTMemRealloc(pSettings->paPairs, (iPair + 32) * sizeof(pSettings->paPairs[0]));
1038 if (!pvNew)
1039 return VERR_NO_MEMORY;
1040 pSettings->paPairs = (PSCMPATRNOPTPAIR)pvNew;
1041 }
1042
1043 pSettings->paPairs[iPair].pszPattern = RTStrDupN(pchLine, cchPattern);
1044 pSettings->paPairs[iPair].pszOptions = RTStrDupN(pchOptions, cchOptions);
1045 pSettings->paPairs[iPair].pszRelativeTo = RTStrDupN(pchDir, cchDir);
1046 int rc;
1047 if ( pSettings->paPairs[iPair].pszPattern
1048 && pSettings->paPairs[iPair].pszOptions
1049 && pSettings->paPairs[iPair].pszRelativeTo)
1050 rc = scmSettingsBaseVerifyString(pSettings->paPairs[iPair].pszOptions);
1051 else
1052 rc = VERR_NO_MEMORY;
1053
1054 /*
1055 * If it checked out fine, expand any relative paths in the pattern.
1056 */
1057 if (RT_SUCCESS(rc))
1058 {
1059 size_t cRelativePaths = 0;
1060 const char *pszSrc = pSettings->paPairs[iPair].pszPattern;
1061 for (;;)
1062 {
1063 if (*pszSrc == '/')
1064 cRelativePaths++;
1065 pszSrc = strchr(pszSrc, '|');
1066 if (!pszSrc)
1067 break;
1068 pszSrc++;
1069 }
1070 if (cRelativePaths > 0)
1071 {
1072 char *pszNewPattern = RTStrAlloc(cchPattern + cRelativePaths * (cchDir - 1) + 1);
1073 if (pszNewPattern)
1074 {
1075 char *pszDst = pszNewPattern;
1076 pszSrc = pSettings->paPairs[iPair].pszPattern;
1077 for (;;)
1078 {
1079 if (*pszSrc == '/')
1080 {
1081 memcpy(pszDst, pchDir, cchDir);
1082 pszDst += cchDir;
1083 pszSrc += 1;
1084 }
1085
1086 /* Look for the next relative path. */
1087 const char *pszSrcNext = strchr(pszSrc, '|');
1088 while (pszSrcNext && pszSrcNext[1] != '/')
1089 pszSrcNext = strchr(pszSrcNext, '|');
1090 if (!pszSrcNext)
1091 break;
1092
1093 /* Copy stuff between current and the next path. */
1094 pszSrcNext++;
1095 memcpy(pszDst, pszSrc, pszSrcNext - pszSrc);
1096 pszDst += pszSrcNext - pszSrc;
1097 pszSrc = pszSrcNext;
1098 }
1099
1100 /* Copy the final portion and replace the pattern. */
1101 strcpy(pszDst, pszSrc);
1102
1103 RTStrFree(pSettings->paPairs[iPair].pszPattern);
1104 pSettings->paPairs[iPair].pszPattern = pszNewPattern;
1105 }
1106 else
1107 rc = VERR_NO_MEMORY;
1108 }
1109 }
1110 if (RT_SUCCESS(rc))
1111 /*
1112 * Commit the pair.
1113 */
1114 pSettings->cPairs = iPair + 1;
1115 else
1116 {
1117 RTStrFree(pSettings->paPairs[iPair].pszPattern);
1118 RTStrFree(pSettings->paPairs[iPair].pszOptions);
1119 RTStrFree(pSettings->paPairs[iPair].pszRelativeTo);
1120 }
1121 return rc;
1122}
1123
1124/**
1125 * Loads in the settings from @a pszFilename.
1126 *
1127 * @returns IPRT status code.
1128 * @param pSettings Where to load the settings file.
1129 * @param pszFilename The file to load.
1130 */
1131static int scmSettingsLoadFile(PSCMSETTINGS pSettings, const char *pszFilename)
1132{
1133 ScmVerbose(NULL, 3, "Loading settings file '%s'...\n", pszFilename);
1134
1135 /* Turn filename into an absolute path and drop the filename. */
1136 char szAbsPath[RTPATH_MAX];
1137 int rc = RTPathAbs(pszFilename, szAbsPath, sizeof(szAbsPath));
1138 if (RT_FAILURE(rc))
1139 {
1140 RTMsgError("%s: RTPathAbs -> %Rrc\n", pszFilename, rc);
1141 return rc;
1142 }
1143 RTPathChangeToUnixSlashes(szAbsPath, true);
1144 size_t cchDir = RTPathFilename(szAbsPath) - &szAbsPath[0];
1145
1146 /* Try open it.*/
1147 SCMSTREAM Stream;
1148 rc = ScmStreamInitForReading(&Stream, pszFilename);
1149 if (RT_SUCCESS(rc))
1150 {
1151 SCMEOL enmEol;
1152 const char *pchLine;
1153 size_t cchLine;
1154 while ((pchLine = ScmStreamGetLine(&Stream, &cchLine, &enmEol)) != NULL)
1155 {
1156 /* Ignore leading spaces. */
1157 while (cchLine > 0 && RT_C_IS_SPACE(*pchLine))
1158 pchLine++, cchLine--;
1159
1160 /* Ignore empty lines and comment lines. */
1161 if (cchLine < 1 || *pchLine == '#')
1162 continue;
1163
1164 /* What kind of line is it? */
1165 const char *pchColon = (const char *)memchr(pchLine, ':', cchLine);
1166 if (pchColon)
1167 rc = scmSettingsAddPair(pSettings, pchLine, cchLine, pchColon - pchLine, szAbsPath, cchDir);
1168 else
1169 rc = scmSettingsBaseParseStringN(&pSettings->Base, pchLine, cchLine, szAbsPath, cchDir);
1170 if (RT_FAILURE(rc))
1171 {
1172 RTMsgError("%s:%d: %Rrc\n", pszFilename, ScmStreamTellLine(&Stream), rc);
1173 break;
1174 }
1175 }
1176
1177 if (RT_SUCCESS(rc))
1178 {
1179 rc = ScmStreamGetStatus(&Stream);
1180 if (RT_FAILURE(rc))
1181 RTMsgError("%s: ScmStreamGetStatus- > %Rrc\n", pszFilename, rc);
1182 }
1183 ScmStreamDelete(&Stream);
1184 }
1185 else
1186 RTMsgError("%s: ScmStreamInitForReading -> %Rrc\n", pszFilename, rc);
1187 return rc;
1188}
1189
1190#if 0 /* unused */
1191/**
1192 * Parse the specified settings file creating a new settings struct from it.
1193 *
1194 * @returns IPRT status code
1195 * @param ppSettings Where to return the new settings.
1196 * @param pszFilename The file to parse.
1197 * @param pSettingsBase The base settings we inherit from.
1198 */
1199static int scmSettingsCreateFromFile(PSCMSETTINGS *ppSettings, const char *pszFilename, PCSCMSETTINGSBASE pSettingsBase)
1200{
1201 PSCMSETTINGS pSettings;
1202 int rc = scmSettingsCreate(&pSettings, pSettingsBase);
1203 if (RT_SUCCESS(rc))
1204 {
1205 rc = scmSettingsLoadFile(pSettings, pszFilename, RTPathFilename(pszFilename) - pszFilename);
1206 if (RT_SUCCESS(rc))
1207 {
1208 *ppSettings = pSettings;
1209 return VINF_SUCCESS;
1210 }
1211
1212 scmSettingsDestroy(pSettings);
1213 }
1214 *ppSettings = NULL;
1215 return rc;
1216}
1217#endif
1218
1219
1220/**
1221 * Create an initial settings structure when starting processing a new file or
1222 * directory.
1223 *
1224 * This will look for .scm-settings files from the root and down to the
1225 * specified directory, combining them into the returned settings structure.
1226 *
1227 * @returns IPRT status code.
1228 * @param ppSettings Where to return the pointer to the top stack
1229 * object.
1230 * @param pBaseSettings The base settings we inherit from (globals
1231 * typically).
1232 * @param pszPath The absolute path to the new directory or file.
1233 */
1234static int scmSettingsCreateForPath(PSCMSETTINGS *ppSettings, PCSCMSETTINGSBASE pBaseSettings, const char *pszPath)
1235{
1236 *ppSettings = NULL; /* try shut up gcc. */
1237
1238 /*
1239 * We'll be working with a stack copy of the path.
1240 */
1241 char szFile[RTPATH_MAX];
1242 size_t cchDir = strlen(pszPath);
1243 if (cchDir >= sizeof(szFile) - sizeof(SCM_SETTINGS_FILENAME))
1244 return VERR_FILENAME_TOO_LONG;
1245
1246 /*
1247 * Create the bottom-most settings.
1248 */
1249 PSCMSETTINGS pSettings;
1250 int rc = scmSettingsCreate(&pSettings, pBaseSettings);
1251 if (RT_FAILURE(rc))
1252 return rc;
1253
1254 /*
1255 * Enumerate the path components from the root and down. Load any setting
1256 * files we find.
1257 */
1258 size_t cComponents = RTPathCountComponents(pszPath);
1259 for (size_t i = 1; i <= cComponents; i++)
1260 {
1261 rc = RTPathCopyComponents(szFile, sizeof(szFile), pszPath, i);
1262 if (RT_SUCCESS(rc))
1263 rc = RTPathAppend(szFile, sizeof(szFile), SCM_SETTINGS_FILENAME);
1264 if (RT_FAILURE(rc))
1265 break;
1266 RTPathChangeToUnixSlashes(szFile, true);
1267
1268 if (RTFileExists(szFile))
1269 {
1270 rc = scmSettingsLoadFile(pSettings, szFile);
1271 if (RT_FAILURE(rc))
1272 break;
1273 }
1274 }
1275
1276 if (RT_SUCCESS(rc))
1277 *ppSettings = pSettings;
1278 else
1279 scmSettingsDestroy(pSettings);
1280 return rc;
1281}
1282
1283/**
1284 * Pushes a new settings set onto the stack.
1285 *
1286 * @param ppSettingsStack The pointer to the pointer to the top stack
1287 * element. This will be used as input and output.
1288 * @param pSettings The settings to push onto the stack.
1289 */
1290static void scmSettingsStackPush(PSCMSETTINGS *ppSettingsStack, PSCMSETTINGS pSettings)
1291{
1292 PSCMSETTINGS pOld = *ppSettingsStack;
1293 pSettings->pDown = pOld;
1294 pSettings->pUp = NULL;
1295 if (pOld)
1296 pOld->pUp = pSettings;
1297 *ppSettingsStack = pSettings;
1298}
1299
1300/**
1301 * Pushes the settings of the specified directory onto the stack.
1302 *
1303 * We will load any .scm-settings in the directory. A stack entry is added even
1304 * if no settings file was found.
1305 *
1306 * @returns IPRT status code.
1307 * @param ppSettingsStack The pointer to the pointer to the top stack
1308 * element. This will be used as input and output.
1309 * @param pszDir The directory to do this for.
1310 */
1311static int scmSettingsStackPushDir(PSCMSETTINGS *ppSettingsStack, const char *pszDir)
1312{
1313 char szFile[RTPATH_MAX];
1314 int rc = RTPathJoin(szFile, sizeof(szFile), pszDir, SCM_SETTINGS_FILENAME);
1315 if (RT_SUCCESS(rc))
1316 {
1317 RTPathChangeToUnixSlashes(szFile, true);
1318
1319 PSCMSETTINGS pSettings;
1320 rc = scmSettingsCreate(&pSettings, &(*ppSettingsStack)->Base);
1321 if (RT_SUCCESS(rc))
1322 {
1323 if (RTFileExists(szFile))
1324 rc = scmSettingsLoadFile(pSettings, szFile);
1325 if (RT_SUCCESS(rc))
1326 {
1327 scmSettingsStackPush(ppSettingsStack, pSettings);
1328 return VINF_SUCCESS;
1329 }
1330
1331 scmSettingsDestroy(pSettings);
1332 }
1333 }
1334 return rc;
1335}
1336
1337
1338/**
1339 * Pops a settings set off the stack.
1340 *
1341 * @returns The popped setttings.
1342 * @param ppSettingsStack The pointer to the pointer to the top stack
1343 * element. This will be used as input and output.
1344 */
1345static PSCMSETTINGS scmSettingsStackPop(PSCMSETTINGS *ppSettingsStack)
1346{
1347 PSCMSETTINGS pRet = *ppSettingsStack;
1348 PSCMSETTINGS pNew = pRet ? pRet->pDown : NULL;
1349 *ppSettingsStack = pNew;
1350 if (pNew)
1351 pNew->pUp = NULL;
1352 if (pRet)
1353 {
1354 pRet->pUp = NULL;
1355 pRet->pDown = NULL;
1356 }
1357 return pRet;
1358}
1359
1360/**
1361 * Pops and destroys the top entry of the stack.
1362 *
1363 * @param ppSettingsStack The pointer to the pointer to the top stack
1364 * element. This will be used as input and output.
1365 */
1366static void scmSettingsStackPopAndDestroy(PSCMSETTINGS *ppSettingsStack)
1367{
1368 scmSettingsDestroy(scmSettingsStackPop(ppSettingsStack));
1369}
1370
1371/**
1372 * Constructs the base settings for the specified file name.
1373 *
1374 * @returns IPRT status code.
1375 * @param pSettingsStack The top element on the settings stack.
1376 * @param pszFilename The file name.
1377 * @param pszBasename The base name (pointer within @a pszFilename).
1378 * @param cchBasename The length of the base name. (For passing to
1379 * RTStrSimplePatternMultiMatch.)
1380 * @param pBase Base settings to initialize.
1381 */
1382static int scmSettingsStackMakeFileBase(PCSCMSETTINGS pSettingsStack, const char *pszFilename,
1383 const char *pszBasename, size_t cchBasename, PSCMSETTINGSBASE pBase)
1384{
1385 ScmVerbose(NULL, 5, "scmSettingsStackMakeFileBase(%s, %.*s)\n", pszFilename, cchBasename, pszBasename);
1386
1387 int rc = scmSettingsBaseInitAndCopy(pBase, &pSettingsStack->Base);
1388 if (RT_SUCCESS(rc))
1389 {
1390 /* find the bottom entry in the stack. */
1391 PCSCMSETTINGS pCur = pSettingsStack;
1392 while (pCur->pDown)
1393 pCur = pCur->pDown;
1394
1395 /* Work our way up thru the stack and look for matching pairs. */
1396 while (pCur)
1397 {
1398 size_t const cPairs = pCur->cPairs;
1399 if (cPairs)
1400 {
1401 for (size_t i = 0; i < cPairs; i++)
1402 if ( RTStrSimplePatternMultiMatch(pCur->paPairs[i].pszPattern, RTSTR_MAX,
1403 pszBasename, cchBasename, NULL)
1404 || RTStrSimplePatternMultiMatch(pCur->paPairs[i].pszPattern, RTSTR_MAX,
1405 pszFilename, RTSTR_MAX, NULL))
1406 {
1407 ScmVerbose(NULL, 5, "scmSettingsStackMakeFileBase: Matched '%s' : '%s'\n",
1408 pCur->paPairs[i].pszPattern, pCur->paPairs[i].pszOptions);
1409 rc = scmSettingsBaseParseString(pBase, pCur->paPairs[i].pszOptions,
1410 pCur->paPairs[i].pszRelativeTo, strlen(pCur->paPairs[i].pszRelativeTo));
1411 if (RT_FAILURE(rc))
1412 break;
1413 }
1414 if (RT_FAILURE(rc))
1415 break;
1416 }
1417
1418 /* advance */
1419 pCur = pCur->pUp;
1420 }
1421 }
1422 if (RT_FAILURE(rc))
1423 scmSettingsBaseDelete(pBase);
1424 return rc;
1425}
1426
1427
1428/* -=-=-=-=-=- misc -=-=-=-=-=- */
1429
1430
1431/**
1432 * Prints the per file banner needed and the message level is high enough.
1433 *
1434 * @param pState The rewrite state.
1435 * @param iLevel The required verbosity level.
1436 */
1437void ScmVerboseBanner(PSCMRWSTATE pState, int iLevel)
1438{
1439 if (iLevel <= g_iVerbosity && !pState->fFirst)
1440 {
1441 RTPrintf("%s: info: --= Rewriting '%s' =--\n", g_szProgName, pState->pszFilename);
1442 pState->fFirst = true;
1443 }
1444}
1445
1446
1447/**
1448 * Prints a verbose message if the level is high enough.
1449 *
1450 * @param pState The rewrite state. Optional.
1451 * @param iLevel The required verbosity level.
1452 * @param pszFormat The message format string. Can be NULL if we
1453 * only want to trigger the per file message.
1454 * @param ... Format arguments.
1455 */
1456void ScmVerbose(PSCMRWSTATE pState, int iLevel, const char *pszFormat, ...)
1457{
1458 if (iLevel <= g_iVerbosity)
1459 {
1460 if (pState && !pState->fFirst)
1461 {
1462 RTPrintf("%s: info: --= Rewriting '%s' =--\n", g_szProgName, pState->pszFilename);
1463 pState->fFirst = true;
1464 }
1465 RTPrintf(pState
1466 ? "%s: info: "
1467 : "%s: info: ",
1468 g_szProgName);
1469 va_list va;
1470 va_start(va, pszFormat);
1471 RTPrintfV(pszFormat, va);
1472 va_end(va);
1473 }
1474}
1475
1476
1477/**
1478 * Prints an error message.
1479 *
1480 * @returns false
1481 * @param pState The rewrite state. Optional.
1482 * @param rc The error code.
1483 * @param pszFormat The message format string.
1484 * @param ... Format arguments.
1485 */
1486bool ScmError(PSCMRWSTATE pState, int rc, const char *pszFormat, ...)
1487{
1488 if (RT_SUCCESS(pState->rc))
1489 pState->rc = rc;
1490
1491 if (!pState->fFirst)
1492 {
1493 RTPrintf("%s: info: --= Rewriting '%s' =--\n", g_szProgName, pState->pszFilename);
1494 pState->fFirst = true;
1495 }
1496 va_list va;
1497 va_start(va, pszFormat);
1498 RTPrintf("%s: error: %s: %N", g_szProgName, pState->pszFilename, pszFormat, &va);
1499 va_end(va);
1500
1501 return false;
1502}
1503
1504
1505/* -=-=-=-=-=- file and directory processing -=-=-=-=-=- */
1506
1507
1508/**
1509 * Processes a file.
1510 *
1511 * @returns IPRT status code.
1512 * @param pState The rewriter state.
1513 * @param pszFilename The file name.
1514 * @param pszBasename The base name (pointer within @a pszFilename).
1515 * @param cchBasename The length of the base name. (For passing to
1516 * RTStrSimplePatternMultiMatch.)
1517 * @param pBaseSettings The base settings to use. It's OK to modify
1518 * these.
1519 */
1520static int scmProcessFileInner(PSCMRWSTATE pState, const char *pszFilename, const char *pszBasename, size_t cchBasename,
1521 PSCMSETTINGSBASE pBaseSettings)
1522{
1523 /*
1524 * Do the file level filtering.
1525 */
1526 if ( pBaseSettings->pszFilterFiles
1527 && *pBaseSettings->pszFilterFiles
1528 && !RTStrSimplePatternMultiMatch(pBaseSettings->pszFilterFiles, RTSTR_MAX, pszBasename, cchBasename, NULL))
1529 {
1530 ScmVerbose(NULL, 5, "skipping '%s': file filter mismatch\n", pszFilename);
1531 g_cFilesSkipped++;
1532 return VINF_SUCCESS;
1533 }
1534 if ( pBaseSettings->pszFilterOutFiles
1535 && *pBaseSettings->pszFilterOutFiles
1536 && ( RTStrSimplePatternMultiMatch(pBaseSettings->pszFilterOutFiles, RTSTR_MAX, pszBasename, cchBasename, NULL)
1537 || RTStrSimplePatternMultiMatch(pBaseSettings->pszFilterOutFiles, RTSTR_MAX, pszFilename, RTSTR_MAX, NULL)) )
1538 {
1539 ScmVerbose(NULL, 5, "skipping '%s': filterd out\n", pszFilename);
1540 g_cFilesSkipped++;
1541 return VINF_SUCCESS;
1542 }
1543 if ( pBaseSettings->fOnlySvnFiles
1544 && !ScmSvnIsInWorkingCopy(pState))
1545 {
1546 ScmVerbose(NULL, 5, "skipping '%s': not in SVN WC\n", pszFilename);
1547 g_cFilesNotInSvn++;
1548 return VINF_SUCCESS;
1549 }
1550
1551 /*
1552 * Try find a matching rewrite config for this filename.
1553 */
1554 PCSCMCFGENTRY pCfg = NULL;
1555 if (!pBaseSettings->pszTreatAsName)
1556 {
1557 for (size_t iCfg = 0; iCfg < RT_ELEMENTS(g_aConfigs); iCfg++)
1558 if (RTStrSimplePatternMultiMatch(g_aConfigs[iCfg].pszFilePattern, RTSTR_MAX, pszBasename, cchBasename, NULL))
1559 {
1560 pCfg = &g_aConfigs[iCfg];
1561 break;
1562 }
1563 }
1564 else
1565 {
1566 size_t cchTreatAsName = strlen(pBaseSettings->pszTreatAsName);
1567 for (size_t iCfg = 0; iCfg < RT_ELEMENTS(g_aConfigs); iCfg++)
1568 if (RTStrSimplePatternMultiMatch(g_aConfigs[iCfg].pszFilePattern, RTSTR_MAX,
1569 pBaseSettings->pszTreatAsName, cchTreatAsName, NULL))
1570 {
1571 pCfg = &g_aConfigs[iCfg];
1572 break;
1573 }
1574 }
1575 if (!pCfg)
1576 {
1577 ScmVerbose(NULL, 2, "skipping '%s': no rewriters configured\n", pszFilename);
1578 g_cFilesNoRewriters++;
1579 return VINF_SUCCESS;
1580 }
1581 ScmVerbose(pState, 4, "matched \"%s\"\n", pCfg->pszFilePattern);
1582
1583 /*
1584 * Create an input stream from the file and check that it's text.
1585 */
1586 SCMSTREAM Stream1;
1587 int rc = ScmStreamInitForReading(&Stream1, pszFilename);
1588 if (RT_FAILURE(rc))
1589 {
1590 RTMsgError("Failed to read '%s': %Rrc\n", pszFilename, rc);
1591 return rc;
1592 }
1593 if (ScmStreamIsText(&Stream1) || pCfg->fBinary)
1594 {
1595 ScmVerboseBanner(pState, 3);
1596
1597 /*
1598 * Gather SCM and editor settings from the stream.
1599 */
1600 rc = scmSettingsBaseLoadFromDocument(pBaseSettings, &Stream1);
1601 if (RT_SUCCESS(rc))
1602 {
1603 ScmStreamRewindForReading(&Stream1);
1604
1605 /*
1606 * Create two more streams for output and push the text thru all the
1607 * rewriters, switching the two streams around when something is
1608 * actually rewritten. Stream1 remains unchanged.
1609 */
1610 SCMSTREAM Stream2;
1611 rc = ScmStreamInitForWriting(&Stream2, &Stream1);
1612 if (RT_SUCCESS(rc))
1613 {
1614 SCMSTREAM Stream3;
1615 rc = ScmStreamInitForWriting(&Stream3, &Stream1);
1616 if (RT_SUCCESS(rc))
1617 {
1618 bool fModified = false;
1619 PSCMSTREAM pIn = &Stream1;
1620 PSCMSTREAM pOut = &Stream2;
1621 for (size_t iRw = 0; iRw < pCfg->cRewriters; iRw++)
1622 {
1623 pState->rc = VINF_SUCCESS;
1624 bool fRc = pCfg->papfnRewriter[iRw](pState, pIn, pOut, pBaseSettings);
1625 if (RT_FAILURE(pState->rc))
1626 break;
1627 if (fRc)
1628 {
1629 PSCMSTREAM pTmp = pOut;
1630 pOut = pIn == &Stream1 ? &Stream3 : pIn;
1631 pIn = pTmp;
1632 fModified = true;
1633 }
1634
1635 ScmStreamRewindForReading(pIn);
1636 ScmStreamRewindForWriting(pOut);
1637 }
1638
1639 rc = pState->rc;
1640 if (RT_SUCCESS(rc))
1641 {
1642 rc = ScmStreamGetStatus(&Stream1);
1643 if (RT_SUCCESS(rc))
1644 rc = ScmStreamGetStatus(&Stream2);
1645 if (RT_SUCCESS(rc))
1646 rc = ScmStreamGetStatus(&Stream3);
1647 if (RT_SUCCESS(rc))
1648 {
1649 /*
1650 * If rewritten, write it back to disk.
1651 */
1652 if (fModified && !pCfg->fBinary)
1653 {
1654 if (!g_fDryRun)
1655 {
1656 ScmVerbose(pState, 1, "writing modified file to \"%s%s\"\n", pszFilename, g_pszChangedSuff);
1657 rc = ScmStreamWriteToFile(pIn, "%s%s", pszFilename, g_pszChangedSuff);
1658 if (RT_FAILURE(rc))
1659 RTMsgError("Error writing '%s%s': %Rrc\n", pszFilename, g_pszChangedSuff, rc);
1660 }
1661 else
1662 {
1663 ScmVerboseBanner(pState, 1);
1664 ScmDiffStreams(pszFilename, &Stream1, pIn, g_fDiffIgnoreEol,
1665 g_fDiffIgnoreLeadingWS, g_fDiffIgnoreTrailingWS, g_fDiffSpecialChars,
1666 pBaseSettings->cchTab, g_pStdOut);
1667 ScmVerbose(pState, 2, "would have modified the file \"%s%s\"\n",
1668 pszFilename, g_pszChangedSuff);
1669 }
1670 g_cFilesModified++;
1671 }
1672 else if (fModified)
1673 rc = RTMsgErrorRc(VERR_INTERNAL_ERROR, "Rewriters modified binary file! Impossible!");
1674
1675 /*
1676 * If pending SVN property changes, apply them.
1677 */
1678 if (pState->cSvnPropChanges && RT_SUCCESS(rc))
1679 {
1680 if (!g_fDryRun)
1681 {
1682 rc = ScmSvnApplyChanges(pState);
1683 if (RT_FAILURE(rc))
1684 RTMsgError("%s: failed to apply SVN property changes (%Rrc)\n", pszFilename, rc);
1685 }
1686 else
1687 ScmSvnDisplayChanges(pState);
1688 if (!fModified)
1689 g_cFilesModified++;
1690 }
1691
1692 if (!fModified && !pState->cSvnPropChanges)
1693 ScmVerbose(pState, 3, "%s: no change\n", pszFilename);
1694 }
1695 else
1696 RTMsgError("%s: stream error %Rrc\n", pszFilename, rc);
1697 }
1698 ScmStreamDelete(&Stream3);
1699 }
1700 else
1701 RTMsgError("Failed to init stream for writing: %Rrc\n", rc);
1702 ScmStreamDelete(&Stream2);
1703 }
1704 else
1705 RTMsgError("Failed to init stream for writing: %Rrc\n", rc);
1706 }
1707 else
1708 RTMsgError("scmSettingsBaseLoadFromDocument: %Rrc\n", rc);
1709 }
1710 else
1711 {
1712 ScmVerbose(pState, 2, "not text file: \"%s\"\n", pszFilename);
1713 g_cFilesBinaries++;
1714 }
1715 ScmStreamDelete(&Stream1);
1716
1717 return rc;
1718}
1719
1720/**
1721 * Processes a file.
1722 *
1723 * This is just a wrapper for scmProcessFileInner for avoid wasting stack in the
1724 * directory recursion method.
1725 *
1726 * @returns IPRT status code.
1727 * @param pszFilename The file name.
1728 * @param pszBasename The base name (pointer within @a pszFilename).
1729 * @param cchBasename The length of the base name. (For passing to
1730 * RTStrSimplePatternMultiMatch.)
1731 * @param pSettingsStack The settings stack (pointer to the top element).
1732 */
1733static int scmProcessFile(const char *pszFilename, const char *pszBasename, size_t cchBasename,
1734 PSCMSETTINGS pSettingsStack)
1735{
1736 SCMSETTINGSBASE Base;
1737 int rc = scmSettingsStackMakeFileBase(pSettingsStack, pszFilename, pszBasename, cchBasename, &Base);
1738 if (RT_SUCCESS(rc))
1739 {
1740 SCMRWSTATE State;
1741 State.fFirst = false;
1742 State.pszFilename = pszFilename;
1743 State.cSvnPropChanges = 0;
1744 State.paSvnPropChanges = NULL;
1745 State.rc = VINF_SUCCESS;
1746
1747 rc = scmProcessFileInner(&State, pszFilename, pszBasename, cchBasename, &Base);
1748
1749 size_t i = State.cSvnPropChanges;
1750 while (i-- > 0)
1751 {
1752 RTStrFree(State.paSvnPropChanges[i].pszName);
1753 RTStrFree(State.paSvnPropChanges[i].pszValue);
1754 }
1755 RTMemFree(State.paSvnPropChanges);
1756
1757 scmSettingsBaseDelete(&Base);
1758
1759 g_cFilesProcessed++;
1760 }
1761 return rc;
1762}
1763
1764
1765/**
1766 * Tries to correct RTDIRENTRY_UNKNOWN.
1767 *
1768 * @returns Corrected type.
1769 * @param pszPath The path to the object in question.
1770 */
1771static RTDIRENTRYTYPE scmFigureUnknownType(const char *pszPath)
1772{
1773 RTFSOBJINFO Info;
1774 int rc = RTPathQueryInfo(pszPath, &Info, RTFSOBJATTRADD_NOTHING);
1775 if (RT_FAILURE(rc))
1776 return RTDIRENTRYTYPE_UNKNOWN;
1777 if (RTFS_IS_DIRECTORY(Info.Attr.fMode))
1778 return RTDIRENTRYTYPE_DIRECTORY;
1779 if (RTFS_IS_FILE(Info.Attr.fMode))
1780 return RTDIRENTRYTYPE_FILE;
1781 return RTDIRENTRYTYPE_UNKNOWN;
1782}
1783
1784/**
1785 * Recurse into a sub-directory and process all the files and directories.
1786 *
1787 * @returns IPRT status code.
1788 * @param pszBuf Path buffer containing the directory path on
1789 * entry. This ends with a dot. This is passed
1790 * along when recursing in order to save stack space
1791 * and avoid needless copying.
1792 * @param cchDir Length of our path in pszbuf.
1793 * @param pEntry Directory entry buffer. This is also passed
1794 * along when recursing to save stack space.
1795 * @param pSettingsStack The settings stack (pointer to the top element).
1796 * @param iRecursion The recursion depth. This is used to restrict
1797 * the recursions.
1798 */
1799static int scmProcessDirTreeRecursion(char *pszBuf, size_t cchDir, PRTDIRENTRY pEntry,
1800 PSCMSETTINGS pSettingsStack, unsigned iRecursion)
1801{
1802 int rc;
1803 Assert(cchDir > 1 && pszBuf[cchDir - 1] == '.');
1804
1805 /*
1806 * Make sure we stop somewhere.
1807 */
1808 if (iRecursion > 128)
1809 {
1810 RTMsgError("recursion too deep: %d\n", iRecursion);
1811 return VINF_SUCCESS; /* ignore */
1812 }
1813
1814 /*
1815 * Check if it's excluded by --only-svn-dir.
1816 */
1817 if (pSettingsStack->Base.fOnlySvnDirs)
1818 {
1819 if (!ScmSvnIsDirInWorkingCopy(pszBuf))
1820 return VINF_SUCCESS;
1821 }
1822 g_cDirsProcessed++;
1823
1824 /*
1825 * Try open and read the directory.
1826 */
1827 PRTDIR pDir;
1828 rc = RTDirOpenFiltered(&pDir, pszBuf, RTDIRFILTER_NONE, 0);
1829 if (RT_FAILURE(rc))
1830 {
1831 RTMsgError("Failed to enumerate directory '%s': %Rrc", pszBuf, rc);
1832 return rc;
1833 }
1834 for (;;)
1835 {
1836 /* Read the next entry. */
1837 rc = RTDirRead(pDir, pEntry, NULL);
1838 if (RT_FAILURE(rc))
1839 {
1840 if (rc == VERR_NO_MORE_FILES)
1841 rc = VINF_SUCCESS;
1842 else
1843 RTMsgError("RTDirRead -> %Rrc\n", rc);
1844 break;
1845 }
1846
1847 /* Skip '.' and '..'. */
1848 if ( pEntry->szName[0] == '.'
1849 && ( pEntry->cbName == 1
1850 || ( pEntry->cbName == 2
1851 && pEntry->szName[1] == '.')))
1852 continue;
1853
1854 /* Enter it into the buffer so we've got a full name to work
1855 with when needed. */
1856 if (pEntry->cbName + cchDir >= RTPATH_MAX)
1857 {
1858 RTMsgError("Skipping too long entry: %s", pEntry->szName);
1859 continue;
1860 }
1861 memcpy(&pszBuf[cchDir - 1], pEntry->szName, pEntry->cbName + 1);
1862
1863 /* Figure the type. */
1864 RTDIRENTRYTYPE enmType = pEntry->enmType;
1865 if (enmType == RTDIRENTRYTYPE_UNKNOWN)
1866 enmType = scmFigureUnknownType(pszBuf);
1867
1868 /* Process the file or directory, skip the rest. */
1869 if (enmType == RTDIRENTRYTYPE_FILE)
1870 rc = scmProcessFile(pszBuf, pEntry->szName, pEntry->cbName, pSettingsStack);
1871 else if (enmType == RTDIRENTRYTYPE_DIRECTORY)
1872 {
1873 /* Append the dot for the benefit of the pattern matching. */
1874 if (pEntry->cbName + cchDir + 5 >= RTPATH_MAX)
1875 {
1876 RTMsgError("Skipping too deep dir entry: %s", pEntry->szName);
1877 continue;
1878 }
1879 memcpy(&pszBuf[cchDir - 1 + pEntry->cbName], "/.", sizeof("/."));
1880 size_t cchSubDir = cchDir - 1 + pEntry->cbName + sizeof("/.") - 1;
1881
1882 if ( !pSettingsStack->Base.pszFilterOutDirs
1883 || !*pSettingsStack->Base.pszFilterOutDirs
1884 || ( !RTStrSimplePatternMultiMatch(pSettingsStack->Base.pszFilterOutDirs, RTSTR_MAX,
1885 pEntry->szName, pEntry->cbName, NULL)
1886 && !RTStrSimplePatternMultiMatch(pSettingsStack->Base.pszFilterOutDirs, RTSTR_MAX,
1887 pszBuf, cchSubDir, NULL)
1888 )
1889 )
1890 {
1891 rc = scmSettingsStackPushDir(&pSettingsStack, pszBuf);
1892 if (RT_SUCCESS(rc))
1893 {
1894 rc = scmProcessDirTreeRecursion(pszBuf, cchSubDir, pEntry, pSettingsStack, iRecursion + 1);
1895 scmSettingsStackPopAndDestroy(&pSettingsStack);
1896 }
1897 }
1898 }
1899 if (RT_FAILURE(rc))
1900 break;
1901 }
1902 RTDirClose(pDir);
1903 return rc;
1904
1905}
1906
1907/**
1908 * Process a directory tree.
1909 *
1910 * @returns IPRT status code.
1911 * @param pszDir The directory to start with. This is pointer to
1912 * a RTPATH_MAX sized buffer.
1913 */
1914static int scmProcessDirTree(char *pszDir, PSCMSETTINGS pSettingsStack)
1915{
1916 /*
1917 * Setup the recursion.
1918 */
1919 int rc = RTPathAppend(pszDir, RTPATH_MAX, ".");
1920 if (RT_SUCCESS(rc))
1921 {
1922 RTPathChangeToUnixSlashes(pszDir, true);
1923
1924 RTDIRENTRY Entry;
1925 rc = scmProcessDirTreeRecursion(pszDir, strlen(pszDir), &Entry, pSettingsStack, 0);
1926 }
1927 else
1928 RTMsgError("RTPathAppend: %Rrc\n", rc);
1929 return rc;
1930}
1931
1932
1933/**
1934 * Processes a file or directory specified as an command line argument.
1935 *
1936 * @returns IPRT status code
1937 * @param pszSomething What we found in the command line arguments.
1938 * @param pSettingsStack The settings stack (pointer to the top element).
1939 */
1940static int scmProcessSomething(const char *pszSomething, PSCMSETTINGS pSettingsStack)
1941{
1942 char szBuf[RTPATH_MAX];
1943 int rc = RTPathAbs(pszSomething, szBuf, sizeof(szBuf));
1944 if (RT_SUCCESS(rc))
1945 {
1946 RTPathChangeToUnixSlashes(szBuf, false /*fForce*/);
1947
1948 PSCMSETTINGS pSettings;
1949 rc = scmSettingsCreateForPath(&pSettings, &pSettingsStack->Base, szBuf);
1950 if (RT_SUCCESS(rc))
1951 {
1952 scmSettingsStackPush(&pSettingsStack, pSettings);
1953
1954 if (RTFileExists(szBuf))
1955 {
1956 const char *pszBasename = RTPathFilename(szBuf);
1957 if (pszBasename)
1958 {
1959 size_t cchBasename = strlen(pszBasename);
1960 rc = scmProcessFile(szBuf, pszBasename, cchBasename, pSettingsStack);
1961 }
1962 else
1963 {
1964 RTMsgError("RTPathFilename: NULL\n");
1965 rc = VERR_IS_A_DIRECTORY;
1966 }
1967 }
1968 else
1969 rc = scmProcessDirTree(szBuf, pSettingsStack);
1970
1971 PSCMSETTINGS pPopped = scmSettingsStackPop(&pSettingsStack);
1972 Assert(pPopped == pSettings); RT_NOREF_PV(pPopped);
1973 scmSettingsDestroy(pSettings);
1974 }
1975 else
1976 RTMsgError("scmSettingsInitStack: %Rrc\n", rc);
1977 }
1978 else
1979 RTMsgError("RTPathAbs: %Rrc\n", rc);
1980 return rc;
1981}
1982
1983/**
1984 * Print some stats.
1985 */
1986static void scmPrintStats(void)
1987{
1988 ScmVerbose(NULL, 0,
1989 g_fDryRun
1990 ? "%u out of %u file%s in %u dir%s would be modified (%u without rewriter%s, %u binar%s, %u not in svn, %u skipped)\n"
1991 : "%u out of %u file%s in %u dir%s was modified (%u without rewriter%s, %u binar%s, %u not in svn, %u skipped)\n",
1992 g_cFilesModified,
1993 g_cFilesProcessed, g_cFilesProcessed == 1 ? "" : "s",
1994 g_cDirsProcessed, g_cDirsProcessed == 1 ? "" : "s",
1995 g_cFilesNoRewriters, g_cFilesNoRewriters == 1 ? "" : "s",
1996 g_cFilesBinaries, g_cFilesBinaries == 1 ? "y" : "ies",
1997 g_cFilesNotInSvn, g_cFilesSkipped);
1998}
1999
2000static void usage(PCRTGETOPTDEF paOpts, size_t cOpts)
2001{
2002 RTPrintf("VirtualBox Source Code Massager\n"
2003 "\n"
2004 "Usage: %s [options] <files & dirs>\n"
2005 "\n"
2006 "Options:\n", g_szProgName);
2007 for (size_t i = 0; i < cOpts; i++)
2008 {
2009 size_t cExtraAdvance = 0;
2010 if ((paOpts[i].fFlags & RTGETOPT_REQ_MASK) == RTGETOPT_REQ_NOTHING)
2011 {
2012 cExtraAdvance = i + 1 < cOpts
2013 && ( strstr(paOpts[i+1].pszLong, "-no-") != NULL
2014 || strstr(paOpts[i+1].pszLong, "-not-") != NULL
2015 || strstr(paOpts[i+1].pszLong, "-dont-") != NULL
2016 || (paOpts[i].iShort == 'q' && paOpts[i+1].iShort == 'v')
2017 || (paOpts[i].iShort == 'd' && paOpts[i+1].iShort == 'D')
2018 );
2019 if (cExtraAdvance)
2020 RTPrintf(" %s, %s\n", paOpts[i].pszLong, paOpts[i + 1].pszLong);
2021 else if (paOpts[i].iShort != SCMOPT_NO_UPDATE_LICENSE)
2022 RTPrintf(" %s\n", paOpts[i].pszLong);
2023 else
2024 {
2025 RTPrintf(" %s,\n"
2026 " %s,\n"
2027 " %s,\n"
2028 " %s,\n"
2029 " %s,\n"
2030 " %s,\n"
2031 " %s\n",
2032 paOpts[i].pszLong,
2033 paOpts[i + 1].pszLong,
2034 paOpts[i + 2].pszLong,
2035 paOpts[i + 3].pszLong,
2036 paOpts[i + 4].pszLong,
2037 paOpts[i + 5].pszLong,
2038 paOpts[i + 6].pszLong);
2039 cExtraAdvance = 6;
2040 }
2041 }
2042 else if ((paOpts[i].fFlags & RTGETOPT_REQ_MASK) == RTGETOPT_REQ_STRING)
2043 RTPrintf(" %s string\n", paOpts[i].pszLong);
2044 else
2045 RTPrintf(" %s value\n", paOpts[i].pszLong);
2046 switch (paOpts[i].iShort)
2047 {
2048 case 'd':
2049 case 'D': RTPrintf(" Default: --dry-run\n"); break;
2050 case 'f': RTPrintf(" Default: none\n"); break;
2051 case 'q':
2052 case 'v': RTPrintf(" Default: -vv\n"); break;
2053
2054 case SCMOPT_DIFF_IGNORE_EOL: RTPrintf(" Default: false\n"); break;
2055 case SCMOPT_DIFF_IGNORE_SPACE: RTPrintf(" Default: false\n"); break;
2056 case SCMOPT_DIFF_IGNORE_LEADING_SPACE: RTPrintf(" Default: false\n"); break;
2057 case SCMOPT_DIFF_IGNORE_TRAILING_SPACE: RTPrintf(" Default: false\n"); break;
2058 case SCMOPT_DIFF_SPECIAL_CHARS: RTPrintf(" Default: true\n"); break;
2059
2060 case SCMOPT_CONVERT_EOL: RTPrintf(" Default: %RTbool\n", g_Defaults.fConvertEol); break;
2061 case SCMOPT_CONVERT_TABS: RTPrintf(" Default: %RTbool\n", g_Defaults.fConvertTabs); break;
2062 case SCMOPT_FORCE_FINAL_EOL: RTPrintf(" Default: %RTbool\n", g_Defaults.fForceFinalEol); break;
2063 case SCMOPT_FORCE_TRAILING_LINE: RTPrintf(" Default: %RTbool\n", g_Defaults.fForceTrailingLine); break;
2064 case SCMOPT_STRIP_TRAILING_BLANKS: RTPrintf(" Default: %RTbool\n", g_Defaults.fStripTrailingBlanks); break;
2065 case SCMOPT_STRIP_TRAILING_LINES: RTPrintf(" Default: %RTbool\n", g_Defaults.fStripTrailingLines); break;
2066 case SCMOPT_FIX_FLOWER_BOX_MARKERS: RTPrintf(" Default: %RTbool\n", g_Defaults.fFixFlowerBoxMarkers); break;
2067 case SCMOPT_MIN_BLANK_LINES_BEFORE_FLOWER_BOX_MARKERS: RTPrintf(" Default: %u\n", g_Defaults.cMinBlankLinesBeforeFlowerBoxMakers); break;
2068
2069 case SCMOPT_FIX_TODOS:
2070 RTPrintf(" Fix @todo statements so doxygen sees them. Default: %RTbool\n", g_Defaults.fFixTodos);
2071 break;
2072 case SCMOPT_UPDATE_COPYRIGHT_YEAR:
2073 RTPrintf(" Update the copyright year. Default: %RTbool\n", g_Defaults.fUpdateCopyrightYear);
2074 break;
2075 case SCMOPT_EXTERNAL_COPYRIGHT:
2076 RTPrintf(" Only external copyright holders. Default: %RTbool\n", g_Defaults.fExternalCopyright);
2077 break;
2078 case SCMOPT_NO_UPDATE_LICENSE:
2079 RTPrintf(" License selection. Default: --license-ose-gpl\n");
2080 break;
2081
2082 case SCMOPT_LGPL_DISCLAIMER:
2083 RTPrintf(" Include LGPL version disclaimer. Default: --no-lgpl-disclaimer\n");
2084 break;
2085
2086 case SCMOPT_ONLY_SVN_DIRS: RTPrintf(" Default: %RTbool\n", g_Defaults.fOnlySvnDirs); break;
2087 case SCMOPT_ONLY_SVN_FILES: RTPrintf(" Default: %RTbool\n", g_Defaults.fOnlySvnFiles); break;
2088 case SCMOPT_SET_SVN_EOL: RTPrintf(" Default: %RTbool\n", g_Defaults.fSetSvnEol); break;
2089 case SCMOPT_SET_SVN_EXECUTABLE: RTPrintf(" Default: %RTbool\n", g_Defaults.fSetSvnExecutable); break;
2090 case SCMOPT_SET_SVN_KEYWORDS: RTPrintf(" Default: %RTbool\n", g_Defaults.fSetSvnKeywords); break;
2091 case SCMOPT_TAB_SIZE: RTPrintf(" Default: %u\n", g_Defaults.cchTab); break;
2092 case SCMOPT_WIDTH: RTPrintf(" Default: %u\n", g_Defaults.cchWidth); break;
2093
2094 case SCMOPT_TREAT_AS:
2095 RTPrintf(" For files not using the default extension.\n");
2096 break;
2097
2098 case SCMOPT_FILTER_OUT_DIRS: RTPrintf(" Default: %s\n", g_Defaults.pszFilterOutDirs); break;
2099 case SCMOPT_FILTER_FILES: RTPrintf(" Default: %s\n", g_Defaults.pszFilterFiles); break;
2100 case SCMOPT_FILTER_OUT_FILES: RTPrintf(" Default: %s\n", g_Defaults.pszFilterOutFiles); break;
2101 default: AssertMsgFailed(("i=%d %d %s\n", i, paOpts[i].iShort, paOpts[i].pszLong));
2102 }
2103 i += cExtraAdvance;
2104 }
2105
2106}
2107
2108int main(int argc, char **argv)
2109{
2110 int rc = RTR3InitExe(argc, &argv, 0);
2111 if (RT_FAILURE(rc))
2112 return 1;
2113
2114 /*
2115 * Init the current year.
2116 */
2117 RTTIMESPEC Now;
2118 RTTIME Time;
2119 RTTimeExplode(&Time, RTTimeNow(&Now));
2120 g_uYear = Time.i32Year;
2121
2122 /*
2123 * Init the settings.
2124 */
2125 PSCMSETTINGS pSettings;
2126 rc = scmSettingsCreate(&pSettings, &g_Defaults);
2127 if (RT_FAILURE(rc))
2128 {
2129 RTMsgError("scmSettingsCreate: %Rrc\n", rc);
2130 return 1;
2131 }
2132
2133 /*
2134 * Parse arguments and process input in order (because this is the only
2135 * thing that works at the moment).
2136 */
2137 static RTGETOPTDEF s_aOpts[14 + RT_ELEMENTS(g_aScmOpts)] =
2138 {
2139 { "--dry-run", 'd', RTGETOPT_REQ_NOTHING },
2140 { "--real-run", 'D', RTGETOPT_REQ_NOTHING },
2141 { "--file-filter", 'f', RTGETOPT_REQ_STRING },
2142 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
2143 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
2144 { "--diff-ignore-eol", SCMOPT_DIFF_IGNORE_EOL, RTGETOPT_REQ_NOTHING },
2145 { "--diff-no-ignore-eol", SCMOPT_DIFF_NO_IGNORE_EOL, RTGETOPT_REQ_NOTHING },
2146 { "--diff-ignore-space", SCMOPT_DIFF_IGNORE_SPACE, RTGETOPT_REQ_NOTHING },
2147 { "--diff-no-ignore-space", SCMOPT_DIFF_NO_IGNORE_SPACE, RTGETOPT_REQ_NOTHING },
2148 { "--diff-ignore-leading-space", SCMOPT_DIFF_IGNORE_LEADING_SPACE, RTGETOPT_REQ_NOTHING },
2149 { "--diff-no-ignore-leading-space", SCMOPT_DIFF_NO_IGNORE_LEADING_SPACE, RTGETOPT_REQ_NOTHING },
2150 { "--diff-ignore-trailing-space", SCMOPT_DIFF_IGNORE_TRAILING_SPACE, RTGETOPT_REQ_NOTHING },
2151 { "--diff-no-ignore-trailing-space", SCMOPT_DIFF_NO_IGNORE_TRAILING_SPACE, RTGETOPT_REQ_NOTHING },
2152 { "--diff-special-chars", SCMOPT_DIFF_SPECIAL_CHARS, RTGETOPT_REQ_NOTHING },
2153 { "--diff-no-special-chars", SCMOPT_DIFF_NO_SPECIAL_CHARS, RTGETOPT_REQ_NOTHING },
2154 };
2155 memcpy(&s_aOpts[RT_ELEMENTS(s_aOpts) - RT_ELEMENTS(g_aScmOpts)], &g_aScmOpts[0], sizeof(g_aScmOpts));
2156
2157 RTGETOPTUNION ValueUnion;
2158 RTGETOPTSTATE GetOptState;
2159 rc = RTGetOptInit(&GetOptState, argc, argv, &s_aOpts[0], RT_ELEMENTS(s_aOpts), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
2160 AssertReleaseRCReturn(rc, 1);
2161 size_t cProcessed = 0;
2162
2163 while ((rc = RTGetOpt(&GetOptState, &ValueUnion)) != 0)
2164 {
2165 switch (rc)
2166 {
2167 case 'd':
2168 g_fDryRun = true;
2169 break;
2170 case 'D':
2171 g_fDryRun = false;
2172 break;
2173
2174 case 'f':
2175 g_pszFileFilter = ValueUnion.psz;
2176 break;
2177
2178 case 'h':
2179 usage(s_aOpts, RT_ELEMENTS(s_aOpts));
2180 return 1;
2181
2182 case 'q':
2183 g_iVerbosity = 0;
2184 break;
2185
2186 case 'v':
2187 g_iVerbosity++;
2188 break;
2189
2190 case 'V':
2191 {
2192 /* The following is assuming that svn does it's job here. */
2193 static const char s_szRev[] = "$Revision: 69349 $";
2194 const char *psz = RTStrStripL(strchr(s_szRev, ' '));
2195 RTPrintf("r%.*s\n", strchr(psz, ' ') - psz, psz);
2196 return 0;
2197 }
2198
2199 case SCMOPT_DIFF_IGNORE_EOL:
2200 g_fDiffIgnoreEol = true;
2201 break;
2202 case SCMOPT_DIFF_NO_IGNORE_EOL:
2203 g_fDiffIgnoreEol = false;
2204 break;
2205
2206 case SCMOPT_DIFF_IGNORE_SPACE:
2207 g_fDiffIgnoreTrailingWS = g_fDiffIgnoreLeadingWS = true;
2208 break;
2209 case SCMOPT_DIFF_NO_IGNORE_SPACE:
2210 g_fDiffIgnoreTrailingWS = g_fDiffIgnoreLeadingWS = false;
2211 break;
2212
2213 case SCMOPT_DIFF_IGNORE_LEADING_SPACE:
2214 g_fDiffIgnoreLeadingWS = true;
2215 break;
2216 case SCMOPT_DIFF_NO_IGNORE_LEADING_SPACE:
2217 g_fDiffIgnoreLeadingWS = false;
2218 break;
2219
2220 case SCMOPT_DIFF_IGNORE_TRAILING_SPACE:
2221 g_fDiffIgnoreTrailingWS = true;
2222 break;
2223 case SCMOPT_DIFF_NO_IGNORE_TRAILING_SPACE:
2224 g_fDiffIgnoreTrailingWS = false;
2225 break;
2226
2227 case SCMOPT_DIFF_SPECIAL_CHARS:
2228 g_fDiffSpecialChars = true;
2229 break;
2230 case SCMOPT_DIFF_NO_SPECIAL_CHARS:
2231 g_fDiffSpecialChars = false;
2232 break;
2233
2234 case VINF_GETOPT_NOT_OPTION:
2235 {
2236 if (!g_fDryRun)
2237 {
2238 if (!cProcessed)
2239 {
2240 RTPrintf("%s: Warning! This program will make changes to your source files and\n"
2241 "%s: there is a slight risk that bugs or a full disk may cause\n"
2242 "%s: LOSS OF DATA. So, please make sure you have checked in\n"
2243 "%s: all your changes already. If you didn't, then don't blame\n"
2244 "%s: anyone for not warning you!\n"
2245 "%s:\n"
2246 "%s: Press any key to continue...\n",
2247 g_szProgName, g_szProgName, g_szProgName, g_szProgName, g_szProgName,
2248 g_szProgName, g_szProgName);
2249 RTStrmGetCh(g_pStdIn);
2250 }
2251 cProcessed++;
2252 }
2253 rc = scmProcessSomething(ValueUnion.psz, pSettings);
2254 if (RT_FAILURE(rc))
2255 {
2256 scmPrintStats();
2257 return RTEXITCODE_FAILURE;
2258 }
2259 break;
2260 }
2261
2262 default:
2263 {
2264 int rc2 = scmSettingsBaseHandleOpt(&pSettings->Base, rc, &ValueUnion, "/", 1);
2265 if (RT_SUCCESS(rc2))
2266 break;
2267 if (rc2 != VERR_GETOPT_UNKNOWN_OPTION)
2268 return 2;
2269 return RTGetOptPrintError(rc, &ValueUnion);
2270 }
2271 }
2272 }
2273
2274 scmPrintStats();
2275 scmSettingsDestroy(pSettings);
2276 return 0;
2277}
2278
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