VirtualBox

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

Last change on this file since 96401 was 96401, checked in by vboxsync, 3 years ago

bldprogs/scm: Teach it to replace the old copyright and license notices with the updated ones (e.g. GPlv2 to GPLv3).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 122.0 KB
Line 
1/* $Id: scm.cpp 96401 2022-08-22 15:06:19Z vboxsync $ */
2/** @file
3 * IPRT Testcase / Tool - Source Code Massager.
4 */
5
6/*
7 * Copyright (C) 2010-2022 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_HEADER_GUARDS,
78 SCMOPT_NO_FIX_HEADER_GUARDS,
79 SCMOPT_PRAGMA_ONCE,
80 SCMOPT_NO_PRAGMA_ONCE,
81 SCMOPT_FIX_HEADER_GUARD_ENDIF,
82 SCMOPT_NO_FIX_HEADER_GUARD_ENDIF,
83 SCMOPT_ENDIF_GUARD_COMMENT,
84 SCMOPT_NO_ENDIF_GUARD_COMMENT,
85 SCMOPT_GUARD_PREFIX,
86 SCMOPT_GUARD_RELATIVE_TO_DIR,
87 SCMOPT_FIX_TODOS,
88 SCMOPT_NO_FIX_TODOS,
89 SCMOPT_FIX_ERR_H,
90 SCMOPT_NO_FIX_ERR_H,
91 SCMOPT_ONLY_GUEST_HOST_PAGE,
92 SCMOPT_NO_ASM_MEM_PAGE_USE,
93 SCMOPT_UNRESTRICTED_ASM_MEM_PAGE_USE,
94 SCMOPT_NO_PAGE_RESTRICTIONS,
95 SCMOPT_NO_RC_USE,
96 SCMOPT_UNRESTRICTED_RC_USE,
97 SCMOPT_UPDATE_COPYRIGHT_YEAR,
98 SCMOPT_NO_UPDATE_COPYRIGHT_YEAR,
99 SCMOPT_EXTERNAL_COPYRIGHT,
100 SCMOPT_NO_EXTERNAL_COPYRIGHT,
101 SCMOPT_NO_UPDATE_LICENSE,
102 SCMOPT_LICENSE_OSE_GPL,
103 SCMOPT_LICENSE_OSE_DUAL_GPL_CDDL,
104 SCMOPT_LICENSE_OSE_CDDL,
105 SCMOPT_LICENSE_LGPL,
106 SCMOPT_LICENSE_MIT,
107 SCMOPT_LICENSE_BASED_ON_MIT,
108 SCMOPT_LGPL_DISCLAIMER,
109 SCMOPT_NO_LGPL_DISCLAIMER,
110 SCMOPT_MIN_BLANK_LINES_BEFORE_FLOWER_BOX_MARKERS,
111 SCMOPT_ONLY_SVN_DIRS,
112 SCMOPT_NOT_ONLY_SVN_DIRS,
113 SCMOPT_ONLY_SVN_FILES,
114 SCMOPT_NOT_ONLY_SVN_FILES,
115 SCMOPT_SET_SVN_EOL,
116 SCMOPT_DONT_SET_SVN_EOL,
117 SCMOPT_SET_SVN_EXECUTABLE,
118 SCMOPT_DONT_SET_SVN_EXECUTABLE,
119 SCMOPT_SET_SVN_KEYWORDS,
120 SCMOPT_DONT_SET_SVN_KEYWORDS,
121 SCMOPT_SKIP_SVN_SYNC_PROCESS,
122 SCMOPT_DONT_SKIP_SVN_SYNC_PROCESS,
123 SCMOPT_SKIP_UNICODE_CHECKS,
124 SCMOPT_DONT_SKIP_UNICODE_CHECKS,
125 SCMOPT_TAB_SIZE,
126 SCMOPT_WIDTH,
127 SCMOPT_FILTER_OUT_DIRS,
128 SCMOPT_FILTER_FILES,
129 SCMOPT_FILTER_OUT_FILES,
130 SCMOPT_TREAT_AS,
131 SCMOPT_ADD_ACTION,
132 SCMOPT_DEL_ACTION,
133 SCMOPT_LAST_SETTINGS = SCMOPT_DEL_ACTION,
134 //
135 SCMOPT_CHECK_RUN,
136 SCMOPT_DIFF_IGNORE_EOL,
137 SCMOPT_DIFF_NO_IGNORE_EOL,
138 SCMOPT_DIFF_IGNORE_SPACE,
139 SCMOPT_DIFF_NO_IGNORE_SPACE,
140 SCMOPT_DIFF_IGNORE_LEADING_SPACE,
141 SCMOPT_DIFF_NO_IGNORE_LEADING_SPACE,
142 SCMOPT_DIFF_IGNORE_TRAILING_SPACE,
143 SCMOPT_DIFF_NO_IGNORE_TRAILING_SPACE,
144 SCMOPT_DIFF_SPECIAL_CHARS,
145 SCMOPT_DIFF_NO_SPECIAL_CHARS,
146 SCMOPT_HELP_CONFIG,
147 SCMOPT_HELP_ACTIONS,
148 SCMOPT_END
149} SCMOPT;
150
151
152/*********************************************************************************************************************************
153* Global Variables *
154*********************************************************************************************************************************/
155const char g_szTabSpaces[16+1] = " ";
156const char g_szAsterisks[255+1] =
157"****************************************************************************************************"
158"****************************************************************************************************"
159"*******************************************************";
160const char g_szSpaces[255+1] =
161" "
162" "
163" ";
164static const char g_szProgName[] = "scm";
165static const char *g_pszChangedSuff = "";
166static bool g_fDryRun = true;
167static bool g_fDiffSpecialChars = true;
168static bool g_fDiffIgnoreEol = false;
169static bool g_fDiffIgnoreLeadingWS = false;
170static bool g_fDiffIgnoreTrailingWS = false;
171static int g_iVerbosity = 2;//99; //0;
172uint32_t g_uYear = 0; /**< The current year. */
173/** @name Statistics
174 * @{ */
175static uint32_t g_cDirsProcessed = 0;
176static uint32_t g_cFilesProcessed = 0;
177static uint32_t g_cFilesModified = 0;
178static uint32_t g_cFilesSkipped = 0;
179static uint32_t g_cFilesNotInSvn = 0;
180static uint32_t g_cFilesNoRewriters = 0;
181static uint32_t g_cFilesBinaries = 0;
182static uint32_t g_cFilesRequiringManualFixing = 0;
183/** @} */
184
185/** The global settings. */
186static SCMSETTINGSBASE const g_Defaults =
187{
188 /* .fConvertEol = */ true,
189 /* .fConvertTabs = */ true,
190 /* .fForceFinalEol = */ true,
191 /* .fForceTrailingLine = */ false,
192 /* .fStripTrailingBlanks = */ true,
193 /* .fStripTrailingLines = */ true,
194 /* .fFixFlowerBoxMarkers = */ true,
195 /* .cMinBlankLinesBeforeFlowerBoxMakers = */ 2,
196 /* .fFixHeaderGuards = */ true,
197 /* .fPragmaOnce = */ true,
198 /* .fFixHeaderGuardEndif = */ true,
199 /* .fEndifGuardComment = */ true,
200 /* .pszGuardPrefix = */ (char *)"VBOX_INCLUDED_SRC_",
201 /* .pszGuardRelativeToDir = */ (char *)"{parent}",
202 /* .fFixTodos = */ true,
203 /* .fFixErrH = */ true,
204 /* .fOnlyGuestHostPage = */ false,
205 /* .fNoASMMemPageUse = */ false,
206 /* .fOnlyHrcVrcInsteadOfRc */ false,
207 /* .fUpdateCopyrightYear = */ false,
208 /* .fExternalCopyright = */ false,
209 /* .fLgplDisclaimer = */ false,
210 /* .enmUpdateLicense = */ kScmLicense_OseGpl,
211 /* .fOnlySvnFiles = */ false,
212 /* .fOnlySvnDirs = */ false,
213 /* .fSetSvnEol = */ false,
214 /* .fSetSvnExecutable = */ false,
215 /* .fSetSvnKeywords = */ false,
216 /* .fSkipSvnSyncProcess = */ false,
217 /* .fSkipUnicodeChecks = */ false,
218 /* .cchTab = */ 8,
219 /* .cchWidth = */ 130,
220 /* .fFreeTreatAs = */ false,
221 /* .pTreatAs = */ NULL,
222 /* .pszFilterFiles = */ (char *)"",
223 /* .pszFilterOutFiles = */ (char *)"*.exe|*.com|20*-*-*.log",
224 /* .pszFilterOutDirs = */ (char *)".svn|.hg|.git|CVS",
225};
226
227/** Option definitions for the base settings. */
228static RTGETOPTDEF g_aScmOpts[] =
229{
230 /* rewriters */
231 { "--convert-eol", SCMOPT_CONVERT_EOL, RTGETOPT_REQ_NOTHING },
232 { "--no-convert-eol", SCMOPT_NO_CONVERT_EOL, RTGETOPT_REQ_NOTHING },
233 { "--convert-tabs", SCMOPT_CONVERT_TABS, RTGETOPT_REQ_NOTHING },
234 { "--no-convert-tabs", SCMOPT_NO_CONVERT_TABS, RTGETOPT_REQ_NOTHING },
235 { "--force-final-eol", SCMOPT_FORCE_FINAL_EOL, RTGETOPT_REQ_NOTHING },
236 { "--no-force-final-eol", SCMOPT_NO_FORCE_FINAL_EOL, RTGETOPT_REQ_NOTHING },
237 { "--force-trailing-line", SCMOPT_FORCE_TRAILING_LINE, RTGETOPT_REQ_NOTHING },
238 { "--no-force-trailing-line", SCMOPT_NO_FORCE_TRAILING_LINE, RTGETOPT_REQ_NOTHING },
239 { "--strip-trailing-blanks", SCMOPT_STRIP_TRAILING_BLANKS, RTGETOPT_REQ_NOTHING },
240 { "--no-strip-trailing-blanks", SCMOPT_NO_STRIP_TRAILING_BLANKS, RTGETOPT_REQ_NOTHING },
241 { "--strip-trailing-lines", SCMOPT_STRIP_TRAILING_LINES, RTGETOPT_REQ_NOTHING },
242 { "--strip-no-trailing-lines", SCMOPT_NO_STRIP_TRAILING_LINES, RTGETOPT_REQ_NOTHING },
243 { "--min-blank-lines-before-flower-box-makers", SCMOPT_MIN_BLANK_LINES_BEFORE_FLOWER_BOX_MARKERS, RTGETOPT_REQ_UINT8 },
244 { "--fix-flower-box-markers", SCMOPT_FIX_FLOWER_BOX_MARKERS, RTGETOPT_REQ_NOTHING },
245 { "--no-fix-flower-box-markers", SCMOPT_NO_FIX_FLOWER_BOX_MARKERS, RTGETOPT_REQ_NOTHING },
246 { "--fix-header-guards", SCMOPT_FIX_HEADER_GUARDS, RTGETOPT_REQ_NOTHING },
247 { "--no-fix-header-guards", SCMOPT_NO_FIX_HEADER_GUARDS, RTGETOPT_REQ_NOTHING },
248 { "--pragma-once", SCMOPT_PRAGMA_ONCE, RTGETOPT_REQ_NOTHING },
249 { "--no-pragma-once", SCMOPT_NO_PRAGMA_ONCE, RTGETOPT_REQ_NOTHING },
250 { "--fix-header-guard-endif", SCMOPT_FIX_HEADER_GUARD_ENDIF, RTGETOPT_REQ_NOTHING },
251 { "--no-fix-header-guard-endif", SCMOPT_NO_FIX_HEADER_GUARD_ENDIF, RTGETOPT_REQ_NOTHING },
252 { "--endif-guard-comment", SCMOPT_ENDIF_GUARD_COMMENT, RTGETOPT_REQ_NOTHING },
253 { "--no-endif-guard-comment", SCMOPT_NO_ENDIF_GUARD_COMMENT, RTGETOPT_REQ_NOTHING },
254 { "--guard-prefix", SCMOPT_GUARD_PREFIX, RTGETOPT_REQ_STRING },
255 { "--guard-relative-to-dir", SCMOPT_GUARD_RELATIVE_TO_DIR, RTGETOPT_REQ_STRING },
256 { "--fix-todos", SCMOPT_FIX_TODOS, RTGETOPT_REQ_NOTHING },
257 { "--no-fix-todos", SCMOPT_NO_FIX_TODOS, RTGETOPT_REQ_NOTHING },
258 { "--fix-err-h", SCMOPT_FIX_ERR_H, RTGETOPT_REQ_NOTHING },
259 { "--no-fix-err-h", SCMOPT_NO_FIX_ERR_H, RTGETOPT_REQ_NOTHING },
260 { "--only-guest-host-page", SCMOPT_ONLY_GUEST_HOST_PAGE, RTGETOPT_REQ_NOTHING },
261 { "--no-page-restrictions", SCMOPT_NO_PAGE_RESTRICTIONS, RTGETOPT_REQ_NOTHING },
262 { "--no-ASMMemPage-use", SCMOPT_NO_ASM_MEM_PAGE_USE, RTGETOPT_REQ_NOTHING },
263 { "--unrestricted-ASMMemPage-use", SCMOPT_UNRESTRICTED_ASM_MEM_PAGE_USE, RTGETOPT_REQ_NOTHING },
264 { "--no-rc-use", SCMOPT_NO_RC_USE, RTGETOPT_REQ_NOTHING },
265 { "--unrestricted-rc-use", SCMOPT_UNRESTRICTED_RC_USE, RTGETOPT_REQ_NOTHING },
266 { "--update-copyright-year", SCMOPT_UPDATE_COPYRIGHT_YEAR, RTGETOPT_REQ_NOTHING },
267 { "--no-update-copyright-year", SCMOPT_NO_UPDATE_COPYRIGHT_YEAR, RTGETOPT_REQ_NOTHING },
268 { "--external-copyright", SCMOPT_EXTERNAL_COPYRIGHT, RTGETOPT_REQ_NOTHING },
269 { "--no-external-copyright", SCMOPT_NO_EXTERNAL_COPYRIGHT, RTGETOPT_REQ_NOTHING },
270 { "--no-update-license", SCMOPT_NO_UPDATE_LICENSE, RTGETOPT_REQ_NOTHING },
271 { "--license-ose-gpl", SCMOPT_LICENSE_OSE_GPL, RTGETOPT_REQ_NOTHING },
272 { "--license-ose-dual", SCMOPT_LICENSE_OSE_DUAL_GPL_CDDL, RTGETOPT_REQ_NOTHING },
273 { "--license-ose-cddl", SCMOPT_LICENSE_OSE_CDDL, RTGETOPT_REQ_NOTHING },
274 { "--license-lgpl", SCMOPT_LICENSE_LGPL, RTGETOPT_REQ_NOTHING },
275 { "--license-mit", SCMOPT_LICENSE_MIT, RTGETOPT_REQ_NOTHING },
276 { "--license-based-on-mit", SCMOPT_LICENSE_BASED_ON_MIT, RTGETOPT_REQ_NOTHING },
277 { "--lgpl-disclaimer", SCMOPT_LGPL_DISCLAIMER, RTGETOPT_REQ_NOTHING },
278 { "--no-lgpl-disclaimer", SCMOPT_NO_LGPL_DISCLAIMER, RTGETOPT_REQ_NOTHING },
279 { "--set-svn-eol", SCMOPT_SET_SVN_EOL, RTGETOPT_REQ_NOTHING },
280 { "--dont-set-svn-eol", SCMOPT_DONT_SET_SVN_EOL, RTGETOPT_REQ_NOTHING },
281 { "--set-svn-executable", SCMOPT_SET_SVN_EXECUTABLE, RTGETOPT_REQ_NOTHING },
282 { "--dont-set-svn-executable", SCMOPT_DONT_SET_SVN_EXECUTABLE, RTGETOPT_REQ_NOTHING },
283 { "--set-svn-keywords", SCMOPT_SET_SVN_KEYWORDS, RTGETOPT_REQ_NOTHING },
284 { "--dont-set-svn-keywords", SCMOPT_DONT_SET_SVN_KEYWORDS, RTGETOPT_REQ_NOTHING },
285 { "--skip-svn-sync-process", SCMOPT_SKIP_SVN_SYNC_PROCESS, RTGETOPT_REQ_NOTHING },
286 { "--dont-skip-svn-sync-process", SCMOPT_DONT_SKIP_SVN_SYNC_PROCESS, RTGETOPT_REQ_NOTHING },
287 { "--skip-unicode-checks", SCMOPT_SKIP_UNICODE_CHECKS, RTGETOPT_REQ_NOTHING },
288 { "--dont-skip-unicode-checks", SCMOPT_DONT_SKIP_UNICODE_CHECKS, RTGETOPT_REQ_NOTHING },
289 { "--tab-size", SCMOPT_TAB_SIZE, RTGETOPT_REQ_UINT8 },
290 { "--width", SCMOPT_WIDTH, RTGETOPT_REQ_UINT8 },
291
292 /* input selection */
293 { "--only-svn-dirs", SCMOPT_ONLY_SVN_DIRS, RTGETOPT_REQ_NOTHING },
294 { "--not-only-svn-dirs", SCMOPT_NOT_ONLY_SVN_DIRS, RTGETOPT_REQ_NOTHING },
295 { "--only-svn-files", SCMOPT_ONLY_SVN_FILES, RTGETOPT_REQ_NOTHING },
296 { "--not-only-svn-files", SCMOPT_NOT_ONLY_SVN_FILES, RTGETOPT_REQ_NOTHING },
297 { "--filter-out-dirs", SCMOPT_FILTER_OUT_DIRS, RTGETOPT_REQ_STRING },
298 { "--filter-files", SCMOPT_FILTER_FILES, RTGETOPT_REQ_STRING },
299 { "--filter-out-files", SCMOPT_FILTER_OUT_FILES, RTGETOPT_REQ_STRING },
300
301 /* rewriter selection */
302 { "--treat-as", SCMOPT_TREAT_AS, RTGETOPT_REQ_STRING },
303 { "--add-action", SCMOPT_ADD_ACTION, RTGETOPT_REQ_STRING },
304 { "--del-action", SCMOPT_DEL_ACTION, RTGETOPT_REQ_STRING },
305
306 /* Additional help */
307 { "--help-config", SCMOPT_HELP_CONFIG, RTGETOPT_REQ_NOTHING },
308 { "--help-actions", SCMOPT_HELP_ACTIONS, RTGETOPT_REQ_NOTHING },
309};
310
311/** Consider files matching the following patterns (base names only). */
312static const char *g_pszFileFilter = NULL;
313
314/* The rewriter configuration. */
315#define SCM_REWRITER_CFG(a_Global, a_szName, fnRewriter) static const SCMREWRITERCFG a_Global = { &fnRewriter, a_szName }
316SCM_REWRITER_CFG(g_StripTrailingBlanks, "strip-trailing-blanks", rewrite_StripTrailingBlanks);
317SCM_REWRITER_CFG(g_ExpandTabs, "expand-tabs", rewrite_ExpandTabs);
318SCM_REWRITER_CFG(g_ForceNativeEol, "force-native-eol", rewrite_ForceNativeEol);
319SCM_REWRITER_CFG(g_ForceLF, "force-lf", rewrite_ForceLF);
320SCM_REWRITER_CFG(g_ForceCRLF, "force-crlf", rewrite_ForceCRLF);
321SCM_REWRITER_CFG(g_AdjustTrailingLines, "adjust-trailing-lines", rewrite_AdjustTrailingLines);
322SCM_REWRITER_CFG(g_SvnNoExecutable, "svn-no-executable", rewrite_SvnNoExecutable);
323SCM_REWRITER_CFG(g_SvnNoKeywords, "svn-no-keywords", rewrite_SvnNoKeywords);
324SCM_REWRITER_CFG(g_SvnNoEolStyle, "svn-no-eol-style", rewrite_SvnNoEolStyle);
325SCM_REWRITER_CFG(g_SvnBinary, "svn-binary", rewrite_SvnBinary);
326SCM_REWRITER_CFG(g_SvnKeywords, "svn-keywords", rewrite_SvnKeywords);
327SCM_REWRITER_CFG(g_SvnSyncProcess, "svn-sync-process", rewrite_SvnSyncProcess);
328SCM_REWRITER_CFG(g_UnicodeChecks, "unicode-checks", rewrite_UnicodeChecks);
329SCM_REWRITER_CFG(g_PageChecks, "page-checks", rewrite_PageChecks);
330SCM_REWRITER_CFG(g_ForceHrcVrcInsteadOfRc, "force-hrc-vrc-no-rc", rewrite_ForceHrcVrcInsteadOfRc);
331SCM_REWRITER_CFG(g_Copyright_CstyleComment, "copyright-c-style", rewrite_Copyright_CstyleComment);
332SCM_REWRITER_CFG(g_Copyright_HashComment, "copyright-hash-style", rewrite_Copyright_HashComment);
333SCM_REWRITER_CFG(g_Copyright_PythonComment, "copyright-python-style", rewrite_Copyright_PythonComment);
334SCM_REWRITER_CFG(g_Copyright_RemComment, "copyright-rem-style", rewrite_Copyright_RemComment);
335SCM_REWRITER_CFG(g_Copyright_SemicolonComment, "copyright-semicolon-style", rewrite_Copyright_SemicolonComment);
336SCM_REWRITER_CFG(g_Copyright_SqlComment, "copyright-sql-style", rewrite_Copyright_SqlComment);
337SCM_REWRITER_CFG(g_Copyright_TickComment, "copyright-tick-style", rewrite_Copyright_TickComment);
338SCM_REWRITER_CFG(g_Copyright_XmlComment, "copyright-xml-style", rewrite_Copyright_XmlComment);
339SCM_REWRITER_CFG(g_Makefile_kup, "makefile-kup", rewrite_Makefile_kup);
340SCM_REWRITER_CFG(g_Makefile_kmk, "makefile-kmk", rewrite_Makefile_kmk);
341SCM_REWRITER_CFG(g_FixFlowerBoxMarkers, "fix-flower-boxes", rewrite_FixFlowerBoxMarkers);
342SCM_REWRITER_CFG(g_FixHeaderGuards, "fix-header-guard", rewrite_FixHeaderGuards);
343SCM_REWRITER_CFG(g_Fix_C_and_CPP_Todos, "fix-c-todos", rewrite_Fix_C_and_CPP_Todos);
344SCM_REWRITER_CFG(g_Fix_Err_H, "fix-err-h", rewrite_Fix_Err_H);
345SCM_REWRITER_CFG(g_C_and_CPP, "c-and-cpp", rewrite_C_and_CPP);
346
347/** The rewriter actions. */
348static PCSCMREWRITERCFG const g_papRewriterActions[] =
349{
350 &g_StripTrailingBlanks,
351 &g_ExpandTabs,
352 &g_ForceNativeEol,
353 &g_ForceLF,
354 &g_ForceCRLF,
355 &g_AdjustTrailingLines,
356 &g_SvnNoExecutable,
357 &g_SvnNoKeywords,
358 &g_SvnNoEolStyle,
359 &g_SvnBinary,
360 &g_SvnKeywords,
361 &g_SvnSyncProcess,
362 &g_Copyright_CstyleComment,
363 &g_Copyright_HashComment,
364 &g_Copyright_PythonComment,
365 &g_Copyright_RemComment,
366 &g_Copyright_SemicolonComment,
367 &g_Copyright_SqlComment,
368 &g_Copyright_TickComment,
369 &g_Makefile_kup,
370 &g_Makefile_kmk,
371 &g_FixFlowerBoxMarkers,
372 &g_FixHeaderGuards,
373 &g_Fix_C_and_CPP_Todos,
374 &g_Fix_Err_H,
375 &g_UnicodeChecks,
376 &g_PageChecks,
377 &g_ForceHrcVrcInsteadOfRc,
378 &g_C_and_CPP,
379};
380
381
382static PCSCMREWRITERCFG const g_apRewritersFor_Makefile_kup[] =
383{
384 &g_SvnNoExecutable,
385 &g_SvnSyncProcess,
386 &g_UnicodeChecks,
387 &g_Makefile_kup
388};
389
390static PCSCMREWRITERCFG const g_apRewritersFor_Makefile_kmk[] =
391{
392 &g_ForceNativeEol,
393 &g_StripTrailingBlanks,
394 &g_AdjustTrailingLines,
395 &g_SvnNoExecutable,
396 &g_SvnKeywords,
397 &g_SvnSyncProcess,
398 &g_UnicodeChecks,
399 &g_Copyright_HashComment,
400 &g_Makefile_kmk
401};
402
403static PCSCMREWRITERCFG const g_apRewritersFor_OtherMakefiles[] =
404{
405 &g_ForceNativeEol,
406 &g_StripTrailingBlanks,
407 &g_AdjustTrailingLines,
408 &g_SvnNoExecutable,
409 &g_SvnKeywords,
410 &g_SvnSyncProcess,
411 &g_UnicodeChecks,
412 &g_Copyright_HashComment,
413};
414
415static PCSCMREWRITERCFG const g_apRewritersFor_C_and_CPP[] =
416{
417 &g_ForceNativeEol,
418 &g_ExpandTabs,
419 &g_StripTrailingBlanks,
420 &g_AdjustTrailingLines,
421 &g_SvnNoExecutable,
422 &g_SvnKeywords,
423 &g_SvnSyncProcess,
424 &g_UnicodeChecks,
425 &g_PageChecks,
426 &g_ForceHrcVrcInsteadOfRc,
427 &g_Copyright_CstyleComment,
428 &g_FixFlowerBoxMarkers,
429 &g_Fix_C_and_CPP_Todos,
430 &g_Fix_Err_H,
431 &g_C_and_CPP,
432};
433
434static PCSCMREWRITERCFG const g_apRewritersFor_H_and_HPP[] =
435{
436 &g_ForceNativeEol,
437 &g_ExpandTabs,
438 &g_StripTrailingBlanks,
439 &g_AdjustTrailingLines,
440 &g_SvnNoExecutable,
441 &g_SvnKeywords,
442 &g_SvnSyncProcess,
443 &g_UnicodeChecks,
444 &g_PageChecks,
445 &g_ForceHrcVrcInsteadOfRc,
446 &g_Copyright_CstyleComment,
447 /// @todo &g_FixFlowerBoxMarkers,
448 &g_FixHeaderGuards,
449 &g_C_and_CPP
450};
451
452static PCSCMREWRITERCFG const g_apRewritersFor_RC[] =
453{
454 &g_ForceNativeEol,
455 &g_ExpandTabs,
456 &g_StripTrailingBlanks,
457 &g_AdjustTrailingLines,
458 &g_SvnNoExecutable,
459 &g_SvnKeywords,
460 &g_SvnSyncProcess,
461 &g_UnicodeChecks,
462 &g_Copyright_CstyleComment,
463};
464
465static PCSCMREWRITERCFG const g_apRewritersFor_DTrace[] =
466{
467 &g_ForceNativeEol,
468 &g_ExpandTabs,
469 &g_StripTrailingBlanks,
470 &g_AdjustTrailingLines,
471 &g_SvnKeywords,
472 &g_SvnSyncProcess,
473 &g_UnicodeChecks,
474 &g_Copyright_CstyleComment,
475};
476
477static PCSCMREWRITERCFG const g_apRewritersFor_DSL[] =
478{
479 &g_ForceNativeEol,
480 &g_ExpandTabs,
481 &g_StripTrailingBlanks,
482 &g_AdjustTrailingLines,
483 &g_SvnNoExecutable,
484 &g_SvnKeywords,
485 &g_SvnSyncProcess,
486 &g_UnicodeChecks,
487 &g_Copyright_CstyleComment,
488};
489
490static PCSCMREWRITERCFG const g_apRewritersFor_ASM[] =
491{
492 &g_ForceNativeEol,
493 &g_ExpandTabs,
494 &g_StripTrailingBlanks,
495 &g_AdjustTrailingLines,
496 &g_SvnNoExecutable,
497 &g_SvnKeywords,
498 &g_SvnSyncProcess,
499 &g_UnicodeChecks,
500 &g_Copyright_SemicolonComment,
501};
502
503static PCSCMREWRITERCFG const g_apRewritersFor_DEF[] =
504{
505 &g_ForceNativeEol,
506 &g_ExpandTabs,
507 &g_StripTrailingBlanks,
508 &g_AdjustTrailingLines,
509 &g_SvnNoExecutable,
510 &g_SvnKeywords,
511 &g_SvnSyncProcess,
512 &g_UnicodeChecks,
513 &g_Copyright_SemicolonComment,
514};
515
516static PCSCMREWRITERCFG const g_apRewritersFor_ShellScripts[] =
517{
518 &g_ForceLF,
519 &g_ExpandTabs,
520 &g_StripTrailingBlanks,
521 &g_SvnSyncProcess,
522 &g_UnicodeChecks,
523 &g_Copyright_HashComment,
524};
525
526static PCSCMREWRITERCFG const g_apRewritersFor_BatchFiles[] =
527{
528 &g_ForceCRLF,
529 &g_ExpandTabs,
530 &g_StripTrailingBlanks,
531 &g_SvnSyncProcess,
532 &g_UnicodeChecks,
533 &g_Copyright_RemComment,
534};
535
536static PCSCMREWRITERCFG const g_apRewritersFor_BasicScripts[] =
537{
538 &g_ForceCRLF,
539 &g_ExpandTabs,
540 &g_StripTrailingBlanks,
541 &g_SvnSyncProcess,
542 &g_UnicodeChecks,
543 &g_Copyright_TickComment,
544};
545
546static PCSCMREWRITERCFG const g_apRewritersFor_SedScripts[] =
547{
548 &g_ForceLF,
549 &g_ExpandTabs,
550 &g_StripTrailingBlanks,
551 &g_SvnSyncProcess,
552 &g_UnicodeChecks,
553 &g_Copyright_HashComment,
554};
555
556static PCSCMREWRITERCFG const g_apRewritersFor_Python[] =
557{
558 /** @todo &g_ForceLFIfExecutable */
559 &g_ExpandTabs,
560 &g_StripTrailingBlanks,
561 &g_AdjustTrailingLines,
562 &g_SvnKeywords,
563 &g_SvnSyncProcess,
564 &g_UnicodeChecks,
565 &g_Copyright_PythonComment,
566};
567
568static PCSCMREWRITERCFG const g_apRewritersFor_Perl[] =
569{
570 /** @todo &g_ForceLFIfExecutable */
571 &g_ExpandTabs,
572 &g_StripTrailingBlanks,
573 &g_AdjustTrailingLines,
574 &g_SvnKeywords,
575 &g_SvnSyncProcess,
576 &g_UnicodeChecks,
577 &g_Copyright_HashComment,
578};
579
580static PCSCMREWRITERCFG const g_apRewritersFor_DriverInfFiles[] =
581{
582 &g_ForceNativeEol,
583 &g_ExpandTabs,
584 &g_StripTrailingBlanks,
585 &g_AdjustTrailingLines,
586 &g_SvnKeywords,
587 &g_SvnNoExecutable,
588 &g_SvnSyncProcess,
589 &g_UnicodeChecks,
590 &g_Copyright_SemicolonComment,
591};
592
593static PCSCMREWRITERCFG const g_apRewritersFor_NsisFiles[] =
594{
595 &g_ForceNativeEol,
596 &g_ExpandTabs,
597 &g_StripTrailingBlanks,
598 &g_AdjustTrailingLines,
599 &g_SvnKeywords,
600 &g_SvnNoExecutable,
601 &g_SvnSyncProcess,
602 &g_UnicodeChecks,
603 &g_Copyright_SemicolonComment,
604};
605
606static PCSCMREWRITERCFG const g_apRewritersFor_Java[] =
607{
608 &g_ForceNativeEol,
609 &g_ExpandTabs,
610 &g_StripTrailingBlanks,
611 &g_AdjustTrailingLines,
612 &g_SvnNoExecutable,
613 &g_SvnKeywords,
614 &g_SvnSyncProcess,
615 &g_UnicodeChecks,
616 &g_Copyright_CstyleComment,
617 &g_FixFlowerBoxMarkers,
618 &g_Fix_C_and_CPP_Todos,
619};
620
621static PCSCMREWRITERCFG const g_apRewritersFor_ScmSettings[] =
622{
623 &g_ForceNativeEol,
624 &g_ExpandTabs,
625 &g_StripTrailingBlanks,
626 &g_AdjustTrailingLines,
627 &g_SvnNoExecutable,
628 &g_SvnKeywords,
629 &g_SvnSyncProcess,
630 &g_UnicodeChecks,
631 &g_Copyright_HashComment,
632};
633
634static PCSCMREWRITERCFG const g_apRewritersFor_Images[] =
635{
636 &g_SvnNoExecutable,
637 &g_SvnBinary,
638 &g_SvnSyncProcess,
639};
640
641static PCSCMREWRITERCFG const g_apRewritersFor_Xslt[] =
642{
643 &g_ForceNativeEol,
644 &g_ExpandTabs,
645 &g_StripTrailingBlanks,
646 &g_AdjustTrailingLines,
647 &g_SvnNoExecutable,
648 &g_SvnKeywords,
649 &g_SvnSyncProcess,
650 &g_UnicodeChecks,
651 &g_Copyright_XmlComment,
652};
653
654static PCSCMREWRITERCFG const g_apRewritersFor_Xml[] =
655{
656 &g_ForceNativeEol,
657 &g_ExpandTabs,
658 &g_StripTrailingBlanks,
659 &g_AdjustTrailingLines,
660 &g_SvnNoExecutable,
661 &g_SvnKeywords,
662 &g_SvnSyncProcess,
663 &g_UnicodeChecks,
664 &g_Copyright_XmlComment,
665};
666
667static PCSCMREWRITERCFG const g_apRewritersFor_Wix[] =
668{
669 &g_ForceNativeEol,
670 &g_ExpandTabs,
671 &g_StripTrailingBlanks,
672 &g_AdjustTrailingLines,
673 &g_SvnNoExecutable,
674 &g_SvnKeywords,
675 &g_SvnSyncProcess,
676 &g_UnicodeChecks,
677 &g_Copyright_XmlComment,
678};
679
680static PCSCMREWRITERCFG const g_apRewritersFor_QtProject[] =
681{
682 &g_ForceNativeEol,
683 &g_StripTrailingBlanks,
684 &g_AdjustTrailingLines,
685 &g_SvnNoExecutable,
686 &g_SvnKeywords,
687 &g_SvnSyncProcess,
688 &g_UnicodeChecks,
689 &g_Copyright_HashComment,
690};
691
692static PCSCMREWRITERCFG const g_apRewritersFor_QtResourceFiles[] =
693{
694 &g_ForceNativeEol,
695 &g_SvnNoExecutable,
696 &g_SvnKeywords,
697 &g_SvnSyncProcess,
698 &g_UnicodeChecks,
699 /** @todo figure out copyright for Qt resource XML files. */
700};
701
702static PCSCMREWRITERCFG const g_apRewritersFor_QtTranslations[] =
703{
704 &g_ForceNativeEol,
705 &g_SvnNoExecutable,
706};
707
708static PCSCMREWRITERCFG const g_apRewritersFor_QtUiFiles[] =
709{
710 &g_ForceNativeEol,
711 &g_SvnNoExecutable,
712 &g_SvnKeywords,
713 &g_SvnSyncProcess,
714 &g_UnicodeChecks,
715 /** @todo copyright is in an XML 'comment' element. */
716};
717
718static PCSCMREWRITERCFG const g_apRewritersFor_SifFiles[] =
719{
720 &g_ForceCRLF,
721 &g_ExpandTabs,
722 &g_StripTrailingBlanks,
723 &g_AdjustTrailingLines,
724 &g_SvnKeywords,
725 &g_SvnNoExecutable,
726 &g_SvnSyncProcess,
727 &g_UnicodeChecks,
728 &g_Copyright_SemicolonComment,
729};
730
731static PCSCMREWRITERCFG const g_apRewritersFor_SqlFiles[] =
732{
733 &g_ForceNativeEol,
734 &g_ExpandTabs,
735 &g_StripTrailingBlanks,
736 &g_AdjustTrailingLines,
737 &g_SvnKeywords,
738 &g_SvnNoExecutable,
739 &g_SvnSyncProcess,
740 &g_UnicodeChecks,
741 &g_Copyright_SqlComment,
742};
743
744static PCSCMREWRITERCFG const g_apRewritersFor_GnuAsm[] =
745{
746 &g_ForceNativeEol,
747 &g_ExpandTabs,
748 &g_StripTrailingBlanks,
749 &g_AdjustTrailingLines,
750 &g_SvnKeywords,
751 &g_SvnNoExecutable,
752 &g_SvnSyncProcess,
753 &g_UnicodeChecks,
754 &g_Copyright_CstyleComment,
755};
756
757static PCSCMREWRITERCFG const g_apRewritersFor_TextFiles[] =
758{
759 &g_ForceNativeEol,
760 &g_StripTrailingBlanks,
761 &g_SvnKeywords,
762 &g_SvnNoExecutable,
763 &g_SvnSyncProcess,
764 &g_UnicodeChecks,
765 /** @todo check for plain copyright + license in text files. */
766};
767
768static PCSCMREWRITERCFG const g_apRewritersFor_PlainTextFiles[] =
769{
770 &g_ForceNativeEol,
771 &g_StripTrailingBlanks,
772 &g_SvnKeywords,
773 &g_SvnNoExecutable,
774 &g_SvnSyncProcess,
775 &g_UnicodeChecks,
776};
777
778static PCSCMREWRITERCFG const g_apRewritersFor_BinaryFiles[] =
779{
780 &g_SvnBinary,
781 &g_SvnSyncProcess,
782};
783
784static PCSCMREWRITERCFG const g_apRewritersFor_FileLists[] = /* both makefile and shell script */
785{
786 &g_ForceLF,
787 &g_ExpandTabs,
788 &g_StripTrailingBlanks,
789 &g_AdjustTrailingLines,
790 &g_SvnSyncProcess,
791 &g_UnicodeChecks,
792 &g_Copyright_HashComment,
793};
794
795
796/**
797 * Array of standard rewriter configurations.
798 */
799static SCMCFGENTRY const g_aConfigs[] =
800{
801#define SCM_CFG_ENTRY(a_szName, a_aRewriters, a_fBinary, a_szFilePatterns) \
802 { RT_ELEMENTS(a_aRewriters), &a_aRewriters[0], a_fBinary, a_szFilePatterns, a_szName }
803 SCM_CFG_ENTRY("kup", g_apRewritersFor_Makefile_kup, false, "Makefile.kup" ),
804 SCM_CFG_ENTRY("kmk", g_apRewritersFor_Makefile_kmk, false, "*.kmk" ),
805 SCM_CFG_ENTRY("c", g_apRewritersFor_C_and_CPP, false, "*.c|*.cpp|*.C|*.CPP|*.cxx|*.cc|*.m|*.mm|*.lds" ),
806 SCM_CFG_ENTRY("h", g_apRewritersFor_H_and_HPP, false, "*.h|*.hpp" ),
807 SCM_CFG_ENTRY("rc", g_apRewritersFor_RC, false, "*.rc" ),
808 SCM_CFG_ENTRY("asm", g_apRewritersFor_ASM, false, "*.asm|*.mac|*.inc" ),
809 SCM_CFG_ENTRY("dtrace", g_apRewritersFor_DTrace, false, "*.d" ),
810 SCM_CFG_ENTRY("def", g_apRewritersFor_DEF, false, "*.def" ),
811 SCM_CFG_ENTRY("iasl", g_apRewritersFor_DSL, false, "*.dsl" ),
812 SCM_CFG_ENTRY("shell", g_apRewritersFor_ShellScripts, false, "*.sh|configure" ),
813 SCM_CFG_ENTRY("batch", g_apRewritersFor_BatchFiles, false, "*.bat|*.cmd|*.btm" ),
814 SCM_CFG_ENTRY("vbs", g_apRewritersFor_BasicScripts, false, "*.vbs|*.vb" ),
815 SCM_CFG_ENTRY("sed", g_apRewritersFor_SedScripts, false, "*.sed" ),
816 SCM_CFG_ENTRY("python", g_apRewritersFor_Python, false, "*.py" ),
817 SCM_CFG_ENTRY("perl", g_apRewritersFor_Perl, false, "*.pl|*.pm" ),
818 SCM_CFG_ENTRY("drvinf", g_apRewritersFor_DriverInfFiles, false, "*.inf" ),
819 SCM_CFG_ENTRY("nsis", g_apRewritersFor_NsisFiles, false, "*.nsh|*.nsi|*.nsis" ),
820 SCM_CFG_ENTRY("java", g_apRewritersFor_Java, false, "*.java" ),
821 SCM_CFG_ENTRY("scm", g_apRewritersFor_ScmSettings, false, "*.scm-settings" ),
822 SCM_CFG_ENTRY("image", g_apRewritersFor_Images, true, "*.png|*.bmp|*.jpg|*.pnm|*.ico|*.icns|*.tiff|*.tif|*.xcf|*.gif" ),
823 SCM_CFG_ENTRY("xslt", g_apRewritersFor_Xslt, false, "*.xsl" ),
824 SCM_CFG_ENTRY("xml", g_apRewritersFor_Xml, false, "*.xml|*.dist|*.qhcp" ),
825 SCM_CFG_ENTRY("wix", g_apRewritersFor_Wix, false, "*.wxi|*.wxs|*.wxl" ),
826 SCM_CFG_ENTRY("qt-pro", g_apRewritersFor_QtProject, false, "*.pro" ),
827 SCM_CFG_ENTRY("qt-rc", g_apRewritersFor_QtResourceFiles, false, "*.qrc" ),
828 SCM_CFG_ENTRY("qt-ts", g_apRewritersFor_QtTranslations, false, "*.ts" ),
829 SCM_CFG_ENTRY("qt-ui", g_apRewritersFor_QtUiFiles, false, "*.ui" ),
830 SCM_CFG_ENTRY("sif", g_apRewritersFor_SifFiles, false, "*.sif" ),
831 SCM_CFG_ENTRY("sql", g_apRewritersFor_SqlFiles, false, "*.pgsql|*.sql" ),
832 SCM_CFG_ENTRY("gas", g_apRewritersFor_GnuAsm, false, "*.S" ),
833 SCM_CFG_ENTRY("binary", g_apRewritersFor_BinaryFiles, true, "*.bin|*.pdf|*.zip|*.bz2|*.gz" ),
834 /* These should be be last: */
835 SCM_CFG_ENTRY("make", g_apRewritersFor_OtherMakefiles, false, "Makefile|makefile|GNUmakefile|SMakefile|Makefile.am|Makefile.in|*.cmake|*.gmk" ),
836 SCM_CFG_ENTRY("text", g_apRewritersFor_TextFiles, false, "*.txt|README*|readme*|ReadMe*|NOTE*|TODO*" ),
837 SCM_CFG_ENTRY("plaintext", g_apRewritersFor_PlainTextFiles, false, "LICENSE|ChangeLog|FAQ|AUTHORS|INSTALL|NEWS" ),
838 SCM_CFG_ENTRY("file-list", g_apRewritersFor_FileLists, false, "files_*" ),
839};
840
841
842
843/* -=-=-=-=-=- settings -=-=-=-=-=- */
844
845/**
846 * Delete the given config entry.
847 *
848 * @param pEntry The configuration entry to delete.
849 */
850static void scmCfgEntryDelete(PSCMCFGENTRY pEntry)
851{
852 RTMemFree((void *)pEntry->paRewriters);
853 pEntry->paRewriters = NULL;
854 RTMemFree(pEntry);
855}
856
857/**
858 * Create a new configuration entry.
859 *
860 * @returns The new entry. NULL if out of memory.
861 * @param pEntry The configuration entry to duplicate.
862 */
863static PSCMCFGENTRY scmCfgEntryNew(void)
864{
865 PSCMCFGENTRY pNew = (PSCMCFGENTRY)RTMemAlloc(sizeof(*pNew));
866 if (pNew)
867 {
868 pNew->pszName = "custom";
869 pNew->pszFilePattern = "custom";
870 pNew->cRewriters = 0;
871 pNew->paRewriters = NULL;
872 pNew->fBinary = false;
873 }
874 return pNew;
875}
876
877/**
878 * Duplicate the given config entry.
879 *
880 * @returns The duplicate. NULL if out of memory.
881 * @param pEntry The configuration entry to duplicate.
882 */
883static PSCMCFGENTRY scmCfgEntryDup(PCSCMCFGENTRY pEntry)
884{
885 if (pEntry)
886 {
887 PSCMCFGENTRY pDup = (PSCMCFGENTRY)RTMemDup(pEntry, sizeof(*pEntry));
888 if (pDup)
889 {
890 size_t cbSrcRewriters = sizeof(pEntry->paRewriters[0]) * pEntry->cRewriters;
891 size_t cbDstRewriters = sizeof(pEntry->paRewriters[0]) * RT_ALIGN_Z(pEntry->cRewriters, 8);
892 pDup->paRewriters = (PCSCMREWRITERCFG const *)RTMemDupEx(pEntry->paRewriters, cbSrcRewriters,
893 cbDstRewriters - cbSrcRewriters);
894 if (pDup->paRewriters)
895 return pDup;
896
897 RTMemFree(pDup);
898 }
899 return NULL;
900 }
901 return scmCfgEntryNew();
902}
903
904/**
905 * Adds a rewriter action to the given config entry (--add-action).
906 *
907 * @returns VINF_SUCCESS.
908 * @param pEntry The configuration entry.
909 * @param pAction The rewriter action to add.
910 */
911static int scmCfgEntryAddAction(PSCMCFGENTRY pEntry, PCSCMREWRITERCFG pAction)
912{
913 PCSCMREWRITERCFG *paRewriters = (PCSCMREWRITERCFG *)pEntry->paRewriters;
914 if (pEntry->cRewriters % 8 == 0)
915 {
916 size_t cbRewriters = sizeof(pEntry->paRewriters[0]) * RT_ALIGN_Z((pEntry->cRewriters + 1), 8);
917 void *pvNew = RTMemRealloc(paRewriters, cbRewriters);
918 if (pvNew)
919 pEntry->paRewriters = paRewriters = (PCSCMREWRITERCFG *)pvNew;
920 else
921 return VERR_NO_MEMORY;
922 }
923
924 paRewriters[pEntry->cRewriters++] = pAction;
925 return VINF_SUCCESS;
926}
927
928/**
929 * Delets an rewriter action from the given config entry (--del-action).
930 *
931 * @param pEntry The configuration entry.
932 * @param pAction The rewriter action to remove.
933 */
934static void scmCfgEntryDelAction(PSCMCFGENTRY pEntry, PCSCMREWRITERCFG pAction)
935{
936 PCSCMREWRITERCFG *paRewriters = (PCSCMREWRITERCFG *)pEntry->paRewriters;
937 size_t const cEntries = pEntry->cRewriters;
938 size_t iDst = 0;
939 for (size_t iSrc = 0; iSrc < cEntries; iSrc++)
940 {
941 PCSCMREWRITERCFG pCurAction = paRewriters[iSrc];
942 if (pCurAction != pAction)
943 paRewriters[iDst++] = pCurAction;
944 }
945 pEntry->cRewriters = iDst;
946}
947
948/**
949 * Init a settings structure with settings from @a pSrc.
950 *
951 * @returns IPRT status code
952 * @param pSettings The settings.
953 * @param pSrc The source settings.
954 */
955static int scmSettingsBaseInitAndCopy(PSCMSETTINGSBASE pSettings, PCSCMSETTINGSBASE pSrc)
956{
957 *pSettings = *pSrc;
958
959 int rc = RTStrDupEx(&pSettings->pszFilterFiles, pSrc->pszFilterFiles);
960 if (RT_SUCCESS(rc))
961 {
962 rc = RTStrDupEx(&pSettings->pszFilterOutFiles, pSrc->pszFilterOutFiles);
963 if (RT_SUCCESS(rc))
964 {
965 rc = RTStrDupEx(&pSettings->pszFilterOutDirs, pSrc->pszFilterOutDirs);
966 if (RT_SUCCESS(rc))
967 {
968 rc = RTStrDupEx(&pSettings->pszGuardPrefix, pSrc->pszGuardPrefix);
969 if (RT_SUCCESS(rc))
970 {
971 if (pSrc->pszGuardRelativeToDir)
972 rc = RTStrDupEx(&pSettings->pszGuardRelativeToDir, pSrc->pszGuardRelativeToDir);
973 if (RT_SUCCESS(rc))
974 {
975
976 if (!pSrc->fFreeTreatAs)
977 return VINF_SUCCESS;
978
979 pSettings->pTreatAs = scmCfgEntryDup(pSrc->pTreatAs);
980 if (pSettings->pTreatAs)
981 return VINF_SUCCESS;
982
983 RTStrFree(pSettings->pszGuardRelativeToDir);
984 }
985 RTStrFree(pSettings->pszGuardPrefix);
986 }
987 }
988 RTStrFree(pSettings->pszFilterOutFiles);
989 }
990 RTStrFree(pSettings->pszFilterFiles);
991 }
992
993 pSettings->pszGuardRelativeToDir = NULL;
994 pSettings->pszGuardPrefix = NULL;
995 pSettings->pszFilterFiles = NULL;
996 pSettings->pszFilterOutFiles = NULL;
997 pSettings->pszFilterOutDirs = NULL;
998 pSettings->pTreatAs = NULL;
999 return rc;
1000}
1001
1002/**
1003 * Init a settings structure.
1004 *
1005 * @returns IPRT status code
1006 * @param pSettings The settings.
1007 */
1008static int scmSettingsBaseInit(PSCMSETTINGSBASE pSettings)
1009{
1010 return scmSettingsBaseInitAndCopy(pSettings, &g_Defaults);
1011}
1012
1013/**
1014 * Deletes the settings, i.e. free any dynamically allocated content.
1015 *
1016 * @param pSettings The settings.
1017 */
1018static void scmSettingsBaseDelete(PSCMSETTINGSBASE pSettings)
1019{
1020 if (pSettings)
1021 {
1022 Assert(pSettings->cchTab != UINT8_MAX);
1023 pSettings->cchTab = UINT8_MAX;
1024
1025 RTStrFree(pSettings->pszGuardPrefix);
1026 RTStrFree(pSettings->pszGuardRelativeToDir);
1027 RTStrFree(pSettings->pszFilterFiles);
1028 RTStrFree(pSettings->pszFilterOutFiles);
1029 RTStrFree(pSettings->pszFilterOutDirs);
1030 if (pSettings->fFreeTreatAs)
1031 scmCfgEntryDelete((PSCMCFGENTRY)pSettings->pTreatAs);
1032
1033 pSettings->pszGuardPrefix = NULL;
1034 pSettings->pszGuardRelativeToDir = NULL;
1035 pSettings->pszFilterOutDirs = NULL;
1036 pSettings->pszFilterOutFiles = NULL;
1037 pSettings->pszFilterFiles = NULL;
1038 pSettings->pTreatAs = NULL;
1039 pSettings->fFreeTreatAs = false;
1040 }
1041}
1042
1043/**
1044 * Processes a RTGetOpt result.
1045 *
1046 * @retval VINF_SUCCESS if handled.
1047 * @retval VERR_OUT_OF_RANGE if the option value was out of range.
1048 * @retval VERR_GETOPT_UNKNOWN_OPTION if the option was not recognized.
1049 *
1050 * @param pSettings The settings to change.
1051 * @param rc The RTGetOpt return value.
1052 * @param pValueUnion The RTGetOpt value union.
1053 * @param pchDir The absolute path to the directory relative
1054 * components in pchLine should be relative to.
1055 * @param cchDir The length of the @a pchDir string.
1056 */
1057static int scmSettingsBaseHandleOpt(PSCMSETTINGSBASE pSettings, int rc, PRTGETOPTUNION pValueUnion,
1058 const char *pchDir, size_t cchDir)
1059{
1060 Assert(pchDir[cchDir - 1] == '/');
1061
1062 switch (rc)
1063 {
1064 case SCMOPT_CONVERT_EOL:
1065 pSettings->fConvertEol = true;
1066 return VINF_SUCCESS;
1067 case SCMOPT_NO_CONVERT_EOL:
1068 pSettings->fConvertEol = false;
1069 return VINF_SUCCESS;
1070
1071 case SCMOPT_CONVERT_TABS:
1072 pSettings->fConvertTabs = true;
1073 return VINF_SUCCESS;
1074 case SCMOPT_NO_CONVERT_TABS:
1075 pSettings->fConvertTabs = false;
1076 return VINF_SUCCESS;
1077
1078 case SCMOPT_FORCE_FINAL_EOL:
1079 pSettings->fForceFinalEol = true;
1080 return VINF_SUCCESS;
1081 case SCMOPT_NO_FORCE_FINAL_EOL:
1082 pSettings->fForceFinalEol = false;
1083 return VINF_SUCCESS;
1084
1085 case SCMOPT_FORCE_TRAILING_LINE:
1086 pSettings->fForceTrailingLine = true;
1087 return VINF_SUCCESS;
1088 case SCMOPT_NO_FORCE_TRAILING_LINE:
1089 pSettings->fForceTrailingLine = false;
1090 return VINF_SUCCESS;
1091
1092
1093 case SCMOPT_STRIP_TRAILING_BLANKS:
1094 pSettings->fStripTrailingBlanks = true;
1095 return VINF_SUCCESS;
1096 case SCMOPT_NO_STRIP_TRAILING_BLANKS:
1097 pSettings->fStripTrailingBlanks = false;
1098 return VINF_SUCCESS;
1099
1100 case SCMOPT_MIN_BLANK_LINES_BEFORE_FLOWER_BOX_MARKERS:
1101 pSettings->cMinBlankLinesBeforeFlowerBoxMakers = pValueUnion->u8;
1102 return VINF_SUCCESS;
1103
1104
1105 case SCMOPT_STRIP_TRAILING_LINES:
1106 pSettings->fStripTrailingLines = true;
1107 return VINF_SUCCESS;
1108 case SCMOPT_NO_STRIP_TRAILING_LINES:
1109 pSettings->fStripTrailingLines = false;
1110 return VINF_SUCCESS;
1111
1112 case SCMOPT_FIX_FLOWER_BOX_MARKERS:
1113 pSettings->fFixFlowerBoxMarkers = true;
1114 return VINF_SUCCESS;
1115 case SCMOPT_NO_FIX_FLOWER_BOX_MARKERS:
1116 pSettings->fFixFlowerBoxMarkers = false;
1117 return VINF_SUCCESS;
1118
1119 case SCMOPT_FIX_HEADER_GUARDS:
1120 pSettings->fFixHeaderGuards = true;
1121 return VINF_SUCCESS;
1122 case SCMOPT_NO_FIX_HEADER_GUARDS:
1123 pSettings->fFixHeaderGuards = false;
1124 return VINF_SUCCESS;
1125
1126 case SCMOPT_PRAGMA_ONCE:
1127 pSettings->fPragmaOnce = true;
1128 return VINF_SUCCESS;
1129 case SCMOPT_NO_PRAGMA_ONCE:
1130 pSettings->fPragmaOnce = false;
1131 return VINF_SUCCESS;
1132
1133 case SCMOPT_FIX_HEADER_GUARD_ENDIF:
1134 pSettings->fFixHeaderGuardEndif = true;
1135 return VINF_SUCCESS;
1136 case SCMOPT_NO_FIX_HEADER_GUARD_ENDIF:
1137 pSettings->fFixHeaderGuardEndif = false;
1138 return VINF_SUCCESS;
1139
1140 case SCMOPT_ENDIF_GUARD_COMMENT:
1141 pSettings->fEndifGuardComment = true;
1142 return VINF_SUCCESS;
1143 case SCMOPT_NO_ENDIF_GUARD_COMMENT:
1144 pSettings->fEndifGuardComment = false;
1145 return VINF_SUCCESS;
1146
1147 case SCMOPT_GUARD_PREFIX:
1148 RTStrFree(pSettings->pszGuardPrefix);
1149 pSettings->pszGuardPrefix = NULL;
1150 return RTStrDupEx(&pSettings->pszGuardPrefix, pValueUnion->psz);
1151
1152 case SCMOPT_GUARD_RELATIVE_TO_DIR:
1153 RTStrFree(pSettings->pszGuardRelativeToDir);
1154 pSettings->pszGuardRelativeToDir = NULL;
1155 if (*pValueUnion->psz != '\0')
1156 {
1157 if ( strcmp(pValueUnion->psz, "{dir}") == 0
1158 || strcmp(pValueUnion->psz, "{parent}") == 0)
1159 return RTStrDupEx(&pSettings->pszGuardRelativeToDir, pValueUnion->psz);
1160 if (cchDir == 1 && *pchDir == '/')
1161 {
1162 pSettings->pszGuardRelativeToDir = RTPathAbsDup(pValueUnion->psz);
1163 if (pSettings->pszGuardRelativeToDir)
1164 return VINF_SUCCESS;
1165 }
1166 else
1167 {
1168 char *pszDir = RTStrDupN(pchDir, cchDir);
1169 if (pszDir)
1170 {
1171 pSettings->pszGuardRelativeToDir = RTPathAbsExDup(pszDir, pValueUnion->psz, RTPATH_STR_F_STYLE_HOST);
1172 RTStrFree(pszDir);
1173 if (pSettings->pszGuardRelativeToDir)
1174 return VINF_SUCCESS;
1175 }
1176 }
1177 RTMsgError("Failed to abspath --guard-relative-to-dir value '%s' - probably out of memory\n", pValueUnion->psz);
1178 return VERR_NO_STR_MEMORY;
1179 }
1180 return VINF_SUCCESS;
1181
1182 case SCMOPT_FIX_TODOS:
1183 pSettings->fFixTodos = true;
1184 return VINF_SUCCESS;
1185 case SCMOPT_NO_FIX_TODOS:
1186 pSettings->fFixTodos = false;
1187 return VINF_SUCCESS;
1188
1189 case SCMOPT_FIX_ERR_H:
1190 pSettings->fFixErrH = true;
1191 return VINF_SUCCESS;
1192 case SCMOPT_NO_FIX_ERR_H:
1193 pSettings->fFixErrH = false;
1194 return VINF_SUCCESS;
1195
1196 case SCMOPT_ONLY_GUEST_HOST_PAGE:
1197 pSettings->fOnlyGuestHostPage = true;
1198 return VINF_SUCCESS;
1199 case SCMOPT_NO_PAGE_RESTRICTIONS:
1200 pSettings->fOnlyGuestHostPage = false;
1201 return VINF_SUCCESS;
1202
1203 case SCMOPT_NO_ASM_MEM_PAGE_USE:
1204 pSettings->fNoASMMemPageUse = true;
1205 return VINF_SUCCESS;
1206 case SCMOPT_UNRESTRICTED_ASM_MEM_PAGE_USE:
1207 pSettings->fNoASMMemPageUse = false;
1208 return VINF_SUCCESS;
1209
1210 case SCMOPT_NO_RC_USE:
1211 pSettings->fOnlyHrcVrcInsteadOfRc = true;
1212 return VINF_SUCCESS;
1213 case SCMOPT_UNRESTRICTED_RC_USE:
1214 pSettings->fOnlyHrcVrcInsteadOfRc = false;
1215 return VINF_SUCCESS;
1216
1217 case SCMOPT_UPDATE_COPYRIGHT_YEAR:
1218 pSettings->fUpdateCopyrightYear = true;
1219 return VINF_SUCCESS;
1220 case SCMOPT_NO_UPDATE_COPYRIGHT_YEAR:
1221 pSettings->fUpdateCopyrightYear = false;
1222 return VINF_SUCCESS;
1223
1224 case SCMOPT_EXTERNAL_COPYRIGHT:
1225 pSettings->fExternalCopyright = true;
1226 return VINF_SUCCESS;
1227 case SCMOPT_NO_EXTERNAL_COPYRIGHT:
1228 pSettings->fExternalCopyright = false;
1229 return VINF_SUCCESS;
1230
1231 case SCMOPT_NO_UPDATE_LICENSE:
1232 pSettings->enmUpdateLicense = kScmLicense_LeaveAlone;
1233 return VINF_SUCCESS;
1234 case SCMOPT_LICENSE_OSE_GPL:
1235 pSettings->enmUpdateLicense = kScmLicense_OseGpl;
1236 return VINF_SUCCESS;
1237 case SCMOPT_LICENSE_OSE_DUAL_GPL_CDDL:
1238 pSettings->enmUpdateLicense = kScmLicense_OseDualGplCddl;
1239 return VINF_SUCCESS;
1240 case SCMOPT_LICENSE_OSE_CDDL:
1241 pSettings->enmUpdateLicense = kScmLicense_OseCddl;
1242 return VINF_SUCCESS;
1243 case SCMOPT_LICENSE_LGPL:
1244 pSettings->enmUpdateLicense = kScmLicense_Lgpl;
1245 return VINF_SUCCESS;
1246 case SCMOPT_LICENSE_MIT:
1247 pSettings->enmUpdateLicense = kScmLicense_Mit;
1248 return VINF_SUCCESS;
1249 case SCMOPT_LICENSE_BASED_ON_MIT:
1250 pSettings->enmUpdateLicense = kScmLicense_BasedOnMit;
1251 return VINF_SUCCESS;
1252
1253 case SCMOPT_LGPL_DISCLAIMER:
1254 pSettings->fLgplDisclaimer = true;
1255 return VINF_SUCCESS;
1256 case SCMOPT_NO_LGPL_DISCLAIMER:
1257 pSettings->fLgplDisclaimer = false;
1258 return VINF_SUCCESS;
1259
1260 case SCMOPT_ONLY_SVN_DIRS:
1261 pSettings->fOnlySvnDirs = true;
1262 return VINF_SUCCESS;
1263 case SCMOPT_NOT_ONLY_SVN_DIRS:
1264 pSettings->fOnlySvnDirs = false;
1265 return VINF_SUCCESS;
1266
1267 case SCMOPT_ONLY_SVN_FILES:
1268 pSettings->fOnlySvnFiles = true;
1269 return VINF_SUCCESS;
1270 case SCMOPT_NOT_ONLY_SVN_FILES:
1271 pSettings->fOnlySvnFiles = false;
1272 return VINF_SUCCESS;
1273
1274 case SCMOPT_SET_SVN_EOL:
1275 pSettings->fSetSvnEol = true;
1276 return VINF_SUCCESS;
1277 case SCMOPT_DONT_SET_SVN_EOL:
1278 pSettings->fSetSvnEol = false;
1279 return VINF_SUCCESS;
1280
1281 case SCMOPT_SET_SVN_EXECUTABLE:
1282 pSettings->fSetSvnExecutable = true;
1283 return VINF_SUCCESS;
1284 case SCMOPT_DONT_SET_SVN_EXECUTABLE:
1285 pSettings->fSetSvnExecutable = false;
1286 return VINF_SUCCESS;
1287
1288 case SCMOPT_SET_SVN_KEYWORDS:
1289 pSettings->fSetSvnKeywords = true;
1290 return VINF_SUCCESS;
1291 case SCMOPT_DONT_SET_SVN_KEYWORDS:
1292 pSettings->fSetSvnKeywords = false;
1293 return VINF_SUCCESS;
1294
1295 case SCMOPT_SKIP_SVN_SYNC_PROCESS:
1296 pSettings->fSkipSvnSyncProcess = true;
1297 return VINF_SUCCESS;
1298 case SCMOPT_DONT_SKIP_SVN_SYNC_PROCESS:
1299 pSettings->fSkipSvnSyncProcess = false;
1300 return VINF_SUCCESS;
1301
1302 case SCMOPT_SKIP_UNICODE_CHECKS:
1303 pSettings->fSkipUnicodeChecks = true;
1304 return VINF_SUCCESS;
1305 case SCMOPT_DONT_SKIP_UNICODE_CHECKS:
1306 pSettings->fSkipUnicodeChecks = false;
1307 return VINF_SUCCESS;
1308
1309 case SCMOPT_TAB_SIZE:
1310 if ( pValueUnion->u8 < 1
1311 || pValueUnion->u8 >= RT_ELEMENTS(g_szTabSpaces))
1312 {
1313 RTMsgError("Invalid tab size: %u - must be in {1..%u}\n",
1314 pValueUnion->u8, RT_ELEMENTS(g_szTabSpaces) - 1);
1315 return VERR_OUT_OF_RANGE;
1316 }
1317 pSettings->cchTab = pValueUnion->u8;
1318 return VINF_SUCCESS;
1319
1320 case SCMOPT_WIDTH:
1321 if (pValueUnion->u8 < 20 || pValueUnion->u8 > 200)
1322 {
1323 RTMsgError("Invalid width size: %u - must be in {20..200} range\n", pValueUnion->u8);
1324 return VERR_OUT_OF_RANGE;
1325 }
1326 pSettings->cchWidth = pValueUnion->u8;
1327 return VINF_SUCCESS;
1328
1329 case SCMOPT_FILTER_OUT_DIRS:
1330 case SCMOPT_FILTER_FILES:
1331 case SCMOPT_FILTER_OUT_FILES:
1332 {
1333 char **ppsz = NULL;
1334 switch (rc)
1335 {
1336 case SCMOPT_FILTER_OUT_DIRS: ppsz = &pSettings->pszFilterOutDirs; break;
1337 case SCMOPT_FILTER_FILES: ppsz = &pSettings->pszFilterFiles; break;
1338 case SCMOPT_FILTER_OUT_FILES: ppsz = &pSettings->pszFilterOutFiles; break;
1339 }
1340
1341 /*
1342 * An empty string zaps the current list.
1343 */
1344 if (!*pValueUnion->psz)
1345 return RTStrATruncate(ppsz, 0);
1346
1347 /*
1348 * Non-empty strings are appended to the pattern list.
1349 *
1350 * Strip leading and trailing pattern separators before attempting
1351 * to append it. If it's just separators, don't do anything.
1352 */
1353 const char *pszSrc = pValueUnion->psz;
1354 while (*pszSrc == '|')
1355 pszSrc++;
1356 size_t cchSrc = strlen(pszSrc);
1357 while (cchSrc > 0 && pszSrc[cchSrc - 1] == '|')
1358 cchSrc--;
1359 if (!cchSrc)
1360 return VINF_SUCCESS;
1361
1362 /* Append it pattern by pattern, turning settings-relative paths into absolute ones. */
1363 for (;;)
1364 {
1365 const char *pszEnd = (const char *)memchr(pszSrc, '|', cchSrc);
1366 size_t cchPattern = pszEnd ? pszEnd - pszSrc : cchSrc;
1367 int rc2;
1368 if (*pszSrc == '/')
1369 rc2 = RTStrAAppendExN(ppsz, 3,
1370 "|", *ppsz && **ppsz != '\0' ? (size_t)1 : (size_t)0,
1371 pchDir, cchDir - 1,
1372 pszSrc, cchPattern);
1373 else
1374 rc2 = RTStrAAppendExN(ppsz, 2,
1375 "|", *ppsz && **ppsz != '\0' ? (size_t)1 : (size_t)0,
1376 pszSrc, cchPattern);
1377 if (RT_FAILURE(rc2))
1378 return rc2;
1379
1380 /* next */
1381 cchSrc -= cchPattern;
1382 if (!cchSrc)
1383 return VINF_SUCCESS;
1384 cchSrc -= 1;
1385 pszSrc += cchPattern + 1;
1386 }
1387 /* not reached */
1388 }
1389
1390 case SCMOPT_TREAT_AS:
1391 if (pSettings->fFreeTreatAs)
1392 {
1393 scmCfgEntryDelete((PSCMCFGENTRY)pSettings->pTreatAs);
1394 pSettings->pTreatAs = NULL;
1395 pSettings->fFreeTreatAs = false;
1396 }
1397
1398 if (*pValueUnion->psz)
1399 {
1400 /* first check the names, then patterns (legacy). */
1401 for (size_t iCfg = 0; iCfg < RT_ELEMENTS(g_aConfigs); iCfg++)
1402 if (strcmp(g_aConfigs[iCfg].pszName, pValueUnion->psz) == 0)
1403 {
1404 pSettings->pTreatAs = &g_aConfigs[iCfg];
1405 return VINF_SUCCESS;
1406 }
1407 for (size_t iCfg = 0; iCfg < RT_ELEMENTS(g_aConfigs); iCfg++)
1408 if (RTStrSimplePatternMultiMatch(g_aConfigs[iCfg].pszFilePattern, RTSTR_MAX,
1409 pValueUnion->psz, RTSTR_MAX, NULL))
1410 {
1411 pSettings->pTreatAs = &g_aConfigs[iCfg];
1412 return VINF_SUCCESS;
1413 }
1414 /* Special help for listing the possibilities? */
1415 if (strcmp(pValueUnion->psz, "help") == 0)
1416 {
1417 RTPrintf("Possible --treat-as values:\n");
1418 for (size_t iCfg = 0; iCfg < RT_ELEMENTS(g_aConfigs); iCfg++)
1419 RTPrintf(" %s (%s)\n", g_aConfigs[iCfg].pszName, g_aConfigs[iCfg].pszFilePattern);
1420 }
1421 return VERR_NOT_FOUND;
1422 }
1423
1424 pSettings->pTreatAs = NULL;
1425 return VINF_SUCCESS;
1426
1427 case SCMOPT_ADD_ACTION:
1428 for (uint32_t iAction = 0; iAction < RT_ELEMENTS(g_papRewriterActions); iAction++)
1429 if (strcmp(g_papRewriterActions[iAction]->pszName, pValueUnion->psz) == 0)
1430 {
1431 PSCMCFGENTRY pEntry = (PSCMCFGENTRY)pSettings->pTreatAs;
1432 if (!pSettings->fFreeTreatAs)
1433 {
1434 pEntry = scmCfgEntryDup(pEntry);
1435 if (!pEntry)
1436 return VERR_NO_MEMORY;
1437 pSettings->pTreatAs = pEntry;
1438 pSettings->fFreeTreatAs = true;
1439 }
1440 return scmCfgEntryAddAction(pEntry, g_papRewriterActions[iAction]);
1441 }
1442 RTMsgError("Unknown --add-action value '%s'. Try --help-actions for a list.", pValueUnion->psz);
1443 return VERR_NOT_FOUND;
1444
1445 case SCMOPT_DEL_ACTION:
1446 {
1447 uint32_t cActions = 0;
1448 for (uint32_t iAction = 0; iAction < RT_ELEMENTS(g_papRewriterActions); iAction++)
1449 if (RTStrSimplePatternMatch(pValueUnion->psz, g_papRewriterActions[iAction]->pszName))
1450 {
1451 cActions++;
1452 PSCMCFGENTRY pEntry = (PSCMCFGENTRY)pSettings->pTreatAs;
1453 if (!pSettings->fFreeTreatAs)
1454 {
1455 pEntry = scmCfgEntryDup(pEntry);
1456 if (!pEntry)
1457 return VERR_NO_MEMORY;
1458 pSettings->pTreatAs = pEntry;
1459 pSettings->fFreeTreatAs = true;
1460 }
1461 scmCfgEntryDelAction(pEntry, g_papRewriterActions[iAction]);
1462 if (!strchr(pValueUnion->psz, '*'))
1463 return VINF_SUCCESS;
1464 }
1465 if (cActions > 0)
1466 return VINF_SUCCESS;
1467 RTMsgError("Unknown --del-action value '%s'. Try --help-actions for a list.", pValueUnion->psz);
1468 return VERR_NOT_FOUND;
1469 }
1470
1471 default:
1472 return VERR_GETOPT_UNKNOWN_OPTION;
1473 }
1474}
1475
1476/**
1477 * Parses an option string.
1478 *
1479 * @returns IPRT status code.
1480 * @param pBase The base settings structure to apply the options
1481 * to.
1482 * @param pszOptions The options to parse.
1483 * @param pchDir The absolute path to the directory relative
1484 * components in pchLine should be relative to.
1485 * @param cchDir The length of the @a pchDir string.
1486 */
1487static int scmSettingsBaseParseString(PSCMSETTINGSBASE pBase, const char *pszLine, const char *pchDir, size_t cchDir)
1488{
1489 int cArgs;
1490 char **papszArgs;
1491 int rc = RTGetOptArgvFromString(&papszArgs, &cArgs, pszLine, RTGETOPTARGV_CNV_QUOTE_BOURNE_SH, NULL);
1492 if (RT_SUCCESS(rc))
1493 {
1494 RTGETOPTUNION ValueUnion;
1495 RTGETOPTSTATE GetOptState;
1496 rc = RTGetOptInit(&GetOptState, cArgs, papszArgs, &g_aScmOpts[0], RT_ELEMENTS(g_aScmOpts), 0, 0 /*fFlags*/);
1497 if (RT_SUCCESS(rc))
1498 {
1499 while ((rc = RTGetOpt(&GetOptState, &ValueUnion)) != 0)
1500 {
1501 rc = scmSettingsBaseHandleOpt(pBase, rc, &ValueUnion, pchDir, cchDir);
1502 if (RT_FAILURE(rc))
1503 break;
1504 }
1505 }
1506 RTGetOptArgvFree(papszArgs);
1507 }
1508
1509 return rc;
1510}
1511
1512/**
1513 * Parses an unterminated option string.
1514 *
1515 * @returns IPRT status code.
1516 * @param pBase The base settings structure to apply the options
1517 * to.
1518 * @param pchLine The line.
1519 * @param cchLine The line length.
1520 * @param pchDir The absolute path to the directory relative
1521 * components in pchLine should be relative to.
1522 * @param cchDir The length of the @a pchDir string.
1523 */
1524static int scmSettingsBaseParseStringN(PSCMSETTINGSBASE pBase, const char *pchLine, size_t cchLine,
1525 const char *pchDir, size_t cchDir)
1526{
1527 char *pszLine = RTStrDupN(pchLine, cchLine);
1528 if (!pszLine)
1529 return VERR_NO_MEMORY;
1530 int rc = scmSettingsBaseParseString(pBase, pszLine, pchDir, cchDir);
1531 RTStrFree(pszLine);
1532 return rc;
1533}
1534
1535/**
1536 * Verifies the options string.
1537 *
1538 * @returns IPRT status code.
1539 * @param pszOptions The options to verify .
1540 */
1541static int scmSettingsBaseVerifyString(const char *pszOptions)
1542{
1543 SCMSETTINGSBASE Base;
1544 int rc = scmSettingsBaseInit(&Base);
1545 if (RT_SUCCESS(rc))
1546 {
1547 rc = scmSettingsBaseParseString(&Base, pszOptions, "/", 1);
1548 scmSettingsBaseDelete(&Base);
1549 }
1550 return rc;
1551}
1552
1553/**
1554 * Loads settings found in editor and SCM settings directives within the
1555 * document (@a pStream).
1556 *
1557 * @returns IPRT status code.
1558 * @param pBase The settings base to load settings into.
1559 * @param pStream The stream to scan for settings directives.
1560 */
1561static int scmSettingsBaseLoadFromDocument(PSCMSETTINGSBASE pBase, PSCMSTREAM pStream)
1562{
1563 /** @todo Editor and SCM settings directives in documents. */
1564 RT_NOREF2(pBase, pStream);
1565 return VINF_SUCCESS;
1566}
1567
1568/**
1569 * Creates a new settings file struct, cloning @a pSettings.
1570 *
1571 * @returns IPRT status code.
1572 * @param ppSettings Where to return the new struct.
1573 * @param pSettingsBase The settings to inherit from.
1574 */
1575static int scmSettingsCreate(PSCMSETTINGS *ppSettings, PCSCMSETTINGSBASE pSettingsBase)
1576{
1577 PSCMSETTINGS pSettings = (PSCMSETTINGS)RTMemAlloc(sizeof(*pSettings));
1578 if (!pSettings)
1579 return VERR_NO_MEMORY;
1580 int rc = scmSettingsBaseInitAndCopy(&pSettings->Base, pSettingsBase);
1581 if (RT_SUCCESS(rc))
1582 {
1583 pSettings->pDown = NULL;
1584 pSettings->pUp = NULL;
1585 pSettings->paPairs = NULL;
1586 pSettings->cPairs = 0;
1587 *ppSettings = pSettings;
1588 return VINF_SUCCESS;
1589 }
1590 RTMemFree(pSettings);
1591 return rc;
1592}
1593
1594/**
1595 * Destroys a settings structure.
1596 *
1597 * @param pSettings The settings structure to destroy. NULL is OK.
1598 */
1599static void scmSettingsDestroy(PSCMSETTINGS pSettings)
1600{
1601 if (pSettings)
1602 {
1603 scmSettingsBaseDelete(&pSettings->Base);
1604 for (size_t i = 0; i < pSettings->cPairs; i++)
1605 {
1606 RTStrFree(pSettings->paPairs[i].pszPattern);
1607 RTStrFree(pSettings->paPairs[i].pszOptions);
1608 RTStrFree(pSettings->paPairs[i].pszRelativeTo);
1609 pSettings->paPairs[i].pszPattern = NULL;
1610 pSettings->paPairs[i].pszOptions = NULL;
1611 pSettings->paPairs[i].pszRelativeTo = NULL;
1612 }
1613 RTMemFree(pSettings->paPairs);
1614 pSettings->paPairs = NULL;
1615 RTMemFree(pSettings);
1616 }
1617}
1618
1619/**
1620 * Adds a pattern/options pair to the settings structure.
1621 *
1622 * @returns IPRT status code.
1623 * @param pSettings The settings.
1624 * @param pchLine The line containing the unparsed pair.
1625 * @param cchLine The length of the line.
1626 * @param offColon The offset of the colon into the line.
1627 * @param pchDir The absolute path to the directory relative
1628 * components in pchLine should be relative to.
1629 * @param cchDir The length of the @a pchDir string.
1630 */
1631static int scmSettingsAddPair(PSCMSETTINGS pSettings, const char *pchLine, size_t cchLine, size_t offColon,
1632 const char *pchDir, size_t cchDir)
1633{
1634 Assert(pchLine[offColon] == ':' && offColon < cchLine);
1635 Assert(pchDir[cchDir - 1] == '/');
1636
1637 /*
1638 * Split the string.
1639 */
1640 size_t cchPattern = offColon;
1641 size_t cchOptions = cchLine - cchPattern - 1;
1642
1643 /* strip spaces everywhere */
1644 while (cchPattern > 0 && RT_C_IS_SPACE(pchLine[cchPattern - 1]))
1645 cchPattern--;
1646 while (cchPattern > 0 && RT_C_IS_SPACE(*pchLine))
1647 cchPattern--, pchLine++;
1648
1649 const char *pchOptions = &pchLine[offColon + 1];
1650 while (cchOptions > 0 && RT_C_IS_SPACE(pchOptions[cchOptions - 1]))
1651 cchOptions--;
1652 while (cchOptions > 0 && RT_C_IS_SPACE(*pchOptions))
1653 cchOptions--, pchOptions++;
1654
1655 /* Quietly ignore empty patterns and empty options. */
1656 if (!cchOptions || !cchPattern)
1657 return VINF_SUCCESS;
1658
1659 /*
1660 * Prepair the pair and verify the option string.
1661 */
1662 uint32_t iPair = pSettings->cPairs;
1663 if ((iPair % 32) == 0)
1664 {
1665 void *pvNew = RTMemRealloc(pSettings->paPairs, (iPair + 32) * sizeof(pSettings->paPairs[0]));
1666 if (!pvNew)
1667 return VERR_NO_MEMORY;
1668 pSettings->paPairs = (PSCMPATRNOPTPAIR)pvNew;
1669 }
1670
1671 pSettings->paPairs[iPair].pszPattern = RTStrDupN(pchLine, cchPattern);
1672 pSettings->paPairs[iPair].pszOptions = RTStrDupN(pchOptions, cchOptions);
1673 pSettings->paPairs[iPair].pszRelativeTo = RTStrDupN(pchDir, cchDir);
1674 int rc;
1675 if ( pSettings->paPairs[iPair].pszPattern
1676 && pSettings->paPairs[iPair].pszOptions
1677 && pSettings->paPairs[iPair].pszRelativeTo)
1678 rc = scmSettingsBaseVerifyString(pSettings->paPairs[iPair].pszOptions);
1679 else
1680 rc = VERR_NO_MEMORY;
1681
1682 /*
1683 * If it checked out fine, expand any relative paths in the pattern.
1684 */
1685 if (RT_SUCCESS(rc))
1686 {
1687 size_t cPattern = 1;
1688 size_t cRelativePaths = 0;
1689 const char *pszSrc = pSettings->paPairs[iPair].pszPattern;
1690 for (;;)
1691 {
1692 if (*pszSrc == '/')
1693 cRelativePaths++;
1694 pszSrc = strchr(pszSrc, '|');
1695 if (!pszSrc)
1696 break;
1697 pszSrc++;
1698 cPattern++;
1699 }
1700 pSettings->paPairs[iPair].fMultiPattern = cPattern > 1;
1701 if (cRelativePaths > 0)
1702 {
1703 char *pszNewPattern = RTStrAlloc(cchPattern + cRelativePaths * (cchDir - 1) + 1);
1704 if (pszNewPattern)
1705 {
1706 char *pszDst = pszNewPattern;
1707 pszSrc = pSettings->paPairs[iPair].pszPattern;
1708 for (;;)
1709 {
1710 if (*pszSrc == '/')
1711 {
1712 memcpy(pszDst, pchDir, cchDir);
1713 pszDst += cchDir;
1714 pszSrc += 1;
1715 }
1716
1717 /* Look for the next relative path. */
1718 const char *pszSrcNext = strchr(pszSrc, '|');
1719 while (pszSrcNext && pszSrcNext[1] != '/')
1720 pszSrcNext = strchr(pszSrcNext, '|');
1721 if (!pszSrcNext)
1722 break;
1723
1724 /* Copy stuff between current and the next path. */
1725 pszSrcNext++;
1726 memcpy(pszDst, pszSrc, pszSrcNext - pszSrc);
1727 pszDst += pszSrcNext - pszSrc;
1728 pszSrc = pszSrcNext;
1729 }
1730
1731 /* Copy the final portion and replace the pattern. */
1732 strcpy(pszDst, pszSrc);
1733
1734 RTStrFree(pSettings->paPairs[iPair].pszPattern);
1735 pSettings->paPairs[iPair].pszPattern = pszNewPattern;
1736 }
1737 else
1738 rc = VERR_NO_MEMORY;
1739 }
1740 }
1741 if (RT_SUCCESS(rc))
1742 /*
1743 * Commit the pair.
1744 */
1745 pSettings->cPairs = iPair + 1;
1746 else
1747 {
1748 RTStrFree(pSettings->paPairs[iPair].pszPattern);
1749 RTStrFree(pSettings->paPairs[iPair].pszOptions);
1750 RTStrFree(pSettings->paPairs[iPair].pszRelativeTo);
1751 }
1752 return rc;
1753}
1754
1755/**
1756 * Loads in the settings from @a pszFilename.
1757 *
1758 * @returns IPRT status code.
1759 * @param pSettings Where to load the settings file.
1760 * @param pszFilename The file to load.
1761 */
1762static int scmSettingsLoadFile(PSCMSETTINGS pSettings, const char *pszFilename)
1763{
1764 ScmVerbose(NULL, 3, "Loading settings file '%s'...\n", pszFilename);
1765
1766 /* Turn filename into an absolute path and drop the filename. */
1767 char szAbsPath[RTPATH_MAX];
1768 int rc = RTPathAbs(pszFilename, szAbsPath, sizeof(szAbsPath));
1769 if (RT_FAILURE(rc))
1770 {
1771 RTMsgError("%s: RTPathAbs -> %Rrc\n", pszFilename, rc);
1772 return rc;
1773 }
1774 RTPathChangeToUnixSlashes(szAbsPath, true);
1775 size_t cchDir = RTPathFilename(szAbsPath) - &szAbsPath[0];
1776
1777 /* Try open it.*/
1778 SCMSTREAM Stream;
1779 rc = ScmStreamInitForReading(&Stream, pszFilename);
1780 if (RT_SUCCESS(rc))
1781 {
1782 SCMEOL enmEol;
1783 const char *pchLine;
1784 size_t cchLine;
1785 while ((pchLine = ScmStreamGetLine(&Stream, &cchLine, &enmEol)) != NULL)
1786 {
1787 /* Ignore leading spaces. */
1788 while (cchLine > 0 && RT_C_IS_SPACE(*pchLine))
1789 pchLine++, cchLine--;
1790
1791 /* Ignore empty lines and comment lines. */
1792 if (cchLine < 1 || *pchLine == '#')
1793 continue;
1794
1795 /* Deal with escaped newlines. */
1796 size_t iFirstLine = ~(size_t)0;
1797 char *pszFreeLine = NULL;
1798 if ( pchLine[cchLine - 1] == '\\'
1799 && ( cchLine < 2
1800 || pchLine[cchLine - 2] != '\\') )
1801 {
1802 iFirstLine = ScmStreamTellLine(&Stream);
1803
1804 cchLine--;
1805 while (cchLine > 0 && RT_C_IS_SPACE(pchLine[cchLine - 1]))
1806 cchLine--;
1807
1808 size_t cchTotal = cchLine;
1809 pszFreeLine = RTStrDupN(pchLine, cchLine);
1810 if (pszFreeLine)
1811 {
1812 /* Append following lines. */
1813 while ((pchLine = ScmStreamGetLine(&Stream, &cchLine, &enmEol)) != NULL)
1814 {
1815 while (cchLine > 0 && RT_C_IS_SPACE(*pchLine))
1816 pchLine++, cchLine--;
1817
1818 bool const fDone = cchLine == 0
1819 || pchLine[cchLine - 1] != '\\'
1820 || (cchLine >= 2 && pchLine[cchLine - 2] == '\\');
1821 if (!fDone)
1822 {
1823 cchLine--;
1824 while (cchLine > 0 && RT_C_IS_SPACE(pchLine[cchLine - 1]))
1825 cchLine--;
1826 }
1827
1828 rc = RTStrRealloc(&pszFreeLine, cchTotal + 1 + cchLine + 1);
1829 if (RT_FAILURE(rc))
1830 break;
1831 pszFreeLine[cchTotal++] = ' ';
1832 memcpy(&pszFreeLine[cchTotal], pchLine, cchLine);
1833 cchTotal += cchLine;
1834 pszFreeLine[cchTotal] = '\0';
1835
1836 if (fDone)
1837 break;
1838 }
1839 }
1840 else
1841 rc = VERR_NO_STR_MEMORY;
1842
1843 if (RT_FAILURE(rc))
1844 {
1845 RTStrFree(pszFreeLine);
1846 rc = RTMsgErrorRc(VERR_NO_MEMORY, "%s: Ran out of memory deal with escaped newlines", pszFilename);
1847 break;
1848 }
1849
1850 pchLine = pszFreeLine;
1851 cchLine = cchTotal;
1852 }
1853
1854 /* What kind of line is it? */
1855 const char *pchColon = (const char *)memchr(pchLine, ':', cchLine);
1856 if (pchColon)
1857 rc = scmSettingsAddPair(pSettings, pchLine, cchLine, pchColon - pchLine, szAbsPath, cchDir);
1858 else
1859 rc = scmSettingsBaseParseStringN(&pSettings->Base, pchLine, cchLine, szAbsPath, cchDir);
1860 if (pszFreeLine)
1861 RTStrFree(pszFreeLine);
1862 if (RT_FAILURE(rc))
1863 {
1864 RTMsgError("%s:%d: %Rrc\n",
1865 pszFilename, iFirstLine == ~(size_t)0 ? ScmStreamTellLine(&Stream) : iFirstLine, rc);
1866 break;
1867 }
1868 }
1869
1870 if (RT_SUCCESS(rc))
1871 {
1872 rc = ScmStreamGetStatus(&Stream);
1873 if (RT_FAILURE(rc))
1874 RTMsgError("%s: ScmStreamGetStatus- > %Rrc\n", pszFilename, rc);
1875 }
1876 ScmStreamDelete(&Stream);
1877 }
1878 else
1879 RTMsgError("%s: ScmStreamInitForReading -> %Rrc\n", pszFilename, rc);
1880 return rc;
1881}
1882
1883#if 0 /* unused */
1884/**
1885 * Parse the specified settings file creating a new settings struct from it.
1886 *
1887 * @returns IPRT status code
1888 * @param ppSettings Where to return the new settings.
1889 * @param pszFilename The file to parse.
1890 * @param pSettingsBase The base settings we inherit from.
1891 */
1892static int scmSettingsCreateFromFile(PSCMSETTINGS *ppSettings, const char *pszFilename, PCSCMSETTINGSBASE pSettingsBase)
1893{
1894 PSCMSETTINGS pSettings;
1895 int rc = scmSettingsCreate(&pSettings, pSettingsBase);
1896 if (RT_SUCCESS(rc))
1897 {
1898 rc = scmSettingsLoadFile(pSettings, pszFilename, RTPathFilename(pszFilename) - pszFilename);
1899 if (RT_SUCCESS(rc))
1900 {
1901 *ppSettings = pSettings;
1902 return VINF_SUCCESS;
1903 }
1904
1905 scmSettingsDestroy(pSettings);
1906 }
1907 *ppSettings = NULL;
1908 return rc;
1909}
1910#endif
1911
1912
1913/**
1914 * Create an initial settings structure when starting processing a new file or
1915 * directory.
1916 *
1917 * This will look for .scm-settings files from the root and down to the
1918 * specified directory, combining them into the returned settings structure.
1919 *
1920 * @returns IPRT status code.
1921 * @param ppSettings Where to return the pointer to the top stack
1922 * object.
1923 * @param pBaseSettings The base settings we inherit from (globals
1924 * typically).
1925 * @param pszPath The absolute path to the new directory or file.
1926 */
1927static int scmSettingsCreateForPath(PSCMSETTINGS *ppSettings, PCSCMSETTINGSBASE pBaseSettings, const char *pszPath)
1928{
1929 *ppSettings = NULL; /* try shut up gcc. */
1930
1931 /*
1932 * We'll be working with a stack copy of the path.
1933 */
1934 char szFile[RTPATH_MAX];
1935 size_t cchDir = strlen(pszPath);
1936 if (cchDir >= sizeof(szFile) - sizeof(SCM_SETTINGS_FILENAME))
1937 return VERR_FILENAME_TOO_LONG;
1938
1939 /*
1940 * Create the bottom-most settings.
1941 */
1942 PSCMSETTINGS pSettings;
1943 int rc = scmSettingsCreate(&pSettings, pBaseSettings);
1944 if (RT_FAILURE(rc))
1945 return rc;
1946
1947 /*
1948 * Enumerate the path components from the root and down. Load any setting
1949 * files we find.
1950 */
1951 size_t cComponents = RTPathCountComponents(pszPath);
1952 for (size_t i = 1; i <= cComponents; i++)
1953 {
1954 rc = RTPathCopyComponents(szFile, sizeof(szFile), pszPath, i);
1955 if (RT_SUCCESS(rc))
1956 rc = RTPathAppend(szFile, sizeof(szFile), SCM_SETTINGS_FILENAME);
1957 if (RT_FAILURE(rc))
1958 break;
1959 RTPathChangeToUnixSlashes(szFile, true);
1960
1961 if (RTFileExists(szFile))
1962 {
1963 rc = scmSettingsLoadFile(pSettings, szFile);
1964 if (RT_FAILURE(rc))
1965 break;
1966 }
1967 }
1968
1969 if (RT_SUCCESS(rc))
1970 *ppSettings = pSettings;
1971 else
1972 scmSettingsDestroy(pSettings);
1973 return rc;
1974}
1975
1976/**
1977 * Pushes a new settings set onto the stack.
1978 *
1979 * @param ppSettingsStack The pointer to the pointer to the top stack
1980 * element. This will be used as input and output.
1981 * @param pSettings The settings to push onto the stack.
1982 */
1983static void scmSettingsStackPush(PSCMSETTINGS *ppSettingsStack, PSCMSETTINGS pSettings)
1984{
1985 PSCMSETTINGS pOld = *ppSettingsStack;
1986 pSettings->pDown = pOld;
1987 pSettings->pUp = NULL;
1988 if (pOld)
1989 pOld->pUp = pSettings;
1990 *ppSettingsStack = pSettings;
1991}
1992
1993/**
1994 * Pushes the settings of the specified directory onto the stack.
1995 *
1996 * We will load any .scm-settings in the directory. A stack entry is added even
1997 * if no settings file was found.
1998 *
1999 * @returns IPRT status code.
2000 * @param ppSettingsStack The pointer to the pointer to the top stack
2001 * element. This will be used as input and output.
2002 * @param pszDir The directory to do this for.
2003 */
2004static int scmSettingsStackPushDir(PSCMSETTINGS *ppSettingsStack, const char *pszDir)
2005{
2006 char szFile[RTPATH_MAX];
2007 int rc = RTPathJoin(szFile, sizeof(szFile), pszDir, SCM_SETTINGS_FILENAME);
2008 if (RT_SUCCESS(rc))
2009 {
2010 RTPathChangeToUnixSlashes(szFile, true);
2011
2012 PSCMSETTINGS pSettings;
2013 rc = scmSettingsCreate(&pSettings, &(*ppSettingsStack)->Base);
2014 if (RT_SUCCESS(rc))
2015 {
2016 if (RTFileExists(szFile))
2017 rc = scmSettingsLoadFile(pSettings, szFile);
2018 if (RT_SUCCESS(rc))
2019 {
2020 scmSettingsStackPush(ppSettingsStack, pSettings);
2021 return VINF_SUCCESS;
2022 }
2023
2024 scmSettingsDestroy(pSettings);
2025 }
2026 }
2027 return rc;
2028}
2029
2030
2031/**
2032 * Pops a settings set off the stack.
2033 *
2034 * @returns The popped settings.
2035 * @param ppSettingsStack The pointer to the pointer to the top stack
2036 * element. This will be used as input and output.
2037 */
2038static PSCMSETTINGS scmSettingsStackPop(PSCMSETTINGS *ppSettingsStack)
2039{
2040 PSCMSETTINGS pRet = *ppSettingsStack;
2041 PSCMSETTINGS pNew = pRet ? pRet->pDown : NULL;
2042 *ppSettingsStack = pNew;
2043 if (pNew)
2044 pNew->pUp = NULL;
2045 if (pRet)
2046 {
2047 pRet->pUp = NULL;
2048 pRet->pDown = NULL;
2049 }
2050 return pRet;
2051}
2052
2053/**
2054 * Pops and destroys the top entry of the stack.
2055 *
2056 * @param ppSettingsStack The pointer to the pointer to the top stack
2057 * element. This will be used as input and output.
2058 */
2059static void scmSettingsStackPopAndDestroy(PSCMSETTINGS *ppSettingsStack)
2060{
2061 scmSettingsDestroy(scmSettingsStackPop(ppSettingsStack));
2062}
2063
2064/**
2065 * Constructs the base settings for the specified file name.
2066 *
2067 * @returns IPRT status code.
2068 * @param pSettingsStack The top element on the settings stack.
2069 * @param pszFilename The file name.
2070 * @param pszBasename The base name (pointer within @a pszFilename).
2071 * @param cchBasename The length of the base name. (For passing to
2072 * RTStrSimplePatternMultiMatch.)
2073 * @param pBase Base settings to initialize.
2074 */
2075static int scmSettingsStackMakeFileBase(PCSCMSETTINGS pSettingsStack, const char *pszFilename,
2076 const char *pszBasename, size_t cchBasename, PSCMSETTINGSBASE pBase)
2077{
2078 ScmVerbose(NULL, 5, "scmSettingsStackMakeFileBase(%s, %.*s)\n", pszFilename, cchBasename, pszBasename);
2079
2080 int rc = scmSettingsBaseInitAndCopy(pBase, &pSettingsStack->Base);
2081 if (RT_SUCCESS(rc))
2082 {
2083 /* find the bottom entry in the stack. */
2084 PCSCMSETTINGS pCur = pSettingsStack;
2085 while (pCur->pDown)
2086 pCur = pCur->pDown;
2087
2088 /* Work our way up thru the stack and look for matching pairs. */
2089 while (pCur)
2090 {
2091 size_t const cPairs = pCur->cPairs;
2092 if (cPairs)
2093 {
2094 for (size_t i = 0; i < cPairs; i++)
2095 if ( !pCur->paPairs[i].fMultiPattern
2096 ? RTStrSimplePatternNMatch(pCur->paPairs[i].pszPattern, RTSTR_MAX,
2097 pszBasename, cchBasename)
2098 || RTStrSimplePatternMatch(pCur->paPairs[i].pszPattern, pszFilename)
2099 : RTStrSimplePatternMultiMatch(pCur->paPairs[i].pszPattern, RTSTR_MAX,
2100 pszBasename, cchBasename, NULL)
2101 || RTStrSimplePatternMultiMatch(pCur->paPairs[i].pszPattern, RTSTR_MAX,
2102 pszFilename, RTSTR_MAX, NULL))
2103 {
2104 ScmVerbose(NULL, 5, "scmSettingsStackMakeFileBase: Matched '%s' : '%s'\n",
2105 pCur->paPairs[i].pszPattern, pCur->paPairs[i].pszOptions);
2106 rc = scmSettingsBaseParseString(pBase, pCur->paPairs[i].pszOptions,
2107 pCur->paPairs[i].pszRelativeTo, strlen(pCur->paPairs[i].pszRelativeTo));
2108 if (RT_FAILURE(rc))
2109 break;
2110 }
2111 if (RT_FAILURE(rc))
2112 break;
2113 }
2114
2115 /* advance */
2116 pCur = pCur->pUp;
2117 }
2118 }
2119 if (RT_FAILURE(rc))
2120 scmSettingsBaseDelete(pBase);
2121 return rc;
2122}
2123
2124
2125/* -=-=-=-=-=- misc -=-=-=-=-=- */
2126
2127
2128/**
2129 * Prints the per file banner needed and the message level is high enough.
2130 *
2131 * @param pState The rewrite state.
2132 * @param iLevel The required verbosity level.
2133 */
2134void ScmVerboseBanner(PSCMRWSTATE pState, int iLevel)
2135{
2136 if (iLevel <= g_iVerbosity && !pState->fFirst)
2137 {
2138 RTPrintf("%s: info: --= Rewriting '%s' =--\n", g_szProgName, pState->pszFilename);
2139 pState->fFirst = true;
2140 }
2141}
2142
2143
2144/**
2145 * Prints a verbose message if the level is high enough.
2146 *
2147 * @param pState The rewrite state. Optional.
2148 * @param iLevel The required verbosity level.
2149 * @param pszFormat The message format string. Can be NULL if we
2150 * only want to trigger the per file message.
2151 * @param ... Format arguments.
2152 */
2153void ScmVerbose(PSCMRWSTATE pState, int iLevel, const char *pszFormat, ...)
2154{
2155 if (iLevel <= g_iVerbosity)
2156 {
2157 if (pState && !pState->fFirst)
2158 {
2159 RTPrintf("%s: info: --= Rewriting '%s' =--\n", g_szProgName, pState->pszFilename);
2160 pState->fFirst = true;
2161 }
2162 RTPrintf(pState
2163 ? "%s: info: "
2164 : "%s: info: ",
2165 g_szProgName);
2166 va_list va;
2167 va_start(va, pszFormat);
2168 RTPrintfV(pszFormat, va);
2169 va_end(va);
2170 }
2171}
2172
2173
2174/**
2175 * Prints an error message.
2176 *
2177 * @returns false
2178 * @param pState The rewrite state. Optional.
2179 * @param rc The error code.
2180 * @param pszFormat The message format string.
2181 * @param ... Format arguments.
2182 */
2183bool ScmError(PSCMRWSTATE pState, int rc, const char *pszFormat, ...)
2184{
2185 if (RT_SUCCESS(pState->rc))
2186 pState->rc = rc;
2187
2188 if (!pState->fFirst)
2189 {
2190 RTPrintf("%s: info: --= Rewriting '%s' =--\n", g_szProgName, pState->pszFilename);
2191 pState->fFirst = true;
2192 }
2193 va_list va;
2194 va_start(va, pszFormat);
2195 RTPrintf("%s: error: %s: %N", g_szProgName, pState->pszFilename, pszFormat, &va);
2196 va_end(va);
2197
2198 return false;
2199}
2200
2201
2202/**
2203 * Prints message indicating that something requires manual fixing.
2204 *
2205 * @returns false
2206 * @param pState The rewrite state. Optional.
2207 * @param rc The error code.
2208 * @param pszFormat The message format string.
2209 * @param ... Format arguments.
2210 */
2211bool ScmFixManually(PSCMRWSTATE pState, const char *pszFormat, ...)
2212{
2213 pState->fNeedsManualRepair = true;
2214
2215 if (!pState->fFirst)
2216 {
2217 RTPrintf("%s: info: --= Rewriting '%s' =--\n", g_szProgName, pState->pszFilename);
2218 pState->fFirst = true;
2219 }
2220 va_list va;
2221 va_start(va, pszFormat);
2222 RTPrintf("%s: error/fixme: %s: %N", g_szProgName, pState->pszFilename, pszFormat, &va);
2223 va_end(va);
2224
2225 return false;
2226}
2227
2228
2229/* -=-=-=-=-=- file and directory processing -=-=-=-=-=- */
2230
2231
2232/**
2233 * Processes a file.
2234 *
2235 * @returns IPRT status code.
2236 * @param pState The rewriter state.
2237 * @param pszFilename The file name.
2238 * @param pszBasename The base name (pointer within @a pszFilename).
2239 * @param cchBasename The length of the base name. (For passing to
2240 * RTStrSimplePatternMultiMatch.)
2241 * @param pBaseSettings The base settings to use. It's OK to modify
2242 * these.
2243 */
2244static int scmProcessFileInner(PSCMRWSTATE pState, const char *pszFilename, const char *pszBasename, size_t cchBasename,
2245 PSCMSETTINGSBASE pBaseSettings)
2246{
2247 /*
2248 * Do the file level filtering.
2249 */
2250 if ( pBaseSettings->pszFilterFiles
2251 && *pBaseSettings->pszFilterFiles
2252 && !RTStrSimplePatternMultiMatch(pBaseSettings->pszFilterFiles, RTSTR_MAX, pszBasename, cchBasename, NULL))
2253 {
2254 ScmVerbose(NULL, 5, "skipping '%s': file filter mismatch\n", pszFilename);
2255 g_cFilesSkipped++;
2256 return VINF_SUCCESS;
2257 }
2258 if ( pBaseSettings->pszFilterOutFiles
2259 && *pBaseSettings->pszFilterOutFiles
2260 && ( RTStrSimplePatternMultiMatch(pBaseSettings->pszFilterOutFiles, RTSTR_MAX, pszBasename, cchBasename, NULL)
2261 || RTStrSimplePatternMultiMatch(pBaseSettings->pszFilterOutFiles, RTSTR_MAX, pszFilename, RTSTR_MAX, NULL)) )
2262 {
2263 ScmVerbose(NULL, 5, "skipping '%s': filterd out\n", pszFilename);
2264 g_cFilesSkipped++;
2265 return VINF_SUCCESS;
2266 }
2267 if ( pBaseSettings->fOnlySvnFiles
2268 && !ScmSvnIsInWorkingCopy(pState))
2269 {
2270 ScmVerbose(NULL, 5, "skipping '%s': not in SVN WC\n", pszFilename);
2271 g_cFilesNotInSvn++;
2272 return VINF_SUCCESS;
2273 }
2274
2275 /*
2276 * Create an input stream from the file and check that it's text.
2277 */
2278 SCMSTREAM Stream1;
2279 int rc = ScmStreamInitForReading(&Stream1, pszFilename);
2280 if (RT_FAILURE(rc))
2281 {
2282 RTMsgError("Failed to read '%s': %Rrc\n", pszFilename, rc);
2283 return rc;
2284 }
2285 bool const fIsText = ScmStreamIsText(&Stream1);
2286
2287 /*
2288 * Try find a matching rewrite config for this filename.
2289 */
2290 PCSCMCFGENTRY pCfg = pBaseSettings->pTreatAs;
2291 if (!pCfg)
2292 {
2293 for (size_t iCfg = 0; iCfg < RT_ELEMENTS(g_aConfigs); iCfg++)
2294 if (RTStrSimplePatternMultiMatch(g_aConfigs[iCfg].pszFilePattern, RTSTR_MAX, pszBasename, cchBasename, NULL))
2295 {
2296 pCfg = &g_aConfigs[iCfg];
2297 break;
2298 }
2299 if (!pCfg)
2300 {
2301 /* On failure try check for hash-bang stuff before giving up. */
2302 if (fIsText)
2303 {
2304 SCMEOL enmIgn;
2305 size_t cchFirst;
2306 const char *pchFirst = ScmStreamGetLine(&Stream1, &cchFirst, &enmIgn);
2307 if (cchFirst >= 9 && pchFirst && *pchFirst == '#')
2308 {
2309 do
2310 {
2311 pchFirst++;
2312 cchFirst--;
2313 } while (cchFirst > 0 && RT_C_IS_BLANK(*pchFirst));
2314 if (*pchFirst == '!')
2315 {
2316 do
2317 {
2318 pchFirst++;
2319 cchFirst--;
2320 } while (cchFirst > 0 && RT_C_IS_BLANK(*pchFirst));
2321 const char *pszTreatAs = NULL;
2322 if ( (cchFirst >= 7 && strncmp(pchFirst, "/bin/sh", 7) == 0)
2323 || (cchFirst >= 9 && strncmp(pchFirst, "/bin/bash", 9) == 0)
2324 || (cchFirst >= 4+9 && strncmp(pchFirst, "/usr/bin/bash", 4+9) == 0) )
2325 pszTreatAs = "shell";
2326 else if ( (cchFirst >= 15 && strncmp(pchFirst, "/usr/bin/python", 15) == 0)
2327 || (cchFirst >= 19 && strncmp(pchFirst, "/usr/bin/env python", 19) == 0) )
2328 pszTreatAs = "python";
2329 else if ( (cchFirst >= 13 && strncmp(pchFirst, "/usr/bin/perl", 13) == 0)
2330 || (cchFirst >= 17 && strncmp(pchFirst, "/usr/bin/env perl", 17) == 0) )
2331 pszTreatAs = "perl";
2332 if (pszTreatAs)
2333 {
2334 for (size_t iCfg = 0; iCfg < RT_ELEMENTS(g_aConfigs); iCfg++)
2335 if (strcmp(pszTreatAs, g_aConfigs[iCfg].pszName) == 0)
2336 {
2337 pCfg = &g_aConfigs[iCfg];
2338 break;
2339 }
2340 Assert(pCfg);
2341 }
2342 }
2343 }
2344 ScmStreamRewindForReading(&Stream1);
2345 }
2346 if (!pCfg)
2347 {
2348 ScmVerbose(NULL, 2, "skipping '%s': no rewriters configured\n", pszFilename);
2349 g_cFilesNoRewriters++;
2350 ScmStreamDelete(&Stream1);
2351 return VINF_SUCCESS;
2352 }
2353 }
2354 ScmVerbose(pState, 4, "matched \"%s\" (%s)\n", pCfg->pszFilePattern, pCfg->pszName);
2355 }
2356 else
2357 ScmVerbose(pState, 4, "treat-as \"%s\"\n", pCfg->pszName);
2358
2359 if (fIsText || pCfg->fBinary)
2360 {
2361 ScmVerboseBanner(pState, 3);
2362
2363 /*
2364 * Gather SCM and editor settings from the stream.
2365 */
2366 rc = scmSettingsBaseLoadFromDocument(pBaseSettings, &Stream1);
2367 if (RT_SUCCESS(rc))
2368 {
2369 ScmStreamRewindForReading(&Stream1);
2370
2371 /*
2372 * Create two more streams for output and push the text thru all the
2373 * rewriters, switching the two streams around when something is
2374 * actually rewritten. Stream1 remains unchanged.
2375 */
2376 SCMSTREAM Stream2;
2377 rc = ScmStreamInitForWriting(&Stream2, &Stream1);
2378 if (RT_SUCCESS(rc))
2379 {
2380 SCMSTREAM Stream3;
2381 rc = ScmStreamInitForWriting(&Stream3, &Stream1);
2382 if (RT_SUCCESS(rc))
2383 {
2384 bool fModified = false;
2385 PSCMSTREAM pIn = &Stream1;
2386 PSCMSTREAM pOut = &Stream2;
2387 for (size_t iRw = 0; iRw < pCfg->cRewriters; iRw++)
2388 {
2389 pState->rc = VINF_SUCCESS;
2390 bool fRc = pCfg->paRewriters[iRw]->pfnRewriter(pState, pIn, pOut, pBaseSettings);
2391 if (RT_FAILURE(pState->rc))
2392 break;
2393 if (fRc)
2394 {
2395 PSCMSTREAM pTmp = pOut;
2396 pOut = pIn == &Stream1 ? &Stream3 : pIn;
2397 pIn = pTmp;
2398 fModified = true;
2399 }
2400
2401 ScmStreamRewindForReading(pIn);
2402 ScmStreamRewindForWriting(pOut);
2403 }
2404
2405 rc = pState->rc;
2406 if (RT_SUCCESS(rc))
2407 {
2408 rc = ScmStreamGetStatus(&Stream1);
2409 if (RT_SUCCESS(rc))
2410 rc = ScmStreamGetStatus(&Stream2);
2411 if (RT_SUCCESS(rc))
2412 rc = ScmStreamGetStatus(&Stream3);
2413 if (RT_SUCCESS(rc))
2414 {
2415 /*
2416 * If rewritten, write it back to disk.
2417 */
2418 if (fModified && !pCfg->fBinary)
2419 {
2420 if (!g_fDryRun)
2421 {
2422 ScmVerbose(pState, 1, "writing modified file to \"%s%s\"\n", pszFilename, g_pszChangedSuff);
2423 rc = ScmStreamWriteToFile(pIn, "%s%s", pszFilename, g_pszChangedSuff);
2424 if (RT_FAILURE(rc))
2425 RTMsgError("Error writing '%s%s': %Rrc\n", pszFilename, g_pszChangedSuff, rc);
2426 }
2427 else
2428 {
2429 ScmVerboseBanner(pState, 1);
2430 ScmDiffStreams(pszFilename, &Stream1, pIn, g_fDiffIgnoreEol,
2431 g_fDiffIgnoreLeadingWS, g_fDiffIgnoreTrailingWS, g_fDiffSpecialChars,
2432 pBaseSettings->cchTab, g_pStdOut);
2433 ScmVerbose(pState, 2, "would have modified the file \"%s%s\"\n",
2434 pszFilename, g_pszChangedSuff);
2435 }
2436 g_cFilesModified++;
2437 }
2438 else if (fModified)
2439 rc = RTMsgErrorRc(VERR_INTERNAL_ERROR, "Rewriters modified binary file! Impossible!");
2440
2441 /*
2442 * If pending SVN property changes, apply them.
2443 */
2444 if (pState->cSvnPropChanges && RT_SUCCESS(rc))
2445 {
2446 if (!g_fDryRun)
2447 {
2448 rc = ScmSvnApplyChanges(pState);
2449 if (RT_FAILURE(rc))
2450 RTMsgError("%s: failed to apply SVN property changes (%Rrc)\n", pszFilename, rc);
2451 }
2452 else
2453 ScmSvnDisplayChanges(pState);
2454 if (!fModified)
2455 g_cFilesModified++;
2456 }
2457
2458 if (!fModified && !pState->cSvnPropChanges)
2459 ScmVerbose(pState, 3, "%s: no change\n", pszFilename);
2460 }
2461 else
2462 RTMsgError("%s: stream error %Rrc\n", pszFilename, rc);
2463 }
2464 ScmStreamDelete(&Stream3);
2465 }
2466 else
2467 RTMsgError("Failed to init stream for writing: %Rrc\n", rc);
2468 ScmStreamDelete(&Stream2);
2469 }
2470 else
2471 RTMsgError("Failed to init stream for writing: %Rrc\n", rc);
2472 }
2473 else
2474 RTMsgError("scmSettingsBaseLoadFromDocument: %Rrc\n", rc);
2475 }
2476 else
2477 {
2478 ScmVerbose(pState, 2, "not text file: \"%s\"\n", pszFilename);
2479 g_cFilesBinaries++;
2480 }
2481 ScmStreamDelete(&Stream1);
2482
2483 return rc;
2484}
2485
2486/**
2487 * Processes a file.
2488 *
2489 * This is just a wrapper for scmProcessFileInner for avoid wasting stack in the
2490 * directory recursion method.
2491 *
2492 * @returns IPRT status code.
2493 * @param pszFilename The file name.
2494 * @param pszBasename The base name (pointer within @a pszFilename).
2495 * @param cchBasename The length of the base name. (For passing to
2496 * RTStrSimplePatternMultiMatch.)
2497 * @param pSettingsStack The settings stack (pointer to the top element).
2498 */
2499static int scmProcessFile(const char *pszFilename, const char *pszBasename, size_t cchBasename,
2500 PSCMSETTINGS pSettingsStack)
2501{
2502 SCMSETTINGSBASE Base;
2503 int rc = scmSettingsStackMakeFileBase(pSettingsStack, pszFilename, pszBasename, cchBasename, &Base);
2504 if (RT_SUCCESS(rc))
2505 {
2506 SCMRWSTATE State;
2507 State.pszFilename = pszFilename;
2508 State.fFirst = false;
2509 State.fNeedsManualRepair = false;
2510 State.fIsInSvnWorkingCopy = 0;
2511 State.cSvnPropChanges = 0;
2512 State.paSvnPropChanges = NULL;
2513 State.rc = VINF_SUCCESS;
2514
2515 rc = scmProcessFileInner(&State, pszFilename, pszBasename, cchBasename, &Base);
2516
2517 size_t i = State.cSvnPropChanges;
2518 while (i-- > 0)
2519 {
2520 RTStrFree(State.paSvnPropChanges[i].pszName);
2521 RTStrFree(State.paSvnPropChanges[i].pszValue);
2522 }
2523 RTMemFree(State.paSvnPropChanges);
2524
2525 scmSettingsBaseDelete(&Base);
2526
2527 if (State.fNeedsManualRepair)
2528 g_cFilesRequiringManualFixing++;
2529 g_cFilesProcessed++;
2530 }
2531 return rc;
2532}
2533
2534/**
2535 * Tries to correct RTDIRENTRY_UNKNOWN.
2536 *
2537 * @returns Corrected type.
2538 * @param pszPath The path to the object in question.
2539 */
2540static RTDIRENTRYTYPE scmFigureUnknownType(const char *pszPath)
2541{
2542 RTFSOBJINFO Info;
2543 int rc = RTPathQueryInfo(pszPath, &Info, RTFSOBJATTRADD_NOTHING);
2544 if (RT_FAILURE(rc))
2545 return RTDIRENTRYTYPE_UNKNOWN;
2546 if (RTFS_IS_DIRECTORY(Info.Attr.fMode))
2547 return RTDIRENTRYTYPE_DIRECTORY;
2548 if (RTFS_IS_FILE(Info.Attr.fMode))
2549 return RTDIRENTRYTYPE_FILE;
2550 return RTDIRENTRYTYPE_UNKNOWN;
2551}
2552
2553/**
2554 * Recurse into a sub-directory and process all the files and directories.
2555 *
2556 * @returns IPRT status code.
2557 * @param pszBuf Path buffer containing the directory path on
2558 * entry. This ends with a dot. This is passed
2559 * along when recursing in order to save stack space
2560 * and avoid needless copying.
2561 * @param cchDir Length of our path in pszbuf.
2562 * @param pEntry Directory entry buffer. This is also passed
2563 * along when recursing to save stack space.
2564 * @param pSettingsStack The settings stack (pointer to the top element).
2565 * @param iRecursion The recursion depth. This is used to restrict
2566 * the recursions.
2567 */
2568static int scmProcessDirTreeRecursion(char *pszBuf, size_t cchDir, PRTDIRENTRY pEntry,
2569 PSCMSETTINGS pSettingsStack, unsigned iRecursion)
2570{
2571 int rc;
2572 Assert(cchDir > 1 && pszBuf[cchDir - 1] == '.');
2573
2574 /*
2575 * Make sure we stop somewhere.
2576 */
2577 if (iRecursion > 128)
2578 {
2579 RTMsgError("recursion too deep: %d\n", iRecursion);
2580 return VINF_SUCCESS; /* ignore */
2581 }
2582
2583 /*
2584 * Check if it's excluded by --only-svn-dir.
2585 */
2586 if (pSettingsStack->Base.fOnlySvnDirs)
2587 {
2588 if (!ScmSvnIsDirInWorkingCopy(pszBuf))
2589 return VINF_SUCCESS;
2590 }
2591 g_cDirsProcessed++;
2592
2593 /*
2594 * Try open and read the directory.
2595 */
2596 RTDIR hDir;
2597 rc = RTDirOpenFiltered(&hDir, pszBuf, RTDIRFILTER_NONE, 0 /*fFlags*/);
2598 if (RT_FAILURE(rc))
2599 {
2600 RTMsgError("Failed to enumerate directory '%s': %Rrc", pszBuf, rc);
2601 return rc;
2602 }
2603 for (;;)
2604 {
2605 /* Read the next entry. */
2606 rc = RTDirRead(hDir, pEntry, NULL);
2607 if (RT_FAILURE(rc))
2608 {
2609 if (rc == VERR_NO_MORE_FILES)
2610 rc = VINF_SUCCESS;
2611 else
2612 RTMsgError("RTDirRead -> %Rrc\n", rc);
2613 break;
2614 }
2615
2616 /* Skip '.' and '..'. */
2617 if ( pEntry->szName[0] == '.'
2618 && ( pEntry->cbName == 1
2619 || ( pEntry->cbName == 2
2620 && pEntry->szName[1] == '.')))
2621 continue;
2622
2623 /* Enter it into the buffer so we've got a full name to work
2624 with when needed. */
2625 if (pEntry->cbName + cchDir >= RTPATH_MAX)
2626 {
2627 RTMsgError("Skipping too long entry: %s", pEntry->szName);
2628 continue;
2629 }
2630 memcpy(&pszBuf[cchDir - 1], pEntry->szName, pEntry->cbName + 1);
2631
2632 /* Figure the type. */
2633 RTDIRENTRYTYPE enmType = pEntry->enmType;
2634 if (enmType == RTDIRENTRYTYPE_UNKNOWN)
2635 enmType = scmFigureUnknownType(pszBuf);
2636
2637 /* Process the file or directory, skip the rest. */
2638 if (enmType == RTDIRENTRYTYPE_FILE)
2639 rc = scmProcessFile(pszBuf, pEntry->szName, pEntry->cbName, pSettingsStack);
2640 else if (enmType == RTDIRENTRYTYPE_DIRECTORY)
2641 {
2642 /* Append the dot for the benefit of the pattern matching. */
2643 if (pEntry->cbName + cchDir + 5 >= RTPATH_MAX)
2644 {
2645 RTMsgError("Skipping too deep dir entry: %s", pEntry->szName);
2646 continue;
2647 }
2648 memcpy(&pszBuf[cchDir - 1 + pEntry->cbName], "/.", sizeof("/."));
2649 size_t cchSubDir = cchDir - 1 + pEntry->cbName + sizeof("/.") - 1;
2650
2651 if ( !pSettingsStack->Base.pszFilterOutDirs
2652 || !*pSettingsStack->Base.pszFilterOutDirs
2653 || ( !RTStrSimplePatternMultiMatch(pSettingsStack->Base.pszFilterOutDirs, RTSTR_MAX,
2654 pEntry->szName, pEntry->cbName, NULL)
2655 && !RTStrSimplePatternMultiMatch(pSettingsStack->Base.pszFilterOutDirs, RTSTR_MAX,
2656 pszBuf, cchSubDir, NULL)
2657 )
2658 )
2659 {
2660 rc = scmSettingsStackPushDir(&pSettingsStack, pszBuf);
2661 if (RT_SUCCESS(rc))
2662 {
2663 rc = scmProcessDirTreeRecursion(pszBuf, cchSubDir, pEntry, pSettingsStack, iRecursion + 1);
2664 scmSettingsStackPopAndDestroy(&pSettingsStack);
2665 }
2666 }
2667 }
2668 if (RT_FAILURE(rc))
2669 break;
2670 }
2671 RTDirClose(hDir);
2672 return rc;
2673
2674}
2675
2676/**
2677 * Process a directory tree.
2678 *
2679 * @returns IPRT status code.
2680 * @param pszDir The directory to start with. This is pointer to
2681 * a RTPATH_MAX sized buffer.
2682 */
2683static int scmProcessDirTree(char *pszDir, PSCMSETTINGS pSettingsStack)
2684{
2685 /*
2686 * Setup the recursion.
2687 */
2688 int rc = RTPathAppend(pszDir, RTPATH_MAX, ".");
2689 if (RT_SUCCESS(rc))
2690 {
2691 RTPathChangeToUnixSlashes(pszDir, true);
2692
2693 RTDIRENTRY Entry;
2694 rc = scmProcessDirTreeRecursion(pszDir, strlen(pszDir), &Entry, pSettingsStack, 0);
2695 }
2696 else
2697 RTMsgError("RTPathAppend: %Rrc\n", rc);
2698 return rc;
2699}
2700
2701
2702/**
2703 * Processes a file or directory specified as an command line argument.
2704 *
2705 * @returns IPRT status code
2706 * @param pszSomething What we found in the command line arguments.
2707 * @param pSettingsStack The settings stack (pointer to the top element).
2708 */
2709static int scmProcessSomething(const char *pszSomething, PSCMSETTINGS pSettingsStack)
2710{
2711 char szBuf[RTPATH_MAX];
2712 int rc = RTPathAbs(pszSomething, szBuf, sizeof(szBuf));
2713 if (RT_SUCCESS(rc))
2714 {
2715 RTPathChangeToUnixSlashes(szBuf, false /*fForce*/);
2716
2717 PSCMSETTINGS pSettings;
2718 rc = scmSettingsCreateForPath(&pSettings, &pSettingsStack->Base, szBuf);
2719 if (RT_SUCCESS(rc))
2720 {
2721 scmSettingsStackPush(&pSettingsStack, pSettings);
2722
2723 if (RTFileExists(szBuf))
2724 {
2725 const char *pszBasename = RTPathFilename(szBuf);
2726 if (pszBasename)
2727 {
2728 size_t cchBasename = strlen(pszBasename);
2729 rc = scmProcessFile(szBuf, pszBasename, cchBasename, pSettingsStack);
2730 }
2731 else
2732 {
2733 RTMsgError("RTPathFilename: NULL\n");
2734 rc = VERR_IS_A_DIRECTORY;
2735 }
2736 }
2737 else
2738 rc = scmProcessDirTree(szBuf, pSettingsStack);
2739
2740 PSCMSETTINGS pPopped = scmSettingsStackPop(&pSettingsStack);
2741 Assert(pPopped == pSettings); RT_NOREF_PV(pPopped);
2742 scmSettingsDestroy(pSettings);
2743 }
2744 else
2745 RTMsgError("scmSettingsInitStack: %Rrc\n", rc);
2746 }
2747 else
2748 RTMsgError("RTPathAbs: %Rrc\n", rc);
2749 return rc;
2750}
2751
2752/**
2753 * Print some stats.
2754 */
2755static void scmPrintStats(void)
2756{
2757 ScmVerbose(NULL, 0,
2758 g_fDryRun
2759 ? "%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"
2760 : "%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",
2761 g_cFilesModified,
2762 g_cFilesProcessed, g_cFilesProcessed == 1 ? "" : "s",
2763 g_cDirsProcessed, g_cDirsProcessed == 1 ? "" : "s",
2764 g_cFilesNoRewriters, g_cFilesNoRewriters == 1 ? "" : "s",
2765 g_cFilesBinaries, g_cFilesBinaries == 1 ? "y" : "ies",
2766 g_cFilesNotInSvn, g_cFilesSkipped);
2767}
2768
2769/**
2770 * Display the rewriter actions.
2771 *
2772 * @returns RTEXITCODE_SUCCESS.
2773 */
2774static int scmHelpActions(void)
2775{
2776 RTPrintf("Available rewriter actions:\n");
2777 for (uint32_t i = 0; i < RT_ELEMENTS(g_papRewriterActions); i++)
2778 RTPrintf(" %s\n", g_papRewriterActions[i]->pszName);
2779 return RTEXITCODE_SUCCESS;
2780}
2781
2782/**
2783 * Display the default configuration.
2784 *
2785 * @returns RTEXITCODE_SUCCESS.
2786 */
2787static int scmHelpConfig(void)
2788{
2789 RTPrintf("Rewriter configuration:\n");
2790 for (size_t iCfg = 0; iCfg < RT_ELEMENTS(g_aConfigs); iCfg++)
2791 {
2792 RTPrintf("\n %s%s - %s:\n",
2793 g_aConfigs[iCfg].pszName, g_aConfigs[iCfg].fBinary ? " (binary)" : "", g_aConfigs[iCfg].pszFilePattern);
2794 for (size_t i = 0; i < g_aConfigs[iCfg].cRewriters; i++)
2795 RTPrintf(" %s\n", g_aConfigs[iCfg].paRewriters[i]->pszName);
2796 }
2797 return RTEXITCODE_SUCCESS;
2798}
2799
2800/**
2801 * Display the primary help text.
2802 *
2803 * @returns RTEXITCODE_SUCCESS.
2804 * @param paOpts Options.
2805 * @param cOpts Number of options.
2806 */
2807static int scmHelp(PCRTGETOPTDEF paOpts, size_t cOpts)
2808{
2809 RTPrintf("VirtualBox Source Code Massager\n"
2810 "\n"
2811 "Usage: %s [options] <files & dirs>\n"
2812 "\n"
2813 "General options:\n", g_szProgName);
2814 for (size_t i = 0; i < cOpts; i++)
2815 {
2816 /* Grouping. */
2817 switch (paOpts[i].iShort)
2818 {
2819 case SCMOPT_DIFF_IGNORE_EOL:
2820 RTPrintf("\nDiff options (dry runs):\n");
2821 break;
2822 case SCMOPT_CONVERT_EOL:
2823 RTPrintf("\nRewriter action options:\n");
2824 break;
2825 case SCMOPT_ONLY_SVN_DIRS:
2826 RTPrintf("\nInput selection options:\n");
2827 break;
2828 case SCMOPT_TREAT_AS:
2829 RTPrintf("\nMisc options:\n");
2830 break;
2831 }
2832
2833 size_t cExtraAdvance = 0;
2834 if ((paOpts[i].fFlags & RTGETOPT_REQ_MASK) == RTGETOPT_REQ_NOTHING)
2835 {
2836 cExtraAdvance = i + 1 < cOpts
2837 && ( strstr(paOpts[i+1].pszLong, "-no-") != NULL
2838 || strstr(paOpts[i+1].pszLong, "-not-") != NULL
2839 || strstr(paOpts[i+1].pszLong, "-dont-") != NULL
2840 || strstr(paOpts[i+1].pszLong, "-unrestricted-") != NULL
2841 || (paOpts[i].iShort == 'q' && paOpts[i+1].iShort == 'v')
2842 || (paOpts[i].iShort == 'd' && paOpts[i+1].iShort == 'D')
2843 );
2844 if (cExtraAdvance)
2845 RTPrintf(" %s, %s\n", paOpts[i].pszLong, paOpts[i + 1].pszLong);
2846 else if (paOpts[i].iShort != SCMOPT_NO_UPDATE_LICENSE)
2847 RTPrintf(" %s\n", paOpts[i].pszLong);
2848 else
2849 {
2850 RTPrintf(" %s,\n"
2851 " %s,\n"
2852 " %s,\n"
2853 " %s,\n"
2854 " %s,\n"
2855 " %s,\n"
2856 " %s\n",
2857 paOpts[i].pszLong,
2858 paOpts[i + 1].pszLong,
2859 paOpts[i + 2].pszLong,
2860 paOpts[i + 3].pszLong,
2861 paOpts[i + 4].pszLong,
2862 paOpts[i + 5].pszLong,
2863 paOpts[i + 6].pszLong);
2864 cExtraAdvance = 6;
2865 }
2866 }
2867 else if ((paOpts[i].fFlags & RTGETOPT_REQ_MASK) == RTGETOPT_REQ_STRING)
2868 switch (paOpts[i].iShort)
2869 {
2870 case SCMOPT_DEL_ACTION:
2871 RTPrintf(" %s pattern\n", paOpts[i].pszLong);
2872 break;
2873 case SCMOPT_FILTER_OUT_DIRS:
2874 case SCMOPT_FILTER_FILES:
2875 case SCMOPT_FILTER_OUT_FILES:
2876 RTPrintf(" %s multi-pattern\n", paOpts[i].pszLong);
2877 break;
2878 default:
2879 RTPrintf(" %s string\n", paOpts[i].pszLong);
2880 }
2881 else
2882 RTPrintf(" %s value\n", paOpts[i].pszLong);
2883 switch (paOpts[i].iShort)
2884 {
2885 case 'd':
2886 case 'D': RTPrintf(" Default: --dry-run\n"); break;
2887 case SCMOPT_CHECK_RUN: RTPrintf(" Default: --dry-run\n"); break;
2888 case 'f': RTPrintf(" Default: none\n"); break;
2889 case 'q':
2890 case 'v': RTPrintf(" Default: -vv\n"); break;
2891 case SCMOPT_HELP_CONFIG: RTPrintf(" Shows the standard file rewriter configurations.\n"); break;
2892 case SCMOPT_HELP_ACTIONS: RTPrintf(" Shows the available rewriter actions.\n"); break;
2893
2894 case SCMOPT_DIFF_IGNORE_EOL: RTPrintf(" Default: false\n"); break;
2895 case SCMOPT_DIFF_IGNORE_SPACE: RTPrintf(" Default: false\n"); break;
2896 case SCMOPT_DIFF_IGNORE_LEADING_SPACE: RTPrintf(" Default: false\n"); break;
2897 case SCMOPT_DIFF_IGNORE_TRAILING_SPACE: RTPrintf(" Default: false\n"); break;
2898 case SCMOPT_DIFF_SPECIAL_CHARS: RTPrintf(" Default: true\n"); break;
2899
2900 case SCMOPT_CONVERT_EOL: RTPrintf(" Default: %RTbool\n", g_Defaults.fConvertEol); break;
2901 case SCMOPT_CONVERT_TABS: RTPrintf(" Default: %RTbool\n", g_Defaults.fConvertTabs); break;
2902 case SCMOPT_FORCE_FINAL_EOL: RTPrintf(" Default: %RTbool\n", g_Defaults.fForceFinalEol); break;
2903 case SCMOPT_FORCE_TRAILING_LINE: RTPrintf(" Default: %RTbool\n", g_Defaults.fForceTrailingLine); break;
2904 case SCMOPT_STRIP_TRAILING_BLANKS: RTPrintf(" Default: %RTbool\n", g_Defaults.fStripTrailingBlanks); break;
2905 case SCMOPT_STRIP_TRAILING_LINES: RTPrintf(" Default: %RTbool\n", g_Defaults.fStripTrailingLines); break;
2906 case SCMOPT_FIX_FLOWER_BOX_MARKERS: RTPrintf(" Default: %RTbool\n", g_Defaults.fFixFlowerBoxMarkers); break;
2907 case SCMOPT_MIN_BLANK_LINES_BEFORE_FLOWER_BOX_MARKERS: RTPrintf(" Default: %u\n", g_Defaults.cMinBlankLinesBeforeFlowerBoxMakers); break;
2908
2909 case SCMOPT_FIX_HEADER_GUARDS:
2910 RTPrintf(" Fix header guards and #pragma once. Default: %RTbool\n", g_Defaults.fFixHeaderGuards);
2911 break;
2912 case SCMOPT_PRAGMA_ONCE:
2913 RTPrintf(" Whether to include #pragma once with the header guard. Default: %RTbool\n", g_Defaults.fPragmaOnce);
2914 break;
2915 case SCMOPT_FIX_HEADER_GUARD_ENDIF:
2916 RTPrintf(" Whether to fix the #endif of a header guard. Default: %RTbool\n", g_Defaults.fFixHeaderGuardEndif);
2917 break;
2918 case SCMOPT_ENDIF_GUARD_COMMENT:
2919 RTPrintf(" Put a comment on the header guard #endif or not. Default: %RTbool\n", g_Defaults.fEndifGuardComment);
2920 break;
2921 case SCMOPT_GUARD_RELATIVE_TO_DIR:
2922 RTPrintf(" Header guard should be normalized relative to given dir.\n"
2923 " When relative to settings files, no preceeding slash.\n"
2924 " Header relative directory specification: {dir} and {parent}\n"
2925 " If empty no normalization takes place. Default: '%s'\n", g_Defaults.pszGuardRelativeToDir);
2926 break;
2927 case SCMOPT_GUARD_PREFIX:
2928 RTPrintf(" Prefix to use with --guard-relative-to-dir. Default: %s\n", g_Defaults.pszGuardPrefix);
2929 break;
2930 case SCMOPT_FIX_TODOS:
2931 RTPrintf(" Fix @todo statements so doxygen sees them. Default: %RTbool\n", g_Defaults.fFixTodos);
2932 break;
2933 case SCMOPT_FIX_ERR_H:
2934 RTPrintf(" Fix err.h/errcore.h usage. Default: %RTbool\n", g_Defaults.fFixErrH);
2935 break;
2936 case SCMOPT_ONLY_GUEST_HOST_PAGE:
2937 RTPrintf(" No PAGE_SIZE, PAGE_SHIFT or PAGE_OFFSET_MASK allowed, must have\n"
2938 " GUEST_ or HOST_ prefix. Also forbids use of PAGE_BASE_MASK,\n"
2939 " PAGE_BASE_HC_MASK, PAGE_BASE_GC_MASK, PAGE_ADDRESS,\n"
2940 " PHYS_PAGE_ADDRESS. Default: %RTbool\n", g_Defaults.fOnlyGuestHostPage);
2941 break;
2942 case SCMOPT_NO_ASM_MEM_PAGE_USE:
2943 RTPrintf(" No ASMMemIsZeroPage or ASMMemZeroPage allowed, must instead use\n"
2944 " ASMMemIsZero and RT_BZERO with appropriate page size. Default: %RTbool\n",
2945 g_Defaults.fNoASMMemPageUse);
2946 break;
2947 case SCMOPT_NO_RC_USE:
2948 RTPrintf(" No rc declaration allowed, must instead use\n"
2949 " vrc for IPRT status codes and hrc for COM status codes. Default: %RTbool\n",
2950 g_Defaults.fOnlyHrcVrcInsteadOfRc);
2951 break;
2952 case SCMOPT_UPDATE_COPYRIGHT_YEAR:
2953 RTPrintf(" Update the copyright year. Default: %RTbool\n", g_Defaults.fUpdateCopyrightYear);
2954 break;
2955 case SCMOPT_EXTERNAL_COPYRIGHT:
2956 RTPrintf(" Only external copyright holders. Default: %RTbool\n", g_Defaults.fExternalCopyright);
2957 break;
2958 case SCMOPT_NO_UPDATE_LICENSE:
2959 RTPrintf(" License selection. Default: --license-ose-gpl\n");
2960 break;
2961
2962 case SCMOPT_LGPL_DISCLAIMER:
2963 RTPrintf(" Include LGPL version disclaimer. Default: --no-lgpl-disclaimer\n");
2964 break;
2965
2966 case SCMOPT_SET_SVN_EOL: RTPrintf(" Default: %RTbool\n", g_Defaults.fSetSvnEol); break;
2967 case SCMOPT_SET_SVN_EXECUTABLE: RTPrintf(" Default: %RTbool\n", g_Defaults.fSetSvnExecutable); break;
2968 case SCMOPT_SET_SVN_KEYWORDS: RTPrintf(" Default: %RTbool\n", g_Defaults.fSetSvnKeywords); break;
2969 case SCMOPT_SKIP_SVN_SYNC_PROCESS: RTPrintf(" Default: %RTbool\n", g_Defaults.fSkipSvnSyncProcess); break;
2970 case SCMOPT_SKIP_UNICODE_CHECKS: RTPrintf(" Default: %RTbool\n", g_Defaults.fSkipUnicodeChecks); break;
2971 case SCMOPT_TAB_SIZE: RTPrintf(" Default: %u\n", g_Defaults.cchTab); break;
2972 case SCMOPT_WIDTH: RTPrintf(" Default: %u\n", g_Defaults.cchWidth); break;
2973
2974 case SCMOPT_ONLY_SVN_DIRS: RTPrintf(" Default: %RTbool\n", g_Defaults.fOnlySvnDirs); break;
2975 case SCMOPT_ONLY_SVN_FILES: RTPrintf(" Default: %RTbool\n", g_Defaults.fOnlySvnFiles); break;
2976 case SCMOPT_FILTER_OUT_DIRS: RTPrintf(" Default: %s\n", g_Defaults.pszFilterOutDirs); break;
2977 case SCMOPT_FILTER_FILES: RTPrintf(" Default: %s\n", g_Defaults.pszFilterFiles); break;
2978 case SCMOPT_FILTER_OUT_FILES: RTPrintf(" Default: %s\n", g_Defaults.pszFilterOutFiles); break;
2979
2980 case SCMOPT_TREAT_AS:
2981 RTPrintf(" For treat the input file(s) differently, restting any --add-action.\n"
2982 " If the value is empty defaults will be used again. Possible values:\n");
2983 for (size_t iCfg = 0; iCfg < RT_ELEMENTS(g_aConfigs); iCfg++)
2984 RTPrintf(" %s (%s)\n", g_aConfigs[iCfg].pszName, g_aConfigs[iCfg].pszFilePattern);
2985 break;
2986
2987 case SCMOPT_ADD_ACTION:
2988 RTPrintf(" Adds a rewriter action. The first use after a --treat-as will copy and\n"
2989 " the action list selected by the --treat-as. The action list will be\n"
2990 " flushed by --treat-as.\n");
2991 break;
2992
2993 case SCMOPT_DEL_ACTION:
2994 RTPrintf(" Deletes one or more rewriter action (pattern). Best used after\n"
2995 " a --treat-as.\n");
2996 break;
2997
2998 default: AssertMsgFailed(("i=%d %d %s\n", i, paOpts[i].iShort, paOpts[i].pszLong));
2999 }
3000 i += cExtraAdvance;
3001 }
3002
3003 return RTEXITCODE_SUCCESS;
3004}
3005
3006int main(int argc, char **argv)
3007{
3008 int rc = RTR3InitExe(argc, &argv, 0);
3009 if (RT_FAILURE(rc))
3010 return 1;
3011
3012 /*
3013 * Init the current year.
3014 */
3015 RTTIMESPEC Now;
3016 RTTIME Time;
3017 RTTimeExplode(&Time, RTTimeNow(&Now));
3018 g_uYear = Time.i32Year;
3019
3020 /*
3021 * Init the settings.
3022 */
3023 PSCMSETTINGS pSettings;
3024 rc = scmSettingsCreate(&pSettings, &g_Defaults);
3025 if (RT_FAILURE(rc))
3026 {
3027 RTMsgError("scmSettingsCreate: %Rrc\n", rc);
3028 return 1;
3029 }
3030
3031 /*
3032 * Parse arguments and process input in order (because this is the only
3033 * thing that works at the moment).
3034 */
3035 static RTGETOPTDEF s_aOpts[14 + RT_ELEMENTS(g_aScmOpts)] =
3036 {
3037 { "--dry-run", 'd', RTGETOPT_REQ_NOTHING },
3038 { "--real-run", 'D', RTGETOPT_REQ_NOTHING },
3039 { "--check-run", SCMOPT_CHECK_RUN, RTGETOPT_REQ_NOTHING },
3040 { "--file-filter", 'f', RTGETOPT_REQ_STRING },
3041 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
3042 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
3043 { "--diff-ignore-eol", SCMOPT_DIFF_IGNORE_EOL, RTGETOPT_REQ_NOTHING },
3044 { "--diff-no-ignore-eol", SCMOPT_DIFF_NO_IGNORE_EOL, RTGETOPT_REQ_NOTHING },
3045 { "--diff-ignore-space", SCMOPT_DIFF_IGNORE_SPACE, RTGETOPT_REQ_NOTHING },
3046 { "--diff-no-ignore-space", SCMOPT_DIFF_NO_IGNORE_SPACE, RTGETOPT_REQ_NOTHING },
3047 { "--diff-ignore-leading-space", SCMOPT_DIFF_IGNORE_LEADING_SPACE, RTGETOPT_REQ_NOTHING },
3048 { "--diff-no-ignore-leading-space", SCMOPT_DIFF_NO_IGNORE_LEADING_SPACE, RTGETOPT_REQ_NOTHING },
3049 { "--diff-ignore-trailing-space", SCMOPT_DIFF_IGNORE_TRAILING_SPACE, RTGETOPT_REQ_NOTHING },
3050 { "--diff-no-ignore-trailing-space", SCMOPT_DIFF_NO_IGNORE_TRAILING_SPACE, RTGETOPT_REQ_NOTHING },
3051 { "--diff-special-chars", SCMOPT_DIFF_SPECIAL_CHARS, RTGETOPT_REQ_NOTHING },
3052 { "--diff-no-special-chars", SCMOPT_DIFF_NO_SPECIAL_CHARS, RTGETOPT_REQ_NOTHING },
3053 };
3054 memcpy(&s_aOpts[RT_ELEMENTS(s_aOpts) - RT_ELEMENTS(g_aScmOpts)], &g_aScmOpts[0], sizeof(g_aScmOpts));
3055
3056 bool fCheckRun = false;
3057 RTGETOPTUNION ValueUnion;
3058 RTGETOPTSTATE GetOptState;
3059 rc = RTGetOptInit(&GetOptState, argc, argv, &s_aOpts[0], RT_ELEMENTS(s_aOpts), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
3060 AssertReleaseRCReturn(rc, 1);
3061
3062 while ( (rc = RTGetOpt(&GetOptState, &ValueUnion)) != 0
3063 && rc != VINF_GETOPT_NOT_OPTION)
3064 {
3065 switch (rc)
3066 {
3067 case 'd':
3068 g_fDryRun = true;
3069 fCheckRun = false;
3070 break;
3071 case 'D':
3072 g_fDryRun = fCheckRun = false;
3073 break;
3074 case SCMOPT_CHECK_RUN:
3075 g_fDryRun = fCheckRun = true;
3076 break;
3077
3078 case 'f':
3079 g_pszFileFilter = ValueUnion.psz;
3080 break;
3081
3082 case 'h':
3083 return scmHelp(s_aOpts, RT_ELEMENTS(s_aOpts));
3084
3085 case SCMOPT_HELP_CONFIG:
3086 return scmHelpConfig();
3087
3088 case SCMOPT_HELP_ACTIONS:
3089 return scmHelpActions();
3090
3091 case 'q':
3092 g_iVerbosity = 0;
3093 break;
3094
3095 case 'v':
3096 g_iVerbosity++;
3097 break;
3098
3099 case 'V':
3100 {
3101 /* The following is assuming that svn does it's job here. */
3102 static const char s_szRev[] = "$Revision: 96401 $";
3103 const char *psz = RTStrStripL(strchr(s_szRev, ' '));
3104 RTPrintf("r%.*s\n", strchr(psz, ' ') - psz, psz);
3105 return 0;
3106 }
3107
3108 case SCMOPT_DIFF_IGNORE_EOL:
3109 g_fDiffIgnoreEol = true;
3110 break;
3111 case SCMOPT_DIFF_NO_IGNORE_EOL:
3112 g_fDiffIgnoreEol = false;
3113 break;
3114
3115 case SCMOPT_DIFF_IGNORE_SPACE:
3116 g_fDiffIgnoreTrailingWS = g_fDiffIgnoreLeadingWS = true;
3117 break;
3118 case SCMOPT_DIFF_NO_IGNORE_SPACE:
3119 g_fDiffIgnoreTrailingWS = g_fDiffIgnoreLeadingWS = false;
3120 break;
3121
3122 case SCMOPT_DIFF_IGNORE_LEADING_SPACE:
3123 g_fDiffIgnoreLeadingWS = true;
3124 break;
3125 case SCMOPT_DIFF_NO_IGNORE_LEADING_SPACE:
3126 g_fDiffIgnoreLeadingWS = false;
3127 break;
3128
3129 case SCMOPT_DIFF_IGNORE_TRAILING_SPACE:
3130 g_fDiffIgnoreTrailingWS = true;
3131 break;
3132 case SCMOPT_DIFF_NO_IGNORE_TRAILING_SPACE:
3133 g_fDiffIgnoreTrailingWS = false;
3134 break;
3135
3136 case SCMOPT_DIFF_SPECIAL_CHARS:
3137 g_fDiffSpecialChars = true;
3138 break;
3139 case SCMOPT_DIFF_NO_SPECIAL_CHARS:
3140 g_fDiffSpecialChars = false;
3141 break;
3142
3143 default:
3144 {
3145 int rc2 = scmSettingsBaseHandleOpt(&pSettings->Base, rc, &ValueUnion, "/", 1);
3146 if (RT_SUCCESS(rc2))
3147 break;
3148 if (rc2 != VERR_GETOPT_UNKNOWN_OPTION)
3149 return 2;
3150 return RTGetOptPrintError(rc, &ValueUnion);
3151 }
3152 }
3153 }
3154
3155 /*
3156 * Process non-options.
3157 */
3158 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
3159 if (rc == VINF_GETOPT_NOT_OPTION)
3160 {
3161 ScmSvnInit();
3162
3163 bool fWarned = g_fDryRun;
3164 while (rc == VINF_GETOPT_NOT_OPTION)
3165 {
3166 if (!fWarned)
3167 {
3168 RTPrintf("%s: Warning! This program will make changes to your source files and\n"
3169 "%s: there is a slight risk that bugs or a full disk may cause\n"
3170 "%s: LOSS OF DATA. So, please make sure you have checked in\n"
3171 "%s: all your changes already. If you didn't, then don't blame\n"
3172 "%s: anyone for not warning you!\n"
3173 "%s:\n"
3174 "%s: Press any key to continue...\n",
3175 g_szProgName, g_szProgName, g_szProgName, g_szProgName, g_szProgName,
3176 g_szProgName, g_szProgName);
3177 RTStrmGetCh(g_pStdIn);
3178 fWarned = true;
3179 }
3180
3181 rc = scmProcessSomething(ValueUnion.psz, pSettings);
3182 if (RT_FAILURE(rc))
3183 {
3184 rcExit = RTEXITCODE_FAILURE;
3185 break;
3186 }
3187
3188 /* next */
3189 rc = RTGetOpt(&GetOptState, &ValueUnion);
3190 if (RT_FAILURE(rc))
3191 rcExit = RTGetOptPrintError(rc, &ValueUnion);
3192 }
3193
3194 scmPrintStats();
3195 ScmSvnTerm();
3196 }
3197 else
3198 RTMsgWarning("No files or directories specified. Doing nothing");
3199
3200 scmSettingsDestroy(pSettings);
3201
3202 /* If we're in checking mode, fail if any files needed modification. */
3203 if ( rcExit == RTEXITCODE_SUCCESS
3204 && fCheckRun
3205 && g_cFilesModified > 0)
3206 {
3207 RTMsgError("Checking mode failed! %u file%s needs modifications", g_cFilesBinaries, g_cFilesBinaries > 1 ? "s" : "");
3208 rcExit = RTEXITCODE_FAILURE;
3209 }
3210
3211 /* Fail if any files require manual repair. */
3212 if (g_cFilesRequiringManualFixing > 0)
3213 {
3214 RTMsgError("%u file%s needs manual modifications", g_cFilesRequiringManualFixing,
3215 g_cFilesRequiringManualFixing > 1 ? "s" : "");
3216 if (rcExit == RTEXITCODE_SUCCESS)
3217 rcExit = RTEXITCODE_FAILURE;
3218 }
3219
3220 return rcExit;
3221}
3222
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