VirtualBox

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

Last change on this file since 97078 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 122.3 KB
Line 
1/* $Id: scm.cpp 96407 2022-08-22 17:43:14Z vboxsync $ */
2/** @file
3 * IPRT Testcase / Tool - Source Code Massager.
4 */
5
6/*
7 * Copyright (C) 2010-2022 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|*.xcf|*.gif" ),
833 SCM_CFG_ENTRY("xslt", g_apRewritersFor_Xslt, false, "*.xsl" ),
834 SCM_CFG_ENTRY("xml", g_apRewritersFor_Xml, false, "*.xml|*.dist|*.qhcp" ),
835 SCM_CFG_ENTRY("wix", g_apRewritersFor_Wix, false, "*.wxi|*.wxs|*.wxl" ),
836 SCM_CFG_ENTRY("qt-pro", g_apRewritersFor_QtProject, false, "*.pro" ),
837 SCM_CFG_ENTRY("qt-rc", g_apRewritersFor_QtResourceFiles, false, "*.qrc" ),
838 SCM_CFG_ENTRY("qt-ts", g_apRewritersFor_QtTranslations, false, "*.ts" ),
839 SCM_CFG_ENTRY("qt-ui", g_apRewritersFor_QtUiFiles, false, "*.ui" ),
840 SCM_CFG_ENTRY("sif", g_apRewritersFor_SifFiles, false, "*.sif" ),
841 SCM_CFG_ENTRY("sql", g_apRewritersFor_SqlFiles, false, "*.pgsql|*.sql" ),
842 SCM_CFG_ENTRY("gas", g_apRewritersFor_GnuAsm, false, "*.S" ),
843 SCM_CFG_ENTRY("binary", g_apRewritersFor_BinaryFiles, true, "*.bin|*.pdf|*.zip|*.bz2|*.gz" ),
844 /* These should be be last: */
845 SCM_CFG_ENTRY("make", g_apRewritersFor_OtherMakefiles, false, "Makefile|makefile|GNUmakefile|SMakefile|Makefile.am|Makefile.in|*.cmake|*.gmk" ),
846 SCM_CFG_ENTRY("text", g_apRewritersFor_TextFiles, false, "*.txt|README*|readme*|ReadMe*|NOTE*|TODO*" ),
847 SCM_CFG_ENTRY("plaintext", g_apRewritersFor_PlainTextFiles, false, "LICENSE|ChangeLog|FAQ|AUTHORS|INSTALL|NEWS" ),
848 SCM_CFG_ENTRY("file-list", g_apRewritersFor_FileLists, false, "files_*" ),
849};
850
851
852
853/* -=-=-=-=-=- settings -=-=-=-=-=- */
854
855/**
856 * Delete the given config entry.
857 *
858 * @param pEntry The configuration entry to delete.
859 */
860static void scmCfgEntryDelete(PSCMCFGENTRY pEntry)
861{
862 RTMemFree((void *)pEntry->paRewriters);
863 pEntry->paRewriters = NULL;
864 RTMemFree(pEntry);
865}
866
867/**
868 * Create a new configuration entry.
869 *
870 * @returns The new entry. NULL if out of memory.
871 * @param pEntry The configuration entry to duplicate.
872 */
873static PSCMCFGENTRY scmCfgEntryNew(void)
874{
875 PSCMCFGENTRY pNew = (PSCMCFGENTRY)RTMemAlloc(sizeof(*pNew));
876 if (pNew)
877 {
878 pNew->pszName = "custom";
879 pNew->pszFilePattern = "custom";
880 pNew->cRewriters = 0;
881 pNew->paRewriters = NULL;
882 pNew->fBinary = false;
883 }
884 return pNew;
885}
886
887/**
888 * Duplicate the given config entry.
889 *
890 * @returns The duplicate. NULL if out of memory.
891 * @param pEntry The configuration entry to duplicate.
892 */
893static PSCMCFGENTRY scmCfgEntryDup(PCSCMCFGENTRY pEntry)
894{
895 if (pEntry)
896 {
897 PSCMCFGENTRY pDup = (PSCMCFGENTRY)RTMemDup(pEntry, sizeof(*pEntry));
898 if (pDup)
899 {
900 size_t cbSrcRewriters = sizeof(pEntry->paRewriters[0]) * pEntry->cRewriters;
901 size_t cbDstRewriters = sizeof(pEntry->paRewriters[0]) * RT_ALIGN_Z(pEntry->cRewriters, 8);
902 pDup->paRewriters = (PCSCMREWRITERCFG const *)RTMemDupEx(pEntry->paRewriters, cbSrcRewriters,
903 cbDstRewriters - cbSrcRewriters);
904 if (pDup->paRewriters)
905 return pDup;
906
907 RTMemFree(pDup);
908 }
909 return NULL;
910 }
911 return scmCfgEntryNew();
912}
913
914/**
915 * Adds a rewriter action to the given config entry (--add-action).
916 *
917 * @returns VINF_SUCCESS.
918 * @param pEntry The configuration entry.
919 * @param pAction The rewriter action to add.
920 */
921static int scmCfgEntryAddAction(PSCMCFGENTRY pEntry, PCSCMREWRITERCFG pAction)
922{
923 PCSCMREWRITERCFG *paRewriters = (PCSCMREWRITERCFG *)pEntry->paRewriters;
924 if (pEntry->cRewriters % 8 == 0)
925 {
926 size_t cbRewriters = sizeof(pEntry->paRewriters[0]) * RT_ALIGN_Z((pEntry->cRewriters + 1), 8);
927 void *pvNew = RTMemRealloc(paRewriters, cbRewriters);
928 if (pvNew)
929 pEntry->paRewriters = paRewriters = (PCSCMREWRITERCFG *)pvNew;
930 else
931 return VERR_NO_MEMORY;
932 }
933
934 paRewriters[pEntry->cRewriters++] = pAction;
935 return VINF_SUCCESS;
936}
937
938/**
939 * Delets an rewriter action from the given config entry (--del-action).
940 *
941 * @param pEntry The configuration entry.
942 * @param pAction The rewriter action to remove.
943 */
944static void scmCfgEntryDelAction(PSCMCFGENTRY pEntry, PCSCMREWRITERCFG pAction)
945{
946 PCSCMREWRITERCFG *paRewriters = (PCSCMREWRITERCFG *)pEntry->paRewriters;
947 size_t const cEntries = pEntry->cRewriters;
948 size_t iDst = 0;
949 for (size_t iSrc = 0; iSrc < cEntries; iSrc++)
950 {
951 PCSCMREWRITERCFG pCurAction = paRewriters[iSrc];
952 if (pCurAction != pAction)
953 paRewriters[iDst++] = pCurAction;
954 }
955 pEntry->cRewriters = iDst;
956}
957
958/**
959 * Init a settings structure with settings from @a pSrc.
960 *
961 * @returns IPRT status code
962 * @param pSettings The settings.
963 * @param pSrc The source settings.
964 */
965static int scmSettingsBaseInitAndCopy(PSCMSETTINGSBASE pSettings, PCSCMSETTINGSBASE pSrc)
966{
967 *pSettings = *pSrc;
968
969 int rc = RTStrDupEx(&pSettings->pszFilterFiles, pSrc->pszFilterFiles);
970 if (RT_SUCCESS(rc))
971 {
972 rc = RTStrDupEx(&pSettings->pszFilterOutFiles, pSrc->pszFilterOutFiles);
973 if (RT_SUCCESS(rc))
974 {
975 rc = RTStrDupEx(&pSettings->pszFilterOutDirs, pSrc->pszFilterOutDirs);
976 if (RT_SUCCESS(rc))
977 {
978 rc = RTStrDupEx(&pSettings->pszGuardPrefix, pSrc->pszGuardPrefix);
979 if (RT_SUCCESS(rc))
980 {
981 if (pSrc->pszGuardRelativeToDir)
982 rc = RTStrDupEx(&pSettings->pszGuardRelativeToDir, pSrc->pszGuardRelativeToDir);
983 if (RT_SUCCESS(rc))
984 {
985
986 if (!pSrc->fFreeTreatAs)
987 return VINF_SUCCESS;
988
989 pSettings->pTreatAs = scmCfgEntryDup(pSrc->pTreatAs);
990 if (pSettings->pTreatAs)
991 return VINF_SUCCESS;
992
993 RTStrFree(pSettings->pszGuardRelativeToDir);
994 }
995 RTStrFree(pSettings->pszGuardPrefix);
996 }
997 }
998 RTStrFree(pSettings->pszFilterOutFiles);
999 }
1000 RTStrFree(pSettings->pszFilterFiles);
1001 }
1002
1003 pSettings->pszGuardRelativeToDir = NULL;
1004 pSettings->pszGuardPrefix = NULL;
1005 pSettings->pszFilterFiles = NULL;
1006 pSettings->pszFilterOutFiles = NULL;
1007 pSettings->pszFilterOutDirs = NULL;
1008 pSettings->pTreatAs = NULL;
1009 return rc;
1010}
1011
1012/**
1013 * Init a settings structure.
1014 *
1015 * @returns IPRT status code
1016 * @param pSettings The settings.
1017 */
1018static int scmSettingsBaseInit(PSCMSETTINGSBASE pSettings)
1019{
1020 return scmSettingsBaseInitAndCopy(pSettings, &g_Defaults);
1021}
1022
1023/**
1024 * Deletes the settings, i.e. free any dynamically allocated content.
1025 *
1026 * @param pSettings The settings.
1027 */
1028static void scmSettingsBaseDelete(PSCMSETTINGSBASE pSettings)
1029{
1030 if (pSettings)
1031 {
1032 Assert(pSettings->cchTab != UINT8_MAX);
1033 pSettings->cchTab = UINT8_MAX;
1034
1035 RTStrFree(pSettings->pszGuardPrefix);
1036 RTStrFree(pSettings->pszGuardRelativeToDir);
1037 RTStrFree(pSettings->pszFilterFiles);
1038 RTStrFree(pSettings->pszFilterOutFiles);
1039 RTStrFree(pSettings->pszFilterOutDirs);
1040 if (pSettings->fFreeTreatAs)
1041 scmCfgEntryDelete((PSCMCFGENTRY)pSettings->pTreatAs);
1042
1043 pSettings->pszGuardPrefix = NULL;
1044 pSettings->pszGuardRelativeToDir = NULL;
1045 pSettings->pszFilterOutDirs = NULL;
1046 pSettings->pszFilterOutFiles = NULL;
1047 pSettings->pszFilterFiles = NULL;
1048 pSettings->pTreatAs = NULL;
1049 pSettings->fFreeTreatAs = false;
1050 }
1051}
1052
1053/**
1054 * Processes a RTGetOpt result.
1055 *
1056 * @retval VINF_SUCCESS if handled.
1057 * @retval VERR_OUT_OF_RANGE if the option value was out of range.
1058 * @retval VERR_GETOPT_UNKNOWN_OPTION if the option was not recognized.
1059 *
1060 * @param pSettings The settings to change.
1061 * @param rc The RTGetOpt return value.
1062 * @param pValueUnion The RTGetOpt value union.
1063 * @param pchDir The absolute path to the directory relative
1064 * components in pchLine should be relative to.
1065 * @param cchDir The length of the @a pchDir string.
1066 */
1067static int scmSettingsBaseHandleOpt(PSCMSETTINGSBASE pSettings, int rc, PRTGETOPTUNION pValueUnion,
1068 const char *pchDir, size_t cchDir)
1069{
1070 Assert(pchDir[cchDir - 1] == '/');
1071
1072 switch (rc)
1073 {
1074 case SCMOPT_CONVERT_EOL:
1075 pSettings->fConvertEol = true;
1076 return VINF_SUCCESS;
1077 case SCMOPT_NO_CONVERT_EOL:
1078 pSettings->fConvertEol = false;
1079 return VINF_SUCCESS;
1080
1081 case SCMOPT_CONVERT_TABS:
1082 pSettings->fConvertTabs = true;
1083 return VINF_SUCCESS;
1084 case SCMOPT_NO_CONVERT_TABS:
1085 pSettings->fConvertTabs = false;
1086 return VINF_SUCCESS;
1087
1088 case SCMOPT_FORCE_FINAL_EOL:
1089 pSettings->fForceFinalEol = true;
1090 return VINF_SUCCESS;
1091 case SCMOPT_NO_FORCE_FINAL_EOL:
1092 pSettings->fForceFinalEol = false;
1093 return VINF_SUCCESS;
1094
1095 case SCMOPT_FORCE_TRAILING_LINE:
1096 pSettings->fForceTrailingLine = true;
1097 return VINF_SUCCESS;
1098 case SCMOPT_NO_FORCE_TRAILING_LINE:
1099 pSettings->fForceTrailingLine = false;
1100 return VINF_SUCCESS;
1101
1102
1103 case SCMOPT_STRIP_TRAILING_BLANKS:
1104 pSettings->fStripTrailingBlanks = true;
1105 return VINF_SUCCESS;
1106 case SCMOPT_NO_STRIP_TRAILING_BLANKS:
1107 pSettings->fStripTrailingBlanks = false;
1108 return VINF_SUCCESS;
1109
1110 case SCMOPT_MIN_BLANK_LINES_BEFORE_FLOWER_BOX_MARKERS:
1111 pSettings->cMinBlankLinesBeforeFlowerBoxMakers = pValueUnion->u8;
1112 return VINF_SUCCESS;
1113
1114
1115 case SCMOPT_STRIP_TRAILING_LINES:
1116 pSettings->fStripTrailingLines = true;
1117 return VINF_SUCCESS;
1118 case SCMOPT_NO_STRIP_TRAILING_LINES:
1119 pSettings->fStripTrailingLines = false;
1120 return VINF_SUCCESS;
1121
1122 case SCMOPT_FIX_FLOWER_BOX_MARKERS:
1123 pSettings->fFixFlowerBoxMarkers = true;
1124 return VINF_SUCCESS;
1125 case SCMOPT_NO_FIX_FLOWER_BOX_MARKERS:
1126 pSettings->fFixFlowerBoxMarkers = false;
1127 return VINF_SUCCESS;
1128
1129 case SCMOPT_FIX_HEADER_GUARDS:
1130 pSettings->fFixHeaderGuards = true;
1131 return VINF_SUCCESS;
1132 case SCMOPT_NO_FIX_HEADER_GUARDS:
1133 pSettings->fFixHeaderGuards = false;
1134 return VINF_SUCCESS;
1135
1136 case SCMOPT_PRAGMA_ONCE:
1137 pSettings->fPragmaOnce = true;
1138 return VINF_SUCCESS;
1139 case SCMOPT_NO_PRAGMA_ONCE:
1140 pSettings->fPragmaOnce = false;
1141 return VINF_SUCCESS;
1142
1143 case SCMOPT_FIX_HEADER_GUARD_ENDIF:
1144 pSettings->fFixHeaderGuardEndif = true;
1145 return VINF_SUCCESS;
1146 case SCMOPT_NO_FIX_HEADER_GUARD_ENDIF:
1147 pSettings->fFixHeaderGuardEndif = false;
1148 return VINF_SUCCESS;
1149
1150 case SCMOPT_ENDIF_GUARD_COMMENT:
1151 pSettings->fEndifGuardComment = true;
1152 return VINF_SUCCESS;
1153 case SCMOPT_NO_ENDIF_GUARD_COMMENT:
1154 pSettings->fEndifGuardComment = false;
1155 return VINF_SUCCESS;
1156
1157 case SCMOPT_GUARD_PREFIX:
1158 RTStrFree(pSettings->pszGuardPrefix);
1159 pSettings->pszGuardPrefix = NULL;
1160 return RTStrDupEx(&pSettings->pszGuardPrefix, pValueUnion->psz);
1161
1162 case SCMOPT_GUARD_RELATIVE_TO_DIR:
1163 RTStrFree(pSettings->pszGuardRelativeToDir);
1164 pSettings->pszGuardRelativeToDir = NULL;
1165 if (*pValueUnion->psz != '\0')
1166 {
1167 if ( strcmp(pValueUnion->psz, "{dir}") == 0
1168 || strcmp(pValueUnion->psz, "{parent}") == 0)
1169 return RTStrDupEx(&pSettings->pszGuardRelativeToDir, pValueUnion->psz);
1170 if (cchDir == 1 && *pchDir == '/')
1171 {
1172 pSettings->pszGuardRelativeToDir = RTPathAbsDup(pValueUnion->psz);
1173 if (pSettings->pszGuardRelativeToDir)
1174 return VINF_SUCCESS;
1175 }
1176 else
1177 {
1178 char *pszDir = RTStrDupN(pchDir, cchDir);
1179 if (pszDir)
1180 {
1181 pSettings->pszGuardRelativeToDir = RTPathAbsExDup(pszDir, pValueUnion->psz, RTPATH_STR_F_STYLE_HOST);
1182 RTStrFree(pszDir);
1183 if (pSettings->pszGuardRelativeToDir)
1184 return VINF_SUCCESS;
1185 }
1186 }
1187 RTMsgError("Failed to abspath --guard-relative-to-dir value '%s' - probably out of memory\n", pValueUnion->psz);
1188 return VERR_NO_STR_MEMORY;
1189 }
1190 return VINF_SUCCESS;
1191
1192 case SCMOPT_FIX_TODOS:
1193 pSettings->fFixTodos = true;
1194 return VINF_SUCCESS;
1195 case SCMOPT_NO_FIX_TODOS:
1196 pSettings->fFixTodos = false;
1197 return VINF_SUCCESS;
1198
1199 case SCMOPT_FIX_ERR_H:
1200 pSettings->fFixErrH = true;
1201 return VINF_SUCCESS;
1202 case SCMOPT_NO_FIX_ERR_H:
1203 pSettings->fFixErrH = false;
1204 return VINF_SUCCESS;
1205
1206 case SCMOPT_ONLY_GUEST_HOST_PAGE:
1207 pSettings->fOnlyGuestHostPage = true;
1208 return VINF_SUCCESS;
1209 case SCMOPT_NO_PAGE_RESTRICTIONS:
1210 pSettings->fOnlyGuestHostPage = false;
1211 return VINF_SUCCESS;
1212
1213 case SCMOPT_NO_ASM_MEM_PAGE_USE:
1214 pSettings->fNoASMMemPageUse = true;
1215 return VINF_SUCCESS;
1216 case SCMOPT_UNRESTRICTED_ASM_MEM_PAGE_USE:
1217 pSettings->fNoASMMemPageUse = false;
1218 return VINF_SUCCESS;
1219
1220 case SCMOPT_NO_RC_USE:
1221 pSettings->fOnlyHrcVrcInsteadOfRc = true;
1222 return VINF_SUCCESS;
1223 case SCMOPT_UNRESTRICTED_RC_USE:
1224 pSettings->fOnlyHrcVrcInsteadOfRc = false;
1225 return VINF_SUCCESS;
1226
1227 case SCMOPT_UPDATE_COPYRIGHT_YEAR:
1228 pSettings->fUpdateCopyrightYear = true;
1229 return VINF_SUCCESS;
1230 case SCMOPT_NO_UPDATE_COPYRIGHT_YEAR:
1231 pSettings->fUpdateCopyrightYear = false;
1232 return VINF_SUCCESS;
1233
1234 case SCMOPT_EXTERNAL_COPYRIGHT:
1235 pSettings->fExternalCopyright = true;
1236 return VINF_SUCCESS;
1237 case SCMOPT_NO_EXTERNAL_COPYRIGHT:
1238 pSettings->fExternalCopyright = false;
1239 return VINF_SUCCESS;
1240
1241 case SCMOPT_NO_UPDATE_LICENSE:
1242 pSettings->enmUpdateLicense = kScmLicense_LeaveAlone;
1243 return VINF_SUCCESS;
1244 case SCMOPT_LICENSE_OSE_GPL:
1245 pSettings->enmUpdateLicense = kScmLicense_OseGpl;
1246 return VINF_SUCCESS;
1247 case SCMOPT_LICENSE_OSE_DUAL_GPL_CDDL:
1248 pSettings->enmUpdateLicense = kScmLicense_OseDualGplCddl;
1249 return VINF_SUCCESS;
1250 case SCMOPT_LICENSE_OSE_CDDL:
1251 pSettings->enmUpdateLicense = kScmLicense_OseCddl;
1252 return VINF_SUCCESS;
1253 case SCMOPT_LICENSE_LGPL:
1254 pSettings->enmUpdateLicense = kScmLicense_Lgpl;
1255 return VINF_SUCCESS;
1256 case SCMOPT_LICENSE_MIT:
1257 pSettings->enmUpdateLicense = kScmLicense_Mit;
1258 return VINF_SUCCESS;
1259 case SCMOPT_LICENSE_BASED_ON_MIT:
1260 pSettings->enmUpdateLicense = kScmLicense_BasedOnMit;
1261 return VINF_SUCCESS;
1262
1263 case SCMOPT_LGPL_DISCLAIMER:
1264 pSettings->fLgplDisclaimer = true;
1265 return VINF_SUCCESS;
1266 case SCMOPT_NO_LGPL_DISCLAIMER:
1267 pSettings->fLgplDisclaimer = false;
1268 return VINF_SUCCESS;
1269
1270 case SCMOPT_ONLY_SVN_DIRS:
1271 pSettings->fOnlySvnDirs = true;
1272 return VINF_SUCCESS;
1273 case SCMOPT_NOT_ONLY_SVN_DIRS:
1274 pSettings->fOnlySvnDirs = false;
1275 return VINF_SUCCESS;
1276
1277 case SCMOPT_ONLY_SVN_FILES:
1278 pSettings->fOnlySvnFiles = true;
1279 return VINF_SUCCESS;
1280 case SCMOPT_NOT_ONLY_SVN_FILES:
1281 pSettings->fOnlySvnFiles = false;
1282 return VINF_SUCCESS;
1283
1284 case SCMOPT_SET_SVN_EOL:
1285 pSettings->fSetSvnEol = true;
1286 return VINF_SUCCESS;
1287 case SCMOPT_DONT_SET_SVN_EOL:
1288 pSettings->fSetSvnEol = false;
1289 return VINF_SUCCESS;
1290
1291 case SCMOPT_SET_SVN_EXECUTABLE:
1292 pSettings->fSetSvnExecutable = true;
1293 return VINF_SUCCESS;
1294 case SCMOPT_DONT_SET_SVN_EXECUTABLE:
1295 pSettings->fSetSvnExecutable = false;
1296 return VINF_SUCCESS;
1297
1298 case SCMOPT_SET_SVN_KEYWORDS:
1299 pSettings->fSetSvnKeywords = true;
1300 return VINF_SUCCESS;
1301 case SCMOPT_DONT_SET_SVN_KEYWORDS:
1302 pSettings->fSetSvnKeywords = false;
1303 return VINF_SUCCESS;
1304
1305 case SCMOPT_SKIP_SVN_SYNC_PROCESS:
1306 pSettings->fSkipSvnSyncProcess = true;
1307 return VINF_SUCCESS;
1308 case SCMOPT_DONT_SKIP_SVN_SYNC_PROCESS:
1309 pSettings->fSkipSvnSyncProcess = false;
1310 return VINF_SUCCESS;
1311
1312 case SCMOPT_SKIP_UNICODE_CHECKS:
1313 pSettings->fSkipUnicodeChecks = true;
1314 return VINF_SUCCESS;
1315 case SCMOPT_DONT_SKIP_UNICODE_CHECKS:
1316 pSettings->fSkipUnicodeChecks = false;
1317 return VINF_SUCCESS;
1318
1319 case SCMOPT_TAB_SIZE:
1320 if ( pValueUnion->u8 < 1
1321 || pValueUnion->u8 >= RT_ELEMENTS(g_szTabSpaces))
1322 {
1323 RTMsgError("Invalid tab size: %u - must be in {1..%u}\n",
1324 pValueUnion->u8, RT_ELEMENTS(g_szTabSpaces) - 1);
1325 return VERR_OUT_OF_RANGE;
1326 }
1327 pSettings->cchTab = pValueUnion->u8;
1328 return VINF_SUCCESS;
1329
1330 case SCMOPT_WIDTH:
1331 if (pValueUnion->u8 < 20 || pValueUnion->u8 > 200)
1332 {
1333 RTMsgError("Invalid width size: %u - must be in {20..200} range\n", pValueUnion->u8);
1334 return VERR_OUT_OF_RANGE;
1335 }
1336 pSettings->cchWidth = pValueUnion->u8;
1337 return VINF_SUCCESS;
1338
1339 case SCMOPT_FILTER_OUT_DIRS:
1340 case SCMOPT_FILTER_FILES:
1341 case SCMOPT_FILTER_OUT_FILES:
1342 {
1343 char **ppsz = NULL;
1344 switch (rc)
1345 {
1346 case SCMOPT_FILTER_OUT_DIRS: ppsz = &pSettings->pszFilterOutDirs; break;
1347 case SCMOPT_FILTER_FILES: ppsz = &pSettings->pszFilterFiles; break;
1348 case SCMOPT_FILTER_OUT_FILES: ppsz = &pSettings->pszFilterOutFiles; break;
1349 }
1350
1351 /*
1352 * An empty string zaps the current list.
1353 */
1354 if (!*pValueUnion->psz)
1355 return RTStrATruncate(ppsz, 0);
1356
1357 /*
1358 * Non-empty strings are appended to the pattern list.
1359 *
1360 * Strip leading and trailing pattern separators before attempting
1361 * to append it. If it's just separators, don't do anything.
1362 */
1363 const char *pszSrc = pValueUnion->psz;
1364 while (*pszSrc == '|')
1365 pszSrc++;
1366 size_t cchSrc = strlen(pszSrc);
1367 while (cchSrc > 0 && pszSrc[cchSrc - 1] == '|')
1368 cchSrc--;
1369 if (!cchSrc)
1370 return VINF_SUCCESS;
1371
1372 /* Append it pattern by pattern, turning settings-relative paths into absolute ones. */
1373 for (;;)
1374 {
1375 const char *pszEnd = (const char *)memchr(pszSrc, '|', cchSrc);
1376 size_t cchPattern = pszEnd ? pszEnd - pszSrc : cchSrc;
1377 int rc2;
1378 if (*pszSrc == '/')
1379 rc2 = RTStrAAppendExN(ppsz, 3,
1380 "|", *ppsz && **ppsz != '\0' ? (size_t)1 : (size_t)0,
1381 pchDir, cchDir - 1,
1382 pszSrc, cchPattern);
1383 else
1384 rc2 = RTStrAAppendExN(ppsz, 2,
1385 "|", *ppsz && **ppsz != '\0' ? (size_t)1 : (size_t)0,
1386 pszSrc, cchPattern);
1387 if (RT_FAILURE(rc2))
1388 return rc2;
1389
1390 /* next */
1391 cchSrc -= cchPattern;
1392 if (!cchSrc)
1393 return VINF_SUCCESS;
1394 cchSrc -= 1;
1395 pszSrc += cchPattern + 1;
1396 }
1397 /* not reached */
1398 }
1399
1400 case SCMOPT_TREAT_AS:
1401 if (pSettings->fFreeTreatAs)
1402 {
1403 scmCfgEntryDelete((PSCMCFGENTRY)pSettings->pTreatAs);
1404 pSettings->pTreatAs = NULL;
1405 pSettings->fFreeTreatAs = false;
1406 }
1407
1408 if (*pValueUnion->psz)
1409 {
1410 /* first check the names, then patterns (legacy). */
1411 for (size_t iCfg = 0; iCfg < RT_ELEMENTS(g_aConfigs); iCfg++)
1412 if (strcmp(g_aConfigs[iCfg].pszName, pValueUnion->psz) == 0)
1413 {
1414 pSettings->pTreatAs = &g_aConfigs[iCfg];
1415 return VINF_SUCCESS;
1416 }
1417 for (size_t iCfg = 0; iCfg < RT_ELEMENTS(g_aConfigs); iCfg++)
1418 if (RTStrSimplePatternMultiMatch(g_aConfigs[iCfg].pszFilePattern, RTSTR_MAX,
1419 pValueUnion->psz, RTSTR_MAX, NULL))
1420 {
1421 pSettings->pTreatAs = &g_aConfigs[iCfg];
1422 return VINF_SUCCESS;
1423 }
1424 /* Special help for listing the possibilities? */
1425 if (strcmp(pValueUnion->psz, "help") == 0)
1426 {
1427 RTPrintf("Possible --treat-as values:\n");
1428 for (size_t iCfg = 0; iCfg < RT_ELEMENTS(g_aConfigs); iCfg++)
1429 RTPrintf(" %s (%s)\n", g_aConfigs[iCfg].pszName, g_aConfigs[iCfg].pszFilePattern);
1430 }
1431 return VERR_NOT_FOUND;
1432 }
1433
1434 pSettings->pTreatAs = NULL;
1435 return VINF_SUCCESS;
1436
1437 case SCMOPT_ADD_ACTION:
1438 for (uint32_t iAction = 0; iAction < RT_ELEMENTS(g_papRewriterActions); iAction++)
1439 if (strcmp(g_papRewriterActions[iAction]->pszName, pValueUnion->psz) == 0)
1440 {
1441 PSCMCFGENTRY pEntry = (PSCMCFGENTRY)pSettings->pTreatAs;
1442 if (!pSettings->fFreeTreatAs)
1443 {
1444 pEntry = scmCfgEntryDup(pEntry);
1445 if (!pEntry)
1446 return VERR_NO_MEMORY;
1447 pSettings->pTreatAs = pEntry;
1448 pSettings->fFreeTreatAs = true;
1449 }
1450 return scmCfgEntryAddAction(pEntry, g_papRewriterActions[iAction]);
1451 }
1452 RTMsgError("Unknown --add-action value '%s'. Try --help-actions for a list.", pValueUnion->psz);
1453 return VERR_NOT_FOUND;
1454
1455 case SCMOPT_DEL_ACTION:
1456 {
1457 uint32_t cActions = 0;
1458 for (uint32_t iAction = 0; iAction < RT_ELEMENTS(g_papRewriterActions); iAction++)
1459 if (RTStrSimplePatternMatch(pValueUnion->psz, g_papRewriterActions[iAction]->pszName))
1460 {
1461 cActions++;
1462 PSCMCFGENTRY pEntry = (PSCMCFGENTRY)pSettings->pTreatAs;
1463 if (!pSettings->fFreeTreatAs)
1464 {
1465 pEntry = scmCfgEntryDup(pEntry);
1466 if (!pEntry)
1467 return VERR_NO_MEMORY;
1468 pSettings->pTreatAs = pEntry;
1469 pSettings->fFreeTreatAs = true;
1470 }
1471 scmCfgEntryDelAction(pEntry, g_papRewriterActions[iAction]);
1472 if (!strchr(pValueUnion->psz, '*'))
1473 return VINF_SUCCESS;
1474 }
1475 if (cActions > 0)
1476 return VINF_SUCCESS;
1477 RTMsgError("Unknown --del-action value '%s'. Try --help-actions for a list.", pValueUnion->psz);
1478 return VERR_NOT_FOUND;
1479 }
1480
1481 default:
1482 return VERR_GETOPT_UNKNOWN_OPTION;
1483 }
1484}
1485
1486/**
1487 * Parses an option string.
1488 *
1489 * @returns IPRT status code.
1490 * @param pBase The base settings structure to apply the options
1491 * to.
1492 * @param pszOptions The options to parse.
1493 * @param pchDir The absolute path to the directory relative
1494 * components in pchLine should be relative to.
1495 * @param cchDir The length of the @a pchDir string.
1496 */
1497static int scmSettingsBaseParseString(PSCMSETTINGSBASE pBase, const char *pszLine, const char *pchDir, size_t cchDir)
1498{
1499 int cArgs;
1500 char **papszArgs;
1501 int rc = RTGetOptArgvFromString(&papszArgs, &cArgs, pszLine, RTGETOPTARGV_CNV_QUOTE_BOURNE_SH, NULL);
1502 if (RT_SUCCESS(rc))
1503 {
1504 RTGETOPTUNION ValueUnion;
1505 RTGETOPTSTATE GetOptState;
1506 rc = RTGetOptInit(&GetOptState, cArgs, papszArgs, &g_aScmOpts[0], RT_ELEMENTS(g_aScmOpts), 0, 0 /*fFlags*/);
1507 if (RT_SUCCESS(rc))
1508 {
1509 while ((rc = RTGetOpt(&GetOptState, &ValueUnion)) != 0)
1510 {
1511 rc = scmSettingsBaseHandleOpt(pBase, rc, &ValueUnion, pchDir, cchDir);
1512 if (RT_FAILURE(rc))
1513 break;
1514 }
1515 }
1516 RTGetOptArgvFree(papszArgs);
1517 }
1518
1519 return rc;
1520}
1521
1522/**
1523 * Parses an unterminated option string.
1524 *
1525 * @returns IPRT status code.
1526 * @param pBase The base settings structure to apply the options
1527 * to.
1528 * @param pchLine The line.
1529 * @param cchLine The line length.
1530 * @param pchDir The absolute path to the directory relative
1531 * components in pchLine should be relative to.
1532 * @param cchDir The length of the @a pchDir string.
1533 */
1534static int scmSettingsBaseParseStringN(PSCMSETTINGSBASE pBase, const char *pchLine, size_t cchLine,
1535 const char *pchDir, size_t cchDir)
1536{
1537 char *pszLine = RTStrDupN(pchLine, cchLine);
1538 if (!pszLine)
1539 return VERR_NO_MEMORY;
1540 int rc = scmSettingsBaseParseString(pBase, pszLine, pchDir, cchDir);
1541 RTStrFree(pszLine);
1542 return rc;
1543}
1544
1545/**
1546 * Verifies the options string.
1547 *
1548 * @returns IPRT status code.
1549 * @param pszOptions The options to verify .
1550 */
1551static int scmSettingsBaseVerifyString(const char *pszOptions)
1552{
1553 SCMSETTINGSBASE Base;
1554 int rc = scmSettingsBaseInit(&Base);
1555 if (RT_SUCCESS(rc))
1556 {
1557 rc = scmSettingsBaseParseString(&Base, pszOptions, "/", 1);
1558 scmSettingsBaseDelete(&Base);
1559 }
1560 return rc;
1561}
1562
1563/**
1564 * Loads settings found in editor and SCM settings directives within the
1565 * document (@a pStream).
1566 *
1567 * @returns IPRT status code.
1568 * @param pBase The settings base to load settings into.
1569 * @param pStream The stream to scan for settings directives.
1570 */
1571static int scmSettingsBaseLoadFromDocument(PSCMSETTINGSBASE pBase, PSCMSTREAM pStream)
1572{
1573 /** @todo Editor and SCM settings directives in documents. */
1574 RT_NOREF2(pBase, pStream);
1575 return VINF_SUCCESS;
1576}
1577
1578/**
1579 * Creates a new settings file struct, cloning @a pSettings.
1580 *
1581 * @returns IPRT status code.
1582 * @param ppSettings Where to return the new struct.
1583 * @param pSettingsBase The settings to inherit from.
1584 */
1585static int scmSettingsCreate(PSCMSETTINGS *ppSettings, PCSCMSETTINGSBASE pSettingsBase)
1586{
1587 PSCMSETTINGS pSettings = (PSCMSETTINGS)RTMemAlloc(sizeof(*pSettings));
1588 if (!pSettings)
1589 return VERR_NO_MEMORY;
1590 int rc = scmSettingsBaseInitAndCopy(&pSettings->Base, pSettingsBase);
1591 if (RT_SUCCESS(rc))
1592 {
1593 pSettings->pDown = NULL;
1594 pSettings->pUp = NULL;
1595 pSettings->paPairs = NULL;
1596 pSettings->cPairs = 0;
1597 *ppSettings = pSettings;
1598 return VINF_SUCCESS;
1599 }
1600 RTMemFree(pSettings);
1601 return rc;
1602}
1603
1604/**
1605 * Destroys a settings structure.
1606 *
1607 * @param pSettings The settings structure to destroy. NULL is OK.
1608 */
1609static void scmSettingsDestroy(PSCMSETTINGS pSettings)
1610{
1611 if (pSettings)
1612 {
1613 scmSettingsBaseDelete(&pSettings->Base);
1614 for (size_t i = 0; i < pSettings->cPairs; i++)
1615 {
1616 RTStrFree(pSettings->paPairs[i].pszPattern);
1617 RTStrFree(pSettings->paPairs[i].pszOptions);
1618 RTStrFree(pSettings->paPairs[i].pszRelativeTo);
1619 pSettings->paPairs[i].pszPattern = NULL;
1620 pSettings->paPairs[i].pszOptions = NULL;
1621 pSettings->paPairs[i].pszRelativeTo = NULL;
1622 }
1623 RTMemFree(pSettings->paPairs);
1624 pSettings->paPairs = NULL;
1625 RTMemFree(pSettings);
1626 }
1627}
1628
1629/**
1630 * Adds a pattern/options pair to the settings structure.
1631 *
1632 * @returns IPRT status code.
1633 * @param pSettings The settings.
1634 * @param pchLine The line containing the unparsed pair.
1635 * @param cchLine The length of the line.
1636 * @param offColon The offset of the colon into the line.
1637 * @param pchDir The absolute path to the directory relative
1638 * components in pchLine should be relative to.
1639 * @param cchDir The length of the @a pchDir string.
1640 */
1641static int scmSettingsAddPair(PSCMSETTINGS pSettings, const char *pchLine, size_t cchLine, size_t offColon,
1642 const char *pchDir, size_t cchDir)
1643{
1644 Assert(pchLine[offColon] == ':' && offColon < cchLine);
1645 Assert(pchDir[cchDir - 1] == '/');
1646
1647 /*
1648 * Split the string.
1649 */
1650 size_t cchPattern = offColon;
1651 size_t cchOptions = cchLine - cchPattern - 1;
1652
1653 /* strip spaces everywhere */
1654 while (cchPattern > 0 && RT_C_IS_SPACE(pchLine[cchPattern - 1]))
1655 cchPattern--;
1656 while (cchPattern > 0 && RT_C_IS_SPACE(*pchLine))
1657 cchPattern--, pchLine++;
1658
1659 const char *pchOptions = &pchLine[offColon + 1];
1660 while (cchOptions > 0 && RT_C_IS_SPACE(pchOptions[cchOptions - 1]))
1661 cchOptions--;
1662 while (cchOptions > 0 && RT_C_IS_SPACE(*pchOptions))
1663 cchOptions--, pchOptions++;
1664
1665 /* Quietly ignore empty patterns and empty options. */
1666 if (!cchOptions || !cchPattern)
1667 return VINF_SUCCESS;
1668
1669 /*
1670 * Prepair the pair and verify the option string.
1671 */
1672 uint32_t iPair = pSettings->cPairs;
1673 if ((iPair % 32) == 0)
1674 {
1675 void *pvNew = RTMemRealloc(pSettings->paPairs, (iPair + 32) * sizeof(pSettings->paPairs[0]));
1676 if (!pvNew)
1677 return VERR_NO_MEMORY;
1678 pSettings->paPairs = (PSCMPATRNOPTPAIR)pvNew;
1679 }
1680
1681 pSettings->paPairs[iPair].pszPattern = RTStrDupN(pchLine, cchPattern);
1682 pSettings->paPairs[iPair].pszOptions = RTStrDupN(pchOptions, cchOptions);
1683 pSettings->paPairs[iPair].pszRelativeTo = RTStrDupN(pchDir, cchDir);
1684 int rc;
1685 if ( pSettings->paPairs[iPair].pszPattern
1686 && pSettings->paPairs[iPair].pszOptions
1687 && pSettings->paPairs[iPair].pszRelativeTo)
1688 rc = scmSettingsBaseVerifyString(pSettings->paPairs[iPair].pszOptions);
1689 else
1690 rc = VERR_NO_MEMORY;
1691
1692 /*
1693 * If it checked out fine, expand any relative paths in the pattern.
1694 */
1695 if (RT_SUCCESS(rc))
1696 {
1697 size_t cPattern = 1;
1698 size_t cRelativePaths = 0;
1699 const char *pszSrc = pSettings->paPairs[iPair].pszPattern;
1700 for (;;)
1701 {
1702 if (*pszSrc == '/')
1703 cRelativePaths++;
1704 pszSrc = strchr(pszSrc, '|');
1705 if (!pszSrc)
1706 break;
1707 pszSrc++;
1708 cPattern++;
1709 }
1710 pSettings->paPairs[iPair].fMultiPattern = cPattern > 1;
1711 if (cRelativePaths > 0)
1712 {
1713 char *pszNewPattern = RTStrAlloc(cchPattern + cRelativePaths * (cchDir - 1) + 1);
1714 if (pszNewPattern)
1715 {
1716 char *pszDst = pszNewPattern;
1717 pszSrc = pSettings->paPairs[iPair].pszPattern;
1718 for (;;)
1719 {
1720 if (*pszSrc == '/')
1721 {
1722 memcpy(pszDst, pchDir, cchDir);
1723 pszDst += cchDir;
1724 pszSrc += 1;
1725 }
1726
1727 /* Look for the next relative path. */
1728 const char *pszSrcNext = strchr(pszSrc, '|');
1729 while (pszSrcNext && pszSrcNext[1] != '/')
1730 pszSrcNext = strchr(pszSrcNext, '|');
1731 if (!pszSrcNext)
1732 break;
1733
1734 /* Copy stuff between current and the next path. */
1735 pszSrcNext++;
1736 memcpy(pszDst, pszSrc, pszSrcNext - pszSrc);
1737 pszDst += pszSrcNext - pszSrc;
1738 pszSrc = pszSrcNext;
1739 }
1740
1741 /* Copy the final portion and replace the pattern. */
1742 strcpy(pszDst, pszSrc);
1743
1744 RTStrFree(pSettings->paPairs[iPair].pszPattern);
1745 pSettings->paPairs[iPair].pszPattern = pszNewPattern;
1746 }
1747 else
1748 rc = VERR_NO_MEMORY;
1749 }
1750 }
1751 if (RT_SUCCESS(rc))
1752 /*
1753 * Commit the pair.
1754 */
1755 pSettings->cPairs = iPair + 1;
1756 else
1757 {
1758 RTStrFree(pSettings->paPairs[iPair].pszPattern);
1759 RTStrFree(pSettings->paPairs[iPair].pszOptions);
1760 RTStrFree(pSettings->paPairs[iPair].pszRelativeTo);
1761 }
1762 return rc;
1763}
1764
1765/**
1766 * Loads in the settings from @a pszFilename.
1767 *
1768 * @returns IPRT status code.
1769 * @param pSettings Where to load the settings file.
1770 * @param pszFilename The file to load.
1771 */
1772static int scmSettingsLoadFile(PSCMSETTINGS pSettings, const char *pszFilename)
1773{
1774 ScmVerbose(NULL, 3, "Loading settings file '%s'...\n", pszFilename);
1775
1776 /* Turn filename into an absolute path and drop the filename. */
1777 char szAbsPath[RTPATH_MAX];
1778 int rc = RTPathAbs(pszFilename, szAbsPath, sizeof(szAbsPath));
1779 if (RT_FAILURE(rc))
1780 {
1781 RTMsgError("%s: RTPathAbs -> %Rrc\n", pszFilename, rc);
1782 return rc;
1783 }
1784 RTPathChangeToUnixSlashes(szAbsPath, true);
1785 size_t cchDir = RTPathFilename(szAbsPath) - &szAbsPath[0];
1786
1787 /* Try open it.*/
1788 SCMSTREAM Stream;
1789 rc = ScmStreamInitForReading(&Stream, pszFilename);
1790 if (RT_SUCCESS(rc))
1791 {
1792 SCMEOL enmEol;
1793 const char *pchLine;
1794 size_t cchLine;
1795 while ((pchLine = ScmStreamGetLine(&Stream, &cchLine, &enmEol)) != NULL)
1796 {
1797 /* Ignore leading spaces. */
1798 while (cchLine > 0 && RT_C_IS_SPACE(*pchLine))
1799 pchLine++, cchLine--;
1800
1801 /* Ignore empty lines and comment lines. */
1802 if (cchLine < 1 || *pchLine == '#')
1803 continue;
1804
1805 /* Deal with escaped newlines. */
1806 size_t iFirstLine = ~(size_t)0;
1807 char *pszFreeLine = NULL;
1808 if ( pchLine[cchLine - 1] == '\\'
1809 && ( cchLine < 2
1810 || pchLine[cchLine - 2] != '\\') )
1811 {
1812 iFirstLine = ScmStreamTellLine(&Stream);
1813
1814 cchLine--;
1815 while (cchLine > 0 && RT_C_IS_SPACE(pchLine[cchLine - 1]))
1816 cchLine--;
1817
1818 size_t cchTotal = cchLine;
1819 pszFreeLine = RTStrDupN(pchLine, cchLine);
1820 if (pszFreeLine)
1821 {
1822 /* Append following lines. */
1823 while ((pchLine = ScmStreamGetLine(&Stream, &cchLine, &enmEol)) != NULL)
1824 {
1825 while (cchLine > 0 && RT_C_IS_SPACE(*pchLine))
1826 pchLine++, cchLine--;
1827
1828 bool const fDone = cchLine == 0
1829 || pchLine[cchLine - 1] != '\\'
1830 || (cchLine >= 2 && pchLine[cchLine - 2] == '\\');
1831 if (!fDone)
1832 {
1833 cchLine--;
1834 while (cchLine > 0 && RT_C_IS_SPACE(pchLine[cchLine - 1]))
1835 cchLine--;
1836 }
1837
1838 rc = RTStrRealloc(&pszFreeLine, cchTotal + 1 + cchLine + 1);
1839 if (RT_FAILURE(rc))
1840 break;
1841 pszFreeLine[cchTotal++] = ' ';
1842 memcpy(&pszFreeLine[cchTotal], pchLine, cchLine);
1843 cchTotal += cchLine;
1844 pszFreeLine[cchTotal] = '\0';
1845
1846 if (fDone)
1847 break;
1848 }
1849 }
1850 else
1851 rc = VERR_NO_STR_MEMORY;
1852
1853 if (RT_FAILURE(rc))
1854 {
1855 RTStrFree(pszFreeLine);
1856 rc = RTMsgErrorRc(VERR_NO_MEMORY, "%s: Ran out of memory deal with escaped newlines", pszFilename);
1857 break;
1858 }
1859
1860 pchLine = pszFreeLine;
1861 cchLine = cchTotal;
1862 }
1863
1864 /* What kind of line is it? */
1865 const char *pchColon = (const char *)memchr(pchLine, ':', cchLine);
1866 if (pchColon)
1867 rc = scmSettingsAddPair(pSettings, pchLine, cchLine, pchColon - pchLine, szAbsPath, cchDir);
1868 else
1869 rc = scmSettingsBaseParseStringN(&pSettings->Base, pchLine, cchLine, szAbsPath, cchDir);
1870 if (pszFreeLine)
1871 RTStrFree(pszFreeLine);
1872 if (RT_FAILURE(rc))
1873 {
1874 RTMsgError("%s:%d: %Rrc\n",
1875 pszFilename, iFirstLine == ~(size_t)0 ? ScmStreamTellLine(&Stream) : iFirstLine, rc);
1876 break;
1877 }
1878 }
1879
1880 if (RT_SUCCESS(rc))
1881 {
1882 rc = ScmStreamGetStatus(&Stream);
1883 if (RT_FAILURE(rc))
1884 RTMsgError("%s: ScmStreamGetStatus- > %Rrc\n", pszFilename, rc);
1885 }
1886 ScmStreamDelete(&Stream);
1887 }
1888 else
1889 RTMsgError("%s: ScmStreamInitForReading -> %Rrc\n", pszFilename, rc);
1890 return rc;
1891}
1892
1893#if 0 /* unused */
1894/**
1895 * Parse the specified settings file creating a new settings struct from it.
1896 *
1897 * @returns IPRT status code
1898 * @param ppSettings Where to return the new settings.
1899 * @param pszFilename The file to parse.
1900 * @param pSettingsBase The base settings we inherit from.
1901 */
1902static int scmSettingsCreateFromFile(PSCMSETTINGS *ppSettings, const char *pszFilename, PCSCMSETTINGSBASE pSettingsBase)
1903{
1904 PSCMSETTINGS pSettings;
1905 int rc = scmSettingsCreate(&pSettings, pSettingsBase);
1906 if (RT_SUCCESS(rc))
1907 {
1908 rc = scmSettingsLoadFile(pSettings, pszFilename, RTPathFilename(pszFilename) - pszFilename);
1909 if (RT_SUCCESS(rc))
1910 {
1911 *ppSettings = pSettings;
1912 return VINF_SUCCESS;
1913 }
1914
1915 scmSettingsDestroy(pSettings);
1916 }
1917 *ppSettings = NULL;
1918 return rc;
1919}
1920#endif
1921
1922
1923/**
1924 * Create an initial settings structure when starting processing a new file or
1925 * directory.
1926 *
1927 * This will look for .scm-settings files from the root and down to the
1928 * specified directory, combining them into the returned settings structure.
1929 *
1930 * @returns IPRT status code.
1931 * @param ppSettings Where to return the pointer to the top stack
1932 * object.
1933 * @param pBaseSettings The base settings we inherit from (globals
1934 * typically).
1935 * @param pszPath The absolute path to the new directory or file.
1936 */
1937static int scmSettingsCreateForPath(PSCMSETTINGS *ppSettings, PCSCMSETTINGSBASE pBaseSettings, const char *pszPath)
1938{
1939 *ppSettings = NULL; /* try shut up gcc. */
1940
1941 /*
1942 * We'll be working with a stack copy of the path.
1943 */
1944 char szFile[RTPATH_MAX];
1945 size_t cchDir = strlen(pszPath);
1946 if (cchDir >= sizeof(szFile) - sizeof(SCM_SETTINGS_FILENAME))
1947 return VERR_FILENAME_TOO_LONG;
1948
1949 /*
1950 * Create the bottom-most settings.
1951 */
1952 PSCMSETTINGS pSettings;
1953 int rc = scmSettingsCreate(&pSettings, pBaseSettings);
1954 if (RT_FAILURE(rc))
1955 return rc;
1956
1957 /*
1958 * Enumerate the path components from the root and down. Load any setting
1959 * files we find.
1960 */
1961 size_t cComponents = RTPathCountComponents(pszPath);
1962 for (size_t i = 1; i <= cComponents; i++)
1963 {
1964 rc = RTPathCopyComponents(szFile, sizeof(szFile), pszPath, i);
1965 if (RT_SUCCESS(rc))
1966 rc = RTPathAppend(szFile, sizeof(szFile), SCM_SETTINGS_FILENAME);
1967 if (RT_FAILURE(rc))
1968 break;
1969 RTPathChangeToUnixSlashes(szFile, true);
1970
1971 if (RTFileExists(szFile))
1972 {
1973 rc = scmSettingsLoadFile(pSettings, szFile);
1974 if (RT_FAILURE(rc))
1975 break;
1976 }
1977 }
1978
1979 if (RT_SUCCESS(rc))
1980 *ppSettings = pSettings;
1981 else
1982 scmSettingsDestroy(pSettings);
1983 return rc;
1984}
1985
1986/**
1987 * Pushes a new settings set onto the stack.
1988 *
1989 * @param ppSettingsStack The pointer to the pointer to the top stack
1990 * element. This will be used as input and output.
1991 * @param pSettings The settings to push onto the stack.
1992 */
1993static void scmSettingsStackPush(PSCMSETTINGS *ppSettingsStack, PSCMSETTINGS pSettings)
1994{
1995 PSCMSETTINGS pOld = *ppSettingsStack;
1996 pSettings->pDown = pOld;
1997 pSettings->pUp = NULL;
1998 if (pOld)
1999 pOld->pUp = pSettings;
2000 *ppSettingsStack = pSettings;
2001}
2002
2003/**
2004 * Pushes the settings of the specified directory onto the stack.
2005 *
2006 * We will load any .scm-settings in the directory. A stack entry is added even
2007 * if no settings file was found.
2008 *
2009 * @returns IPRT status code.
2010 * @param ppSettingsStack The pointer to the pointer to the top stack
2011 * element. This will be used as input and output.
2012 * @param pszDir The directory to do this for.
2013 */
2014static int scmSettingsStackPushDir(PSCMSETTINGS *ppSettingsStack, const char *pszDir)
2015{
2016 char szFile[RTPATH_MAX];
2017 int rc = RTPathJoin(szFile, sizeof(szFile), pszDir, SCM_SETTINGS_FILENAME);
2018 if (RT_SUCCESS(rc))
2019 {
2020 RTPathChangeToUnixSlashes(szFile, true);
2021
2022 PSCMSETTINGS pSettings;
2023 rc = scmSettingsCreate(&pSettings, &(*ppSettingsStack)->Base);
2024 if (RT_SUCCESS(rc))
2025 {
2026 if (RTFileExists(szFile))
2027 rc = scmSettingsLoadFile(pSettings, szFile);
2028 if (RT_SUCCESS(rc))
2029 {
2030 scmSettingsStackPush(ppSettingsStack, pSettings);
2031 return VINF_SUCCESS;
2032 }
2033
2034 scmSettingsDestroy(pSettings);
2035 }
2036 }
2037 return rc;
2038}
2039
2040
2041/**
2042 * Pops a settings set off the stack.
2043 *
2044 * @returns The popped settings.
2045 * @param ppSettingsStack The pointer to the pointer to the top stack
2046 * element. This will be used as input and output.
2047 */
2048static PSCMSETTINGS scmSettingsStackPop(PSCMSETTINGS *ppSettingsStack)
2049{
2050 PSCMSETTINGS pRet = *ppSettingsStack;
2051 PSCMSETTINGS pNew = pRet ? pRet->pDown : NULL;
2052 *ppSettingsStack = pNew;
2053 if (pNew)
2054 pNew->pUp = NULL;
2055 if (pRet)
2056 {
2057 pRet->pUp = NULL;
2058 pRet->pDown = NULL;
2059 }
2060 return pRet;
2061}
2062
2063/**
2064 * Pops and destroys the top entry of the stack.
2065 *
2066 * @param ppSettingsStack The pointer to the pointer to the top stack
2067 * element. This will be used as input and output.
2068 */
2069static void scmSettingsStackPopAndDestroy(PSCMSETTINGS *ppSettingsStack)
2070{
2071 scmSettingsDestroy(scmSettingsStackPop(ppSettingsStack));
2072}
2073
2074/**
2075 * Constructs the base settings for the specified file name.
2076 *
2077 * @returns IPRT status code.
2078 * @param pSettingsStack The top element on the settings stack.
2079 * @param pszFilename The file name.
2080 * @param pszBasename The base name (pointer within @a pszFilename).
2081 * @param cchBasename The length of the base name. (For passing to
2082 * RTStrSimplePatternMultiMatch.)
2083 * @param pBase Base settings to initialize.
2084 */
2085static int scmSettingsStackMakeFileBase(PCSCMSETTINGS pSettingsStack, const char *pszFilename,
2086 const char *pszBasename, size_t cchBasename, PSCMSETTINGSBASE pBase)
2087{
2088 ScmVerbose(NULL, 5, "scmSettingsStackMakeFileBase(%s, %.*s)\n", pszFilename, cchBasename, pszBasename);
2089
2090 int rc = scmSettingsBaseInitAndCopy(pBase, &pSettingsStack->Base);
2091 if (RT_SUCCESS(rc))
2092 {
2093 /* find the bottom entry in the stack. */
2094 PCSCMSETTINGS pCur = pSettingsStack;
2095 while (pCur->pDown)
2096 pCur = pCur->pDown;
2097
2098 /* Work our way up thru the stack and look for matching pairs. */
2099 while (pCur)
2100 {
2101 size_t const cPairs = pCur->cPairs;
2102 if (cPairs)
2103 {
2104 for (size_t i = 0; i < cPairs; i++)
2105 if ( !pCur->paPairs[i].fMultiPattern
2106 ? RTStrSimplePatternNMatch(pCur->paPairs[i].pszPattern, RTSTR_MAX,
2107 pszBasename, cchBasename)
2108 || RTStrSimplePatternMatch(pCur->paPairs[i].pszPattern, pszFilename)
2109 : RTStrSimplePatternMultiMatch(pCur->paPairs[i].pszPattern, RTSTR_MAX,
2110 pszBasename, cchBasename, NULL)
2111 || RTStrSimplePatternMultiMatch(pCur->paPairs[i].pszPattern, RTSTR_MAX,
2112 pszFilename, RTSTR_MAX, NULL))
2113 {
2114 ScmVerbose(NULL, 5, "scmSettingsStackMakeFileBase: Matched '%s' : '%s'\n",
2115 pCur->paPairs[i].pszPattern, pCur->paPairs[i].pszOptions);
2116 rc = scmSettingsBaseParseString(pBase, pCur->paPairs[i].pszOptions,
2117 pCur->paPairs[i].pszRelativeTo, strlen(pCur->paPairs[i].pszRelativeTo));
2118 if (RT_FAILURE(rc))
2119 break;
2120 }
2121 if (RT_FAILURE(rc))
2122 break;
2123 }
2124
2125 /* advance */
2126 pCur = pCur->pUp;
2127 }
2128 }
2129 if (RT_FAILURE(rc))
2130 scmSettingsBaseDelete(pBase);
2131 return rc;
2132}
2133
2134
2135/* -=-=-=-=-=- misc -=-=-=-=-=- */
2136
2137
2138/**
2139 * Prints the per file banner needed and the message level is high enough.
2140 *
2141 * @param pState The rewrite state.
2142 * @param iLevel The required verbosity level.
2143 */
2144void ScmVerboseBanner(PSCMRWSTATE pState, int iLevel)
2145{
2146 if (iLevel <= g_iVerbosity && !pState->fFirst)
2147 {
2148 RTPrintf("%s: info: --= Rewriting '%s' =--\n", g_szProgName, pState->pszFilename);
2149 pState->fFirst = true;
2150 }
2151}
2152
2153
2154/**
2155 * Prints a verbose message if the level is high enough.
2156 *
2157 * @param pState The rewrite state. Optional.
2158 * @param iLevel The required verbosity level.
2159 * @param pszFormat The message format string. Can be NULL if we
2160 * only want to trigger the per file message.
2161 * @param ... Format arguments.
2162 */
2163void ScmVerbose(PSCMRWSTATE pState, int iLevel, const char *pszFormat, ...)
2164{
2165 if (iLevel <= g_iVerbosity)
2166 {
2167 if (pState && !pState->fFirst)
2168 {
2169 RTPrintf("%s: info: --= Rewriting '%s' =--\n", g_szProgName, pState->pszFilename);
2170 pState->fFirst = true;
2171 }
2172 RTPrintf(pState
2173 ? "%s: info: "
2174 : "%s: info: ",
2175 g_szProgName);
2176 va_list va;
2177 va_start(va, pszFormat);
2178 RTPrintfV(pszFormat, va);
2179 va_end(va);
2180 }
2181}
2182
2183
2184/**
2185 * Prints an error message.
2186 *
2187 * @returns false
2188 * @param pState The rewrite state. Optional.
2189 * @param rc The error code.
2190 * @param pszFormat The message format string.
2191 * @param ... Format arguments.
2192 */
2193bool ScmError(PSCMRWSTATE pState, int rc, const char *pszFormat, ...)
2194{
2195 if (RT_SUCCESS(pState->rc))
2196 pState->rc = rc;
2197
2198 if (!pState->fFirst)
2199 {
2200 RTPrintf("%s: info: --= Rewriting '%s' =--\n", g_szProgName, pState->pszFilename);
2201 pState->fFirst = true;
2202 }
2203 va_list va;
2204 va_start(va, pszFormat);
2205 RTPrintf("%s: error: %s: %N", g_szProgName, pState->pszFilename, pszFormat, &va);
2206 va_end(va);
2207
2208 return false;
2209}
2210
2211
2212/**
2213 * Prints message indicating that something requires manual fixing.
2214 *
2215 * @returns false
2216 * @param pState The rewrite state. Optional.
2217 * @param rc The error code.
2218 * @param pszFormat The message format string.
2219 * @param ... Format arguments.
2220 */
2221bool ScmFixManually(PSCMRWSTATE pState, const char *pszFormat, ...)
2222{
2223 pState->fNeedsManualRepair = true;
2224
2225 if (!pState->fFirst)
2226 {
2227 RTPrintf("%s: info: --= Rewriting '%s' =--\n", g_szProgName, pState->pszFilename);
2228 pState->fFirst = true;
2229 }
2230 va_list va;
2231 va_start(va, pszFormat);
2232 RTPrintf("%s: error/fixme: %s: %N", g_szProgName, pState->pszFilename, pszFormat, &va);
2233 va_end(va);
2234
2235 return false;
2236}
2237
2238
2239/* -=-=-=-=-=- file and directory processing -=-=-=-=-=- */
2240
2241
2242/**
2243 * Processes a file.
2244 *
2245 * @returns IPRT status code.
2246 * @param pState The rewriter state.
2247 * @param pszFilename The file name.
2248 * @param pszBasename The base name (pointer within @a pszFilename).
2249 * @param cchBasename The length of the base name. (For passing to
2250 * RTStrSimplePatternMultiMatch.)
2251 * @param pBaseSettings The base settings to use. It's OK to modify
2252 * these.
2253 */
2254static int scmProcessFileInner(PSCMRWSTATE pState, const char *pszFilename, const char *pszBasename, size_t cchBasename,
2255 PSCMSETTINGSBASE pBaseSettings)
2256{
2257 /*
2258 * Do the file level filtering.
2259 */
2260 if ( pBaseSettings->pszFilterFiles
2261 && *pBaseSettings->pszFilterFiles
2262 && !RTStrSimplePatternMultiMatch(pBaseSettings->pszFilterFiles, RTSTR_MAX, pszBasename, cchBasename, NULL))
2263 {
2264 ScmVerbose(NULL, 5, "skipping '%s': file filter mismatch\n", pszFilename);
2265 g_cFilesSkipped++;
2266 return VINF_SUCCESS;
2267 }
2268 if ( pBaseSettings->pszFilterOutFiles
2269 && *pBaseSettings->pszFilterOutFiles
2270 && ( RTStrSimplePatternMultiMatch(pBaseSettings->pszFilterOutFiles, RTSTR_MAX, pszBasename, cchBasename, NULL)
2271 || RTStrSimplePatternMultiMatch(pBaseSettings->pszFilterOutFiles, RTSTR_MAX, pszFilename, RTSTR_MAX, NULL)) )
2272 {
2273 ScmVerbose(NULL, 5, "skipping '%s': filterd out\n", pszFilename);
2274 g_cFilesSkipped++;
2275 return VINF_SUCCESS;
2276 }
2277 if ( pBaseSettings->fOnlySvnFiles
2278 && !ScmSvnIsInWorkingCopy(pState))
2279 {
2280 ScmVerbose(NULL, 5, "skipping '%s': not in SVN WC\n", pszFilename);
2281 g_cFilesNotInSvn++;
2282 return VINF_SUCCESS;
2283 }
2284
2285 /*
2286 * Create an input stream from the file and check that it's text.
2287 */
2288 SCMSTREAM Stream1;
2289 int rc = ScmStreamInitForReading(&Stream1, pszFilename);
2290 if (RT_FAILURE(rc))
2291 {
2292 RTMsgError("Failed to read '%s': %Rrc\n", pszFilename, rc);
2293 return rc;
2294 }
2295 bool const fIsText = ScmStreamIsText(&Stream1);
2296
2297 /*
2298 * Try find a matching rewrite config for this filename.
2299 */
2300 PCSCMCFGENTRY pCfg = pBaseSettings->pTreatAs;
2301 if (!pCfg)
2302 {
2303 for (size_t iCfg = 0; iCfg < RT_ELEMENTS(g_aConfigs); iCfg++)
2304 if (RTStrSimplePatternMultiMatch(g_aConfigs[iCfg].pszFilePattern, RTSTR_MAX, pszBasename, cchBasename, NULL))
2305 {
2306 pCfg = &g_aConfigs[iCfg];
2307 break;
2308 }
2309 if (!pCfg)
2310 {
2311 /* On failure try check for hash-bang stuff before giving up. */
2312 if (fIsText)
2313 {
2314 SCMEOL enmIgn;
2315 size_t cchFirst;
2316 const char *pchFirst = ScmStreamGetLine(&Stream1, &cchFirst, &enmIgn);
2317 if (cchFirst >= 9 && pchFirst && *pchFirst == '#')
2318 {
2319 do
2320 {
2321 pchFirst++;
2322 cchFirst--;
2323 } while (cchFirst > 0 && RT_C_IS_BLANK(*pchFirst));
2324 if (*pchFirst == '!')
2325 {
2326 do
2327 {
2328 pchFirst++;
2329 cchFirst--;
2330 } while (cchFirst > 0 && RT_C_IS_BLANK(*pchFirst));
2331 const char *pszTreatAs = NULL;
2332 if ( (cchFirst >= 7 && strncmp(pchFirst, "/bin/sh", 7) == 0)
2333 || (cchFirst >= 9 && strncmp(pchFirst, "/bin/bash", 9) == 0)
2334 || (cchFirst >= 4+9 && strncmp(pchFirst, "/usr/bin/bash", 4+9) == 0) )
2335 pszTreatAs = "shell";
2336 else if ( (cchFirst >= 15 && strncmp(pchFirst, "/usr/bin/python", 15) == 0)
2337 || (cchFirst >= 19 && strncmp(pchFirst, "/usr/bin/env python", 19) == 0) )
2338 pszTreatAs = "python";
2339 else if ( (cchFirst >= 13 && strncmp(pchFirst, "/usr/bin/perl", 13) == 0)
2340 || (cchFirst >= 17 && strncmp(pchFirst, "/usr/bin/env perl", 17) == 0) )
2341 pszTreatAs = "perl";
2342 if (pszTreatAs)
2343 {
2344 for (size_t iCfg = 0; iCfg < RT_ELEMENTS(g_aConfigs); iCfg++)
2345 if (strcmp(pszTreatAs, g_aConfigs[iCfg].pszName) == 0)
2346 {
2347 pCfg = &g_aConfigs[iCfg];
2348 break;
2349 }
2350 Assert(pCfg);
2351 }
2352 }
2353 }
2354 ScmStreamRewindForReading(&Stream1);
2355 }
2356 if (!pCfg)
2357 {
2358 ScmVerbose(NULL, 2, "skipping '%s': no rewriters configured\n", pszFilename);
2359 g_cFilesNoRewriters++;
2360 ScmStreamDelete(&Stream1);
2361 return VINF_SUCCESS;
2362 }
2363 }
2364 ScmVerbose(pState, 4, "matched \"%s\" (%s)\n", pCfg->pszFilePattern, pCfg->pszName);
2365 }
2366 else
2367 ScmVerbose(pState, 4, "treat-as \"%s\"\n", pCfg->pszName);
2368
2369 if (fIsText || pCfg->fBinary)
2370 {
2371 ScmVerboseBanner(pState, 3);
2372
2373 /*
2374 * Gather SCM and editor settings from the stream.
2375 */
2376 rc = scmSettingsBaseLoadFromDocument(pBaseSettings, &Stream1);
2377 if (RT_SUCCESS(rc))
2378 {
2379 ScmStreamRewindForReading(&Stream1);
2380
2381 /*
2382 * Create two more streams for output and push the text thru all the
2383 * rewriters, switching the two streams around when something is
2384 * actually rewritten. Stream1 remains unchanged.
2385 */
2386 SCMSTREAM Stream2;
2387 rc = ScmStreamInitForWriting(&Stream2, &Stream1);
2388 if (RT_SUCCESS(rc))
2389 {
2390 SCMSTREAM Stream3;
2391 rc = ScmStreamInitForWriting(&Stream3, &Stream1);
2392 if (RT_SUCCESS(rc))
2393 {
2394 bool fModified = false;
2395 PSCMSTREAM pIn = &Stream1;
2396 PSCMSTREAM pOut = &Stream2;
2397 for (size_t iRw = 0; iRw < pCfg->cRewriters; iRw++)
2398 {
2399 pState->rc = VINF_SUCCESS;
2400 bool fRc = pCfg->paRewriters[iRw]->pfnRewriter(pState, pIn, pOut, pBaseSettings);
2401 if (RT_FAILURE(pState->rc))
2402 break;
2403 if (fRc)
2404 {
2405 PSCMSTREAM pTmp = pOut;
2406 pOut = pIn == &Stream1 ? &Stream3 : pIn;
2407 pIn = pTmp;
2408 fModified = true;
2409 }
2410
2411 ScmStreamRewindForReading(pIn);
2412 ScmStreamRewindForWriting(pOut);
2413 }
2414
2415 rc = pState->rc;
2416 if (RT_SUCCESS(rc))
2417 {
2418 rc = ScmStreamGetStatus(&Stream1);
2419 if (RT_SUCCESS(rc))
2420 rc = ScmStreamGetStatus(&Stream2);
2421 if (RT_SUCCESS(rc))
2422 rc = ScmStreamGetStatus(&Stream3);
2423 if (RT_SUCCESS(rc))
2424 {
2425 /*
2426 * If rewritten, write it back to disk.
2427 */
2428 if (fModified && !pCfg->fBinary)
2429 {
2430 if (!g_fDryRun)
2431 {
2432 ScmVerbose(pState, 1, "writing modified file to \"%s%s\"\n", pszFilename, g_pszChangedSuff);
2433 rc = ScmStreamWriteToFile(pIn, "%s%s", pszFilename, g_pszChangedSuff);
2434 if (RT_FAILURE(rc))
2435 RTMsgError("Error writing '%s%s': %Rrc\n", pszFilename, g_pszChangedSuff, rc);
2436 }
2437 else
2438 {
2439 ScmVerboseBanner(pState, 1);
2440 ScmDiffStreams(pszFilename, &Stream1, pIn, g_fDiffIgnoreEol,
2441 g_fDiffIgnoreLeadingWS, g_fDiffIgnoreTrailingWS, g_fDiffSpecialChars,
2442 pBaseSettings->cchTab, g_pStdOut);
2443 ScmVerbose(pState, 2, "would have modified the file \"%s%s\"\n",
2444 pszFilename, g_pszChangedSuff);
2445 }
2446 g_cFilesModified++;
2447 }
2448 else if (fModified)
2449 rc = RTMsgErrorRc(VERR_INTERNAL_ERROR, "Rewriters modified binary file! Impossible!");
2450
2451 /*
2452 * If pending SVN property changes, apply them.
2453 */
2454 if (pState->cSvnPropChanges && RT_SUCCESS(rc))
2455 {
2456 if (!g_fDryRun)
2457 {
2458 rc = ScmSvnApplyChanges(pState);
2459 if (RT_FAILURE(rc))
2460 RTMsgError("%s: failed to apply SVN property changes (%Rrc)\n", pszFilename, rc);
2461 }
2462 else
2463 ScmSvnDisplayChanges(pState);
2464 if (!fModified)
2465 g_cFilesModified++;
2466 }
2467
2468 if (!fModified && !pState->cSvnPropChanges)
2469 ScmVerbose(pState, 3, "%s: no change\n", pszFilename);
2470 }
2471 else
2472 RTMsgError("%s: stream error %Rrc\n", pszFilename, rc);
2473 }
2474 ScmStreamDelete(&Stream3);
2475 }
2476 else
2477 RTMsgError("Failed to init stream for writing: %Rrc\n", rc);
2478 ScmStreamDelete(&Stream2);
2479 }
2480 else
2481 RTMsgError("Failed to init stream for writing: %Rrc\n", rc);
2482 }
2483 else
2484 RTMsgError("scmSettingsBaseLoadFromDocument: %Rrc\n", rc);
2485 }
2486 else
2487 {
2488 ScmVerbose(pState, 2, "not text file: \"%s\"\n", pszFilename);
2489 g_cFilesBinaries++;
2490 }
2491 ScmStreamDelete(&Stream1);
2492
2493 return rc;
2494}
2495
2496/**
2497 * Processes a file.
2498 *
2499 * This is just a wrapper for scmProcessFileInner for avoid wasting stack in the
2500 * directory recursion method.
2501 *
2502 * @returns IPRT status code.
2503 * @param pszFilename The file name.
2504 * @param pszBasename The base name (pointer within @a pszFilename).
2505 * @param cchBasename The length of the base name. (For passing to
2506 * RTStrSimplePatternMultiMatch.)
2507 * @param pSettingsStack The settings stack (pointer to the top element).
2508 */
2509static int scmProcessFile(const char *pszFilename, const char *pszBasename, size_t cchBasename,
2510 PSCMSETTINGS pSettingsStack)
2511{
2512 SCMSETTINGSBASE Base;
2513 int rc = scmSettingsStackMakeFileBase(pSettingsStack, pszFilename, pszBasename, cchBasename, &Base);
2514 if (RT_SUCCESS(rc))
2515 {
2516 SCMRWSTATE State;
2517 State.pszFilename = pszFilename;
2518 State.fFirst = false;
2519 State.fNeedsManualRepair = false;
2520 State.fIsInSvnWorkingCopy = 0;
2521 State.cSvnPropChanges = 0;
2522 State.paSvnPropChanges = NULL;
2523 State.rc = VINF_SUCCESS;
2524
2525 rc = scmProcessFileInner(&State, pszFilename, pszBasename, cchBasename, &Base);
2526
2527 size_t i = State.cSvnPropChanges;
2528 while (i-- > 0)
2529 {
2530 RTStrFree(State.paSvnPropChanges[i].pszName);
2531 RTStrFree(State.paSvnPropChanges[i].pszValue);
2532 }
2533 RTMemFree(State.paSvnPropChanges);
2534
2535 scmSettingsBaseDelete(&Base);
2536
2537 if (State.fNeedsManualRepair)
2538 g_cFilesRequiringManualFixing++;
2539 g_cFilesProcessed++;
2540 }
2541 return rc;
2542}
2543
2544/**
2545 * Tries to correct RTDIRENTRY_UNKNOWN.
2546 *
2547 * @returns Corrected type.
2548 * @param pszPath The path to the object in question.
2549 */
2550static RTDIRENTRYTYPE scmFigureUnknownType(const char *pszPath)
2551{
2552 RTFSOBJINFO Info;
2553 int rc = RTPathQueryInfo(pszPath, &Info, RTFSOBJATTRADD_NOTHING);
2554 if (RT_FAILURE(rc))
2555 return RTDIRENTRYTYPE_UNKNOWN;
2556 if (RTFS_IS_DIRECTORY(Info.Attr.fMode))
2557 return RTDIRENTRYTYPE_DIRECTORY;
2558 if (RTFS_IS_FILE(Info.Attr.fMode))
2559 return RTDIRENTRYTYPE_FILE;
2560 return RTDIRENTRYTYPE_UNKNOWN;
2561}
2562
2563/**
2564 * Recurse into a sub-directory and process all the files and directories.
2565 *
2566 * @returns IPRT status code.
2567 * @param pszBuf Path buffer containing the directory path on
2568 * entry. This ends with a dot. This is passed
2569 * along when recursing in order to save stack space
2570 * and avoid needless copying.
2571 * @param cchDir Length of our path in pszbuf.
2572 * @param pEntry Directory entry buffer. This is also passed
2573 * along when recursing to save stack space.
2574 * @param pSettingsStack The settings stack (pointer to the top element).
2575 * @param iRecursion The recursion depth. This is used to restrict
2576 * the recursions.
2577 */
2578static int scmProcessDirTreeRecursion(char *pszBuf, size_t cchDir, PRTDIRENTRY pEntry,
2579 PSCMSETTINGS pSettingsStack, unsigned iRecursion)
2580{
2581 int rc;
2582 Assert(cchDir > 1 && pszBuf[cchDir - 1] == '.');
2583
2584 /*
2585 * Make sure we stop somewhere.
2586 */
2587 if (iRecursion > 128)
2588 {
2589 RTMsgError("recursion too deep: %d\n", iRecursion);
2590 return VINF_SUCCESS; /* ignore */
2591 }
2592
2593 /*
2594 * Check if it's excluded by --only-svn-dir.
2595 */
2596 if (pSettingsStack->Base.fOnlySvnDirs)
2597 {
2598 if (!ScmSvnIsDirInWorkingCopy(pszBuf))
2599 return VINF_SUCCESS;
2600 }
2601 g_cDirsProcessed++;
2602
2603 /*
2604 * Try open and read the directory.
2605 */
2606 RTDIR hDir;
2607 rc = RTDirOpenFiltered(&hDir, pszBuf, RTDIRFILTER_NONE, 0 /*fFlags*/);
2608 if (RT_FAILURE(rc))
2609 {
2610 RTMsgError("Failed to enumerate directory '%s': %Rrc", pszBuf, rc);
2611 return rc;
2612 }
2613 for (;;)
2614 {
2615 /* Read the next entry. */
2616 rc = RTDirRead(hDir, pEntry, NULL);
2617 if (RT_FAILURE(rc))
2618 {
2619 if (rc == VERR_NO_MORE_FILES)
2620 rc = VINF_SUCCESS;
2621 else
2622 RTMsgError("RTDirRead -> %Rrc\n", rc);
2623 break;
2624 }
2625
2626 /* Skip '.' and '..'. */
2627 if ( pEntry->szName[0] == '.'
2628 && ( pEntry->cbName == 1
2629 || ( pEntry->cbName == 2
2630 && pEntry->szName[1] == '.')))
2631 continue;
2632
2633 /* Enter it into the buffer so we've got a full name to work
2634 with when needed. */
2635 if (pEntry->cbName + cchDir >= RTPATH_MAX)
2636 {
2637 RTMsgError("Skipping too long entry: %s", pEntry->szName);
2638 continue;
2639 }
2640 memcpy(&pszBuf[cchDir - 1], pEntry->szName, pEntry->cbName + 1);
2641
2642 /* Figure the type. */
2643 RTDIRENTRYTYPE enmType = pEntry->enmType;
2644 if (enmType == RTDIRENTRYTYPE_UNKNOWN)
2645 enmType = scmFigureUnknownType(pszBuf);
2646
2647 /* Process the file or directory, skip the rest. */
2648 if (enmType == RTDIRENTRYTYPE_FILE)
2649 rc = scmProcessFile(pszBuf, pEntry->szName, pEntry->cbName, pSettingsStack);
2650 else if (enmType == RTDIRENTRYTYPE_DIRECTORY)
2651 {
2652 /* Append the dot for the benefit of the pattern matching. */
2653 if (pEntry->cbName + cchDir + 5 >= RTPATH_MAX)
2654 {
2655 RTMsgError("Skipping too deep dir entry: %s", pEntry->szName);
2656 continue;
2657 }
2658 memcpy(&pszBuf[cchDir - 1 + pEntry->cbName], "/.", sizeof("/."));
2659 size_t cchSubDir = cchDir - 1 + pEntry->cbName + sizeof("/.") - 1;
2660
2661 if ( !pSettingsStack->Base.pszFilterOutDirs
2662 || !*pSettingsStack->Base.pszFilterOutDirs
2663 || ( !RTStrSimplePatternMultiMatch(pSettingsStack->Base.pszFilterOutDirs, RTSTR_MAX,
2664 pEntry->szName, pEntry->cbName, NULL)
2665 && !RTStrSimplePatternMultiMatch(pSettingsStack->Base.pszFilterOutDirs, RTSTR_MAX,
2666 pszBuf, cchSubDir, NULL)
2667 )
2668 )
2669 {
2670 rc = scmSettingsStackPushDir(&pSettingsStack, pszBuf);
2671 if (RT_SUCCESS(rc))
2672 {
2673 rc = scmProcessDirTreeRecursion(pszBuf, cchSubDir, pEntry, pSettingsStack, iRecursion + 1);
2674 scmSettingsStackPopAndDestroy(&pSettingsStack);
2675 }
2676 }
2677 }
2678 if (RT_FAILURE(rc))
2679 break;
2680 }
2681 RTDirClose(hDir);
2682 return rc;
2683
2684}
2685
2686/**
2687 * Process a directory tree.
2688 *
2689 * @returns IPRT status code.
2690 * @param pszDir The directory to start with. This is pointer to
2691 * a RTPATH_MAX sized buffer.
2692 */
2693static int scmProcessDirTree(char *pszDir, PSCMSETTINGS pSettingsStack)
2694{
2695 /*
2696 * Setup the recursion.
2697 */
2698 int rc = RTPathAppend(pszDir, RTPATH_MAX, ".");
2699 if (RT_SUCCESS(rc))
2700 {
2701 RTPathChangeToUnixSlashes(pszDir, true);
2702
2703 RTDIRENTRY Entry;
2704 rc = scmProcessDirTreeRecursion(pszDir, strlen(pszDir), &Entry, pSettingsStack, 0);
2705 }
2706 else
2707 RTMsgError("RTPathAppend: %Rrc\n", rc);
2708 return rc;
2709}
2710
2711
2712/**
2713 * Processes a file or directory specified as an command line argument.
2714 *
2715 * @returns IPRT status code
2716 * @param pszSomething What we found in the command line arguments.
2717 * @param pSettingsStack The settings stack (pointer to the top element).
2718 */
2719static int scmProcessSomething(const char *pszSomething, PSCMSETTINGS pSettingsStack)
2720{
2721 char szBuf[RTPATH_MAX];
2722 int rc = RTPathAbs(pszSomething, szBuf, sizeof(szBuf));
2723 if (RT_SUCCESS(rc))
2724 {
2725 RTPathChangeToUnixSlashes(szBuf, false /*fForce*/);
2726
2727 PSCMSETTINGS pSettings;
2728 rc = scmSettingsCreateForPath(&pSettings, &pSettingsStack->Base, szBuf);
2729 if (RT_SUCCESS(rc))
2730 {
2731 scmSettingsStackPush(&pSettingsStack, pSettings);
2732
2733 if (RTFileExists(szBuf))
2734 {
2735 const char *pszBasename = RTPathFilename(szBuf);
2736 if (pszBasename)
2737 {
2738 size_t cchBasename = strlen(pszBasename);
2739 rc = scmProcessFile(szBuf, pszBasename, cchBasename, pSettingsStack);
2740 }
2741 else
2742 {
2743 RTMsgError("RTPathFilename: NULL\n");
2744 rc = VERR_IS_A_DIRECTORY;
2745 }
2746 }
2747 else
2748 rc = scmProcessDirTree(szBuf, pSettingsStack);
2749
2750 PSCMSETTINGS pPopped = scmSettingsStackPop(&pSettingsStack);
2751 Assert(pPopped == pSettings); RT_NOREF_PV(pPopped);
2752 scmSettingsDestroy(pSettings);
2753 }
2754 else
2755 RTMsgError("scmSettingsInitStack: %Rrc\n", rc);
2756 }
2757 else
2758 RTMsgError("RTPathAbs: %Rrc\n", rc);
2759 return rc;
2760}
2761
2762/**
2763 * Print some stats.
2764 */
2765static void scmPrintStats(void)
2766{
2767 ScmVerbose(NULL, 0,
2768 g_fDryRun
2769 ? "%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"
2770 : "%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",
2771 g_cFilesModified,
2772 g_cFilesProcessed, g_cFilesProcessed == 1 ? "" : "s",
2773 g_cDirsProcessed, g_cDirsProcessed == 1 ? "" : "s",
2774 g_cFilesNoRewriters, g_cFilesNoRewriters == 1 ? "" : "s",
2775 g_cFilesBinaries, g_cFilesBinaries == 1 ? "y" : "ies",
2776 g_cFilesNotInSvn, g_cFilesSkipped);
2777}
2778
2779/**
2780 * Display the rewriter actions.
2781 *
2782 * @returns RTEXITCODE_SUCCESS.
2783 */
2784static int scmHelpActions(void)
2785{
2786 RTPrintf("Available rewriter actions:\n");
2787 for (uint32_t i = 0; i < RT_ELEMENTS(g_papRewriterActions); i++)
2788 RTPrintf(" %s\n", g_papRewriterActions[i]->pszName);
2789 return RTEXITCODE_SUCCESS;
2790}
2791
2792/**
2793 * Display the default configuration.
2794 *
2795 * @returns RTEXITCODE_SUCCESS.
2796 */
2797static int scmHelpConfig(void)
2798{
2799 RTPrintf("Rewriter configuration:\n");
2800 for (size_t iCfg = 0; iCfg < RT_ELEMENTS(g_aConfigs); iCfg++)
2801 {
2802 RTPrintf("\n %s%s - %s:\n",
2803 g_aConfigs[iCfg].pszName, g_aConfigs[iCfg].fBinary ? " (binary)" : "", g_aConfigs[iCfg].pszFilePattern);
2804 for (size_t i = 0; i < g_aConfigs[iCfg].cRewriters; i++)
2805 RTPrintf(" %s\n", g_aConfigs[iCfg].paRewriters[i]->pszName);
2806 }
2807 return RTEXITCODE_SUCCESS;
2808}
2809
2810/**
2811 * Display the primary help text.
2812 *
2813 * @returns RTEXITCODE_SUCCESS.
2814 * @param paOpts Options.
2815 * @param cOpts Number of options.
2816 */
2817static int scmHelp(PCRTGETOPTDEF paOpts, size_t cOpts)
2818{
2819 RTPrintf("VirtualBox Source Code Massager\n"
2820 "\n"
2821 "Usage: %s [options] <files & dirs>\n"
2822 "\n"
2823 "General options:\n", g_szProgName);
2824 for (size_t i = 0; i < cOpts; i++)
2825 {
2826 /* Grouping. */
2827 switch (paOpts[i].iShort)
2828 {
2829 case SCMOPT_DIFF_IGNORE_EOL:
2830 RTPrintf("\nDiff options (dry runs):\n");
2831 break;
2832 case SCMOPT_CONVERT_EOL:
2833 RTPrintf("\nRewriter action options:\n");
2834 break;
2835 case SCMOPT_ONLY_SVN_DIRS:
2836 RTPrintf("\nInput selection options:\n");
2837 break;
2838 case SCMOPT_TREAT_AS:
2839 RTPrintf("\nMisc options:\n");
2840 break;
2841 }
2842
2843 size_t cExtraAdvance = 0;
2844 if ((paOpts[i].fFlags & RTGETOPT_REQ_MASK) == RTGETOPT_REQ_NOTHING)
2845 {
2846 cExtraAdvance = i + 1 < cOpts
2847 && ( strstr(paOpts[i+1].pszLong, "-no-") != NULL
2848 || strstr(paOpts[i+1].pszLong, "-not-") != NULL
2849 || strstr(paOpts[i+1].pszLong, "-dont-") != NULL
2850 || strstr(paOpts[i+1].pszLong, "-unrestricted-") != NULL
2851 || (paOpts[i].iShort == 'q' && paOpts[i+1].iShort == 'v')
2852 || (paOpts[i].iShort == 'd' && paOpts[i+1].iShort == 'D')
2853 );
2854 if (cExtraAdvance)
2855 RTPrintf(" %s, %s\n", paOpts[i].pszLong, paOpts[i + 1].pszLong);
2856 else if (paOpts[i].iShort != SCMOPT_NO_UPDATE_LICENSE)
2857 RTPrintf(" %s\n", paOpts[i].pszLong);
2858 else
2859 {
2860 RTPrintf(" %s,\n"
2861 " %s,\n"
2862 " %s,\n"
2863 " %s,\n"
2864 " %s,\n"
2865 " %s,\n"
2866 " %s\n",
2867 paOpts[i].pszLong,
2868 paOpts[i + 1].pszLong,
2869 paOpts[i + 2].pszLong,
2870 paOpts[i + 3].pszLong,
2871 paOpts[i + 4].pszLong,
2872 paOpts[i + 5].pszLong,
2873 paOpts[i + 6].pszLong);
2874 cExtraAdvance = 6;
2875 }
2876 }
2877 else if ((paOpts[i].fFlags & RTGETOPT_REQ_MASK) == RTGETOPT_REQ_STRING)
2878 switch (paOpts[i].iShort)
2879 {
2880 case SCMOPT_DEL_ACTION:
2881 RTPrintf(" %s pattern\n", paOpts[i].pszLong);
2882 break;
2883 case SCMOPT_FILTER_OUT_DIRS:
2884 case SCMOPT_FILTER_FILES:
2885 case SCMOPT_FILTER_OUT_FILES:
2886 RTPrintf(" %s multi-pattern\n", paOpts[i].pszLong);
2887 break;
2888 default:
2889 RTPrintf(" %s string\n", paOpts[i].pszLong);
2890 }
2891 else
2892 RTPrintf(" %s value\n", paOpts[i].pszLong);
2893 switch (paOpts[i].iShort)
2894 {
2895 case 'd':
2896 case 'D': RTPrintf(" Default: --dry-run\n"); break;
2897 case SCMOPT_CHECK_RUN: RTPrintf(" Default: --dry-run\n"); break;
2898 case 'f': RTPrintf(" Default: none\n"); break;
2899 case 'q':
2900 case 'v': RTPrintf(" Default: -vv\n"); break;
2901 case SCMOPT_HELP_CONFIG: RTPrintf(" Shows the standard file rewriter configurations.\n"); break;
2902 case SCMOPT_HELP_ACTIONS: RTPrintf(" Shows the available rewriter actions.\n"); break;
2903
2904 case SCMOPT_DIFF_IGNORE_EOL: RTPrintf(" Default: false\n"); break;
2905 case SCMOPT_DIFF_IGNORE_SPACE: RTPrintf(" Default: false\n"); break;
2906 case SCMOPT_DIFF_IGNORE_LEADING_SPACE: RTPrintf(" Default: false\n"); break;
2907 case SCMOPT_DIFF_IGNORE_TRAILING_SPACE: RTPrintf(" Default: false\n"); break;
2908 case SCMOPT_DIFF_SPECIAL_CHARS: RTPrintf(" Default: true\n"); break;
2909
2910 case SCMOPT_CONVERT_EOL: RTPrintf(" Default: %RTbool\n", g_Defaults.fConvertEol); break;
2911 case SCMOPT_CONVERT_TABS: RTPrintf(" Default: %RTbool\n", g_Defaults.fConvertTabs); break;
2912 case SCMOPT_FORCE_FINAL_EOL: RTPrintf(" Default: %RTbool\n", g_Defaults.fForceFinalEol); break;
2913 case SCMOPT_FORCE_TRAILING_LINE: RTPrintf(" Default: %RTbool\n", g_Defaults.fForceTrailingLine); break;
2914 case SCMOPT_STRIP_TRAILING_BLANKS: RTPrintf(" Default: %RTbool\n", g_Defaults.fStripTrailingBlanks); break;
2915 case SCMOPT_STRIP_TRAILING_LINES: RTPrintf(" Default: %RTbool\n", g_Defaults.fStripTrailingLines); break;
2916 case SCMOPT_FIX_FLOWER_BOX_MARKERS: RTPrintf(" Default: %RTbool\n", g_Defaults.fFixFlowerBoxMarkers); break;
2917 case SCMOPT_MIN_BLANK_LINES_BEFORE_FLOWER_BOX_MARKERS: RTPrintf(" Default: %u\n", g_Defaults.cMinBlankLinesBeforeFlowerBoxMakers); break;
2918
2919 case SCMOPT_FIX_HEADER_GUARDS:
2920 RTPrintf(" Fix header guards and #pragma once. Default: %RTbool\n", g_Defaults.fFixHeaderGuards);
2921 break;
2922 case SCMOPT_PRAGMA_ONCE:
2923 RTPrintf(" Whether to include #pragma once with the header guard. Default: %RTbool\n", g_Defaults.fPragmaOnce);
2924 break;
2925 case SCMOPT_FIX_HEADER_GUARD_ENDIF:
2926 RTPrintf(" Whether to fix the #endif of a header guard. Default: %RTbool\n", g_Defaults.fFixHeaderGuardEndif);
2927 break;
2928 case SCMOPT_ENDIF_GUARD_COMMENT:
2929 RTPrintf(" Put a comment on the header guard #endif or not. Default: %RTbool\n", g_Defaults.fEndifGuardComment);
2930 break;
2931 case SCMOPT_GUARD_RELATIVE_TO_DIR:
2932 RTPrintf(" Header guard should be normalized relative to given dir.\n"
2933 " When relative to settings files, no preceeding slash.\n"
2934 " Header relative directory specification: {dir} and {parent}\n"
2935 " If empty no normalization takes place. Default: '%s'\n", g_Defaults.pszGuardRelativeToDir);
2936 break;
2937 case SCMOPT_GUARD_PREFIX:
2938 RTPrintf(" Prefix to use with --guard-relative-to-dir. Default: %s\n", g_Defaults.pszGuardPrefix);
2939 break;
2940 case SCMOPT_FIX_TODOS:
2941 RTPrintf(" Fix @todo statements so doxygen sees them. Default: %RTbool\n", g_Defaults.fFixTodos);
2942 break;
2943 case SCMOPT_FIX_ERR_H:
2944 RTPrintf(" Fix err.h/errcore.h usage. Default: %RTbool\n", g_Defaults.fFixErrH);
2945 break;
2946 case SCMOPT_ONLY_GUEST_HOST_PAGE:
2947 RTPrintf(" No PAGE_SIZE, PAGE_SHIFT or PAGE_OFFSET_MASK allowed, must have\n"
2948 " GUEST_ or HOST_ prefix. Also forbids use of PAGE_BASE_MASK,\n"
2949 " PAGE_BASE_HC_MASK, PAGE_BASE_GC_MASK, PAGE_ADDRESS,\n"
2950 " PHYS_PAGE_ADDRESS. Default: %RTbool\n", g_Defaults.fOnlyGuestHostPage);
2951 break;
2952 case SCMOPT_NO_ASM_MEM_PAGE_USE:
2953 RTPrintf(" No ASMMemIsZeroPage or ASMMemZeroPage allowed, must instead use\n"
2954 " ASMMemIsZero and RT_BZERO with appropriate page size. Default: %RTbool\n",
2955 g_Defaults.fNoASMMemPageUse);
2956 break;
2957 case SCMOPT_NO_RC_USE:
2958 RTPrintf(" No rc declaration allowed, must instead use\n"
2959 " vrc for IPRT status codes and hrc for COM status codes. Default: %RTbool\n",
2960 g_Defaults.fOnlyHrcVrcInsteadOfRc);
2961 break;
2962 case SCMOPT_UPDATE_COPYRIGHT_YEAR:
2963 RTPrintf(" Update the copyright year. Default: %RTbool\n", g_Defaults.fUpdateCopyrightYear);
2964 break;
2965 case SCMOPT_EXTERNAL_COPYRIGHT:
2966 RTPrintf(" Only external copyright holders. Default: %RTbool\n", g_Defaults.fExternalCopyright);
2967 break;
2968 case SCMOPT_NO_UPDATE_LICENSE:
2969 RTPrintf(" License selection. Default: --license-ose-gpl\n");
2970 break;
2971
2972 case SCMOPT_LGPL_DISCLAIMER:
2973 RTPrintf(" Include LGPL version disclaimer. Default: --no-lgpl-disclaimer\n");
2974 break;
2975
2976 case SCMOPT_SET_SVN_EOL: RTPrintf(" Default: %RTbool\n", g_Defaults.fSetSvnEol); break;
2977 case SCMOPT_SET_SVN_EXECUTABLE: RTPrintf(" Default: %RTbool\n", g_Defaults.fSetSvnExecutable); break;
2978 case SCMOPT_SET_SVN_KEYWORDS: RTPrintf(" Default: %RTbool\n", g_Defaults.fSetSvnKeywords); break;
2979 case SCMOPT_SKIP_SVN_SYNC_PROCESS: RTPrintf(" Default: %RTbool\n", g_Defaults.fSkipSvnSyncProcess); break;
2980 case SCMOPT_SKIP_UNICODE_CHECKS: RTPrintf(" Default: %RTbool\n", g_Defaults.fSkipUnicodeChecks); break;
2981 case SCMOPT_TAB_SIZE: RTPrintf(" Default: %u\n", g_Defaults.cchTab); break;
2982 case SCMOPT_WIDTH: RTPrintf(" Default: %u\n", g_Defaults.cchWidth); break;
2983
2984 case SCMOPT_ONLY_SVN_DIRS: RTPrintf(" Default: %RTbool\n", g_Defaults.fOnlySvnDirs); break;
2985 case SCMOPT_ONLY_SVN_FILES: RTPrintf(" Default: %RTbool\n", g_Defaults.fOnlySvnFiles); break;
2986 case SCMOPT_FILTER_OUT_DIRS: RTPrintf(" Default: %s\n", g_Defaults.pszFilterOutDirs); break;
2987 case SCMOPT_FILTER_FILES: RTPrintf(" Default: %s\n", g_Defaults.pszFilterFiles); break;
2988 case SCMOPT_FILTER_OUT_FILES: RTPrintf(" Default: %s\n", g_Defaults.pszFilterOutFiles); break;
2989
2990 case SCMOPT_TREAT_AS:
2991 RTPrintf(" For treat the input file(s) differently, restting any --add-action.\n"
2992 " If the value is empty defaults will be used again. Possible values:\n");
2993 for (size_t iCfg = 0; iCfg < RT_ELEMENTS(g_aConfigs); iCfg++)
2994 RTPrintf(" %s (%s)\n", g_aConfigs[iCfg].pszName, g_aConfigs[iCfg].pszFilePattern);
2995 break;
2996
2997 case SCMOPT_ADD_ACTION:
2998 RTPrintf(" Adds a rewriter action. The first use after a --treat-as will copy and\n"
2999 " the action list selected by the --treat-as. The action list will be\n"
3000 " flushed by --treat-as.\n");
3001 break;
3002
3003 case SCMOPT_DEL_ACTION:
3004 RTPrintf(" Deletes one or more rewriter action (pattern). Best used after\n"
3005 " a --treat-as.\n");
3006 break;
3007
3008 default: AssertMsgFailed(("i=%d %d %s\n", i, paOpts[i].iShort, paOpts[i].pszLong));
3009 }
3010 i += cExtraAdvance;
3011 }
3012
3013 return RTEXITCODE_SUCCESS;
3014}
3015
3016int main(int argc, char **argv)
3017{
3018 int rc = RTR3InitExe(argc, &argv, 0);
3019 if (RT_FAILURE(rc))
3020 return 1;
3021
3022 /*
3023 * Init the current year.
3024 */
3025 RTTIMESPEC Now;
3026 RTTIME Time;
3027 RTTimeExplode(&Time, RTTimeNow(&Now));
3028 g_uYear = Time.i32Year;
3029
3030 /*
3031 * Init the settings.
3032 */
3033 PSCMSETTINGS pSettings;
3034 rc = scmSettingsCreate(&pSettings, &g_Defaults);
3035 if (RT_FAILURE(rc))
3036 {
3037 RTMsgError("scmSettingsCreate: %Rrc\n", rc);
3038 return 1;
3039 }
3040
3041 /*
3042 * Parse arguments and process input in order (because this is the only
3043 * thing that works at the moment).
3044 */
3045 static RTGETOPTDEF s_aOpts[14 + RT_ELEMENTS(g_aScmOpts)] =
3046 {
3047 { "--dry-run", 'd', RTGETOPT_REQ_NOTHING },
3048 { "--real-run", 'D', RTGETOPT_REQ_NOTHING },
3049 { "--check-run", SCMOPT_CHECK_RUN, RTGETOPT_REQ_NOTHING },
3050 { "--file-filter", 'f', RTGETOPT_REQ_STRING },
3051 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
3052 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
3053 { "--diff-ignore-eol", SCMOPT_DIFF_IGNORE_EOL, RTGETOPT_REQ_NOTHING },
3054 { "--diff-no-ignore-eol", SCMOPT_DIFF_NO_IGNORE_EOL, RTGETOPT_REQ_NOTHING },
3055 { "--diff-ignore-space", SCMOPT_DIFF_IGNORE_SPACE, RTGETOPT_REQ_NOTHING },
3056 { "--diff-no-ignore-space", SCMOPT_DIFF_NO_IGNORE_SPACE, RTGETOPT_REQ_NOTHING },
3057 { "--diff-ignore-leading-space", SCMOPT_DIFF_IGNORE_LEADING_SPACE, RTGETOPT_REQ_NOTHING },
3058 { "--diff-no-ignore-leading-space", SCMOPT_DIFF_NO_IGNORE_LEADING_SPACE, RTGETOPT_REQ_NOTHING },
3059 { "--diff-ignore-trailing-space", SCMOPT_DIFF_IGNORE_TRAILING_SPACE, RTGETOPT_REQ_NOTHING },
3060 { "--diff-no-ignore-trailing-space", SCMOPT_DIFF_NO_IGNORE_TRAILING_SPACE, RTGETOPT_REQ_NOTHING },
3061 { "--diff-special-chars", SCMOPT_DIFF_SPECIAL_CHARS, RTGETOPT_REQ_NOTHING },
3062 { "--diff-no-special-chars", SCMOPT_DIFF_NO_SPECIAL_CHARS, RTGETOPT_REQ_NOTHING },
3063 };
3064 memcpy(&s_aOpts[RT_ELEMENTS(s_aOpts) - RT_ELEMENTS(g_aScmOpts)], &g_aScmOpts[0], sizeof(g_aScmOpts));
3065
3066 bool fCheckRun = false;
3067 RTGETOPTUNION ValueUnion;
3068 RTGETOPTSTATE GetOptState;
3069 rc = RTGetOptInit(&GetOptState, argc, argv, &s_aOpts[0], RT_ELEMENTS(s_aOpts), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
3070 AssertReleaseRCReturn(rc, 1);
3071
3072 while ( (rc = RTGetOpt(&GetOptState, &ValueUnion)) != 0
3073 && rc != VINF_GETOPT_NOT_OPTION)
3074 {
3075 switch (rc)
3076 {
3077 case 'd':
3078 g_fDryRun = true;
3079 fCheckRun = false;
3080 break;
3081 case 'D':
3082 g_fDryRun = fCheckRun = false;
3083 break;
3084 case SCMOPT_CHECK_RUN:
3085 g_fDryRun = fCheckRun = true;
3086 break;
3087
3088 case 'f':
3089 g_pszFileFilter = ValueUnion.psz;
3090 break;
3091
3092 case 'h':
3093 return scmHelp(s_aOpts, RT_ELEMENTS(s_aOpts));
3094
3095 case SCMOPT_HELP_CONFIG:
3096 return scmHelpConfig();
3097
3098 case SCMOPT_HELP_ACTIONS:
3099 return scmHelpActions();
3100
3101 case 'q':
3102 g_iVerbosity = 0;
3103 break;
3104
3105 case 'v':
3106 g_iVerbosity++;
3107 break;
3108
3109 case 'V':
3110 {
3111 /* The following is assuming that svn does it's job here. */
3112 static const char s_szRev[] = "$Revision: 96407 $";
3113 const char *psz = RTStrStripL(strchr(s_szRev, ' '));
3114 RTPrintf("r%.*s\n", strchr(psz, ' ') - psz, psz);
3115 return 0;
3116 }
3117
3118 case SCMOPT_DIFF_IGNORE_EOL:
3119 g_fDiffIgnoreEol = true;
3120 break;
3121 case SCMOPT_DIFF_NO_IGNORE_EOL:
3122 g_fDiffIgnoreEol = false;
3123 break;
3124
3125 case SCMOPT_DIFF_IGNORE_SPACE:
3126 g_fDiffIgnoreTrailingWS = g_fDiffIgnoreLeadingWS = true;
3127 break;
3128 case SCMOPT_DIFF_NO_IGNORE_SPACE:
3129 g_fDiffIgnoreTrailingWS = g_fDiffIgnoreLeadingWS = false;
3130 break;
3131
3132 case SCMOPT_DIFF_IGNORE_LEADING_SPACE:
3133 g_fDiffIgnoreLeadingWS = true;
3134 break;
3135 case SCMOPT_DIFF_NO_IGNORE_LEADING_SPACE:
3136 g_fDiffIgnoreLeadingWS = false;
3137 break;
3138
3139 case SCMOPT_DIFF_IGNORE_TRAILING_SPACE:
3140 g_fDiffIgnoreTrailingWS = true;
3141 break;
3142 case SCMOPT_DIFF_NO_IGNORE_TRAILING_SPACE:
3143 g_fDiffIgnoreTrailingWS = false;
3144 break;
3145
3146 case SCMOPT_DIFF_SPECIAL_CHARS:
3147 g_fDiffSpecialChars = true;
3148 break;
3149 case SCMOPT_DIFF_NO_SPECIAL_CHARS:
3150 g_fDiffSpecialChars = false;
3151 break;
3152
3153 default:
3154 {
3155 int rc2 = scmSettingsBaseHandleOpt(&pSettings->Base, rc, &ValueUnion, "/", 1);
3156 if (RT_SUCCESS(rc2))
3157 break;
3158 if (rc2 != VERR_GETOPT_UNKNOWN_OPTION)
3159 return 2;
3160 return RTGetOptPrintError(rc, &ValueUnion);
3161 }
3162 }
3163 }
3164
3165 /*
3166 * Process non-options.
3167 */
3168 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
3169 if (rc == VINF_GETOPT_NOT_OPTION)
3170 {
3171 ScmSvnInit();
3172
3173 bool fWarned = g_fDryRun;
3174 while (rc == VINF_GETOPT_NOT_OPTION)
3175 {
3176 if (!fWarned)
3177 {
3178 RTPrintf("%s: Warning! This program will make changes to your source files and\n"
3179 "%s: there is a slight risk that bugs or a full disk may cause\n"
3180 "%s: LOSS OF DATA. So, please make sure you have checked in\n"
3181 "%s: all your changes already. If you didn't, then don't blame\n"
3182 "%s: anyone for not warning you!\n"
3183 "%s:\n"
3184 "%s: Press any key to continue...\n",
3185 g_szProgName, g_szProgName, g_szProgName, g_szProgName, g_szProgName,
3186 g_szProgName, g_szProgName);
3187 RTStrmGetCh(g_pStdIn);
3188 fWarned = true;
3189 }
3190
3191 rc = scmProcessSomething(ValueUnion.psz, pSettings);
3192 if (RT_FAILURE(rc))
3193 {
3194 rcExit = RTEXITCODE_FAILURE;
3195 break;
3196 }
3197
3198 /* next */
3199 rc = RTGetOpt(&GetOptState, &ValueUnion);
3200 if (RT_FAILURE(rc))
3201 rcExit = RTGetOptPrintError(rc, &ValueUnion);
3202 }
3203
3204 scmPrintStats();
3205 ScmSvnTerm();
3206 }
3207 else
3208 RTMsgWarning("No files or directories specified. Doing nothing");
3209
3210 scmSettingsDestroy(pSettings);
3211
3212 /* If we're in checking mode, fail if any files needed modification. */
3213 if ( rcExit == RTEXITCODE_SUCCESS
3214 && fCheckRun
3215 && g_cFilesModified > 0)
3216 {
3217 RTMsgError("Checking mode failed! %u file%s needs modifications", g_cFilesBinaries, g_cFilesBinaries > 1 ? "s" : "");
3218 rcExit = RTEXITCODE_FAILURE;
3219 }
3220
3221 /* Fail if any files require manual repair. */
3222 if (g_cFilesRequiringManualFixing > 0)
3223 {
3224 RTMsgError("%u file%s needs manual modifications", g_cFilesRequiringManualFixing,
3225 g_cFilesRequiringManualFixing > 1 ? "s" : "");
3226 if (rcExit == RTEXITCODE_SUCCESS)
3227 rcExit = RTEXITCODE_FAILURE;
3228 }
3229
3230 return rcExit;
3231}
3232
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