VirtualBox

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

Last change on this file since 76451 was 76451, checked in by vboxsync, 6 years ago

scm: Added crude pass for adjusting err.h to errcore.h when possible. Currently disabled.

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

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