VirtualBox

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

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

scm: Introduce a check to force the use of either hrc for COM status codes or vrc for IPRT ones instead of using rc for both causing confusion when to use which, bugref:10223

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