VirtualBox

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

Last change on this file since 98113 was 98113, checked in by vboxsync, 22 months ago

scm,webtools: scm updates

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

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