VirtualBox

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

Last change on this file was 103383, checked in by vboxsync, 4 months ago

scm: A couple of parfait things. bugref:3409

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

© 2023 Oracle
ContactPrivacy policyTerms of Use