VirtualBox

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

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

scm: windows .inf files

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

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