VirtualBox

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

最後變更 在這個檔案從76506是 76506,由 vboxsync 提交於 6 年 前

scm: Header guard massaging for adding #pragma once. bugref:9344

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

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