VirtualBox

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

Last change on this file since 63001 was 62884, checked in by vboxsync, 8 years ago

scm: gcc warnings

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 62.7 KB
Line 
1/* $Id: scm.cpp 62884 2016-08-02 15:53:26Z vboxsync $ */
2/** @file
3 * IPRT Testcase / Tool - Source Code Massager.
4 */
5
6/*
7 * Copyright (C) 2010-2016 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_MIN_BLANK_LINES_BEFORE_FLOWER_BOX_MARKERS,
78 SCMOPT_ONLY_SVN_DIRS,
79 SCMOPT_NOT_ONLY_SVN_DIRS,
80 SCMOPT_ONLY_SVN_FILES,
81 SCMOPT_NOT_ONLY_SVN_FILES,
82 SCMOPT_SET_SVN_EOL,
83 SCMOPT_DONT_SET_SVN_EOL,
84 SCMOPT_SET_SVN_EXECUTABLE,
85 SCMOPT_DONT_SET_SVN_EXECUTABLE,
86 SCMOPT_SET_SVN_KEYWORDS,
87 SCMOPT_DONT_SET_SVN_KEYWORDS,
88 SCMOPT_TAB_SIZE,
89 SCMOPT_WIDTH,
90 SCMOPT_FILTER_OUT_DIRS,
91 SCMOPT_FILTER_FILES,
92 SCMOPT_FILTER_OUT_FILES,
93 SCMOPT_LAST_SETTINGS = SCMOPT_FILTER_OUT_FILES,
94 //
95 SCMOPT_DIFF_IGNORE_EOL,
96 SCMOPT_DIFF_NO_IGNORE_EOL,
97 SCMOPT_DIFF_IGNORE_SPACE,
98 SCMOPT_DIFF_NO_IGNORE_SPACE,
99 SCMOPT_DIFF_IGNORE_LEADING_SPACE,
100 SCMOPT_DIFF_NO_IGNORE_LEADING_SPACE,
101 SCMOPT_DIFF_IGNORE_TRAILING_SPACE,
102 SCMOPT_DIFF_NO_IGNORE_TRAILING_SPACE,
103 SCMOPT_DIFF_SPECIAL_CHARS,
104 SCMOPT_DIFF_NO_SPECIAL_CHARS,
105 SCMOPT_END
106} SCMOPT;
107
108
109/*********************************************************************************************************************************
110* Global Variables *
111*********************************************************************************************************************************/
112const char g_szTabSpaces[16+1] = " ";
113const char g_szAsterisks[255+1] =
114"****************************************************************************************************"
115"****************************************************************************************************"
116"*******************************************************";
117const char g_szSpaces[255+1] =
118" "
119" "
120" ";
121static const char g_szProgName[] = "scm";
122static const char *g_pszChangedSuff = "";
123static bool g_fDryRun = true;
124static bool g_fDiffSpecialChars = true;
125static bool g_fDiffIgnoreEol = false;
126static bool g_fDiffIgnoreLeadingWS = false;
127static bool g_fDiffIgnoreTrailingWS = false;
128static int g_iVerbosity = 2;//99; //0;
129
130/** The global settings. */
131static SCMSETTINGSBASE const g_Defaults =
132{
133 /* .fConvertEol = */ true,
134 /* .fConvertTabs = */ true,
135 /* .fForceFinalEol = */ true,
136 /* .fForceTrailingLine = */ false,
137 /* .fStripTrailingBlanks = */ true,
138 /* .fStripTrailingLines = */ true,
139 /* .fFixFlowerBoxMarkers = */ true,
140 /* .cMinBlankLinesBeforeFlowerBoxMakers = */ 2,
141 /* .fOnlySvnFiles = */ false,
142 /* .fOnlySvnDirs = */ false,
143 /* .fSetSvnEol = */ false,
144 /* .fSetSvnExecutable = */ false,
145 /* .fSetSvnKeywords = */ false,
146 /* .cchTab = */ 8,
147 /* .cchWidth = */ 130,
148 /* .pszFilterFiles = */ (char *)"",
149 /* .pszFilterOutFiles = */ (char *)"*.exe|*.com|20*-*-*.log",
150 /* .pszFilterOutDirs = */ (char *)".svn|.hg|.git|CVS",
151};
152
153/** Option definitions for the base settings. */
154static RTGETOPTDEF g_aScmOpts[] =
155{
156 { "--convert-eol", SCMOPT_CONVERT_EOL, RTGETOPT_REQ_NOTHING },
157 { "--no-convert-eol", SCMOPT_NO_CONVERT_EOL, RTGETOPT_REQ_NOTHING },
158 { "--convert-tabs", SCMOPT_CONVERT_TABS, RTGETOPT_REQ_NOTHING },
159 { "--no-convert-tabs", SCMOPT_NO_CONVERT_TABS, RTGETOPT_REQ_NOTHING },
160 { "--force-final-eol", SCMOPT_FORCE_FINAL_EOL, RTGETOPT_REQ_NOTHING },
161 { "--no-force-final-eol", SCMOPT_NO_FORCE_FINAL_EOL, RTGETOPT_REQ_NOTHING },
162 { "--force-trailing-line", SCMOPT_FORCE_TRAILING_LINE, RTGETOPT_REQ_NOTHING },
163 { "--no-force-trailing-line", SCMOPT_NO_FORCE_TRAILING_LINE, RTGETOPT_REQ_NOTHING },
164 { "--strip-trailing-blanks", SCMOPT_STRIP_TRAILING_BLANKS, RTGETOPT_REQ_NOTHING },
165 { "--no-strip-trailing-blanks", SCMOPT_NO_STRIP_TRAILING_BLANKS, RTGETOPT_REQ_NOTHING },
166 { "--strip-trailing-lines", SCMOPT_STRIP_TRAILING_LINES, RTGETOPT_REQ_NOTHING },
167 { "--strip-no-trailing-lines", SCMOPT_NO_STRIP_TRAILING_LINES, RTGETOPT_REQ_NOTHING },
168 { "--min-blank-lines-before-flower-box-makers", SCMOPT_FIX_FLOWER_BOX_MARKERS, RTGETOPT_REQ_UINT8 },
169 { "--fix-flower-box-markers", SCMOPT_FIX_FLOWER_BOX_MARKERS, RTGETOPT_REQ_NOTHING },
170 { "--no-fix-flower-box-markers", SCMOPT_NO_FIX_FLOWER_BOX_MARKERS, RTGETOPT_REQ_NOTHING },
171 { "--only-svn-dirs", SCMOPT_ONLY_SVN_DIRS, RTGETOPT_REQ_NOTHING },
172 { "--not-only-svn-dirs", SCMOPT_NOT_ONLY_SVN_DIRS, RTGETOPT_REQ_NOTHING },
173 { "--only-svn-files", SCMOPT_ONLY_SVN_FILES, RTGETOPT_REQ_NOTHING },
174 { "--not-only-svn-files", SCMOPT_NOT_ONLY_SVN_FILES, RTGETOPT_REQ_NOTHING },
175 { "--set-svn-eol", SCMOPT_SET_SVN_EOL, RTGETOPT_REQ_NOTHING },
176 { "--dont-set-svn-eol", SCMOPT_DONT_SET_SVN_EOL, RTGETOPT_REQ_NOTHING },
177 { "--set-svn-executable", SCMOPT_SET_SVN_EXECUTABLE, RTGETOPT_REQ_NOTHING },
178 { "--dont-set-svn-executable", SCMOPT_DONT_SET_SVN_EXECUTABLE, RTGETOPT_REQ_NOTHING },
179 { "--set-svn-keywords", SCMOPT_SET_SVN_KEYWORDS, RTGETOPT_REQ_NOTHING },
180 { "--dont-set-svn-keywords", SCMOPT_DONT_SET_SVN_KEYWORDS, RTGETOPT_REQ_NOTHING },
181 { "--tab-size", SCMOPT_TAB_SIZE, RTGETOPT_REQ_UINT8 },
182 { "--width", SCMOPT_WIDTH, RTGETOPT_REQ_UINT8 },
183 { "--filter-out-dirs", SCMOPT_FILTER_OUT_DIRS, RTGETOPT_REQ_STRING },
184 { "--filter-files", SCMOPT_FILTER_FILES, RTGETOPT_REQ_STRING },
185 { "--filter-out-files", SCMOPT_FILTER_OUT_FILES, RTGETOPT_REQ_STRING },
186};
187
188/** Consider files matching the following patterns (base names only). */
189static const char *g_pszFileFilter = NULL;
190
191static PFNSCMREWRITER const g_aRewritersFor_Makefile_kup[] =
192{
193 rewrite_SvnNoExecutable,
194 rewrite_Makefile_kup
195};
196
197static PFNSCMREWRITER const g_aRewritersFor_Makefile_kmk[] =
198{
199 rewrite_ForceNativeEol,
200 rewrite_StripTrailingBlanks,
201 rewrite_AdjustTrailingLines,
202 rewrite_SvnNoExecutable,
203 rewrite_SvnKeywords,
204 rewrite_Makefile_kmk
205};
206
207static PFNSCMREWRITER const g_aRewritersFor_C_and_CPP[] =
208{
209 rewrite_ForceNativeEol,
210 rewrite_ExpandTabs,
211 rewrite_StripTrailingBlanks,
212 rewrite_AdjustTrailingLines,
213 rewrite_SvnNoExecutable,
214 rewrite_SvnKeywords,
215 rewrite_FixFlowerBoxMarkers,
216 rewrite_C_and_CPP
217};
218
219static PFNSCMREWRITER const g_aRewritersFor_H_and_HPP[] =
220{
221 rewrite_ForceNativeEol,
222 rewrite_ExpandTabs,
223 rewrite_StripTrailingBlanks,
224 rewrite_AdjustTrailingLines,
225 rewrite_SvnNoExecutable,
226 rewrite_C_and_CPP
227};
228
229static PFNSCMREWRITER const g_aRewritersFor_RC[] =
230{
231 rewrite_ForceNativeEol,
232 rewrite_ExpandTabs,
233 rewrite_StripTrailingBlanks,
234 rewrite_AdjustTrailingLines,
235 rewrite_SvnNoExecutable,
236 rewrite_SvnKeywords
237};
238
239static PFNSCMREWRITER const g_aRewritersFor_DEF[] =
240{
241 rewrite_ForceNativeEol,
242 rewrite_ExpandTabs,
243 rewrite_StripTrailingBlanks,
244 rewrite_AdjustTrailingLines,
245 rewrite_SvnNoExecutable,
246 rewrite_SvnKeywords
247};
248
249static PFNSCMREWRITER const g_aRewritersFor_ShellScripts[] =
250{
251 rewrite_ForceLF,
252 rewrite_ExpandTabs,
253 rewrite_StripTrailingBlanks
254};
255
256static PFNSCMREWRITER const g_aRewritersFor_BatchFiles[] =
257{
258 rewrite_ForceCRLF,
259 rewrite_ExpandTabs,
260 rewrite_StripTrailingBlanks
261};
262
263static PFNSCMREWRITER const g_aRewritersFor_SedScripts[] =
264{
265 rewrite_ForceLF,
266 rewrite_ExpandTabs,
267 rewrite_StripTrailingBlanks
268};
269
270static PFNSCMREWRITER const g_aRewritersFor_Python[] =
271{
272 /** @todo rewrite_ForceLFIfExecutable */
273 rewrite_ExpandTabs,
274 rewrite_StripTrailingBlanks,
275 rewrite_AdjustTrailingLines,
276 rewrite_SvnKeywords
277};
278
279
280static SCMCFGENTRY const g_aConfigs[] =
281{
282 { RT_ELEMENTS(g_aRewritersFor_Makefile_kup), &g_aRewritersFor_Makefile_kup[0], "Makefile.kup" },
283 { RT_ELEMENTS(g_aRewritersFor_Makefile_kmk), &g_aRewritersFor_Makefile_kmk[0], "Makefile.kmk|Config.kmk" },
284 { RT_ELEMENTS(g_aRewritersFor_C_and_CPP), &g_aRewritersFor_C_and_CPP[0], "*.c|*.cpp|*.C|*.CPP|*.cxx|*.cc|*.m|*.mm" },
285 { RT_ELEMENTS(g_aRewritersFor_H_and_HPP), &g_aRewritersFor_H_and_HPP[0], "*.h|*.hpp" },
286 { RT_ELEMENTS(g_aRewritersFor_RC), &g_aRewritersFor_RC[0], "*.rc" },
287 { RT_ELEMENTS(g_aRewritersFor_DEF), &g_aRewritersFor_DEF[0], "*.def" },
288 { RT_ELEMENTS(g_aRewritersFor_ShellScripts), &g_aRewritersFor_ShellScripts[0], "*.sh|configure" },
289 { RT_ELEMENTS(g_aRewritersFor_BatchFiles), &g_aRewritersFor_BatchFiles[0], "*.bat|*.cmd|*.btm|*.vbs|*.ps1" },
290 { RT_ELEMENTS(g_aRewritersFor_SedScripts), &g_aRewritersFor_SedScripts[0], "*.sed" },
291 { RT_ELEMENTS(g_aRewritersFor_Python), &g_aRewritersFor_Python[0], "*.py" },
292};
293
294
295
296/* -=-=-=-=-=- settings -=-=-=-=-=- */
297
298
299/**
300 * Init a settings structure with settings from @a pSrc.
301 *
302 * @returns IPRT status code
303 * @param pSettings The settings.
304 * @param pSrc The source settings.
305 */
306static int scmSettingsBaseInitAndCopy(PSCMSETTINGSBASE pSettings, PCSCMSETTINGSBASE pSrc)
307{
308 *pSettings = *pSrc;
309
310 int rc = RTStrDupEx(&pSettings->pszFilterFiles, pSrc->pszFilterFiles);
311 if (RT_SUCCESS(rc))
312 {
313 rc = RTStrDupEx(&pSettings->pszFilterOutFiles, pSrc->pszFilterOutFiles);
314 if (RT_SUCCESS(rc))
315 {
316 rc = RTStrDupEx(&pSettings->pszFilterOutDirs, pSrc->pszFilterOutDirs);
317 if (RT_SUCCESS(rc))
318 return VINF_SUCCESS;
319
320 RTStrFree(pSettings->pszFilterOutFiles);
321 }
322 RTStrFree(pSettings->pszFilterFiles);
323 }
324
325 pSettings->pszFilterFiles = NULL;
326 pSettings->pszFilterOutFiles = NULL;
327 pSettings->pszFilterOutDirs = NULL;
328 return rc;
329}
330
331/**
332 * Init a settings structure.
333 *
334 * @returns IPRT status code
335 * @param pSettings The settings.
336 */
337static int scmSettingsBaseInit(PSCMSETTINGSBASE pSettings)
338{
339 return scmSettingsBaseInitAndCopy(pSettings, &g_Defaults);
340}
341
342/**
343 * Deletes the settings, i.e. free any dynamically allocated content.
344 *
345 * @param pSettings The settings.
346 */
347static void scmSettingsBaseDelete(PSCMSETTINGSBASE pSettings)
348{
349 if (pSettings)
350 {
351 Assert(pSettings->cchTab != UINT8_MAX);
352 pSettings->cchTab = UINT8_MAX;
353
354 RTStrFree(pSettings->pszFilterFiles);
355 pSettings->pszFilterFiles = NULL;
356
357 RTStrFree(pSettings->pszFilterOutFiles);
358 pSettings->pszFilterOutFiles = NULL;
359
360 RTStrFree(pSettings->pszFilterOutDirs);
361 pSettings->pszFilterOutDirs = NULL;
362 }
363}
364
365
366/**
367 * Processes a RTGetOpt result.
368 *
369 * @retval VINF_SUCCESS if handled.
370 * @retval VERR_OUT_OF_RANGE if the option value was out of range.
371 * @retval VERR_GETOPT_UNKNOWN_OPTION if the option was not recognized.
372 *
373 * @param pSettings The settings to change.
374 * @param rc The RTGetOpt return value.
375 * @param pValueUnion The RTGetOpt value union.
376 */
377static int scmSettingsBaseHandleOpt(PSCMSETTINGSBASE pSettings, int rc, PRTGETOPTUNION pValueUnion)
378{
379 switch (rc)
380 {
381 case SCMOPT_CONVERT_EOL:
382 pSettings->fConvertEol = true;
383 return VINF_SUCCESS;
384 case SCMOPT_NO_CONVERT_EOL:
385 pSettings->fConvertEol = false;
386 return VINF_SUCCESS;
387
388 case SCMOPT_CONVERT_TABS:
389 pSettings->fConvertTabs = true;
390 return VINF_SUCCESS;
391 case SCMOPT_NO_CONVERT_TABS:
392 pSettings->fConvertTabs = false;
393 return VINF_SUCCESS;
394
395 case SCMOPT_FORCE_FINAL_EOL:
396 pSettings->fForceFinalEol = true;
397 return VINF_SUCCESS;
398 case SCMOPT_NO_FORCE_FINAL_EOL:
399 pSettings->fForceFinalEol = false;
400 return VINF_SUCCESS;
401
402 case SCMOPT_FORCE_TRAILING_LINE:
403 pSettings->fForceTrailingLine = true;
404 return VINF_SUCCESS;
405 case SCMOPT_NO_FORCE_TRAILING_LINE:
406 pSettings->fForceTrailingLine = false;
407 return VINF_SUCCESS;
408
409
410 case SCMOPT_STRIP_TRAILING_BLANKS:
411 pSettings->fStripTrailingBlanks = true;
412 return VINF_SUCCESS;
413 case SCMOPT_NO_STRIP_TRAILING_BLANKS:
414 pSettings->fStripTrailingBlanks = false;
415 return VINF_SUCCESS;
416
417 case SCMOPT_MIN_BLANK_LINES_BEFORE_FLOWER_BOX_MARKERS:
418 pSettings->cMinBlankLinesBeforeFlowerBoxMakers = pValueUnion->u8;
419 return VINF_SUCCESS;
420
421
422 case SCMOPT_STRIP_TRAILING_LINES:
423 pSettings->fStripTrailingLines = true;
424 return VINF_SUCCESS;
425 case SCMOPT_NO_STRIP_TRAILING_LINES:
426 pSettings->fStripTrailingLines = false;
427 return VINF_SUCCESS;
428
429 case SCMOPT_FIX_FLOWER_BOX_MARKERS:
430 pSettings->fFixFlowerBoxMarkers = true;
431 return VINF_SUCCESS;
432 case SCMOPT_NO_FIX_FLOWER_BOX_MARKERS:
433 pSettings->fFixFlowerBoxMarkers = false;
434 return VINF_SUCCESS;
435
436 case SCMOPT_ONLY_SVN_DIRS:
437 pSettings->fOnlySvnDirs = true;
438 return VINF_SUCCESS;
439 case SCMOPT_NOT_ONLY_SVN_DIRS:
440 pSettings->fOnlySvnDirs = false;
441 return VINF_SUCCESS;
442
443 case SCMOPT_ONLY_SVN_FILES:
444 pSettings->fOnlySvnFiles = true;
445 return VINF_SUCCESS;
446 case SCMOPT_NOT_ONLY_SVN_FILES:
447 pSettings->fOnlySvnFiles = false;
448 return VINF_SUCCESS;
449
450 case SCMOPT_SET_SVN_EOL:
451 pSettings->fSetSvnEol = true;
452 return VINF_SUCCESS;
453 case SCMOPT_DONT_SET_SVN_EOL:
454 pSettings->fSetSvnEol = false;
455 return VINF_SUCCESS;
456
457 case SCMOPT_SET_SVN_EXECUTABLE:
458 pSettings->fSetSvnExecutable = true;
459 return VINF_SUCCESS;
460 case SCMOPT_DONT_SET_SVN_EXECUTABLE:
461 pSettings->fSetSvnExecutable = false;
462 return VINF_SUCCESS;
463
464 case SCMOPT_SET_SVN_KEYWORDS:
465 pSettings->fSetSvnKeywords = true;
466 return VINF_SUCCESS;
467 case SCMOPT_DONT_SET_SVN_KEYWORDS:
468 pSettings->fSetSvnKeywords = false;
469 return VINF_SUCCESS;
470
471 case SCMOPT_TAB_SIZE:
472 if ( pValueUnion->u8 < 1
473 || pValueUnion->u8 >= RT_ELEMENTS(g_szTabSpaces))
474 {
475 RTMsgError("Invalid tab size: %u - must be in {1..%u}\n",
476 pValueUnion->u8, RT_ELEMENTS(g_szTabSpaces) - 1);
477 return VERR_OUT_OF_RANGE;
478 }
479 pSettings->cchTab = pValueUnion->u8;
480 return VINF_SUCCESS;
481
482 case SCMOPT_WIDTH:
483 if (pValueUnion->u8 < 20 || pValueUnion->u8 > 200)
484 {
485 RTMsgError("Invalid width size: %u - must be in {20..200} range\n", pValueUnion->u8);
486 return VERR_OUT_OF_RANGE;
487 }
488 pSettings->cchWidth = pValueUnion->u8;
489 return VINF_SUCCESS;
490
491 case SCMOPT_FILTER_OUT_DIRS:
492 case SCMOPT_FILTER_FILES:
493 case SCMOPT_FILTER_OUT_FILES:
494 {
495 char **ppsz = NULL;
496 switch (rc)
497 {
498 case SCMOPT_FILTER_OUT_DIRS: ppsz = &pSettings->pszFilterOutDirs; break;
499 case SCMOPT_FILTER_FILES: ppsz = &pSettings->pszFilterFiles; break;
500 case SCMOPT_FILTER_OUT_FILES: ppsz = &pSettings->pszFilterOutFiles; break;
501 }
502
503 /*
504 * An empty string zaps the current list.
505 */
506 if (!*pValueUnion->psz)
507 return RTStrATruncate(ppsz, 0);
508
509 /*
510 * Non-empty strings are appended to the pattern list.
511 *
512 * Strip leading and trailing pattern separators before attempting
513 * to append it. If it's just separators, don't do anything.
514 */
515 const char *pszSrc = pValueUnion->psz;
516 while (*pszSrc == '|')
517 pszSrc++;
518 size_t cchSrc = strlen(pszSrc);
519 while (cchSrc > 0 && pszSrc[cchSrc - 1] == '|')
520 cchSrc--;
521 if (!cchSrc)
522 return VINF_SUCCESS;
523
524 return RTStrAAppendExN(ppsz, 2,
525 "|", *ppsz && **ppsz ? (size_t)1 : (size_t)0,
526 pszSrc, cchSrc);
527 }
528
529 default:
530 return VERR_GETOPT_UNKNOWN_OPTION;
531 }
532}
533
534/**
535 * Parses an option string.
536 *
537 * @returns IPRT status code.
538 * @param pBase The base settings structure to apply the options
539 * to.
540 * @param pszOptions The options to parse.
541 */
542static int scmSettingsBaseParseString(PSCMSETTINGSBASE pBase, const char *pszLine)
543{
544 int cArgs;
545 char **papszArgs;
546 int rc = RTGetOptArgvFromString(&papszArgs, &cArgs, pszLine, RTGETOPTARGV_CNV_QUOTE_BOURNE_SH, NULL);
547 if (RT_SUCCESS(rc))
548 {
549 RTGETOPTUNION ValueUnion;
550 RTGETOPTSTATE GetOptState;
551 rc = RTGetOptInit(&GetOptState, cArgs, papszArgs, &g_aScmOpts[0], RT_ELEMENTS(g_aScmOpts), 0, 0 /*fFlags*/);
552 if (RT_SUCCESS(rc))
553 {
554 while ((rc = RTGetOpt(&GetOptState, &ValueUnion)) != 0)
555 {
556 rc = scmSettingsBaseHandleOpt(pBase, rc, &ValueUnion);
557 if (RT_FAILURE(rc))
558 break;
559 }
560 }
561 RTGetOptArgvFree(papszArgs);
562 }
563
564 return rc;
565}
566
567/**
568 * Parses an unterminated option string.
569 *
570 * @returns IPRT status code.
571 * @param pBase The base settings structure to apply the options
572 * to.
573 * @param pchLine The line.
574 * @param cchLine The line length.
575 */
576static int scmSettingsBaseParseStringN(PSCMSETTINGSBASE pBase, const char *pchLine, size_t cchLine)
577{
578 char *pszLine = RTStrDupN(pchLine, cchLine);
579 if (!pszLine)
580 return VERR_NO_MEMORY;
581 int rc = scmSettingsBaseParseString(pBase, pszLine);
582 RTStrFree(pszLine);
583 return rc;
584}
585
586/**
587 * Verifies the options string.
588 *
589 * @returns IPRT status code.
590 * @param pszOptions The options to verify .
591 */
592static int scmSettingsBaseVerifyString(const char *pszOptions)
593{
594 SCMSETTINGSBASE Base;
595 int rc = scmSettingsBaseInit(&Base);
596 if (RT_SUCCESS(rc))
597 {
598 rc = scmSettingsBaseParseString(&Base, pszOptions);
599 scmSettingsBaseDelete(&Base);
600 }
601 return rc;
602}
603
604/**
605 * Loads settings found in editor and SCM settings directives within the
606 * document (@a pStream).
607 *
608 * @returns IPRT status code.
609 * @param pBase The settings base to load settings into.
610 * @param pStream The stream to scan for settings directives.
611 */
612static int scmSettingsBaseLoadFromDocument(PSCMSETTINGSBASE pBase, PSCMSTREAM pStream)
613{
614 /** @todo Editor and SCM settings directives in documents. */
615 RT_NOREF2(pBase, pStream);
616 return VINF_SUCCESS;
617}
618
619/**
620 * Creates a new settings file struct, cloning @a pSettings.
621 *
622 * @returns IPRT status code.
623 * @param ppSettings Where to return the new struct.
624 * @param pSettingsBase The settings to inherit from.
625 */
626static int scmSettingsCreate(PSCMSETTINGS *ppSettings, PCSCMSETTINGSBASE pSettingsBase)
627{
628 PSCMSETTINGS pSettings = (PSCMSETTINGS)RTMemAlloc(sizeof(*pSettings));
629 if (!pSettings)
630 return VERR_NO_MEMORY;
631 int rc = scmSettingsBaseInitAndCopy(&pSettings->Base, pSettingsBase);
632 if (RT_SUCCESS(rc))
633 {
634 pSettings->pDown = NULL;
635 pSettings->pUp = NULL;
636 pSettings->paPairs = NULL;
637 pSettings->cPairs = 0;
638 *ppSettings = pSettings;
639 return VINF_SUCCESS;
640 }
641 RTMemFree(pSettings);
642 return rc;
643}
644
645/**
646 * Destroys a settings structure.
647 *
648 * @param pSettings The settings structure to destroy. NULL is OK.
649 */
650static void scmSettingsDestroy(PSCMSETTINGS pSettings)
651{
652 if (pSettings)
653 {
654 scmSettingsBaseDelete(&pSettings->Base);
655 for (size_t i = 0; i < pSettings->cPairs; i++)
656 {
657 RTStrFree(pSettings->paPairs[i].pszPattern);
658 RTStrFree(pSettings->paPairs[i].pszOptions);
659 pSettings->paPairs[i].pszPattern = NULL;
660 pSettings->paPairs[i].pszOptions = NULL;
661 }
662 RTMemFree(pSettings->paPairs);
663 pSettings->paPairs = NULL;
664 RTMemFree(pSettings);
665 }
666}
667
668/**
669 * Adds a pattern/options pair to the settings structure.
670 *
671 * @returns IPRT status code.
672 * @param pSettings The settings.
673 * @param pchLine The line containing the unparsed pair.
674 * @param cchLine The length of the line.
675 */
676static int scmSettingsAddPair(PSCMSETTINGS pSettings, const char *pchLine, size_t cchLine)
677{
678 /*
679 * Split the string.
680 */
681 const char *pchOptions = (const char *)memchr(pchLine, ':', cchLine);
682 if (!pchOptions)
683 return VERR_INVALID_PARAMETER;
684 size_t cchPattern = pchOptions - pchLine;
685 size_t cchOptions = cchLine - cchPattern - 1;
686 pchOptions++;
687
688 /* strip spaces everywhere */
689 while (cchPattern > 0 && RT_C_IS_SPACE(pchLine[cchPattern - 1]))
690 cchPattern--;
691 while (cchPattern > 0 && RT_C_IS_SPACE(*pchLine))
692 cchPattern--, pchLine++;
693
694 while (cchOptions > 0 && RT_C_IS_SPACE(pchOptions[cchOptions - 1]))
695 cchOptions--;
696 while (cchOptions > 0 && RT_C_IS_SPACE(*pchOptions))
697 cchOptions--, pchOptions++;
698
699 /* Quietly ignore empty patterns and empty options. */
700 if (!cchOptions || !cchPattern)
701 return VINF_SUCCESS;
702
703 /*
704 * Add the pair and verify the option string.
705 */
706 uint32_t iPair = pSettings->cPairs;
707 if ((iPair % 32) == 0)
708 {
709 void *pvNew = RTMemRealloc(pSettings->paPairs, (iPair + 32) * sizeof(pSettings->paPairs[0]));
710 if (!pvNew)
711 return VERR_NO_MEMORY;
712 pSettings->paPairs = (PSCMPATRNOPTPAIR)pvNew;
713 }
714
715 pSettings->paPairs[iPair].pszPattern = RTStrDupN(pchLine, cchPattern);
716 pSettings->paPairs[iPair].pszOptions = RTStrDupN(pchOptions, cchOptions);
717 int rc;
718 if ( pSettings->paPairs[iPair].pszPattern
719 && pSettings->paPairs[iPair].pszOptions)
720 rc = scmSettingsBaseVerifyString(pSettings->paPairs[iPair].pszOptions);
721 else
722 rc = VERR_NO_MEMORY;
723 if (RT_SUCCESS(rc))
724 pSettings->cPairs = iPair + 1;
725 else
726 {
727 RTStrFree(pSettings->paPairs[iPair].pszPattern);
728 RTStrFree(pSettings->paPairs[iPair].pszOptions);
729 }
730 return rc;
731}
732
733/**
734 * Loads in the settings from @a pszFilename.
735 *
736 * @returns IPRT status code.
737 * @param pSettings Where to load the settings file.
738 * @param pszFilename The file to load.
739 */
740static int scmSettingsLoadFile(PSCMSETTINGS pSettings, const char *pszFilename)
741{
742 ScmVerbose(NULL, 3, "Loading settings file '%s'...\n", pszFilename);
743
744 SCMSTREAM Stream;
745 int rc = ScmStreamInitForReading(&Stream, pszFilename);
746 if (RT_FAILURE(rc))
747 {
748 RTMsgError("%s: ScmStreamInitForReading -> %Rrc\n", pszFilename, rc);
749 return rc;
750 }
751
752 SCMEOL enmEol;
753 const char *pchLine;
754 size_t cchLine;
755 while ((pchLine = ScmStreamGetLine(&Stream, &cchLine, &enmEol)) != NULL)
756 {
757 /* Ignore leading spaces. */
758 while (cchLine > 0 && RT_C_IS_SPACE(*pchLine))
759 pchLine++, cchLine--;
760
761 /* Ignore empty lines and comment lines. */
762 if (cchLine < 1 || *pchLine == '#')
763 continue;
764
765 /* What kind of line is it? */
766 const char *pchColon = (const char *)memchr(pchLine, ':', cchLine);
767 if (pchColon)
768 rc = scmSettingsAddPair(pSettings, pchLine, cchLine);
769 else
770 rc = scmSettingsBaseParseStringN(&pSettings->Base, pchLine, cchLine);
771 if (RT_FAILURE(rc))
772 {
773 RTMsgError("%s:%d: %Rrc\n", pszFilename, ScmStreamTellLine(&Stream), rc);
774 break;
775 }
776 }
777
778 if (RT_SUCCESS(rc))
779 {
780 rc = ScmStreamGetStatus(&Stream);
781 if (RT_FAILURE(rc))
782 RTMsgError("%s: ScmStreamGetStatus- > %Rrc\n", pszFilename, rc);
783 }
784
785 ScmStreamDelete(&Stream);
786 return rc;
787}
788
789#if 0 /* unused */
790/**
791 * Parse the specified settings file creating a new settings struct from it.
792 *
793 * @returns IPRT status code
794 * @param ppSettings Where to return the new settings.
795 * @param pszFilename The file to parse.
796 * @param pSettingsBase The base settings we inherit from.
797 */
798static int scmSettingsCreateFromFile(PSCMSETTINGS *ppSettings, const char *pszFilename, PCSCMSETTINGSBASE pSettingsBase)
799{
800 PSCMSETTINGS pSettings;
801 int rc = scmSettingsCreate(&pSettings, pSettingsBase);
802 if (RT_SUCCESS(rc))
803 {
804 rc = scmSettingsLoadFile(pSettings, pszFilename);
805 if (RT_SUCCESS(rc))
806 {
807 *ppSettings = pSettings;
808 return VINF_SUCCESS;
809 }
810
811 scmSettingsDestroy(pSettings);
812 }
813 *ppSettings = NULL;
814 return rc;
815}
816#endif
817
818
819/**
820 * Create an initial settings structure when starting processing a new file or
821 * directory.
822 *
823 * This will look for .scm-settings files from the root and down to the
824 * specified directory, combining them into the returned settings structure.
825 *
826 * @returns IPRT status code.
827 * @param ppSettings Where to return the pointer to the top stack
828 * object.
829 * @param pBaseSettings The base settings we inherit from (globals
830 * typically).
831 * @param pszPath The absolute path to the new directory or file.
832 */
833static int scmSettingsCreateForPath(PSCMSETTINGS *ppSettings, PCSCMSETTINGSBASE pBaseSettings, const char *pszPath)
834{
835 *ppSettings = NULL; /* try shut up gcc. */
836
837 /*
838 * We'll be working with a stack copy of the path.
839 */
840 char szFile[RTPATH_MAX];
841 size_t cchDir = strlen(pszPath);
842 if (cchDir >= sizeof(szFile) - sizeof(SCM_SETTINGS_FILENAME))
843 return VERR_FILENAME_TOO_LONG;
844
845 /*
846 * Create the bottom-most settings.
847 */
848 PSCMSETTINGS pSettings;
849 int rc = scmSettingsCreate(&pSettings, pBaseSettings);
850 if (RT_FAILURE(rc))
851 return rc;
852
853 /*
854 * Enumerate the path components from the root and down. Load any setting
855 * files we find.
856 */
857 size_t cComponents = RTPathCountComponents(pszPath);
858 for (size_t i = 1; i <= cComponents; i++)
859 {
860 rc = RTPathCopyComponents(szFile, sizeof(szFile), pszPath, i);
861 if (RT_SUCCESS(rc))
862 rc = RTPathAppend(szFile, sizeof(szFile), SCM_SETTINGS_FILENAME);
863 if (RT_FAILURE(rc))
864 break;
865 if (RTFileExists(szFile))
866 {
867 rc = scmSettingsLoadFile(pSettings, szFile);
868 if (RT_FAILURE(rc))
869 break;
870 }
871 }
872
873 if (RT_SUCCESS(rc))
874 *ppSettings = pSettings;
875 else
876 scmSettingsDestroy(pSettings);
877 return rc;
878}
879
880/**
881 * Pushes a new settings set onto the stack.
882 *
883 * @param ppSettingsStack The pointer to the pointer to the top stack
884 * element. This will be used as input and output.
885 * @param pSettings The settings to push onto the stack.
886 */
887static void scmSettingsStackPush(PSCMSETTINGS *ppSettingsStack, PSCMSETTINGS pSettings)
888{
889 PSCMSETTINGS pOld = *ppSettingsStack;
890 pSettings->pDown = pOld;
891 pSettings->pUp = NULL;
892 if (pOld)
893 pOld->pUp = pSettings;
894 *ppSettingsStack = pSettings;
895}
896
897/**
898 * Pushes the settings of the specified directory onto the stack.
899 *
900 * We will load any .scm-settings in the directory. A stack entry is added even
901 * if no settings file was found.
902 *
903 * @returns IPRT status code.
904 * @param ppSettingsStack The pointer to the pointer to the top stack
905 * element. This will be used as input and output.
906 * @param pszDir The directory to do this for.
907 */
908static int scmSettingsStackPushDir(PSCMSETTINGS *ppSettingsStack, const char *pszDir)
909{
910 char szFile[RTPATH_MAX];
911 int rc = RTPathJoin(szFile, sizeof(szFile), pszDir, SCM_SETTINGS_FILENAME);
912 if (RT_SUCCESS(rc))
913 {
914 PSCMSETTINGS pSettings;
915 rc = scmSettingsCreate(&pSettings, &(*ppSettingsStack)->Base);
916 if (RT_SUCCESS(rc))
917 {
918 if (RTFileExists(szFile))
919 rc = scmSettingsLoadFile(pSettings, szFile);
920 if (RT_SUCCESS(rc))
921 {
922 scmSettingsStackPush(ppSettingsStack, pSettings);
923 return VINF_SUCCESS;
924 }
925
926 scmSettingsDestroy(pSettings);
927 }
928 }
929 return rc;
930}
931
932
933/**
934 * Pops a settings set off the stack.
935 *
936 * @returns The popped setttings.
937 * @param ppSettingsStack The pointer to the pointer to the top stack
938 * element. This will be used as input and output.
939 */
940static PSCMSETTINGS scmSettingsStackPop(PSCMSETTINGS *ppSettingsStack)
941{
942 PSCMSETTINGS pRet = *ppSettingsStack;
943 PSCMSETTINGS pNew = pRet ? pRet->pDown : NULL;
944 *ppSettingsStack = pNew;
945 if (pNew)
946 pNew->pUp = NULL;
947 if (pRet)
948 {
949 pRet->pUp = NULL;
950 pRet->pDown = NULL;
951 }
952 return pRet;
953}
954
955/**
956 * Pops and destroys the top entry of the stack.
957 *
958 * @param ppSettingsStack The pointer to the pointer to the top stack
959 * element. This will be used as input and output.
960 */
961static void scmSettingsStackPopAndDestroy(PSCMSETTINGS *ppSettingsStack)
962{
963 scmSettingsDestroy(scmSettingsStackPop(ppSettingsStack));
964}
965
966/**
967 * Constructs the base settings for the specified file name.
968 *
969 * @returns IPRT status code.
970 * @param pSettingsStack The top element on the settings stack.
971 * @param pszFilename The file name.
972 * @param pszBasename The base name (pointer within @a pszFilename).
973 * @param cchBasename The length of the base name. (For passing to
974 * RTStrSimplePatternMultiMatch.)
975 * @param pBase Base settings to initialize.
976 */
977static int scmSettingsStackMakeFileBase(PCSCMSETTINGS pSettingsStack, const char *pszFilename,
978 const char *pszBasename, size_t cchBasename, PSCMSETTINGSBASE pBase)
979{
980 int rc = scmSettingsBaseInitAndCopy(pBase, &pSettingsStack->Base);
981 if (RT_SUCCESS(rc))
982 {
983 /* find the bottom entry in the stack. */
984 PCSCMSETTINGS pCur = pSettingsStack;
985 while (pCur->pDown)
986 pCur = pCur->pDown;
987
988 /* Work our way up thru the stack and look for matching pairs. */
989 while (pCur)
990 {
991 size_t const cPairs = pCur->cPairs;
992 if (cPairs)
993 {
994 for (size_t i = 0; i < cPairs; i++)
995 if ( RTStrSimplePatternMultiMatch(pCur->paPairs[i].pszPattern, RTSTR_MAX,
996 pszBasename, cchBasename, NULL)
997 || RTStrSimplePatternMultiMatch(pCur->paPairs[i].pszPattern, RTSTR_MAX,
998 pszFilename, RTSTR_MAX, NULL))
999 {
1000 rc = scmSettingsBaseParseString(pBase, pCur->paPairs[i].pszOptions);
1001 if (RT_FAILURE(rc))
1002 break;
1003 }
1004 if (RT_FAILURE(rc))
1005 break;
1006 }
1007
1008 /* advance */
1009 pCur = pCur->pUp;
1010 }
1011 }
1012 if (RT_FAILURE(rc))
1013 scmSettingsBaseDelete(pBase);
1014 return rc;
1015}
1016
1017
1018/* -=-=-=-=-=- misc -=-=-=-=-=- */
1019
1020
1021/**
1022 * Prints a verbose message if the level is high enough.
1023 *
1024 * @param pState The rewrite state. Optional.
1025 * @param iLevel The required verbosity level.
1026 * @param pszFormat The message format string. Can be NULL if we
1027 * only want to trigger the per file message.
1028 * @param ... Format arguments.
1029 */
1030void ScmVerbose(PSCMRWSTATE pState, int iLevel, const char *pszFormat, ...)
1031{
1032 if (iLevel <= g_iVerbosity)
1033 {
1034 if (pState && !pState->fFirst)
1035 {
1036 RTPrintf("%s: info: --= Rewriting '%s' =--\n", g_szProgName, pState->pszFilename);
1037 pState->fFirst = true;
1038 }
1039 if (pszFormat)
1040 {
1041 RTPrintf(pState
1042 ? "%s: info: "
1043 : "%s: info: ",
1044 g_szProgName);
1045 va_list va;
1046 va_start(va, pszFormat);
1047 RTPrintfV(pszFormat, va);
1048 va_end(va);
1049 }
1050 }
1051}
1052
1053
1054/* -=-=-=-=-=- file and directory processing -=-=-=-=-=- */
1055
1056
1057/**
1058 * Processes a file.
1059 *
1060 * @returns IPRT status code.
1061 * @param pState The rewriter state.
1062 * @param pszFilename The file name.
1063 * @param pszBasename The base name (pointer within @a pszFilename).
1064 * @param cchBasename The length of the base name. (For passing to
1065 * RTStrSimplePatternMultiMatch.)
1066 * @param pBaseSettings The base settings to use. It's OK to modify
1067 * these.
1068 */
1069static int scmProcessFileInner(PSCMRWSTATE pState, const char *pszFilename, const char *pszBasename, size_t cchBasename,
1070 PSCMSETTINGSBASE pBaseSettings)
1071{
1072 /*
1073 * Do the file level filtering.
1074 */
1075 if ( pBaseSettings->pszFilterFiles
1076 && *pBaseSettings->pszFilterFiles
1077 && !RTStrSimplePatternMultiMatch(pBaseSettings->pszFilterFiles, RTSTR_MAX, pszBasename, cchBasename, NULL))
1078 {
1079 ScmVerbose(NULL, 5, "skipping '%s': file filter mismatch\n", pszFilename);
1080 return VINF_SUCCESS;
1081 }
1082 if ( pBaseSettings->pszFilterOutFiles
1083 && *pBaseSettings->pszFilterOutFiles
1084 && ( RTStrSimplePatternMultiMatch(pBaseSettings->pszFilterOutFiles, RTSTR_MAX, pszBasename, cchBasename, NULL)
1085 || RTStrSimplePatternMultiMatch(pBaseSettings->pszFilterOutFiles, RTSTR_MAX, pszFilename, RTSTR_MAX, NULL)) )
1086 {
1087 ScmVerbose(NULL, 5, "skipping '%s': filterd out\n", pszFilename);
1088 return VINF_SUCCESS;
1089 }
1090 if ( pBaseSettings->fOnlySvnFiles
1091 && !ScmSvnIsInWorkingCopy(pState))
1092 {
1093 ScmVerbose(NULL, 5, "skipping '%s': not in SVN WC\n", pszFilename);
1094 return VINF_SUCCESS;
1095 }
1096
1097 /*
1098 * Try find a matching rewrite config for this filename.
1099 */
1100 PCSCMCFGENTRY pCfg = NULL;
1101 for (size_t iCfg = 0; iCfg < RT_ELEMENTS(g_aConfigs); iCfg++)
1102 if (RTStrSimplePatternMultiMatch(g_aConfigs[iCfg].pszFilePattern, RTSTR_MAX, pszBasename, cchBasename, NULL))
1103 {
1104 pCfg = &g_aConfigs[iCfg];
1105 break;
1106 }
1107 if (!pCfg)
1108 {
1109 ScmVerbose(NULL, 4, "skipping '%s': no rewriters configured\n", pszFilename);
1110 return VINF_SUCCESS;
1111 }
1112 ScmVerbose(pState, 4, "matched \"%s\"\n", pCfg->pszFilePattern);
1113
1114 /*
1115 * Create an input stream from the file and check that it's text.
1116 */
1117 SCMSTREAM Stream1;
1118 int rc = ScmStreamInitForReading(&Stream1, pszFilename);
1119 if (RT_FAILURE(rc))
1120 {
1121 RTMsgError("Failed to read '%s': %Rrc\n", pszFilename, rc);
1122 return rc;
1123 }
1124 if (ScmStreamIsText(&Stream1))
1125 {
1126 ScmVerbose(pState, 3, NULL);
1127
1128 /*
1129 * Gather SCM and editor settings from the stream.
1130 */
1131 rc = scmSettingsBaseLoadFromDocument(pBaseSettings, &Stream1);
1132 if (RT_SUCCESS(rc))
1133 {
1134 ScmStreamRewindForReading(&Stream1);
1135
1136 /*
1137 * Create two more streams for output and push the text thru all the
1138 * rewriters, switching the two streams around when something is
1139 * actually rewritten. Stream1 remains unchanged.
1140 */
1141 SCMSTREAM Stream2;
1142 rc = ScmStreamInitForWriting(&Stream2, &Stream1);
1143 if (RT_SUCCESS(rc))
1144 {
1145 SCMSTREAM Stream3;
1146 rc = ScmStreamInitForWriting(&Stream3, &Stream1);
1147 if (RT_SUCCESS(rc))
1148 {
1149 bool fModified = false;
1150 PSCMSTREAM pIn = &Stream1;
1151 PSCMSTREAM pOut = &Stream2;
1152 for (size_t iRw = 0; iRw < pCfg->cRewriters; iRw++)
1153 {
1154 bool fRc = pCfg->papfnRewriter[iRw](pState, pIn, pOut, pBaseSettings);
1155 if (fRc)
1156 {
1157 PSCMSTREAM pTmp = pOut;
1158 pOut = pIn == &Stream1 ? &Stream3 : pIn;
1159 pIn = pTmp;
1160 fModified = true;
1161 }
1162 ScmStreamRewindForReading(pIn);
1163 ScmStreamRewindForWriting(pOut);
1164 }
1165
1166 rc = ScmStreamGetStatus(&Stream1);
1167 if (RT_SUCCESS(rc))
1168 rc = ScmStreamGetStatus(&Stream2);
1169 if (RT_SUCCESS(rc))
1170 rc = ScmStreamGetStatus(&Stream3);
1171 if (RT_SUCCESS(rc))
1172 {
1173 /*
1174 * If rewritten, write it back to disk.
1175 */
1176 if (fModified)
1177 {
1178 if (!g_fDryRun)
1179 {
1180 ScmVerbose(pState, 1, "writing modified file to \"%s%s\"\n", pszFilename, g_pszChangedSuff);
1181 rc = ScmStreamWriteToFile(pIn, "%s%s", pszFilename, g_pszChangedSuff);
1182 if (RT_FAILURE(rc))
1183 RTMsgError("Error writing '%s%s': %Rrc\n", pszFilename, g_pszChangedSuff, rc);
1184 }
1185 else
1186 {
1187 ScmVerbose(pState, 1, NULL);
1188 ScmDiffStreams(pszFilename, &Stream1, pIn, g_fDiffIgnoreEol, g_fDiffIgnoreLeadingWS,
1189 g_fDiffIgnoreTrailingWS, g_fDiffSpecialChars, pBaseSettings->cchTab, g_pStdOut);
1190 ScmVerbose(pState, 2, "would have modified the file \"%s%s\"\n", pszFilename, g_pszChangedSuff);
1191 }
1192 }
1193
1194 /*
1195 * If pending SVN property changes, apply them.
1196 */
1197 if (pState->cSvnPropChanges && RT_SUCCESS(rc))
1198 {
1199 if (!g_fDryRun)
1200 {
1201 rc = ScmSvnApplyChanges(pState);
1202 if (RT_FAILURE(rc))
1203 RTMsgError("%s: failed to apply SVN property changes (%Rrc)\n", pszFilename, rc);
1204 }
1205 else
1206 ScmSvnDisplayChanges(pState);
1207 }
1208
1209 if (!fModified && !pState->cSvnPropChanges)
1210 ScmVerbose(pState, 3, "no change\n", pszFilename);
1211 }
1212 else
1213 RTMsgError("%s: stream error %Rrc\n", pszFilename, rc);
1214 ScmStreamDelete(&Stream3);
1215 }
1216 else
1217 RTMsgError("Failed to init stream for writing: %Rrc\n", rc);
1218 ScmStreamDelete(&Stream2);
1219 }
1220 else
1221 RTMsgError("Failed to init stream for writing: %Rrc\n", rc);
1222 }
1223 else
1224 RTMsgError("scmSettingsBaseLoadFromDocument: %Rrc\n", rc);
1225 }
1226 else
1227 ScmVerbose(pState, 4, "not text file: \"%s\"\n", pszFilename);
1228 ScmStreamDelete(&Stream1);
1229
1230 return rc;
1231}
1232
1233/**
1234 * Processes a file.
1235 *
1236 * This is just a wrapper for scmProcessFileInner for avoid wasting stack in the
1237 * directory recursion method.
1238 *
1239 * @returns IPRT status code.
1240 * @param pszFilename The file name.
1241 * @param pszBasename The base name (pointer within @a pszFilename).
1242 * @param cchBasename The length of the base name. (For passing to
1243 * RTStrSimplePatternMultiMatch.)
1244 * @param pSettingsStack The settings stack (pointer to the top element).
1245 */
1246static int scmProcessFile(const char *pszFilename, const char *pszBasename, size_t cchBasename,
1247 PSCMSETTINGS pSettingsStack)
1248{
1249 SCMSETTINGSBASE Base;
1250 int rc = scmSettingsStackMakeFileBase(pSettingsStack, pszFilename, pszBasename, cchBasename, &Base);
1251 if (RT_SUCCESS(rc))
1252 {
1253 SCMRWSTATE State;
1254 State.fFirst = false;
1255 State.pszFilename = pszFilename;
1256 State.cSvnPropChanges = 0;
1257 State.paSvnPropChanges = NULL;
1258
1259 rc = scmProcessFileInner(&State, pszFilename, pszBasename, cchBasename, &Base);
1260
1261 size_t i = State.cSvnPropChanges;
1262 while (i-- > 0)
1263 {
1264 RTStrFree(State.paSvnPropChanges[i].pszName);
1265 RTStrFree(State.paSvnPropChanges[i].pszValue);
1266 }
1267 RTMemFree(State.paSvnPropChanges);
1268
1269 scmSettingsBaseDelete(&Base);
1270 }
1271 return rc;
1272}
1273
1274
1275/**
1276 * Tries to correct RTDIRENTRY_UNKNOWN.
1277 *
1278 * @returns Corrected type.
1279 * @param pszPath The path to the object in question.
1280 */
1281static RTDIRENTRYTYPE scmFigureUnknownType(const char *pszPath)
1282{
1283 RTFSOBJINFO Info;
1284 int rc = RTPathQueryInfo(pszPath, &Info, RTFSOBJATTRADD_NOTHING);
1285 if (RT_FAILURE(rc))
1286 return RTDIRENTRYTYPE_UNKNOWN;
1287 if (RTFS_IS_DIRECTORY(Info.Attr.fMode))
1288 return RTDIRENTRYTYPE_DIRECTORY;
1289 if (RTFS_IS_FILE(Info.Attr.fMode))
1290 return RTDIRENTRYTYPE_FILE;
1291 return RTDIRENTRYTYPE_UNKNOWN;
1292}
1293
1294/**
1295 * Recurse into a sub-directory and process all the files and directories.
1296 *
1297 * @returns IPRT status code.
1298 * @param pszBuf Path buffer containing the directory path on
1299 * entry. This ends with a dot. This is passed
1300 * along when recursing in order to save stack space
1301 * and avoid needless copying.
1302 * @param cchDir Length of our path in pszbuf.
1303 * @param pEntry Directory entry buffer. This is also passed
1304 * along when recursing to save stack space.
1305 * @param pSettingsStack The settings stack (pointer to the top element).
1306 * @param iRecursion The recursion depth. This is used to restrict
1307 * the recursions.
1308 */
1309static int scmProcessDirTreeRecursion(char *pszBuf, size_t cchDir, PRTDIRENTRY pEntry,
1310 PSCMSETTINGS pSettingsStack, unsigned iRecursion)
1311{
1312 int rc;
1313 Assert(cchDir > 1 && pszBuf[cchDir - 1] == '.');
1314
1315 /*
1316 * Make sure we stop somewhere.
1317 */
1318 if (iRecursion > 128)
1319 {
1320 RTMsgError("recursion too deep: %d\n", iRecursion);
1321 return VINF_SUCCESS; /* ignore */
1322 }
1323
1324 /*
1325 * Check if it's excluded by --only-svn-dir.
1326 */
1327 if (pSettingsStack->Base.fOnlySvnDirs)
1328 {
1329 if (!ScmSvnIsDirInWorkingCopy(pszBuf))
1330 return VINF_SUCCESS;
1331 }
1332
1333 /*
1334 * Try open and read the directory.
1335 */
1336 PRTDIR pDir;
1337 rc = RTDirOpenFiltered(&pDir, pszBuf, RTDIRFILTER_NONE, 0);
1338 if (RT_FAILURE(rc))
1339 {
1340 RTMsgError("Failed to enumerate directory '%s': %Rrc", pszBuf, rc);
1341 return rc;
1342 }
1343 for (;;)
1344 {
1345 /* Read the next entry. */
1346 rc = RTDirRead(pDir, pEntry, NULL);
1347 if (RT_FAILURE(rc))
1348 {
1349 if (rc == VERR_NO_MORE_FILES)
1350 rc = VINF_SUCCESS;
1351 else
1352 RTMsgError("RTDirRead -> %Rrc\n", rc);
1353 break;
1354 }
1355
1356 /* Skip '.' and '..'. */
1357 if ( pEntry->szName[0] == '.'
1358 && ( pEntry->cbName == 1
1359 || ( pEntry->cbName == 2
1360 && pEntry->szName[1] == '.')))
1361 continue;
1362
1363 /* Enter it into the buffer so we've got a full name to work
1364 with when needed. */
1365 if (pEntry->cbName + cchDir >= RTPATH_MAX)
1366 {
1367 RTMsgError("Skipping too long entry: %s", pEntry->szName);
1368 continue;
1369 }
1370 memcpy(&pszBuf[cchDir - 1], pEntry->szName, pEntry->cbName + 1);
1371
1372 /* Figure the type. */
1373 RTDIRENTRYTYPE enmType = pEntry->enmType;
1374 if (enmType == RTDIRENTRYTYPE_UNKNOWN)
1375 enmType = scmFigureUnknownType(pszBuf);
1376
1377 /* Process the file or directory, skip the rest. */
1378 if (enmType == RTDIRENTRYTYPE_FILE)
1379 rc = scmProcessFile(pszBuf, pEntry->szName, pEntry->cbName, pSettingsStack);
1380 else if (enmType == RTDIRENTRYTYPE_DIRECTORY)
1381 {
1382 /* Append the dot for the benefit of the pattern matching. */
1383 if (pEntry->cbName + cchDir + 5 >= RTPATH_MAX)
1384 {
1385 RTMsgError("Skipping too deep dir entry: %s", pEntry->szName);
1386 continue;
1387 }
1388 memcpy(&pszBuf[cchDir - 1 + pEntry->cbName], "/.", sizeof("/."));
1389 size_t cchSubDir = cchDir - 1 + pEntry->cbName + sizeof("/.") - 1;
1390
1391 if ( !pSettingsStack->Base.pszFilterOutDirs
1392 || !*pSettingsStack->Base.pszFilterOutDirs
1393 || ( !RTStrSimplePatternMultiMatch(pSettingsStack->Base.pszFilterOutDirs, RTSTR_MAX,
1394 pEntry->szName, pEntry->cbName, NULL)
1395 && !RTStrSimplePatternMultiMatch(pSettingsStack->Base.pszFilterOutDirs, RTSTR_MAX,
1396 pszBuf, cchSubDir, NULL)
1397 )
1398 )
1399 {
1400 rc = scmSettingsStackPushDir(&pSettingsStack, pszBuf);
1401 if (RT_SUCCESS(rc))
1402 {
1403 rc = scmProcessDirTreeRecursion(pszBuf, cchSubDir, pEntry, pSettingsStack, iRecursion + 1);
1404 scmSettingsStackPopAndDestroy(&pSettingsStack);
1405 }
1406 }
1407 }
1408 if (RT_FAILURE(rc))
1409 break;
1410 }
1411 RTDirClose(pDir);
1412 return rc;
1413
1414}
1415
1416/**
1417 * Process a directory tree.
1418 *
1419 * @returns IPRT status code.
1420 * @param pszDir The directory to start with. This is pointer to
1421 * a RTPATH_MAX sized buffer.
1422 */
1423static int scmProcessDirTree(char *pszDir, PSCMSETTINGS pSettingsStack)
1424{
1425 /*
1426 * Setup the recursion.
1427 */
1428 int rc = RTPathAppend(pszDir, RTPATH_MAX, ".");
1429 if (RT_SUCCESS(rc))
1430 {
1431 RTDIRENTRY Entry;
1432 rc = scmProcessDirTreeRecursion(pszDir, strlen(pszDir), &Entry, pSettingsStack, 0);
1433 }
1434 else
1435 RTMsgError("RTPathAppend: %Rrc\n", rc);
1436 return rc;
1437}
1438
1439
1440/**
1441 * Processes a file or directory specified as an command line argument.
1442 *
1443 * @returns IPRT status code
1444 * @param pszSomething What we found in the command line arguments.
1445 * @param pSettingsStack The settings stack (pointer to the top element).
1446 */
1447static int scmProcessSomething(const char *pszSomething, PSCMSETTINGS pSettingsStack)
1448{
1449 char szBuf[RTPATH_MAX];
1450 int rc = RTPathAbs(pszSomething, szBuf, sizeof(szBuf));
1451 if (RT_SUCCESS(rc))
1452 {
1453 RTPathChangeToUnixSlashes(szBuf, false /*fForce*/);
1454
1455 PSCMSETTINGS pSettings;
1456 rc = scmSettingsCreateForPath(&pSettings, &pSettingsStack->Base, szBuf);
1457 if (RT_SUCCESS(rc))
1458 {
1459 scmSettingsStackPush(&pSettingsStack, pSettings);
1460
1461 if (RTFileExists(szBuf))
1462 {
1463 const char *pszBasename = RTPathFilename(szBuf);
1464 if (pszBasename)
1465 {
1466 size_t cchBasename = strlen(pszBasename);
1467 rc = scmProcessFile(szBuf, pszBasename, cchBasename, pSettingsStack);
1468 }
1469 else
1470 {
1471 RTMsgError("RTPathFilename: NULL\n");
1472 rc = VERR_IS_A_DIRECTORY;
1473 }
1474 }
1475 else
1476 rc = scmProcessDirTree(szBuf, pSettingsStack);
1477
1478 PSCMSETTINGS pPopped = scmSettingsStackPop(&pSettingsStack);
1479 Assert(pPopped == pSettings); RT_NOREF_PV(pPopped);
1480 scmSettingsDestroy(pSettings);
1481 }
1482 else
1483 RTMsgError("scmSettingsInitStack: %Rrc\n", rc);
1484 }
1485 else
1486 RTMsgError("RTPathAbs: %Rrc\n", rc);
1487 return rc;
1488}
1489
1490static void usage(PCRTGETOPTDEF paOpts, size_t cOpts)
1491{
1492 RTPrintf("VirtualBox Source Code Massager\n"
1493 "\n"
1494 "Usage: %s [options] <files & dirs>\n"
1495 "\n"
1496 "Options:\n", g_szProgName);
1497 for (size_t i = 0; i < cOpts; i++)
1498 {
1499 bool fAdvanceTwo = false;
1500 if ((paOpts[i].fFlags & RTGETOPT_REQ_MASK) == RTGETOPT_REQ_NOTHING)
1501 {
1502 fAdvanceTwo = i + 1 < cOpts
1503 && ( strstr(paOpts[i+1].pszLong, "-no-") != NULL
1504 || strstr(paOpts[i+1].pszLong, "-not-") != NULL
1505 || strstr(paOpts[i+1].pszLong, "-dont-") != NULL
1506 || (paOpts[i].iShort == 'q' && paOpts[i+1].iShort == 'v')
1507 || (paOpts[i].iShort == 'd' && paOpts[i+1].iShort == 'D')
1508 );
1509 if (fAdvanceTwo)
1510 RTPrintf(" %s, %s\n", paOpts[i].pszLong, paOpts[i + 1].pszLong);
1511 else
1512 RTPrintf(" %s\n", paOpts[i].pszLong);
1513 }
1514 else if ((paOpts[i].fFlags & RTGETOPT_REQ_MASK) == RTGETOPT_REQ_STRING)
1515 RTPrintf(" %s string\n", paOpts[i].pszLong);
1516 else
1517 RTPrintf(" %s value\n", paOpts[i].pszLong);
1518 switch (paOpts[i].iShort)
1519 {
1520 case 'd':
1521 case 'D': RTPrintf(" Default: --dry-run\n"); break;
1522 case 'f': RTPrintf(" Default: none\n"); break;
1523 case 'q':
1524 case 'v': RTPrintf(" Default: -vv\n"); break;
1525
1526 case SCMOPT_DIFF_IGNORE_EOL: RTPrintf(" Default: false\n"); break;
1527 case SCMOPT_DIFF_IGNORE_SPACE: RTPrintf(" Default: false\n"); break;
1528 case SCMOPT_DIFF_IGNORE_LEADING_SPACE: RTPrintf(" Default: false\n"); break;
1529 case SCMOPT_DIFF_IGNORE_TRAILING_SPACE: RTPrintf(" Default: false\n"); break;
1530 case SCMOPT_DIFF_SPECIAL_CHARS: RTPrintf(" Default: true\n"); break;
1531
1532 case SCMOPT_CONVERT_EOL: RTPrintf(" Default: %RTbool\n", g_Defaults.fConvertEol); break;
1533 case SCMOPT_CONVERT_TABS: RTPrintf(" Default: %RTbool\n", g_Defaults.fConvertTabs); break;
1534 case SCMOPT_FORCE_FINAL_EOL: RTPrintf(" Default: %RTbool\n", g_Defaults.fForceFinalEol); break;
1535 case SCMOPT_FORCE_TRAILING_LINE: RTPrintf(" Default: %RTbool\n", g_Defaults.fForceTrailingLine); break;
1536 case SCMOPT_STRIP_TRAILING_BLANKS: RTPrintf(" Default: %RTbool\n", g_Defaults.fStripTrailingBlanks); break;
1537 case SCMOPT_STRIP_TRAILING_LINES: RTPrintf(" Default: %RTbool\n", g_Defaults.fStripTrailingLines); break;
1538 case SCMOPT_FIX_FLOWER_BOX_MARKERS: RTPrintf(" Default: %RTbool\n", g_Defaults.fFixFlowerBoxMarkers); break;
1539 case SCMOPT_MIN_BLANK_LINES_BEFORE_FLOWER_BOX_MARKERS: RTPrintf(" Default: %u\n", g_Defaults.cMinBlankLinesBeforeFlowerBoxMakers); break;
1540 case SCMOPT_ONLY_SVN_DIRS: RTPrintf(" Default: %RTbool\n", g_Defaults.fOnlySvnDirs); break;
1541 case SCMOPT_ONLY_SVN_FILES: RTPrintf(" Default: %RTbool\n", g_Defaults.fOnlySvnFiles); break;
1542 case SCMOPT_SET_SVN_EOL: RTPrintf(" Default: %RTbool\n", g_Defaults.fSetSvnEol); break;
1543 case SCMOPT_SET_SVN_EXECUTABLE: RTPrintf(" Default: %RTbool\n", g_Defaults.fSetSvnExecutable); break;
1544 case SCMOPT_SET_SVN_KEYWORDS: RTPrintf(" Default: %RTbool\n", g_Defaults.fSetSvnKeywords); break;
1545 case SCMOPT_TAB_SIZE: RTPrintf(" Default: %u\n", g_Defaults.cchTab); break;
1546 case SCMOPT_WIDTH: RTPrintf(" Default: %u\n", g_Defaults.cchWidth); break;
1547 case SCMOPT_FILTER_OUT_DIRS: RTPrintf(" Default: %s\n", g_Defaults.pszFilterOutDirs); break;
1548 case SCMOPT_FILTER_FILES: RTPrintf(" Default: %s\n", g_Defaults.pszFilterFiles); break;
1549 case SCMOPT_FILTER_OUT_FILES: RTPrintf(" Default: %s\n", g_Defaults.pszFilterOutFiles); break;
1550 default: AssertMsgFailed(("i=%d %d %s\n", i, paOpts[i].iShort, paOpts[i].pszLong));
1551 }
1552 i += fAdvanceTwo;
1553 }
1554
1555}
1556
1557int main(int argc, char **argv)
1558{
1559 int rc = RTR3InitExe(argc, &argv, 0);
1560 if (RT_FAILURE(rc))
1561 return 1;
1562
1563 /*
1564 * Init the settings.
1565 */
1566 PSCMSETTINGS pSettings;
1567 rc = scmSettingsCreate(&pSettings, &g_Defaults);
1568 if (RT_FAILURE(rc))
1569 {
1570 RTMsgError("scmSettingsCreate: %Rrc\n", rc);
1571 return 1;
1572 }
1573
1574 /*
1575 * Parse arguments and process input in order (because this is the only
1576 * thing that works at the moment).
1577 */
1578 static RTGETOPTDEF s_aOpts[14 + RT_ELEMENTS(g_aScmOpts)] =
1579 {
1580 { "--dry-run", 'd', RTGETOPT_REQ_NOTHING },
1581 { "--real-run", 'D', RTGETOPT_REQ_NOTHING },
1582 { "--file-filter", 'f', RTGETOPT_REQ_STRING },
1583 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
1584 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
1585 { "--diff-ignore-eol", SCMOPT_DIFF_IGNORE_EOL, RTGETOPT_REQ_NOTHING },
1586 { "--diff-no-ignore-eol", SCMOPT_DIFF_NO_IGNORE_EOL, RTGETOPT_REQ_NOTHING },
1587 { "--diff-ignore-space", SCMOPT_DIFF_IGNORE_SPACE, RTGETOPT_REQ_NOTHING },
1588 { "--diff-no-ignore-space", SCMOPT_DIFF_NO_IGNORE_SPACE, RTGETOPT_REQ_NOTHING },
1589 { "--diff-ignore-leading-space", SCMOPT_DIFF_IGNORE_LEADING_SPACE, RTGETOPT_REQ_NOTHING },
1590 { "--diff-no-ignore-leading-space", SCMOPT_DIFF_NO_IGNORE_LEADING_SPACE, RTGETOPT_REQ_NOTHING },
1591 { "--diff-ignore-trailing-space", SCMOPT_DIFF_IGNORE_TRAILING_SPACE, RTGETOPT_REQ_NOTHING },
1592 { "--diff-no-ignore-trailing-space", SCMOPT_DIFF_NO_IGNORE_TRAILING_SPACE, RTGETOPT_REQ_NOTHING },
1593 { "--diff-special-chars", SCMOPT_DIFF_SPECIAL_CHARS, RTGETOPT_REQ_NOTHING },
1594 { "--diff-no-special-chars", SCMOPT_DIFF_NO_SPECIAL_CHARS, RTGETOPT_REQ_NOTHING },
1595 };
1596 memcpy(&s_aOpts[RT_ELEMENTS(s_aOpts) - RT_ELEMENTS(g_aScmOpts)], &g_aScmOpts[0], sizeof(g_aScmOpts));
1597
1598 RTGETOPTUNION ValueUnion;
1599 RTGETOPTSTATE GetOptState;
1600 rc = RTGetOptInit(&GetOptState, argc, argv, &s_aOpts[0], RT_ELEMENTS(s_aOpts), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1601 AssertReleaseRCReturn(rc, 1);
1602 size_t cProcessed = 0;
1603
1604 while ((rc = RTGetOpt(&GetOptState, &ValueUnion)) != 0)
1605 {
1606 switch (rc)
1607 {
1608 case 'd':
1609 g_fDryRun = true;
1610 break;
1611 case 'D':
1612 g_fDryRun = false;
1613 break;
1614
1615 case 'f':
1616 g_pszFileFilter = ValueUnion.psz;
1617 break;
1618
1619 case 'h':
1620 usage(s_aOpts, RT_ELEMENTS(s_aOpts));
1621 return 1;
1622
1623 case 'q':
1624 g_iVerbosity = 0;
1625 break;
1626
1627 case 'v':
1628 g_iVerbosity++;
1629 break;
1630
1631 case 'V':
1632 {
1633 /* The following is assuming that svn does it's job here. */
1634 static const char s_szRev[] = "$Revision: 62884 $";
1635 const char *psz = RTStrStripL(strchr(s_szRev, ' '));
1636 RTPrintf("r%.*s\n", strchr(psz, ' ') - psz, psz);
1637 return 0;
1638 }
1639
1640 case SCMOPT_DIFF_IGNORE_EOL:
1641 g_fDiffIgnoreEol = true;
1642 break;
1643 case SCMOPT_DIFF_NO_IGNORE_EOL:
1644 g_fDiffIgnoreEol = false;
1645 break;
1646
1647 case SCMOPT_DIFF_IGNORE_SPACE:
1648 g_fDiffIgnoreTrailingWS = g_fDiffIgnoreLeadingWS = true;
1649 break;
1650 case SCMOPT_DIFF_NO_IGNORE_SPACE:
1651 g_fDiffIgnoreTrailingWS = g_fDiffIgnoreLeadingWS = false;
1652 break;
1653
1654 case SCMOPT_DIFF_IGNORE_LEADING_SPACE:
1655 g_fDiffIgnoreLeadingWS = true;
1656 break;
1657 case SCMOPT_DIFF_NO_IGNORE_LEADING_SPACE:
1658 g_fDiffIgnoreLeadingWS = false;
1659 break;
1660
1661 case SCMOPT_DIFF_IGNORE_TRAILING_SPACE:
1662 g_fDiffIgnoreTrailingWS = true;
1663 break;
1664 case SCMOPT_DIFF_NO_IGNORE_TRAILING_SPACE:
1665 g_fDiffIgnoreTrailingWS = false;
1666 break;
1667
1668 case SCMOPT_DIFF_SPECIAL_CHARS:
1669 g_fDiffSpecialChars = true;
1670 break;
1671 case SCMOPT_DIFF_NO_SPECIAL_CHARS:
1672 g_fDiffSpecialChars = false;
1673 break;
1674
1675 case VINF_GETOPT_NOT_OPTION:
1676 {
1677 if (!g_fDryRun)
1678 {
1679 if (!cProcessed)
1680 {
1681 RTPrintf("%s: Warning! This program will make changes to your source files and\n"
1682 "%s: there is a slight risk that bugs or a full disk may cause\n"
1683 "%s: LOSS OF DATA. So, please make sure you have checked in\n"
1684 "%s: all your changes already. If you didn't, then don't blame\n"
1685 "%s: anyone for not warning you!\n"
1686 "%s:\n"
1687 "%s: Press any key to continue...\n",
1688 g_szProgName, g_szProgName, g_szProgName, g_szProgName, g_szProgName,
1689 g_szProgName, g_szProgName);
1690 RTStrmGetCh(g_pStdIn);
1691 }
1692 cProcessed++;
1693 }
1694 rc = scmProcessSomething(ValueUnion.psz, pSettings);
1695 if (RT_FAILURE(rc))
1696 return rc;
1697 break;
1698 }
1699
1700 default:
1701 {
1702 int rc2 = scmSettingsBaseHandleOpt(&pSettings->Base, rc, &ValueUnion);
1703 if (RT_SUCCESS(rc2))
1704 break;
1705 if (rc2 != VERR_GETOPT_UNKNOWN_OPTION)
1706 return 2;
1707 return RTGetOptPrintError(rc, &ValueUnion);
1708 }
1709 }
1710 }
1711
1712 scmSettingsDestroy(pSettings);
1713 return 0;
1714}
1715
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