VirtualBox

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

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

scm: Fix typo in comment. Two tees in "settings" is enough.
dabbrev-expand was keen to pick it and clone it all over.

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