VirtualBox

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

Last change on this file since 62881 was 62854, checked in by vboxsync, 9 years ago

scm: warnings

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 62.6 KB
Line 
1/* $Id: scm.cpp 62854 2016-08-01 22:36:01Z 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/**
790 * Parse the specified settings file creating a new settings struct from it.
791 *
792 * @returns IPRT status code
793 * @param ppSettings Where to return the new settings.
794 * @param pszFilename The file to parse.
795 * @param pSettingsBase The base settings we inherit from.
796 */
797static int scmSettingsCreateFromFile(PSCMSETTINGS *ppSettings, const char *pszFilename, PCSCMSETTINGSBASE pSettingsBase)
798{
799 PSCMSETTINGS pSettings;
800 int rc = scmSettingsCreate(&pSettings, pSettingsBase);
801 if (RT_SUCCESS(rc))
802 {
803 rc = scmSettingsLoadFile(pSettings, pszFilename);
804 if (RT_SUCCESS(rc))
805 {
806 *ppSettings = pSettings;
807 return VINF_SUCCESS;
808 }
809
810 scmSettingsDestroy(pSettings);
811 }
812 *ppSettings = NULL;
813 return rc;
814}
815
816
817/**
818 * Create an initial settings structure when starting processing a new file or
819 * directory.
820 *
821 * This will look for .scm-settings files from the root and down to the
822 * specified directory, combining them into the returned settings structure.
823 *
824 * @returns IPRT status code.
825 * @param ppSettings Where to return the pointer to the top stack
826 * object.
827 * @param pBaseSettings The base settings we inherit from (globals
828 * typically).
829 * @param pszPath The absolute path to the new directory or file.
830 */
831static int scmSettingsCreateForPath(PSCMSETTINGS *ppSettings, PCSCMSETTINGSBASE pBaseSettings, const char *pszPath)
832{
833 *ppSettings = NULL; /* try shut up gcc. */
834
835 /*
836 * We'll be working with a stack copy of the path.
837 */
838 char szFile[RTPATH_MAX];
839 size_t cchDir = strlen(pszPath);
840 if (cchDir >= sizeof(szFile) - sizeof(SCM_SETTINGS_FILENAME))
841 return VERR_FILENAME_TOO_LONG;
842
843 /*
844 * Create the bottom-most settings.
845 */
846 PSCMSETTINGS pSettings;
847 int rc = scmSettingsCreate(&pSettings, pBaseSettings);
848 if (RT_FAILURE(rc))
849 return rc;
850
851 /*
852 * Enumerate the path components from the root and down. Load any setting
853 * files we find.
854 */
855 size_t cComponents = RTPathCountComponents(pszPath);
856 for (size_t i = 1; i <= cComponents; i++)
857 {
858 rc = RTPathCopyComponents(szFile, sizeof(szFile), pszPath, i);
859 if (RT_SUCCESS(rc))
860 rc = RTPathAppend(szFile, sizeof(szFile), SCM_SETTINGS_FILENAME);
861 if (RT_FAILURE(rc))
862 break;
863 if (RTFileExists(szFile))
864 {
865 rc = scmSettingsLoadFile(pSettings, szFile);
866 if (RT_FAILURE(rc))
867 break;
868 }
869 }
870
871 if (RT_SUCCESS(rc))
872 *ppSettings = pSettings;
873 else
874 scmSettingsDestroy(pSettings);
875 return rc;
876}
877
878/**
879 * Pushes a new settings set onto the stack.
880 *
881 * @param ppSettingsStack The pointer to the pointer to the top stack
882 * element. This will be used as input and output.
883 * @param pSettings The settings to push onto the stack.
884 */
885static void scmSettingsStackPush(PSCMSETTINGS *ppSettingsStack, PSCMSETTINGS pSettings)
886{
887 PSCMSETTINGS pOld = *ppSettingsStack;
888 pSettings->pDown = pOld;
889 pSettings->pUp = NULL;
890 if (pOld)
891 pOld->pUp = pSettings;
892 *ppSettingsStack = pSettings;
893}
894
895/**
896 * Pushes the settings of the specified directory onto the stack.
897 *
898 * We will load any .scm-settings in the directory. A stack entry is added even
899 * if no settings file was found.
900 *
901 * @returns IPRT status code.
902 * @param ppSettingsStack The pointer to the pointer to the top stack
903 * element. This will be used as input and output.
904 * @param pszDir The directory to do this for.
905 */
906static int scmSettingsStackPushDir(PSCMSETTINGS *ppSettingsStack, const char *pszDir)
907{
908 char szFile[RTPATH_MAX];
909 int rc = RTPathJoin(szFile, sizeof(szFile), pszDir, SCM_SETTINGS_FILENAME);
910 if (RT_SUCCESS(rc))
911 {
912 PSCMSETTINGS pSettings;
913 rc = scmSettingsCreate(&pSettings, &(*ppSettingsStack)->Base);
914 if (RT_SUCCESS(rc))
915 {
916 if (RTFileExists(szFile))
917 rc = scmSettingsLoadFile(pSettings, szFile);
918 if (RT_SUCCESS(rc))
919 {
920 scmSettingsStackPush(ppSettingsStack, pSettings);
921 return VINF_SUCCESS;
922 }
923
924 scmSettingsDestroy(pSettings);
925 }
926 }
927 return rc;
928}
929
930
931/**
932 * Pops a settings set off the stack.
933 *
934 * @returns The popped setttings.
935 * @param ppSettingsStack The pointer to the pointer to the top stack
936 * element. This will be used as input and output.
937 */
938static PSCMSETTINGS scmSettingsStackPop(PSCMSETTINGS *ppSettingsStack)
939{
940 PSCMSETTINGS pRet = *ppSettingsStack;
941 PSCMSETTINGS pNew = pRet ? pRet->pDown : NULL;
942 *ppSettingsStack = pNew;
943 if (pNew)
944 pNew->pUp = NULL;
945 if (pRet)
946 {
947 pRet->pUp = NULL;
948 pRet->pDown = NULL;
949 }
950 return pRet;
951}
952
953/**
954 * Pops and destroys the top entry of the stack.
955 *
956 * @param ppSettingsStack The pointer to the pointer to the top stack
957 * element. This will be used as input and output.
958 */
959static void scmSettingsStackPopAndDestroy(PSCMSETTINGS *ppSettingsStack)
960{
961 scmSettingsDestroy(scmSettingsStackPop(ppSettingsStack));
962}
963
964/**
965 * Constructs the base settings for the specified file name.
966 *
967 * @returns IPRT status code.
968 * @param pSettingsStack The top element on the settings stack.
969 * @param pszFilename The file name.
970 * @param pszBasename The base name (pointer within @a pszFilename).
971 * @param cchBasename The length of the base name. (For passing to
972 * RTStrSimplePatternMultiMatch.)
973 * @param pBase Base settings to initialize.
974 */
975static int scmSettingsStackMakeFileBase(PCSCMSETTINGS pSettingsStack, const char *pszFilename,
976 const char *pszBasename, size_t cchBasename, PSCMSETTINGSBASE pBase)
977{
978 int rc = scmSettingsBaseInitAndCopy(pBase, &pSettingsStack->Base);
979 if (RT_SUCCESS(rc))
980 {
981 /* find the bottom entry in the stack. */
982 PCSCMSETTINGS pCur = pSettingsStack;
983 while (pCur->pDown)
984 pCur = pCur->pDown;
985
986 /* Work our way up thru the stack and look for matching pairs. */
987 while (pCur)
988 {
989 size_t const cPairs = pCur->cPairs;
990 if (cPairs)
991 {
992 for (size_t i = 0; i < cPairs; i++)
993 if ( RTStrSimplePatternMultiMatch(pCur->paPairs[i].pszPattern, RTSTR_MAX,
994 pszBasename, cchBasename, NULL)
995 || RTStrSimplePatternMultiMatch(pCur->paPairs[i].pszPattern, RTSTR_MAX,
996 pszFilename, RTSTR_MAX, NULL))
997 {
998 rc = scmSettingsBaseParseString(pBase, pCur->paPairs[i].pszOptions);
999 if (RT_FAILURE(rc))
1000 break;
1001 }
1002 if (RT_FAILURE(rc))
1003 break;
1004 }
1005
1006 /* advance */
1007 pCur = pCur->pUp;
1008 }
1009 }
1010 if (RT_FAILURE(rc))
1011 scmSettingsBaseDelete(pBase);
1012 return rc;
1013}
1014
1015
1016/* -=-=-=-=-=- misc -=-=-=-=-=- */
1017
1018
1019/**
1020 * Prints a verbose message if the level is high enough.
1021 *
1022 * @param pState The rewrite state. Optional.
1023 * @param iLevel The required verbosity level.
1024 * @param pszFormat The message format string. Can be NULL if we
1025 * only want to trigger the per file message.
1026 * @param ... Format arguments.
1027 */
1028void ScmVerbose(PSCMRWSTATE pState, int iLevel, const char *pszFormat, ...)
1029{
1030 if (iLevel <= g_iVerbosity)
1031 {
1032 if (pState && !pState->fFirst)
1033 {
1034 RTPrintf("%s: info: --= Rewriting '%s' =--\n", g_szProgName, pState->pszFilename);
1035 pState->fFirst = true;
1036 }
1037 if (pszFormat)
1038 {
1039 RTPrintf(pState
1040 ? "%s: info: "
1041 : "%s: info: ",
1042 g_szProgName);
1043 va_list va;
1044 va_start(va, pszFormat);
1045 RTPrintfV(pszFormat, va);
1046 va_end(va);
1047 }
1048 }
1049}
1050
1051
1052/* -=-=-=-=-=- file and directory processing -=-=-=-=-=- */
1053
1054
1055/**
1056 * Processes a file.
1057 *
1058 * @returns IPRT status code.
1059 * @param pState The rewriter state.
1060 * @param pszFilename The file name.
1061 * @param pszBasename The base name (pointer within @a pszFilename).
1062 * @param cchBasename The length of the base name. (For passing to
1063 * RTStrSimplePatternMultiMatch.)
1064 * @param pBaseSettings The base settings to use. It's OK to modify
1065 * these.
1066 */
1067static int scmProcessFileInner(PSCMRWSTATE pState, const char *pszFilename, const char *pszBasename, size_t cchBasename,
1068 PSCMSETTINGSBASE pBaseSettings)
1069{
1070 /*
1071 * Do the file level filtering.
1072 */
1073 if ( pBaseSettings->pszFilterFiles
1074 && *pBaseSettings->pszFilterFiles
1075 && !RTStrSimplePatternMultiMatch(pBaseSettings->pszFilterFiles, RTSTR_MAX, pszBasename, cchBasename, NULL))
1076 {
1077 ScmVerbose(NULL, 5, "skipping '%s': file filter mismatch\n", pszFilename);
1078 return VINF_SUCCESS;
1079 }
1080 if ( pBaseSettings->pszFilterOutFiles
1081 && *pBaseSettings->pszFilterOutFiles
1082 && ( RTStrSimplePatternMultiMatch(pBaseSettings->pszFilterOutFiles, RTSTR_MAX, pszBasename, cchBasename, NULL)
1083 || RTStrSimplePatternMultiMatch(pBaseSettings->pszFilterOutFiles, RTSTR_MAX, pszFilename, RTSTR_MAX, NULL)) )
1084 {
1085 ScmVerbose(NULL, 5, "skipping '%s': filterd out\n", pszFilename);
1086 return VINF_SUCCESS;
1087 }
1088 if ( pBaseSettings->fOnlySvnFiles
1089 && !ScmSvnIsInWorkingCopy(pState))
1090 {
1091 ScmVerbose(NULL, 5, "skipping '%s': not in SVN WC\n", pszFilename);
1092 return VINF_SUCCESS;
1093 }
1094
1095 /*
1096 * Try find a matching rewrite config for this filename.
1097 */
1098 PCSCMCFGENTRY pCfg = NULL;
1099 for (size_t iCfg = 0; iCfg < RT_ELEMENTS(g_aConfigs); iCfg++)
1100 if (RTStrSimplePatternMultiMatch(g_aConfigs[iCfg].pszFilePattern, RTSTR_MAX, pszBasename, cchBasename, NULL))
1101 {
1102 pCfg = &g_aConfigs[iCfg];
1103 break;
1104 }
1105 if (!pCfg)
1106 {
1107 ScmVerbose(NULL, 4, "skipping '%s': no rewriters configured\n", pszFilename);
1108 return VINF_SUCCESS;
1109 }
1110 ScmVerbose(pState, 4, "matched \"%s\"\n", pCfg->pszFilePattern);
1111
1112 /*
1113 * Create an input stream from the file and check that it's text.
1114 */
1115 SCMSTREAM Stream1;
1116 int rc = ScmStreamInitForReading(&Stream1, pszFilename);
1117 if (RT_FAILURE(rc))
1118 {
1119 RTMsgError("Failed to read '%s': %Rrc\n", pszFilename, rc);
1120 return rc;
1121 }
1122 if (ScmStreamIsText(&Stream1))
1123 {
1124 ScmVerbose(pState, 3, NULL);
1125
1126 /*
1127 * Gather SCM and editor settings from the stream.
1128 */
1129 rc = scmSettingsBaseLoadFromDocument(pBaseSettings, &Stream1);
1130 if (RT_SUCCESS(rc))
1131 {
1132 ScmStreamRewindForReading(&Stream1);
1133
1134 /*
1135 * Create two more streams for output and push the text thru all the
1136 * rewriters, switching the two streams around when something is
1137 * actually rewritten. Stream1 remains unchanged.
1138 */
1139 SCMSTREAM Stream2;
1140 rc = ScmStreamInitForWriting(&Stream2, &Stream1);
1141 if (RT_SUCCESS(rc))
1142 {
1143 SCMSTREAM Stream3;
1144 rc = ScmStreamInitForWriting(&Stream3, &Stream1);
1145 if (RT_SUCCESS(rc))
1146 {
1147 bool fModified = false;
1148 PSCMSTREAM pIn = &Stream1;
1149 PSCMSTREAM pOut = &Stream2;
1150 for (size_t iRw = 0; iRw < pCfg->cRewriters; iRw++)
1151 {
1152 bool fRc = pCfg->papfnRewriter[iRw](pState, pIn, pOut, pBaseSettings);
1153 if (fRc)
1154 {
1155 PSCMSTREAM pTmp = pOut;
1156 pOut = pIn == &Stream1 ? &Stream3 : pIn;
1157 pIn = pTmp;
1158 fModified = true;
1159 }
1160 ScmStreamRewindForReading(pIn);
1161 ScmStreamRewindForWriting(pOut);
1162 }
1163
1164 rc = ScmStreamGetStatus(&Stream1);
1165 if (RT_SUCCESS(rc))
1166 rc = ScmStreamGetStatus(&Stream2);
1167 if (RT_SUCCESS(rc))
1168 rc = ScmStreamGetStatus(&Stream3);
1169 if (RT_SUCCESS(rc))
1170 {
1171 /*
1172 * If rewritten, write it back to disk.
1173 */
1174 if (fModified)
1175 {
1176 if (!g_fDryRun)
1177 {
1178 ScmVerbose(pState, 1, "writing modified file to \"%s%s\"\n", pszFilename, g_pszChangedSuff);
1179 rc = ScmStreamWriteToFile(pIn, "%s%s", pszFilename, g_pszChangedSuff);
1180 if (RT_FAILURE(rc))
1181 RTMsgError("Error writing '%s%s': %Rrc\n", pszFilename, g_pszChangedSuff, rc);
1182 }
1183 else
1184 {
1185 ScmVerbose(pState, 1, NULL);
1186 ScmDiffStreams(pszFilename, &Stream1, pIn, g_fDiffIgnoreEol, g_fDiffIgnoreLeadingWS,
1187 g_fDiffIgnoreTrailingWS, g_fDiffSpecialChars, pBaseSettings->cchTab, g_pStdOut);
1188 ScmVerbose(pState, 2, "would have modified the file \"%s%s\"\n", pszFilename, g_pszChangedSuff);
1189 }
1190 }
1191
1192 /*
1193 * If pending SVN property changes, apply them.
1194 */
1195 if (pState->cSvnPropChanges && RT_SUCCESS(rc))
1196 {
1197 if (!g_fDryRun)
1198 {
1199 rc = ScmSvnApplyChanges(pState);
1200 if (RT_FAILURE(rc))
1201 RTMsgError("%s: failed to apply SVN property changes (%Rrc)\n", pszFilename, rc);
1202 }
1203 else
1204 ScmSvnDisplayChanges(pState);
1205 }
1206
1207 if (!fModified && !pState->cSvnPropChanges)
1208 ScmVerbose(pState, 3, "no change\n", pszFilename);
1209 }
1210 else
1211 RTMsgError("%s: stream error %Rrc\n", pszFilename, rc);
1212 ScmStreamDelete(&Stream3);
1213 }
1214 else
1215 RTMsgError("Failed to init stream for writing: %Rrc\n", rc);
1216 ScmStreamDelete(&Stream2);
1217 }
1218 else
1219 RTMsgError("Failed to init stream for writing: %Rrc\n", rc);
1220 }
1221 else
1222 RTMsgError("scmSettingsBaseLoadFromDocument: %Rrc\n", rc);
1223 }
1224 else
1225 ScmVerbose(pState, 4, "not text file: \"%s\"\n", pszFilename);
1226 ScmStreamDelete(&Stream1);
1227
1228 return rc;
1229}
1230
1231/**
1232 * Processes a file.
1233 *
1234 * This is just a wrapper for scmProcessFileInner for avoid wasting stack in the
1235 * directory recursion method.
1236 *
1237 * @returns IPRT status code.
1238 * @param pszFilename The file name.
1239 * @param pszBasename The base name (pointer within @a pszFilename).
1240 * @param cchBasename The length of the base name. (For passing to
1241 * RTStrSimplePatternMultiMatch.)
1242 * @param pSettingsStack The settings stack (pointer to the top element).
1243 */
1244static int scmProcessFile(const char *pszFilename, const char *pszBasename, size_t cchBasename,
1245 PSCMSETTINGS pSettingsStack)
1246{
1247 SCMSETTINGSBASE Base;
1248 int rc = scmSettingsStackMakeFileBase(pSettingsStack, pszFilename, pszBasename, cchBasename, &Base);
1249 if (RT_SUCCESS(rc))
1250 {
1251 SCMRWSTATE State;
1252 State.fFirst = false;
1253 State.pszFilename = pszFilename;
1254 State.cSvnPropChanges = 0;
1255 State.paSvnPropChanges = NULL;
1256
1257 rc = scmProcessFileInner(&State, pszFilename, pszBasename, cchBasename, &Base);
1258
1259 size_t i = State.cSvnPropChanges;
1260 while (i-- > 0)
1261 {
1262 RTStrFree(State.paSvnPropChanges[i].pszName);
1263 RTStrFree(State.paSvnPropChanges[i].pszValue);
1264 }
1265 RTMemFree(State.paSvnPropChanges);
1266
1267 scmSettingsBaseDelete(&Base);
1268 }
1269 return rc;
1270}
1271
1272
1273/**
1274 * Tries to correct RTDIRENTRY_UNKNOWN.
1275 *
1276 * @returns Corrected type.
1277 * @param pszPath The path to the object in question.
1278 */
1279static RTDIRENTRYTYPE scmFigureUnknownType(const char *pszPath)
1280{
1281 RTFSOBJINFO Info;
1282 int rc = RTPathQueryInfo(pszPath, &Info, RTFSOBJATTRADD_NOTHING);
1283 if (RT_FAILURE(rc))
1284 return RTDIRENTRYTYPE_UNKNOWN;
1285 if (RTFS_IS_DIRECTORY(Info.Attr.fMode))
1286 return RTDIRENTRYTYPE_DIRECTORY;
1287 if (RTFS_IS_FILE(Info.Attr.fMode))
1288 return RTDIRENTRYTYPE_FILE;
1289 return RTDIRENTRYTYPE_UNKNOWN;
1290}
1291
1292/**
1293 * Recurse into a sub-directory and process all the files and directories.
1294 *
1295 * @returns IPRT status code.
1296 * @param pszBuf Path buffer containing the directory path on
1297 * entry. This ends with a dot. This is passed
1298 * along when recursing in order to save stack space
1299 * and avoid needless copying.
1300 * @param cchDir Length of our path in pszbuf.
1301 * @param pEntry Directory entry buffer. This is also passed
1302 * along when recursing to save stack space.
1303 * @param pSettingsStack The settings stack (pointer to the top element).
1304 * @param iRecursion The recursion depth. This is used to restrict
1305 * the recursions.
1306 */
1307static int scmProcessDirTreeRecursion(char *pszBuf, size_t cchDir, PRTDIRENTRY pEntry,
1308 PSCMSETTINGS pSettingsStack, unsigned iRecursion)
1309{
1310 int rc;
1311 Assert(cchDir > 1 && pszBuf[cchDir - 1] == '.');
1312
1313 /*
1314 * Make sure we stop somewhere.
1315 */
1316 if (iRecursion > 128)
1317 {
1318 RTMsgError("recursion too deep: %d\n", iRecursion);
1319 return VINF_SUCCESS; /* ignore */
1320 }
1321
1322 /*
1323 * Check if it's excluded by --only-svn-dir.
1324 */
1325 if (pSettingsStack->Base.fOnlySvnDirs)
1326 {
1327 if (!ScmSvnIsDirInWorkingCopy(pszBuf))
1328 return VINF_SUCCESS;
1329 }
1330
1331 /*
1332 * Try open and read the directory.
1333 */
1334 PRTDIR pDir;
1335 rc = RTDirOpenFiltered(&pDir, pszBuf, RTDIRFILTER_NONE, 0);
1336 if (RT_FAILURE(rc))
1337 {
1338 RTMsgError("Failed to enumerate directory '%s': %Rrc", pszBuf, rc);
1339 return rc;
1340 }
1341 for (;;)
1342 {
1343 /* Read the next entry. */
1344 rc = RTDirRead(pDir, pEntry, NULL);
1345 if (RT_FAILURE(rc))
1346 {
1347 if (rc == VERR_NO_MORE_FILES)
1348 rc = VINF_SUCCESS;
1349 else
1350 RTMsgError("RTDirRead -> %Rrc\n", rc);
1351 break;
1352 }
1353
1354 /* Skip '.' and '..'. */
1355 if ( pEntry->szName[0] == '.'
1356 && ( pEntry->cbName == 1
1357 || ( pEntry->cbName == 2
1358 && pEntry->szName[1] == '.')))
1359 continue;
1360
1361 /* Enter it into the buffer so we've got a full name to work
1362 with when needed. */
1363 if (pEntry->cbName + cchDir >= RTPATH_MAX)
1364 {
1365 RTMsgError("Skipping too long entry: %s", pEntry->szName);
1366 continue;
1367 }
1368 memcpy(&pszBuf[cchDir - 1], pEntry->szName, pEntry->cbName + 1);
1369
1370 /* Figure the type. */
1371 RTDIRENTRYTYPE enmType = pEntry->enmType;
1372 if (enmType == RTDIRENTRYTYPE_UNKNOWN)
1373 enmType = scmFigureUnknownType(pszBuf);
1374
1375 /* Process the file or directory, skip the rest. */
1376 if (enmType == RTDIRENTRYTYPE_FILE)
1377 rc = scmProcessFile(pszBuf, pEntry->szName, pEntry->cbName, pSettingsStack);
1378 else if (enmType == RTDIRENTRYTYPE_DIRECTORY)
1379 {
1380 /* Append the dot for the benefit of the pattern matching. */
1381 if (pEntry->cbName + cchDir + 5 >= RTPATH_MAX)
1382 {
1383 RTMsgError("Skipping too deep dir entry: %s", pEntry->szName);
1384 continue;
1385 }
1386 memcpy(&pszBuf[cchDir - 1 + pEntry->cbName], "/.", sizeof("/."));
1387 size_t cchSubDir = cchDir - 1 + pEntry->cbName + sizeof("/.") - 1;
1388
1389 if ( !pSettingsStack->Base.pszFilterOutDirs
1390 || !*pSettingsStack->Base.pszFilterOutDirs
1391 || ( !RTStrSimplePatternMultiMatch(pSettingsStack->Base.pszFilterOutDirs, RTSTR_MAX,
1392 pEntry->szName, pEntry->cbName, NULL)
1393 && !RTStrSimplePatternMultiMatch(pSettingsStack->Base.pszFilterOutDirs, RTSTR_MAX,
1394 pszBuf, cchSubDir, NULL)
1395 )
1396 )
1397 {
1398 rc = scmSettingsStackPushDir(&pSettingsStack, pszBuf);
1399 if (RT_SUCCESS(rc))
1400 {
1401 rc = scmProcessDirTreeRecursion(pszBuf, cchSubDir, pEntry, pSettingsStack, iRecursion + 1);
1402 scmSettingsStackPopAndDestroy(&pSettingsStack);
1403 }
1404 }
1405 }
1406 if (RT_FAILURE(rc))
1407 break;
1408 }
1409 RTDirClose(pDir);
1410 return rc;
1411
1412}
1413
1414/**
1415 * Process a directory tree.
1416 *
1417 * @returns IPRT status code.
1418 * @param pszDir The directory to start with. This is pointer to
1419 * a RTPATH_MAX sized buffer.
1420 */
1421static int scmProcessDirTree(char *pszDir, PSCMSETTINGS pSettingsStack)
1422{
1423 /*
1424 * Setup the recursion.
1425 */
1426 int rc = RTPathAppend(pszDir, RTPATH_MAX, ".");
1427 if (RT_SUCCESS(rc))
1428 {
1429 RTDIRENTRY Entry;
1430 rc = scmProcessDirTreeRecursion(pszDir, strlen(pszDir), &Entry, pSettingsStack, 0);
1431 }
1432 else
1433 RTMsgError("RTPathAppend: %Rrc\n", rc);
1434 return rc;
1435}
1436
1437
1438/**
1439 * Processes a file or directory specified as an command line argument.
1440 *
1441 * @returns IPRT status code
1442 * @param pszSomething What we found in the command line arguments.
1443 * @param pSettingsStack The settings stack (pointer to the top element).
1444 */
1445static int scmProcessSomething(const char *pszSomething, PSCMSETTINGS pSettingsStack)
1446{
1447 char szBuf[RTPATH_MAX];
1448 int rc = RTPathAbs(pszSomething, szBuf, sizeof(szBuf));
1449 if (RT_SUCCESS(rc))
1450 {
1451 RTPathChangeToUnixSlashes(szBuf, false /*fForce*/);
1452
1453 PSCMSETTINGS pSettings;
1454 rc = scmSettingsCreateForPath(&pSettings, &pSettingsStack->Base, szBuf);
1455 if (RT_SUCCESS(rc))
1456 {
1457 scmSettingsStackPush(&pSettingsStack, pSettings);
1458
1459 if (RTFileExists(szBuf))
1460 {
1461 const char *pszBasename = RTPathFilename(szBuf);
1462 if (pszBasename)
1463 {
1464 size_t cchBasename = strlen(pszBasename);
1465 rc = scmProcessFile(szBuf, pszBasename, cchBasename, pSettingsStack);
1466 }
1467 else
1468 {
1469 RTMsgError("RTPathFilename: NULL\n");
1470 rc = VERR_IS_A_DIRECTORY;
1471 }
1472 }
1473 else
1474 rc = scmProcessDirTree(szBuf, pSettingsStack);
1475
1476 PSCMSETTINGS pPopped = scmSettingsStackPop(&pSettingsStack);
1477 Assert(pPopped == pSettings); RT_NOREF_PV(pPopped);
1478 scmSettingsDestroy(pSettings);
1479 }
1480 else
1481 RTMsgError("scmSettingsInitStack: %Rrc\n", rc);
1482 }
1483 else
1484 RTMsgError("RTPathAbs: %Rrc\n", rc);
1485 return rc;
1486}
1487
1488static void usage(PCRTGETOPTDEF paOpts, size_t cOpts)
1489{
1490 RTPrintf("VirtualBox Source Code Massager\n"
1491 "\n"
1492 "Usage: %s [options] <files & dirs>\n"
1493 "\n"
1494 "Options:\n", g_szProgName);
1495 for (size_t i = 0; i < cOpts; i++)
1496 {
1497 bool fAdvanceTwo = false;
1498 if ((paOpts[i].fFlags & RTGETOPT_REQ_MASK) == RTGETOPT_REQ_NOTHING)
1499 {
1500 fAdvanceTwo = i + 1 < cOpts
1501 && ( strstr(paOpts[i+1].pszLong, "-no-") != NULL
1502 || strstr(paOpts[i+1].pszLong, "-not-") != NULL
1503 || strstr(paOpts[i+1].pszLong, "-dont-") != NULL
1504 || (paOpts[i].iShort == 'q' && paOpts[i+1].iShort == 'v')
1505 || (paOpts[i].iShort == 'd' && paOpts[i+1].iShort == 'D')
1506 );
1507 if (fAdvanceTwo)
1508 RTPrintf(" %s, %s\n", paOpts[i].pszLong, paOpts[i + 1].pszLong);
1509 else
1510 RTPrintf(" %s\n", paOpts[i].pszLong);
1511 }
1512 else if ((paOpts[i].fFlags & RTGETOPT_REQ_MASK) == RTGETOPT_REQ_STRING)
1513 RTPrintf(" %s string\n", paOpts[i].pszLong);
1514 else
1515 RTPrintf(" %s value\n", paOpts[i].pszLong);
1516 switch (paOpts[i].iShort)
1517 {
1518 case 'd':
1519 case 'D': RTPrintf(" Default: --dry-run\n"); break;
1520 case 'f': RTPrintf(" Default: none\n"); break;
1521 case 'q':
1522 case 'v': RTPrintf(" Default: -vv\n"); break;
1523
1524 case SCMOPT_DIFF_IGNORE_EOL: RTPrintf(" Default: false\n"); break;
1525 case SCMOPT_DIFF_IGNORE_SPACE: RTPrintf(" Default: false\n"); break;
1526 case SCMOPT_DIFF_IGNORE_LEADING_SPACE: RTPrintf(" Default: false\n"); break;
1527 case SCMOPT_DIFF_IGNORE_TRAILING_SPACE: RTPrintf(" Default: false\n"); break;
1528 case SCMOPT_DIFF_SPECIAL_CHARS: RTPrintf(" Default: true\n"); break;
1529
1530 case SCMOPT_CONVERT_EOL: RTPrintf(" Default: %RTbool\n", g_Defaults.fConvertEol); break;
1531 case SCMOPT_CONVERT_TABS: RTPrintf(" Default: %RTbool\n", g_Defaults.fConvertTabs); break;
1532 case SCMOPT_FORCE_FINAL_EOL: RTPrintf(" Default: %RTbool\n", g_Defaults.fForceFinalEol); break;
1533 case SCMOPT_FORCE_TRAILING_LINE: RTPrintf(" Default: %RTbool\n", g_Defaults.fForceTrailingLine); break;
1534 case SCMOPT_STRIP_TRAILING_BLANKS: RTPrintf(" Default: %RTbool\n", g_Defaults.fStripTrailingBlanks); break;
1535 case SCMOPT_STRIP_TRAILING_LINES: RTPrintf(" Default: %RTbool\n", g_Defaults.fStripTrailingLines); break;
1536 case SCMOPT_FIX_FLOWER_BOX_MARKERS: RTPrintf(" Default: %RTbool\n", g_Defaults.fFixFlowerBoxMarkers); break;
1537 case SCMOPT_MIN_BLANK_LINES_BEFORE_FLOWER_BOX_MARKERS: RTPrintf(" Default: %u\n", g_Defaults.cMinBlankLinesBeforeFlowerBoxMakers); break;
1538 case SCMOPT_ONLY_SVN_DIRS: RTPrintf(" Default: %RTbool\n", g_Defaults.fOnlySvnDirs); break;
1539 case SCMOPT_ONLY_SVN_FILES: RTPrintf(" Default: %RTbool\n", g_Defaults.fOnlySvnFiles); break;
1540 case SCMOPT_SET_SVN_EOL: RTPrintf(" Default: %RTbool\n", g_Defaults.fSetSvnEol); break;
1541 case SCMOPT_SET_SVN_EXECUTABLE: RTPrintf(" Default: %RTbool\n", g_Defaults.fSetSvnExecutable); break;
1542 case SCMOPT_SET_SVN_KEYWORDS: RTPrintf(" Default: %RTbool\n", g_Defaults.fSetSvnKeywords); break;
1543 case SCMOPT_TAB_SIZE: RTPrintf(" Default: %u\n", g_Defaults.cchTab); break;
1544 case SCMOPT_WIDTH: RTPrintf(" Default: %u\n", g_Defaults.cchWidth); break;
1545 case SCMOPT_FILTER_OUT_DIRS: RTPrintf(" Default: %s\n", g_Defaults.pszFilterOutDirs); break;
1546 case SCMOPT_FILTER_FILES: RTPrintf(" Default: %s\n", g_Defaults.pszFilterFiles); break;
1547 case SCMOPT_FILTER_OUT_FILES: RTPrintf(" Default: %s\n", g_Defaults.pszFilterOutFiles); break;
1548 default: AssertMsgFailed(("i=%d %d %s\n", i, paOpts[i].iShort, paOpts[i].pszLong));
1549 }
1550 i += fAdvanceTwo;
1551 }
1552
1553}
1554
1555int main(int argc, char **argv)
1556{
1557 int rc = RTR3InitExe(argc, &argv, 0);
1558 if (RT_FAILURE(rc))
1559 return 1;
1560
1561 /*
1562 * Init the settings.
1563 */
1564 PSCMSETTINGS pSettings;
1565 rc = scmSettingsCreate(&pSettings, &g_Defaults);
1566 if (RT_FAILURE(rc))
1567 {
1568 RTMsgError("scmSettingsCreate: %Rrc\n", rc);
1569 return 1;
1570 }
1571
1572 /*
1573 * Parse arguments and process input in order (because this is the only
1574 * thing that works at the moment).
1575 */
1576 static RTGETOPTDEF s_aOpts[14 + RT_ELEMENTS(g_aScmOpts)] =
1577 {
1578 { "--dry-run", 'd', RTGETOPT_REQ_NOTHING },
1579 { "--real-run", 'D', RTGETOPT_REQ_NOTHING },
1580 { "--file-filter", 'f', RTGETOPT_REQ_STRING },
1581 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
1582 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
1583 { "--diff-ignore-eol", SCMOPT_DIFF_IGNORE_EOL, RTGETOPT_REQ_NOTHING },
1584 { "--diff-no-ignore-eol", SCMOPT_DIFF_NO_IGNORE_EOL, RTGETOPT_REQ_NOTHING },
1585 { "--diff-ignore-space", SCMOPT_DIFF_IGNORE_SPACE, RTGETOPT_REQ_NOTHING },
1586 { "--diff-no-ignore-space", SCMOPT_DIFF_NO_IGNORE_SPACE, RTGETOPT_REQ_NOTHING },
1587 { "--diff-ignore-leading-space", SCMOPT_DIFF_IGNORE_LEADING_SPACE, RTGETOPT_REQ_NOTHING },
1588 { "--diff-no-ignore-leading-space", SCMOPT_DIFF_NO_IGNORE_LEADING_SPACE, RTGETOPT_REQ_NOTHING },
1589 { "--diff-ignore-trailing-space", SCMOPT_DIFF_IGNORE_TRAILING_SPACE, RTGETOPT_REQ_NOTHING },
1590 { "--diff-no-ignore-trailing-space", SCMOPT_DIFF_NO_IGNORE_TRAILING_SPACE, RTGETOPT_REQ_NOTHING },
1591 { "--diff-special-chars", SCMOPT_DIFF_SPECIAL_CHARS, RTGETOPT_REQ_NOTHING },
1592 { "--diff-no-special-chars", SCMOPT_DIFF_NO_SPECIAL_CHARS, RTGETOPT_REQ_NOTHING },
1593 };
1594 memcpy(&s_aOpts[RT_ELEMENTS(s_aOpts) - RT_ELEMENTS(g_aScmOpts)], &g_aScmOpts[0], sizeof(g_aScmOpts));
1595
1596 RTGETOPTUNION ValueUnion;
1597 RTGETOPTSTATE GetOptState;
1598 rc = RTGetOptInit(&GetOptState, argc, argv, &s_aOpts[0], RT_ELEMENTS(s_aOpts), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1599 AssertReleaseRCReturn(rc, 1);
1600 size_t cProcessed = 0;
1601
1602 while ((rc = RTGetOpt(&GetOptState, &ValueUnion)) != 0)
1603 {
1604 switch (rc)
1605 {
1606 case 'd':
1607 g_fDryRun = true;
1608 break;
1609 case 'D':
1610 g_fDryRun = false;
1611 break;
1612
1613 case 'f':
1614 g_pszFileFilter = ValueUnion.psz;
1615 break;
1616
1617 case 'h':
1618 usage(s_aOpts, RT_ELEMENTS(s_aOpts));
1619 return 1;
1620
1621 case 'q':
1622 g_iVerbosity = 0;
1623 break;
1624
1625 case 'v':
1626 g_iVerbosity++;
1627 break;
1628
1629 case 'V':
1630 {
1631 /* The following is assuming that svn does it's job here. */
1632 static const char s_szRev[] = "$Revision: 62854 $";
1633 const char *psz = RTStrStripL(strchr(s_szRev, ' '));
1634 RTPrintf("r%.*s\n", strchr(psz, ' ') - psz, psz);
1635 return 0;
1636 }
1637
1638 case SCMOPT_DIFF_IGNORE_EOL:
1639 g_fDiffIgnoreEol = true;
1640 break;
1641 case SCMOPT_DIFF_NO_IGNORE_EOL:
1642 g_fDiffIgnoreEol = false;
1643 break;
1644
1645 case SCMOPT_DIFF_IGNORE_SPACE:
1646 g_fDiffIgnoreTrailingWS = g_fDiffIgnoreLeadingWS = true;
1647 break;
1648 case SCMOPT_DIFF_NO_IGNORE_SPACE:
1649 g_fDiffIgnoreTrailingWS = g_fDiffIgnoreLeadingWS = false;
1650 break;
1651
1652 case SCMOPT_DIFF_IGNORE_LEADING_SPACE:
1653 g_fDiffIgnoreLeadingWS = true;
1654 break;
1655 case SCMOPT_DIFF_NO_IGNORE_LEADING_SPACE:
1656 g_fDiffIgnoreLeadingWS = false;
1657 break;
1658
1659 case SCMOPT_DIFF_IGNORE_TRAILING_SPACE:
1660 g_fDiffIgnoreTrailingWS = true;
1661 break;
1662 case SCMOPT_DIFF_NO_IGNORE_TRAILING_SPACE:
1663 g_fDiffIgnoreTrailingWS = false;
1664 break;
1665
1666 case SCMOPT_DIFF_SPECIAL_CHARS:
1667 g_fDiffSpecialChars = true;
1668 break;
1669 case SCMOPT_DIFF_NO_SPECIAL_CHARS:
1670 g_fDiffSpecialChars = false;
1671 break;
1672
1673 case VINF_GETOPT_NOT_OPTION:
1674 {
1675 if (!g_fDryRun)
1676 {
1677 if (!cProcessed)
1678 {
1679 RTPrintf("%s: Warning! This program will make changes to your source files and\n"
1680 "%s: there is a slight risk that bugs or a full disk may cause\n"
1681 "%s: LOSS OF DATA. So, please make sure you have checked in\n"
1682 "%s: all your changes already. If you didn't, then don't blame\n"
1683 "%s: anyone for not warning you!\n"
1684 "%s:\n"
1685 "%s: Press any key to continue...\n",
1686 g_szProgName, g_szProgName, g_szProgName, g_szProgName, g_szProgName,
1687 g_szProgName, g_szProgName);
1688 RTStrmGetCh(g_pStdIn);
1689 }
1690 cProcessed++;
1691 }
1692 rc = scmProcessSomething(ValueUnion.psz, pSettings);
1693 if (RT_FAILURE(rc))
1694 return rc;
1695 break;
1696 }
1697
1698 default:
1699 {
1700 int rc2 = scmSettingsBaseHandleOpt(&pSettings->Base, rc, &ValueUnion);
1701 if (RT_SUCCESS(rc2))
1702 break;
1703 if (rc2 != VERR_GETOPT_UNKNOWN_OPTION)
1704 return 2;
1705 return RTGetOptPrintError(rc, &ValueUnion);
1706 }
1707 }
1708 }
1709
1710 scmSettingsDestroy(pSettings);
1711 return 0;
1712}
1713
Note: See TracBrowser for help on using the repository browser.

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