Changeset 40534 in vbox for trunk/src/bldprogs/scmrw.cpp
- Timestamp:
- Mar 19, 2012 11:49:34 AM (13 years ago)
- File:
-
- 1 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/bldprogs/scmrw.cpp
r40530 r40534 35 35 #include <iprt/string.h> 36 36 37 #include "scmstream.h" 38 #include "scmdiff.h" 39 40 41 /******************************************************************************* 42 * Defined Constants And Macros * 43 *******************************************************************************/ 44 /** The name of the settings files. */ 45 #define SCM_SETTINGS_FILENAME ".scm-settings" 46 47 48 /******************************************************************************* 49 * Structures and Typedefs * 50 *******************************************************************************/ 51 /** Pointer to const massager settings. */ 52 typedef struct SCMSETTINGSBASE const *PCSCMSETTINGSBASE; 53 54 /** 55 * SVN property. 56 */ 57 typedef struct SCMSVNPROP 58 { 59 /** The property. */ 60 char *pszName; 61 /** The value. 62 * When used to record updates, this can be set to NULL to trigger the 63 * deletion of the property. */ 64 char *pszValue; 65 } SCMSVNPROP; 66 /** Pointer to a SVN property. */ 67 typedef SCMSVNPROP *PSCMSVNPROP; 68 /** Pointer to a const SVN property. */ 69 typedef SCMSVNPROP const *PCSCMSVNPROP; 70 71 72 /** 73 * Rewriter state. 74 */ 75 typedef struct SCMRWSTATE 76 { 77 /** The filename. */ 78 const char *pszFilename; 79 /** Set after the printing the first verbose message about a file under 80 * rewrite. */ 81 bool fFirst; 82 /** The number of SVN property changes. */ 83 size_t cSvnPropChanges; 84 /** Pointer to an array of SVN property changes. */ 85 PSCMSVNPROP paSvnPropChanges; 86 } SCMRWSTATE; 87 /** Pointer to the rewriter state. */ 88 typedef SCMRWSTATE *PSCMRWSTATE; 89 90 /** 91 * A rewriter. 92 * 93 * This works like a stream editor, reading @a pIn, modifying it and writing it 94 * to @a pOut. 95 * 96 * @returns true if any changes were made, false if not. 97 * @param pIn The input stream. 98 * @param pOut The output stream. 99 * @param pSettings The settings. 100 */ 101 typedef bool (*PFNSCMREWRITER)(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings); 102 103 104 /** 105 * Configuration entry. 106 */ 107 typedef struct SCMCFGENTRY 108 { 109 /** Number of rewriters. */ 110 size_t cRewriters; 111 /** Pointer to an array of rewriters. */ 112 PFNSCMREWRITER const *papfnRewriter; 113 /** File pattern (simple). */ 114 const char *pszFilePattern; 115 } SCMCFGENTRY; 116 typedef SCMCFGENTRY *PSCMCFGENTRY; 117 typedef SCMCFGENTRY const *PCSCMCFGENTRY; 118 119 120 /** 121 * Source Code Massager Settings. 122 */ 123 typedef struct SCMSETTINGSBASE 124 { 125 bool fConvertEol; 126 bool fConvertTabs; 127 bool fForceFinalEol; 128 bool fForceTrailingLine; 129 bool fStripTrailingBlanks; 130 bool fStripTrailingLines; 131 /** Only process files that are part of a SVN working copy. */ 132 bool fOnlySvnFiles; 133 /** Only recurse into directories containing an .svn dir. */ 134 bool fOnlySvnDirs; 135 /** Set svn:eol-style if missing or incorrect. */ 136 bool fSetSvnEol; 137 /** Set svn:executable according to type (unusually this means deleting it). */ 138 bool fSetSvnExecutable; 139 /** Set svn:keyword if completely or partially missing. */ 140 bool fSetSvnKeywords; 141 /** */ 142 unsigned cchTab; 143 /** Only consider files matching these patterns. This is only applied to the 144 * base names. */ 145 char *pszFilterFiles; 146 /** Filter out files matching the following patterns. This is applied to base 147 * names as well as the absolute paths. */ 148 char *pszFilterOutFiles; 149 /** Filter out directories matching the following patterns. This is applied 150 * to base names as well as the absolute paths. All absolute paths ends with a 151 * slash and dot ("/."). */ 152 char *pszFilterOutDirs; 153 } SCMSETTINGSBASE; 154 /** Pointer to massager settings. */ 155 typedef SCMSETTINGSBASE *PSCMSETTINGSBASE; 156 157 /** 158 * Option identifiers. 159 * 160 * @note The first chunk, down to SCMOPT_TAB_SIZE, are alternately set & 161 * clear. So, the option setting a flag (boolean) will have an even 162 * number and the one clearing it will have an odd number. 163 * @note Down to SCMOPT_LAST_SETTINGS corresponds exactly to SCMSETTINGSBASE. 164 */ 165 typedef enum SCMOPT 166 { 167 SCMOPT_CONVERT_EOL = 10000, 168 SCMOPT_NO_CONVERT_EOL, 169 SCMOPT_CONVERT_TABS, 170 SCMOPT_NO_CONVERT_TABS, 171 SCMOPT_FORCE_FINAL_EOL, 172 SCMOPT_NO_FORCE_FINAL_EOL, 173 SCMOPT_FORCE_TRAILING_LINE, 174 SCMOPT_NO_FORCE_TRAILING_LINE, 175 SCMOPT_STRIP_TRAILING_BLANKS, 176 SCMOPT_NO_STRIP_TRAILING_BLANKS, 177 SCMOPT_STRIP_TRAILING_LINES, 178 SCMOPT_NO_STRIP_TRAILING_LINES, 179 SCMOPT_ONLY_SVN_DIRS, 180 SCMOPT_NOT_ONLY_SVN_DIRS, 181 SCMOPT_ONLY_SVN_FILES, 182 SCMOPT_NOT_ONLY_SVN_FILES, 183 SCMOPT_SET_SVN_EOL, 184 SCMOPT_DONT_SET_SVN_EOL, 185 SCMOPT_SET_SVN_EXECUTABLE, 186 SCMOPT_DONT_SET_SVN_EXECUTABLE, 187 SCMOPT_SET_SVN_KEYWORDS, 188 SCMOPT_DONT_SET_SVN_KEYWORDS, 189 SCMOPT_TAB_SIZE, 190 SCMOPT_FILTER_OUT_DIRS, 191 SCMOPT_FILTER_FILES, 192 SCMOPT_FILTER_OUT_FILES, 193 SCMOPT_LAST_SETTINGS = SCMOPT_FILTER_OUT_FILES, 194 // 195 SCMOPT_DIFF_IGNORE_EOL, 196 SCMOPT_DIFF_NO_IGNORE_EOL, 197 SCMOPT_DIFF_IGNORE_SPACE, 198 SCMOPT_DIFF_NO_IGNORE_SPACE, 199 SCMOPT_DIFF_IGNORE_LEADING_SPACE, 200 SCMOPT_DIFF_NO_IGNORE_LEADING_SPACE, 201 SCMOPT_DIFF_IGNORE_TRAILING_SPACE, 202 SCMOPT_DIFF_NO_IGNORE_TRAILING_SPACE, 203 SCMOPT_DIFF_SPECIAL_CHARS, 204 SCMOPT_DIFF_NO_SPECIAL_CHARS, 205 SCMOPT_END 206 } SCMOPT; 207 208 209 /** 210 * File/dir pattern + options. 211 */ 212 typedef struct SCMPATRNOPTPAIR 213 { 214 char *pszPattern; 215 char *pszOptions; 216 } SCMPATRNOPTPAIR; 217 /** Pointer to a pattern + option pair. */ 218 typedef SCMPATRNOPTPAIR *PSCMPATRNOPTPAIR; 219 220 221 /** Pointer to a settings set. */ 222 typedef struct SCMSETTINGS *PSCMSETTINGS; 223 /** 224 * Settings set. 225 * 226 * This structure is constructed from the command line arguments or any 227 * .scm-settings file found in a directory we recurse into. When recursing in 228 * and out of a directory, we push and pop a settings set for it. 229 * 230 * The .scm-settings file has two kinds of setttings, first there are the 231 * unqualified base settings and then there are the settings which applies to a 232 * set of files or directories. The former are lines with command line options. 233 * For the latter, the options are preceded by a string pattern and a colon. 234 * The pattern specifies which files (and/or directories) the options applies 235 * to. 236 * 237 * We parse the base options into the Base member and put the others into the 238 * paPairs array. 239 */ 240 typedef struct SCMSETTINGS 241 { 242 /** Pointer to the setting file below us in the stack. */ 243 PSCMSETTINGS pDown; 244 /** Pointer to the setting file above us in the stack. */ 245 PSCMSETTINGS pUp; 246 /** File/dir patterns and their options. */ 247 PSCMPATRNOPTPAIR paPairs; 248 /** The number of entires in paPairs. */ 249 uint32_t cPairs; 250 /** The base settings that was read out of the file. */ 251 SCMSETTINGSBASE Base; 252 } SCMSETTINGS; 253 /** Pointer to a const settings set. */ 254 typedef SCMSETTINGS const *PCSCMSETTINGS; 255 256 257 /******************************************************************************* 258 * Internal Functions * 259 *******************************************************************************/ 260 static bool rewrite_StripTrailingBlanks(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings); 261 static bool rewrite_ExpandTabs(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings); 262 static bool rewrite_ForceNativeEol(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings); 263 static bool rewrite_ForceLF(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings); 264 static bool rewrite_ForceCRLF(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings); 265 static bool rewrite_AdjustTrailingLines(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings); 266 static bool rewrite_SvnNoExecutable(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings); 267 static bool rewrite_SvnKeywords(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings); 268 static bool rewrite_Makefile_kup(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings); 269 static bool rewrite_Makefile_kmk(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings); 270 static bool rewrite_C_and_CPP(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings); 271 272 273 /******************************************************************************* 274 * Global Variables * 275 *******************************************************************************/ 276 static const char g_szProgName[] = "scm"; 277 static const char *g_pszChangedSuff = ""; 278 static const char g_szTabSpaces[16+1] = " "; 279 static bool g_fDryRun = true; 280 static bool g_fDiffSpecialChars = true; 281 static bool g_fDiffIgnoreEol = false; 282 static bool g_fDiffIgnoreLeadingWS = false; 283 static bool g_fDiffIgnoreTrailingWS = false; 284 static int g_iVerbosity = 2;//99; //0; 285 286 /** The global settings. */ 287 static SCMSETTINGSBASE const g_Defaults = 288 { 289 /* .fConvertEol = */ true, 290 /* .fConvertTabs = */ true, 291 /* .fForceFinalEol = */ true, 292 /* .fForceTrailingLine = */ false, 293 /* .fStripTrailingBlanks = */ true, 294 /* .fStripTrailingLines = */ true, 295 /* .fOnlySvnFiles = */ false, 296 /* .fOnlySvnDirs = */ false, 297 /* .fSetSvnEol = */ false, 298 /* .fSetSvnExecutable = */ false, 299 /* .fSetSvnKeywords = */ false, 300 /* .cchTab = */ 8, 301 /* .pszFilterFiles = */ (char *)"", 302 /* .pszFilterOutFiles = */ (char *)"*.exe|*.com|20*-*-*.log", 303 /* .pszFilterOutDirs = */ (char *)".svn|.hg|.git|CVS", 304 }; 305 306 /** Option definitions for the base settings. */ 307 static RTGETOPTDEF g_aScmOpts[] = 308 { 309 { "--convert-eol", SCMOPT_CONVERT_EOL, RTGETOPT_REQ_NOTHING }, 310 { "--no-convert-eol", SCMOPT_NO_CONVERT_EOL, RTGETOPT_REQ_NOTHING }, 311 { "--convert-tabs", SCMOPT_CONVERT_TABS, RTGETOPT_REQ_NOTHING }, 312 { "--no-convert-tabs", SCMOPT_NO_CONVERT_TABS, RTGETOPT_REQ_NOTHING }, 313 { "--force-final-eol", SCMOPT_FORCE_FINAL_EOL, RTGETOPT_REQ_NOTHING }, 314 { "--no-force-final-eol", SCMOPT_NO_FORCE_FINAL_EOL, RTGETOPT_REQ_NOTHING }, 315 { "--force-trailing-line", SCMOPT_FORCE_TRAILING_LINE, RTGETOPT_REQ_NOTHING }, 316 { "--no-force-trailing-line", SCMOPT_NO_FORCE_TRAILING_LINE, RTGETOPT_REQ_NOTHING }, 317 { "--strip-trailing-blanks", SCMOPT_STRIP_TRAILING_BLANKS, RTGETOPT_REQ_NOTHING }, 318 { "--no-strip-trailing-blanks", SCMOPT_NO_STRIP_TRAILING_BLANKS, RTGETOPT_REQ_NOTHING }, 319 { "--strip-trailing-lines", SCMOPT_STRIP_TRAILING_LINES, RTGETOPT_REQ_NOTHING }, 320 { "--strip-no-trailing-lines", SCMOPT_NO_STRIP_TRAILING_LINES, RTGETOPT_REQ_NOTHING }, 321 { "--only-svn-dirs", SCMOPT_ONLY_SVN_DIRS, RTGETOPT_REQ_NOTHING }, 322 { "--not-only-svn-dirs", SCMOPT_NOT_ONLY_SVN_DIRS, RTGETOPT_REQ_NOTHING }, 323 { "--only-svn-files", SCMOPT_ONLY_SVN_FILES, RTGETOPT_REQ_NOTHING }, 324 { "--not-only-svn-files", SCMOPT_NOT_ONLY_SVN_FILES, RTGETOPT_REQ_NOTHING }, 325 { "--set-svn-eol", SCMOPT_SET_SVN_EOL, RTGETOPT_REQ_NOTHING }, 326 { "--dont-set-svn-eol", SCMOPT_DONT_SET_SVN_EOL, RTGETOPT_REQ_NOTHING }, 327 { "--set-svn-executable", SCMOPT_SET_SVN_EXECUTABLE, RTGETOPT_REQ_NOTHING }, 328 { "--dont-set-svn-executable", SCMOPT_DONT_SET_SVN_EXECUTABLE, RTGETOPT_REQ_NOTHING }, 329 { "--set-svn-keywords", SCMOPT_SET_SVN_KEYWORDS, RTGETOPT_REQ_NOTHING }, 330 { "--dont-set-svn-keywords", SCMOPT_DONT_SET_SVN_KEYWORDS, RTGETOPT_REQ_NOTHING }, 331 { "--tab-size", SCMOPT_TAB_SIZE, RTGETOPT_REQ_UINT8 }, 332 { "--filter-out-dirs", SCMOPT_FILTER_OUT_DIRS, RTGETOPT_REQ_STRING }, 333 { "--filter-files", SCMOPT_FILTER_FILES, RTGETOPT_REQ_STRING }, 334 { "--filter-out-files", SCMOPT_FILTER_OUT_FILES, RTGETOPT_REQ_STRING }, 335 }; 336 337 /** Consider files matching the following patterns (base names only). */ 338 static const char *g_pszFileFilter = NULL; 339 340 static PFNSCMREWRITER const g_aRewritersFor_Makefile_kup[] = 341 { 342 rewrite_SvnNoExecutable, 343 rewrite_Makefile_kup 344 }; 345 346 static PFNSCMREWRITER const g_aRewritersFor_Makefile_kmk[] = 347 { 348 rewrite_ForceNativeEol, 349 rewrite_StripTrailingBlanks, 350 rewrite_AdjustTrailingLines, 351 rewrite_SvnNoExecutable, 352 rewrite_SvnKeywords, 353 rewrite_Makefile_kmk 354 }; 355 356 static PFNSCMREWRITER const g_aRewritersFor_C_and_CPP[] = 357 { 358 rewrite_ForceNativeEol, 359 rewrite_ExpandTabs, 360 rewrite_StripTrailingBlanks, 361 rewrite_AdjustTrailingLines, 362 rewrite_SvnNoExecutable, 363 rewrite_SvnKeywords, 364 rewrite_C_and_CPP 365 }; 366 367 static PFNSCMREWRITER const g_aRewritersFor_H_and_HPP[] = 368 { 369 rewrite_ForceNativeEol, 370 rewrite_ExpandTabs, 371 rewrite_StripTrailingBlanks, 372 rewrite_AdjustTrailingLines, 373 rewrite_SvnNoExecutable, 374 rewrite_C_and_CPP 375 }; 376 377 static PFNSCMREWRITER const g_aRewritersFor_RC[] = 378 { 379 rewrite_ForceNativeEol, 380 rewrite_ExpandTabs, 381 rewrite_StripTrailingBlanks, 382 rewrite_AdjustTrailingLines, 383 rewrite_SvnNoExecutable, 384 rewrite_SvnKeywords 385 }; 386 387 static PFNSCMREWRITER const g_aRewritersFor_ShellScripts[] = 388 { 389 rewrite_ForceLF, 390 rewrite_ExpandTabs, 391 rewrite_StripTrailingBlanks 392 }; 393 394 static PFNSCMREWRITER const g_aRewritersFor_BatchFiles[] = 395 { 396 rewrite_ForceCRLF, 397 rewrite_ExpandTabs, 398 rewrite_StripTrailingBlanks 399 }; 400 401 static SCMCFGENTRY const g_aConfigs[] = 402 { 403 { RT_ELEMENTS(g_aRewritersFor_Makefile_kup), &g_aRewritersFor_Makefile_kup[0], "Makefile.kup" }, 404 { RT_ELEMENTS(g_aRewritersFor_Makefile_kmk), &g_aRewritersFor_Makefile_kmk[0], "Makefile.kmk|Config.kmk" }, 405 { RT_ELEMENTS(g_aRewritersFor_C_and_CPP), &g_aRewritersFor_C_and_CPP[0], "*.c|*.cpp|*.C|*.CPP|*.cxx|*.cc" }, 406 { RT_ELEMENTS(g_aRewritersFor_H_and_HPP), &g_aRewritersFor_H_and_HPP[0], "*.h|*.hpp" }, 407 { RT_ELEMENTS(g_aRewritersFor_RC), &g_aRewritersFor_RC[0], "*.rc" }, 408 { RT_ELEMENTS(g_aRewritersFor_ShellScripts), &g_aRewritersFor_ShellScripts[0], "*.sh|configure" }, 409 { RT_ELEMENTS(g_aRewritersFor_BatchFiles), &g_aRewritersFor_BatchFiles[0], "*.bat|*.cmd|*.btm|*.vbs|*.ps1" }, 410 }; 411 412 413 414 /* -=-=-=-=-=- settings -=-=-=-=-=- */ 415 416 417 /** 418 * Init a settings structure with settings from @a pSrc. 419 * 420 * @returns IPRT status code 421 * @param pSettings The settings. 422 * @param pSrc The source settings. 423 */ 424 static int scmSettingsBaseInitAndCopy(PSCMSETTINGSBASE pSettings, PCSCMSETTINGSBASE pSrc) 425 { 426 *pSettings = *pSrc; 427 428 int rc = RTStrDupEx(&pSettings->pszFilterFiles, pSrc->pszFilterFiles); 429 if (RT_SUCCESS(rc)) 430 { 431 rc = RTStrDupEx(&pSettings->pszFilterOutFiles, pSrc->pszFilterOutFiles); 432 if (RT_SUCCESS(rc)) 433 { 434 rc = RTStrDupEx(&pSettings->pszFilterOutDirs, pSrc->pszFilterOutDirs); 435 if (RT_SUCCESS(rc)) 436 return VINF_SUCCESS; 437 438 RTStrFree(pSettings->pszFilterOutFiles); 439 } 440 RTStrFree(pSettings->pszFilterFiles); 441 } 442 443 pSettings->pszFilterFiles = NULL; 444 pSettings->pszFilterOutFiles = NULL; 445 pSettings->pszFilterOutDirs = NULL; 446 return rc; 447 } 448 449 /** 450 * Init a settings structure. 451 * 452 * @returns IPRT status code 453 * @param pSettings The settings. 454 */ 455 static int scmSettingsBaseInit(PSCMSETTINGSBASE pSettings) 456 { 457 return scmSettingsBaseInitAndCopy(pSettings, &g_Defaults); 458 } 459 460 /** 461 * Deletes the settings, i.e. free any dynamically allocated content. 462 * 463 * @param pSettings The settings. 464 */ 465 static void scmSettingsBaseDelete(PSCMSETTINGSBASE pSettings) 466 { 467 if (pSettings) 468 { 469 Assert(pSettings->cchTab != ~(unsigned)0); 470 pSettings->cchTab = ~(unsigned)0; 471 472 RTStrFree(pSettings->pszFilterFiles); 473 pSettings->pszFilterFiles = NULL; 474 475 RTStrFree(pSettings->pszFilterOutFiles); 476 pSettings->pszFilterOutFiles = NULL; 477 478 RTStrFree(pSettings->pszFilterOutDirs); 479 pSettings->pszFilterOutDirs = NULL; 480 } 481 } 482 483 484 /** 485 * Processes a RTGetOpt result. 486 * 487 * @retval VINF_SUCCESS if handled. 488 * @retval VERR_OUT_OF_RANGE if the option value was out of range. 489 * @retval VERR_GETOPT_UNKNOWN_OPTION if the option was not recognized. 490 * 491 * @param pSettings The settings to change. 492 * @param rc The RTGetOpt return value. 493 * @param pValueUnion The RTGetOpt value union. 494 */ 495 static int scmSettingsBaseHandleOpt(PSCMSETTINGSBASE pSettings, int rc, PRTGETOPTUNION pValueUnion) 496 { 497 switch (rc) 498 { 499 case SCMOPT_CONVERT_EOL: 500 pSettings->fConvertEol = true; 501 return VINF_SUCCESS; 502 case SCMOPT_NO_CONVERT_EOL: 503 pSettings->fConvertEol = false; 504 return VINF_SUCCESS; 505 506 case SCMOPT_CONVERT_TABS: 507 pSettings->fConvertTabs = true; 508 return VINF_SUCCESS; 509 case SCMOPT_NO_CONVERT_TABS: 510 pSettings->fConvertTabs = false; 511 return VINF_SUCCESS; 512 513 case SCMOPT_FORCE_FINAL_EOL: 514 pSettings->fForceFinalEol = true; 515 return VINF_SUCCESS; 516 case SCMOPT_NO_FORCE_FINAL_EOL: 517 pSettings->fForceFinalEol = false; 518 return VINF_SUCCESS; 519 520 case SCMOPT_FORCE_TRAILING_LINE: 521 pSettings->fForceTrailingLine = true; 522 return VINF_SUCCESS; 523 case SCMOPT_NO_FORCE_TRAILING_LINE: 524 pSettings->fForceTrailingLine = false; 525 return VINF_SUCCESS; 526 527 case SCMOPT_STRIP_TRAILING_BLANKS: 528 pSettings->fStripTrailingBlanks = true; 529 return VINF_SUCCESS; 530 case SCMOPT_NO_STRIP_TRAILING_BLANKS: 531 pSettings->fStripTrailingBlanks = false; 532 return VINF_SUCCESS; 533 534 case SCMOPT_STRIP_TRAILING_LINES: 535 pSettings->fStripTrailingLines = true; 536 return VINF_SUCCESS; 537 case SCMOPT_NO_STRIP_TRAILING_LINES: 538 pSettings->fStripTrailingLines = false; 539 return VINF_SUCCESS; 540 541 case SCMOPT_ONLY_SVN_DIRS: 542 pSettings->fOnlySvnDirs = true; 543 return VINF_SUCCESS; 544 case SCMOPT_NOT_ONLY_SVN_DIRS: 545 pSettings->fOnlySvnDirs = false; 546 return VINF_SUCCESS; 547 548 case SCMOPT_ONLY_SVN_FILES: 549 pSettings->fOnlySvnFiles = true; 550 return VINF_SUCCESS; 551 case SCMOPT_NOT_ONLY_SVN_FILES: 552 pSettings->fOnlySvnFiles = false; 553 return VINF_SUCCESS; 554 555 case SCMOPT_SET_SVN_EOL: 556 pSettings->fSetSvnEol = true; 557 return VINF_SUCCESS; 558 case SCMOPT_DONT_SET_SVN_EOL: 559 pSettings->fSetSvnEol = false; 560 return VINF_SUCCESS; 561 562 case SCMOPT_SET_SVN_EXECUTABLE: 563 pSettings->fSetSvnExecutable = true; 564 return VINF_SUCCESS; 565 case SCMOPT_DONT_SET_SVN_EXECUTABLE: 566 pSettings->fSetSvnExecutable = false; 567 return VINF_SUCCESS; 568 569 case SCMOPT_SET_SVN_KEYWORDS: 570 pSettings->fSetSvnKeywords = true; 571 return VINF_SUCCESS; 572 case SCMOPT_DONT_SET_SVN_KEYWORDS: 573 pSettings->fSetSvnKeywords = false; 574 return VINF_SUCCESS; 575 576 case SCMOPT_TAB_SIZE: 577 if ( pValueUnion->u8 < 1 578 || pValueUnion->u8 >= RT_ELEMENTS(g_szTabSpaces)) 579 { 580 RTMsgError("Invalid tab size: %u - must be in {1..%u}\n", 581 pValueUnion->u8, RT_ELEMENTS(g_szTabSpaces) - 1); 582 return VERR_OUT_OF_RANGE; 583 } 584 pSettings->cchTab = pValueUnion->u8; 585 return VINF_SUCCESS; 586 587 case SCMOPT_FILTER_OUT_DIRS: 588 case SCMOPT_FILTER_FILES: 589 case SCMOPT_FILTER_OUT_FILES: 590 { 591 char **ppsz = NULL; 592 switch (rc) 593 { 594 case SCMOPT_FILTER_OUT_DIRS: ppsz = &pSettings->pszFilterOutDirs; break; 595 case SCMOPT_FILTER_FILES: ppsz = &pSettings->pszFilterFiles; break; 596 case SCMOPT_FILTER_OUT_FILES: ppsz = &pSettings->pszFilterOutFiles; break; 597 } 598 599 /* 600 * An empty string zaps the current list. 601 */ 602 if (!*pValueUnion->psz) 603 return RTStrATruncate(ppsz, 0); 604 605 /* 606 * Non-empty strings are appended to the pattern list. 607 * 608 * Strip leading and trailing pattern separators before attempting 609 * to append it. If it's just separators, don't do anything. 610 */ 611 const char *pszSrc = pValueUnion->psz; 612 while (*pszSrc == '|') 613 pszSrc++; 614 size_t cchSrc = strlen(pszSrc); 615 while (cchSrc > 0 && pszSrc[cchSrc - 1] == '|') 616 cchSrc--; 617 if (!cchSrc) 618 return VINF_SUCCESS; 619 620 return RTStrAAppendExN(ppsz, 2, 621 "|", *ppsz && **ppsz ? 1 : 0, 622 pszSrc, cchSrc); 623 } 624 625 default: 626 return VERR_GETOPT_UNKNOWN_OPTION; 627 } 628 } 629 630 /** 631 * Parses an option string. 632 * 633 * @returns IPRT status code. 634 * @param pBase The base settings structure to apply the options 635 * to. 636 * @param pszOptions The options to parse. 637 */ 638 static int scmSettingsBaseParseString(PSCMSETTINGSBASE pBase, const char *pszLine) 639 { 640 int cArgs; 641 char **papszArgs; 642 int rc = RTGetOptArgvFromString(&papszArgs, &cArgs, pszLine, NULL); 643 if (RT_SUCCESS(rc)) 644 { 645 RTGETOPTUNION ValueUnion; 646 RTGETOPTSTATE GetOptState; 647 rc = RTGetOptInit(&GetOptState, cArgs, papszArgs, &g_aScmOpts[0], RT_ELEMENTS(g_aScmOpts), 0, 0 /*fFlags*/); 648 if (RT_SUCCESS(rc)) 649 { 650 while ((rc = RTGetOpt(&GetOptState, &ValueUnion)) != 0) 651 { 652 rc = scmSettingsBaseHandleOpt(pBase, rc, &ValueUnion); 653 if (RT_FAILURE(rc)) 654 break; 655 } 656 } 657 RTGetOptArgvFree(papszArgs); 658 } 659 660 return rc; 661 } 662 663 /** 664 * Parses an unterminated option string. 665 * 666 * @returns IPRT status code. 667 * @param pBase The base settings structure to apply the options 668 * to. 669 * @param pchLine The line. 670 * @param cchLine The line length. 671 */ 672 static int scmSettingsBaseParseStringN(PSCMSETTINGSBASE pBase, const char *pchLine, size_t cchLine) 673 { 674 char *pszLine = RTStrDupN(pchLine, cchLine); 675 if (!pszLine) 676 return VERR_NO_MEMORY; 677 int rc = scmSettingsBaseParseString(pBase, pszLine); 678 RTStrFree(pszLine); 679 return rc; 680 } 681 682 /** 683 * Verifies the options string. 684 * 685 * @returns IPRT status code. 686 * @param pszOptions The options to verify . 687 */ 688 static int scmSettingsBaseVerifyString(const char *pszOptions) 689 { 690 SCMSETTINGSBASE Base; 691 int rc = scmSettingsBaseInit(&Base); 692 if (RT_SUCCESS(rc)) 693 { 694 rc = scmSettingsBaseParseString(&Base, pszOptions); 695 scmSettingsBaseDelete(&Base); 696 } 697 return rc; 698 } 699 700 /** 701 * Loads settings found in editor and SCM settings directives within the 702 * document (@a pStream). 703 * 704 * @returns IPRT status code. 705 * @param pBase The settings base to load settings into. 706 * @param pStream The stream to scan for settings directives. 707 */ 708 static int scmSettingsBaseLoadFromDocument(PSCMSETTINGSBASE pBase, PSCMSTREAM pStream) 709 { 710 /** @todo Editor and SCM settings directives in documents. */ 711 return VINF_SUCCESS; 712 } 713 714 /** 715 * Creates a new settings file struct, cloning @a pSettings. 716 * 717 * @returns IPRT status code. 718 * @param ppSettings Where to return the new struct. 719 * @param pSettingsBase The settings to inherit from. 720 */ 721 static int scmSettingsCreate(PSCMSETTINGS *ppSettings, PCSCMSETTINGSBASE pSettingsBase) 722 { 723 PSCMSETTINGS pSettings = (PSCMSETTINGS)RTMemAlloc(sizeof(*pSettings)); 724 if (!pSettings) 725 return VERR_NO_MEMORY; 726 int rc = scmSettingsBaseInitAndCopy(&pSettings->Base, pSettingsBase); 727 if (RT_SUCCESS(rc)) 728 { 729 pSettings->pDown = NULL; 730 pSettings->pUp = NULL; 731 pSettings->paPairs = NULL; 732 pSettings->cPairs = 0; 733 *ppSettings = pSettings; 734 return VINF_SUCCESS; 735 } 736 RTMemFree(pSettings); 737 return rc; 738 } 739 740 /** 741 * Destroys a settings structure. 742 * 743 * @param pSettings The settings structure to destroy. NULL is OK. 744 */ 745 static void scmSettingsDestroy(PSCMSETTINGS pSettings) 746 { 747 if (pSettings) 748 { 749 scmSettingsBaseDelete(&pSettings->Base); 750 for (size_t i = 0; i < pSettings->cPairs; i++) 751 { 752 RTStrFree(pSettings->paPairs[i].pszPattern); 753 RTStrFree(pSettings->paPairs[i].pszOptions); 754 pSettings->paPairs[i].pszPattern = NULL; 755 pSettings->paPairs[i].pszOptions = NULL; 756 } 757 RTMemFree(pSettings->paPairs); 758 pSettings->paPairs = NULL; 759 RTMemFree(pSettings); 760 } 761 } 762 763 /** 764 * Adds a pattern/options pair to the settings structure. 765 * 766 * @returns IPRT status code. 767 * @param pSettings The settings. 768 * @param pchLine The line containing the unparsed pair. 769 * @param cchLine The length of the line. 770 */ 771 static int scmSettingsAddPair(PSCMSETTINGS pSettings, const char *pchLine, size_t cchLine) 772 { 773 /* 774 * Split the string. 775 */ 776 const char *pchOptions = (const char *)memchr(pchLine, ':', cchLine); 777 if (!pchOptions) 778 return VERR_INVALID_PARAMETER; 779 size_t cchPattern = pchOptions - pchLine; 780 size_t cchOptions = cchLine - cchPattern - 1; 781 pchOptions++; 782 783 /* strip spaces everywhere */ 784 while (cchPattern > 0 && RT_C_IS_SPACE(pchLine[cchPattern - 1])) 785 cchPattern--; 786 while (cchPattern > 0 && RT_C_IS_SPACE(*pchLine)) 787 cchPattern--, pchLine++; 788 789 while (cchOptions > 0 && RT_C_IS_SPACE(pchOptions[cchOptions - 1])) 790 cchOptions--; 791 while (cchOptions > 0 && RT_C_IS_SPACE(*pchOptions)) 792 cchOptions--, pchOptions++; 793 794 /* Quietly ignore empty patterns and empty options. */ 795 if (!cchOptions || !cchPattern) 796 return VINF_SUCCESS; 797 798 /* 799 * Add the pair and verify the option string. 800 */ 801 uint32_t iPair = pSettings->cPairs; 802 if ((iPair % 32) == 0) 803 { 804 void *pvNew = RTMemRealloc(pSettings->paPairs, (iPair + 32) * sizeof(pSettings->paPairs[0])); 805 if (!pvNew) 806 return VERR_NO_MEMORY; 807 pSettings->paPairs = (PSCMPATRNOPTPAIR)pvNew; 808 } 809 810 pSettings->paPairs[iPair].pszPattern = RTStrDupN(pchLine, cchPattern); 811 pSettings->paPairs[iPair].pszOptions = RTStrDupN(pchOptions, cchOptions); 812 int rc; 813 if ( pSettings->paPairs[iPair].pszPattern 814 && pSettings->paPairs[iPair].pszOptions) 815 rc = scmSettingsBaseVerifyString(pSettings->paPairs[iPair].pszOptions); 816 else 817 rc = VERR_NO_MEMORY; 818 if (RT_SUCCESS(rc)) 819 pSettings->cPairs = iPair + 1; 820 else 821 { 822 RTStrFree(pSettings->paPairs[iPair].pszPattern); 823 RTStrFree(pSettings->paPairs[iPair].pszOptions); 824 } 825 return rc; 826 } 827 828 /** 829 * Loads in the settings from @a pszFilename. 830 * 831 * @returns IPRT status code. 832 * @param pSettings Where to load the settings file. 833 * @param pszFilename The file to load. 834 */ 835 static int scmSettingsLoadFile(PSCMSETTINGS pSettings, const char *pszFilename) 836 { 837 SCMSTREAM Stream; 838 int rc = ScmStreamInitForReading(&Stream, pszFilename); 839 if (RT_FAILURE(rc)) 840 { 841 RTMsgError("%s: ScmStreamInitForReading -> %Rrc\n", pszFilename, rc); 842 return rc; 843 } 844 845 SCMEOL enmEol; 846 const char *pchLine; 847 size_t cchLine; 848 while ((pchLine = ScmStreamGetLine(&Stream, &cchLine, &enmEol)) != NULL) 849 { 850 /* Ignore leading spaces. */ 851 while (cchLine > 0 && RT_C_IS_SPACE(*pchLine)) 852 pchLine++, cchLine--; 853 854 /* Ignore empty lines and comment lines. */ 855 if (cchLine < 1 || *pchLine == '#') 856 continue; 857 858 /* What kind of line is it? */ 859 const char *pchColon = (const char *)memchr(pchLine, ':', cchLine); 860 if (pchColon) 861 rc = scmSettingsAddPair(pSettings, pchLine, cchLine); 862 else 863 rc = scmSettingsBaseParseStringN(&pSettings->Base, pchLine, cchLine); 864 if (RT_FAILURE(rc)) 865 { 866 RTMsgError("%s:%d: %Rrc\n", pszFilename, ScmStreamTellLine(&Stream), rc); 867 break; 868 } 869 } 870 871 if (RT_SUCCESS(rc)) 872 { 873 rc = ScmStreamGetStatus(&Stream); 874 if (RT_FAILURE(rc)) 875 RTMsgError("%s: ScmStreamGetStatus- > %Rrc\n", pszFilename, rc); 876 } 877 878 ScmStreamDelete(&Stream); 879 return rc; 880 } 881 882 /** 883 * Parse the specified settings file creating a new settings struct from it. 884 * 885 * @returns IPRT status code 886 * @param ppSettings Where to return the new settings. 887 * @param pszFilename The file to parse. 888 * @param pSettingsBase The base settings we inherit from. 889 */ 890 static int scmSettingsCreateFromFile(PSCMSETTINGS *ppSettings, const char *pszFilename, PCSCMSETTINGSBASE pSettingsBase) 891 { 892 PSCMSETTINGS pSettings; 893 int rc = scmSettingsCreate(&pSettings, pSettingsBase); 894 if (RT_SUCCESS(rc)) 895 { 896 rc = scmSettingsLoadFile(pSettings, pszFilename); 897 if (RT_SUCCESS(rc)) 898 { 899 *ppSettings = pSettings; 900 return VINF_SUCCESS; 901 } 902 903 scmSettingsDestroy(pSettings); 904 } 905 *ppSettings = NULL; 906 return rc; 907 } 908 909 910 /** 911 * Create an initial settings structure when starting processing a new file or 912 * directory. 913 * 914 * This will look for .scm-settings files from the root and down to the 915 * specified directory, combining them into the returned settings structure. 916 * 917 * @returns IPRT status code. 918 * @param ppSettings Where to return the pointer to the top stack 919 * object. 920 * @param pBaseSettings The base settings we inherit from (globals 921 * typically). 922 * @param pszPath The absolute path to the new directory or file. 923 */ 924 static int scmSettingsCreateForPath(PSCMSETTINGS *ppSettings, PCSCMSETTINGSBASE pBaseSettings, const char *pszPath) 925 { 926 *ppSettings = NULL; /* try shut up gcc. */ 927 928 /* 929 * We'll be working with a stack copy of the path. 930 */ 931 char szFile[RTPATH_MAX]; 932 size_t cchDir = strlen(pszPath); 933 if (cchDir >= sizeof(szFile) - sizeof(SCM_SETTINGS_FILENAME)) 934 return VERR_FILENAME_TOO_LONG; 935 936 /* 937 * Create the bottom-most settings. 938 */ 939 PSCMSETTINGS pSettings; 940 int rc = scmSettingsCreate(&pSettings, pBaseSettings); 941 if (RT_FAILURE(rc)) 942 return rc; 943 944 /* 945 * Enumerate the path components from the root and down. Load any setting 946 * files we find. 947 */ 948 size_t cComponents = RTPathCountComponents(pszPath); 949 for (size_t i = 1; i <= cComponents; i++) 950 { 951 rc = RTPathCopyComponents(szFile, sizeof(szFile), pszPath, i); 952 if (RT_SUCCESS(rc)) 953 rc = RTPathAppend(szFile, sizeof(szFile), SCM_SETTINGS_FILENAME); 954 if (RT_FAILURE(rc)) 955 break; 956 if (RTFileExists(szFile)) 957 { 958 rc = scmSettingsLoadFile(pSettings, szFile); 959 if (RT_FAILURE(rc)) 960 break; 961 } 962 } 963 964 if (RT_SUCCESS(rc)) 965 *ppSettings = pSettings; 966 else 967 scmSettingsDestroy(pSettings); 968 return rc; 969 } 970 971 /** 972 * Pushes a new settings set onto the stack. 973 * 974 * @param ppSettingsStack The pointer to the pointer to the top stack 975 * element. This will be used as input and output. 976 * @param pSettings The settings to push onto the stack. 977 */ 978 static void scmSettingsStackPush(PSCMSETTINGS *ppSettingsStack, PSCMSETTINGS pSettings) 979 { 980 PSCMSETTINGS pOld = *ppSettingsStack; 981 pSettings->pDown = pOld; 982 pSettings->pUp = NULL; 983 if (pOld) 984 pOld->pUp = pSettings; 985 *ppSettingsStack = pSettings; 986 } 987 988 /** 989 * Pushes the settings of the specified directory onto the stack. 990 * 991 * We will load any .scm-settings in the directory. A stack entry is added even 992 * if no settings file was found. 993 * 994 * @returns IPRT status code. 995 * @param ppSettingsStack The pointer to the pointer to the top stack 996 * element. This will be used as input and output. 997 * @param pszDir The directory to do this for. 998 */ 999 static int scmSettingsStackPushDir(PSCMSETTINGS *ppSettingsStack, const char *pszDir) 1000 { 1001 char szFile[RTPATH_MAX]; 1002 int rc = RTPathJoin(szFile, sizeof(szFile), pszDir, SCM_SETTINGS_FILENAME); 1003 if (RT_SUCCESS(rc)) 1004 { 1005 PSCMSETTINGS pSettings; 1006 rc = scmSettingsCreate(&pSettings, &(*ppSettingsStack)->Base); 1007 if (RT_SUCCESS(rc)) 1008 { 1009 if (RTFileExists(szFile)) 1010 rc = scmSettingsLoadFile(pSettings, szFile); 1011 if (RT_SUCCESS(rc)) 1012 { 1013 scmSettingsStackPush(ppSettingsStack, pSettings); 1014 return VINF_SUCCESS; 1015 } 1016 1017 scmSettingsDestroy(pSettings); 1018 } 1019 } 1020 return rc; 1021 } 1022 1023 1024 /** 1025 * Pops a settings set off the stack. 1026 * 1027 * @returns The popped setttings. 1028 * @param ppSettingsStack The pointer to the pointer to the top stack 1029 * element. This will be used as input and output. 1030 */ 1031 static PSCMSETTINGS scmSettingsStackPop(PSCMSETTINGS *ppSettingsStack) 1032 { 1033 PSCMSETTINGS pRet = *ppSettingsStack; 1034 PSCMSETTINGS pNew = pRet ? pRet->pDown : NULL; 1035 *ppSettingsStack = pNew; 1036 if (pNew) 1037 pNew->pUp = NULL; 1038 if (pRet) 1039 { 1040 pRet->pUp = NULL; 1041 pRet->pDown = NULL; 1042 } 1043 return pRet; 1044 } 1045 1046 /** 1047 * Pops and destroys the top entry of the stack. 1048 * 1049 * @param ppSettingsStack The pointer to the pointer to the top stack 1050 * element. This will be used as input and output. 1051 */ 1052 static void scmSettingsStackPopAndDestroy(PSCMSETTINGS *ppSettingsStack) 1053 { 1054 scmSettingsDestroy(scmSettingsStackPop(ppSettingsStack)); 1055 } 1056 1057 /** 1058 * Constructs the base settings for the specified file name. 1059 * 1060 * @returns IPRT status code. 1061 * @param pSettingsStack The top element on the settings stack. 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 pBase Base settings to initialize. 1067 */ 1068 static int scmSettingsStackMakeFileBase(PCSCMSETTINGS pSettingsStack, const char *pszFilename, 1069 const char *pszBasename, size_t cchBasename, PSCMSETTINGSBASE pBase) 1070 { 1071 int rc = scmSettingsBaseInitAndCopy(pBase, &pSettingsStack->Base); 1072 if (RT_SUCCESS(rc)) 1073 { 1074 /* find the bottom entry in the stack. */ 1075 PCSCMSETTINGS pCur = pSettingsStack; 1076 while (pCur->pDown) 1077 pCur = pCur->pDown; 1078 1079 /* Work our way up thru the stack and look for matching pairs. */ 1080 while (pCur) 1081 { 1082 size_t const cPairs = pCur->cPairs; 1083 if (cPairs) 1084 { 1085 for (size_t i = 0; i < cPairs; i++) 1086 if ( RTStrSimplePatternMultiMatch(pCur->paPairs[i].pszPattern, RTSTR_MAX, 1087 pszBasename, cchBasename, NULL) 1088 || RTStrSimplePatternMultiMatch(pCur->paPairs[i].pszPattern, RTSTR_MAX, 1089 pszFilename, RTSTR_MAX, NULL)) 1090 { 1091 rc = scmSettingsBaseParseString(pBase, pCur->paPairs[i].pszOptions); 1092 if (RT_FAILURE(rc)) 1093 break; 1094 } 1095 if (RT_FAILURE(rc)) 1096 break; 1097 } 1098 1099 /* advance */ 1100 pCur = pCur->pUp; 1101 } 1102 } 1103 if (RT_FAILURE(rc)) 1104 scmSettingsBaseDelete(pBase); 1105 return rc; 1106 } 1107 1108 1109 /* -=-=-=-=-=- misc -=-=-=-=-=- */ 1110 1111 1112 /** 1113 * Prints a verbose message if the level is high enough. 1114 * 1115 * @param pState The rewrite state. Optional. 1116 * @param iLevel The required verbosity level. 1117 * @param pszFormat The message format string. Can be NULL if we 1118 * only want to trigger the per file message. 1119 * @param ... Format arguments. 1120 */ 1121 static void ScmVerbose(PSCMRWSTATE pState, int iLevel, const char *pszFormat, ...) 1122 { 1123 if (iLevel <= g_iVerbosity) 1124 { 1125 if (pState && !pState->fFirst) 1126 { 1127 RTPrintf("%s: info: --= Rewriting '%s' =--\n", g_szProgName, pState->pszFilename); 1128 pState->fFirst = true; 1129 } 1130 if (pszFormat) 1131 { 1132 RTPrintf(pState 1133 ? "%s: info: " 1134 : "%s: info: ", 1135 g_szProgName); 1136 va_list va; 1137 va_start(va, pszFormat); 1138 RTPrintfV(pszFormat, va); 1139 va_end(va); 1140 } 1141 } 1142 } 1143 1144 1145 /* -=-=-=-=-=- subversion -=-=-=-=-=- */ 1146 1147 #define SCM_WITHOUT_LIBSVN 1148 1149 #ifdef SCM_WITHOUT_LIBSVN 1150 1151 /** 1152 * Callback that is call for each path to search. 1153 */ 1154 static DECLCALLBACK(int) scmSvnFindSvnBinaryCallback(char const *pchPath, size_t cchPath, void *pvUser1, void *pvUser2) 1155 { 1156 char *pszDst = (char *)pvUser1; 1157 size_t cchDst = (size_t)pvUser2; 1158 if (cchDst > cchPath) 1159 { 1160 memcpy(pszDst, pchPath, cchPath); 1161 pszDst[cchPath] = '\0'; 1162 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) 1163 int rc = RTPathAppend(pszDst, cchDst, "svn.exe"); 1164 #else 1165 int rc = RTPathAppend(pszDst, cchDst, "svn"); 1166 #endif 1167 if ( RT_SUCCESS(rc) 1168 && RTFileExists(pszDst)) 1169 return VINF_SUCCESS; 1170 } 1171 return VERR_TRY_AGAIN; 1172 } 1173 1174 1175 /** 1176 * Finds the svn binary. 1177 * 1178 * @param pszPath Where to store it. Worst case, we'll return 1179 * "svn" here. 1180 * @param cchPath The size of the buffer pointed to by @a pszPath. 1181 */ 1182 static void scmSvnFindSvnBinary(char *pszPath, size_t cchPath) 1183 { 1184 /** @todo code page fun... */ 1185 Assert(cchPath >= sizeof("svn")); 1186 #ifdef RT_OS_WINDOWS 1187 const char *pszEnvVar = RTEnvGet("Path"); 1188 #else 1189 const char *pszEnvVar = RTEnvGet("PATH"); 1190 #endif 1191 if (pszPath) 1192 { 1193 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) 1194 int rc = RTPathTraverseList(pszEnvVar, ';', scmSvnFindSvnBinaryCallback, pszPath, (void *)cchPath); 1195 #else 1196 int rc = RTPathTraverseList(pszEnvVar, ':', scmSvnFindSvnBinaryCallback, pszPath, (void *)cchPath); 1197 #endif 1198 if (RT_SUCCESS(rc)) 1199 return; 1200 } 1201 strcpy(pszPath, "svn"); 1202 } 1203 1204 1205 /** 1206 * Construct a dot svn filename for the file being rewritten. 1207 * 1208 * @returns IPRT status code. 1209 * @param pState The rewrite state (for the name). 1210 * @param pszDir The directory, including ".svn/". 1211 * @param pszSuff The filename suffix. 1212 * @param pszDst The output buffer. RTPATH_MAX in size. 1213 */ 1214 static int scmSvnConstructName(PSCMRWSTATE pState, const char *pszDir, const char *pszSuff, char *pszDst) 1215 { 1216 strcpy(pszDst, pState->pszFilename); /* ASSUMES sizeof(szBuf) <= sizeof(szPath) */ 1217 RTPathStripFilename(pszDst); 1218 1219 int rc = RTPathAppend(pszDst, RTPATH_MAX, pszDir); 1220 if (RT_SUCCESS(rc)) 1221 { 1222 rc = RTPathAppend(pszDst, RTPATH_MAX, RTPathFilename(pState->pszFilename)); 1223 if (RT_SUCCESS(rc)) 1224 { 1225 size_t cchDst = strlen(pszDst); 1226 size_t cchSuff = strlen(pszSuff); 1227 if (cchDst + cchSuff < RTPATH_MAX) 1228 { 1229 memcpy(&pszDst[cchDst], pszSuff, cchSuff + 1); 1230 return VINF_SUCCESS; 1231 } 1232 else 1233 rc = VERR_BUFFER_OVERFLOW; 1234 } 1235 } 1236 return rc; 1237 } 1238 1239 /** 1240 * Interprets the specified string as decimal numbers. 1241 * 1242 * @returns true if parsed successfully, false if not. 1243 * @param pch The string (not terminated). 1244 * @param cch The string length. 1245 * @param pu Where to return the value. 1246 */ 1247 static bool scmSvnReadNumber(const char *pch, size_t cch, size_t *pu) 1248 { 1249 size_t u = 0; 1250 while (cch-- > 0) 1251 { 1252 char ch = *pch++; 1253 if (ch < '0' || ch > '9') 1254 return false; 1255 u *= 10; 1256 u += ch - '0'; 1257 } 1258 *pu = u; 1259 return true; 1260 } 1261 1262 #endif /* SCM_WITHOUT_LIBSVN */ 1263 1264 /** 1265 * Checks if the file we're operating on is part of a SVN working copy. 1266 * 1267 * @returns true if it is, false if it isn't or we cannot tell. 1268 * @param pState The rewrite state to work on. 1269 */ 1270 static bool scmSvnIsInWorkingCopy(PSCMRWSTATE pState) 1271 { 1272 #ifdef SCM_WITHOUT_LIBSVN 1273 /* 1274 * Hack: check if the .svn/text-base/<file>.svn-base file exists. 1275 */ 1276 char szPath[RTPATH_MAX]; 1277 int rc = scmSvnConstructName(pState, ".svn/text-base/", ".svn-base", szPath); 1278 if (RT_SUCCESS(rc)) 1279 return RTFileExists(szPath); 1280 1281 #else 1282 NOREF(pState); 1283 #endif 1284 return false; 1285 } 1286 1287 /** 1288 * Queries the value of an SVN property. 1289 * 1290 * This will automatically adjust for scheduled changes. 1291 * 1292 * @returns IPRT status code. 1293 * @retval VERR_INVALID_STATE if not a SVN WC file. 1294 * @retval VERR_NOT_FOUND if the property wasn't found. 1295 * @param pState The rewrite state to work on. 1296 * @param pszName The property name. 1297 * @param ppszValue Where to return the property value. Free this 1298 * using RTStrFree. Optional. 1299 */ 1300 static int scmSvnQueryProperty(PSCMRWSTATE pState, const char *pszName, char **ppszValue) 1301 { 1302 /* 1303 * Look it up in the scheduled changes. 1304 */ 1305 uint32_t i = pState->cSvnPropChanges; 1306 while (i-- > 0) 1307 if (!strcmp(pState->paSvnPropChanges[i].pszName, pszName)) 1308 { 1309 const char *pszValue = pState->paSvnPropChanges[i].pszValue; 1310 if (!pszValue) 1311 return VERR_NOT_FOUND; 1312 if (ppszValue) 1313 return RTStrDupEx(ppszValue, pszValue); 1314 return VINF_SUCCESS; 1315 } 1316 1317 #ifdef SCM_WITHOUT_LIBSVN 1318 /* 1319 * Hack: Read the .svn/props/<file>.svn-work file exists. 1320 */ 1321 char szPath[RTPATH_MAX]; 1322 int rc = scmSvnConstructName(pState, ".svn/props/", ".svn-work", szPath); 1323 if (RT_SUCCESS(rc) && !RTFileExists(szPath)) 1324 rc = scmSvnConstructName(pState, ".svn/prop-base/", ".svn-base", szPath); 1325 if (RT_SUCCESS(rc)) 1326 { 1327 SCMSTREAM Stream; 1328 rc = ScmStreamInitForReading(&Stream, szPath); 1329 if (RT_SUCCESS(rc)) 1330 { 1331 /* 1332 * The current format is K len\n<name>\nV len\n<value>\n" ... END. 1333 */ 1334 rc = VERR_NOT_FOUND; 1335 size_t const cchName = strlen(pszName); 1336 SCMEOL enmEol; 1337 size_t cchLine; 1338 const char *pchLine; 1339 while ((pchLine = ScmStreamGetLine(&Stream, &cchLine, &enmEol)) != NULL) 1340 { 1341 /* 1342 * Parse the 'K num' / 'END' line. 1343 */ 1344 if ( cchLine == 3 1345 && !memcmp(pchLine, "END", 3)) 1346 break; 1347 size_t cchKey; 1348 if ( cchLine < 3 1349 || pchLine[0] != 'K' 1350 || pchLine[1] != ' ' 1351 || !scmSvnReadNumber(&pchLine[2], cchLine - 2, &cchKey) 1352 || cchKey == 0 1353 || cchKey > 4096) 1354 { 1355 RTMsgError("%s:%u: Unexpected data '%.*s'\n", szPath, ScmStreamTellLine(&Stream), cchLine, pchLine); 1356 rc = VERR_PARSE_ERROR; 1357 break; 1358 } 1359 1360 /* 1361 * Match the key and skip to the value line. Don't bother with 1362 * names containing EOL markers. 1363 */ 1364 size_t const offKey = ScmStreamTell(&Stream); 1365 bool fMatch = cchName == cchKey; 1366 if (fMatch) 1367 { 1368 pchLine = ScmStreamGetLine(&Stream, &cchLine, &enmEol); 1369 if (!pchLine) 1370 break; 1371 fMatch = cchLine == cchName 1372 && !memcmp(pchLine, pszName, cchName); 1373 } 1374 1375 if (RT_FAILURE(ScmStreamSeekAbsolute(&Stream, offKey + cchKey))) 1376 break; 1377 if (RT_FAILURE(ScmStreamSeekByLine(&Stream, ScmStreamTellLine(&Stream) + 1))) 1378 break; 1379 1380 /* 1381 * Read and Parse the 'V num' line. 1382 */ 1383 pchLine = ScmStreamGetLine(&Stream, &cchLine, &enmEol); 1384 if (!pchLine) 1385 break; 1386 size_t cchValue; 1387 if ( cchLine < 3 1388 || pchLine[0] != 'V' 1389 || pchLine[1] != ' ' 1390 || !scmSvnReadNumber(&pchLine[2], cchLine - 2, &cchValue) 1391 || cchValue > _1M) 1392 { 1393 RTMsgError("%s:%u: Unexpected data '%.*s'\n", szPath, ScmStreamTellLine(&Stream), cchLine, pchLine); 1394 rc = VERR_PARSE_ERROR; 1395 break; 1396 } 1397 1398 /* 1399 * If we have a match, allocate a return buffer and read the 1400 * value into it. Otherwise skip this value and continue 1401 * searching. 1402 */ 1403 if (fMatch) 1404 { 1405 if (!ppszValue) 1406 rc = VINF_SUCCESS; 1407 else 1408 { 1409 char *pszValue; 1410 rc = RTStrAllocEx(&pszValue, cchValue + 1); 1411 if (RT_SUCCESS(rc)) 1412 { 1413 rc = ScmStreamRead(&Stream, pszValue, cchValue); 1414 if (RT_SUCCESS(rc)) 1415 *ppszValue = pszValue; 1416 else 1417 RTStrFree(pszValue); 1418 } 1419 } 1420 break; 1421 } 1422 1423 if (RT_FAILURE(ScmStreamSeekRelative(&Stream, cchValue))) 1424 break; 1425 if (RT_FAILURE(ScmStreamSeekByLine(&Stream, ScmStreamTellLine(&Stream) + 1))) 1426 break; 1427 } 1428 1429 if (RT_FAILURE(ScmStreamGetStatus(&Stream))) 1430 { 1431 rc = ScmStreamGetStatus(&Stream); 1432 RTMsgError("%s: stream error %Rrc\n", szPath, rc); 1433 } 1434 ScmStreamDelete(&Stream); 1435 } 1436 } 1437 1438 if (rc == VERR_FILE_NOT_FOUND) 1439 rc = VERR_NOT_FOUND; 1440 return rc; 1441 1442 #else 1443 NOREF(pState); 1444 #endif 1445 return VERR_NOT_FOUND; 1446 } 1447 1448 1449 /** 1450 * Schedules the setting of a property. 1451 * 1452 * @returns IPRT status code. 1453 * @retval VERR_INVALID_STATE if not a SVN WC file. 1454 * @param pState The rewrite state to work on. 1455 * @param pszName The name of the property to set. 1456 * @param pszValue The value. NULL means deleting it. 1457 */ 1458 static int scmSvnSetProperty(PSCMRWSTATE pState, const char *pszName, const char *pszValue) 1459 { 1460 /* 1461 * Update any existing entry first. 1462 */ 1463 size_t i = pState->cSvnPropChanges; 1464 while (i-- > 0) 1465 if (!strcmp(pState->paSvnPropChanges[i].pszName, pszName)) 1466 { 1467 if (!pszValue) 1468 { 1469 RTStrFree(pState->paSvnPropChanges[i].pszValue); 1470 pState->paSvnPropChanges[i].pszValue = NULL; 1471 } 1472 else 1473 { 1474 char *pszCopy; 1475 int rc = RTStrDupEx(&pszCopy, pszValue); 1476 if (RT_FAILURE(rc)) 1477 return rc; 1478 pState->paSvnPropChanges[i].pszValue = pszCopy; 1479 } 1480 return VINF_SUCCESS; 1481 } 1482 1483 /* 1484 * Insert a new entry. 1485 */ 1486 i = pState->cSvnPropChanges; 1487 if ((i % 32) == 0) 1488 { 1489 void *pvNew = RTMemRealloc(pState->paSvnPropChanges, (i + 32) * sizeof(SCMSVNPROP)); 1490 if (!pvNew) 1491 return VERR_NO_MEMORY; 1492 pState->paSvnPropChanges = (PSCMSVNPROP)pvNew; 1493 } 1494 1495 pState->paSvnPropChanges[i].pszName = RTStrDup(pszName); 1496 pState->paSvnPropChanges[i].pszValue = pszValue ? RTStrDup(pszValue) : NULL; 1497 if ( pState->paSvnPropChanges[i].pszName 1498 && (pState->paSvnPropChanges[i].pszValue || !pszValue) ) 1499 pState->cSvnPropChanges = i + 1; 1500 else 1501 { 1502 RTStrFree(pState->paSvnPropChanges[i].pszName); 1503 pState->paSvnPropChanges[i].pszName = NULL; 1504 RTStrFree(pState->paSvnPropChanges[i].pszValue); 1505 pState->paSvnPropChanges[i].pszValue = NULL; 1506 return VERR_NO_MEMORY; 1507 } 1508 return VINF_SUCCESS; 1509 } 1510 1511 1512 /** 1513 * Schedules a property deletion. 1514 * 1515 * @returns IPRT status code. 1516 * @param pState The rewrite state to work on. 1517 * @param pszName The name of the property to delete. 1518 */ 1519 static int scmSvnDelProperty(PSCMRWSTATE pState, const char *pszName) 1520 { 1521 return scmSvnSetProperty(pState, pszName, NULL); 1522 } 1523 1524 1525 /** 1526 * Applies any SVN property changes to the work copy of the file. 1527 * 1528 * @returns IPRT status code. 1529 * @param pState The rewrite state which SVN property changes 1530 * should be applied. 1531 */ 1532 static int scmSvnDisplayChanges(PSCMRWSTATE pState) 1533 { 1534 size_t i = pState->cSvnPropChanges; 1535 while (i-- > 0) 1536 { 1537 const char *pszName = pState->paSvnPropChanges[i].pszName; 1538 const char *pszValue = pState->paSvnPropChanges[i].pszValue; 1539 if (pszValue) 1540 ScmVerbose(pState, 0, "svn ps '%s' '%s' %s\n", pszName, pszValue, pState->pszFilename); 1541 else 1542 ScmVerbose(pState, 0, "svn pd '%s' %s\n", pszName, pszValue, pState->pszFilename); 1543 } 1544 1545 return VINF_SUCCESS; 1546 } 1547 1548 /** 1549 * Applies any SVN property changes to the work copy of the file. 1550 * 1551 * @returns IPRT status code. 1552 * @param pState The rewrite state which SVN property changes 1553 * should be applied. 1554 */ 1555 static int scmSvnApplyChanges(PSCMRWSTATE pState) 1556 { 1557 #ifdef SCM_WITHOUT_LIBSVN 1558 /* 1559 * This sucks. We gotta find svn(.exe). 1560 */ 1561 static char s_szSvnPath[RTPATH_MAX]; 1562 if (s_szSvnPath[0] == '\0') 1563 scmSvnFindSvnBinary(s_szSvnPath, sizeof(s_szSvnPath)); 1564 1565 /* 1566 * Iterate thru the changes and apply them by starting the svn client. 1567 */ 1568 for (size_t i = 0; i <pState->cSvnPropChanges; i++) 1569 { 1570 const char *apszArgv[6]; 1571 apszArgv[0] = s_szSvnPath; 1572 apszArgv[1] = pState->paSvnPropChanges[i].pszValue ? "ps" : "pd"; 1573 apszArgv[2] = pState->paSvnPropChanges[i].pszName; 1574 int iArg = 3; 1575 if (pState->paSvnPropChanges[i].pszValue) 1576 apszArgv[iArg++] = pState->paSvnPropChanges[i].pszValue; 1577 apszArgv[iArg++] = pState->pszFilename; 1578 apszArgv[iArg++] = NULL; 1579 ScmVerbose(pState, 2, "executing: %s %s %s %s %s\n", 1580 apszArgv[0], apszArgv[1], apszArgv[2], apszArgv[3], apszArgv[4]); 1581 1582 RTPROCESS pid; 1583 int rc = RTProcCreate(s_szSvnPath, apszArgv, RTENV_DEFAULT, 0 /*fFlags*/, &pid); 1584 if (RT_SUCCESS(rc)) 1585 { 1586 RTPROCSTATUS Status; 1587 rc = RTProcWait(pid, RTPROCWAIT_FLAGS_BLOCK, &Status); 1588 if ( RT_SUCCESS(rc) 1589 && ( Status.enmReason != RTPROCEXITREASON_NORMAL 1590 || Status.iStatus != 0) ) 1591 { 1592 RTMsgError("%s: %s %s %s %s %s -> %s %u\n", 1593 pState->pszFilename, apszArgv[0], apszArgv[1], apszArgv[2], apszArgv[3], apszArgv[4], 1594 Status.enmReason == RTPROCEXITREASON_NORMAL ? "exit code" 1595 : Status.enmReason == RTPROCEXITREASON_SIGNAL ? "signal" 1596 : Status.enmReason == RTPROCEXITREASON_ABEND ? "abnormal end" 1597 : "abducted by alien", 1598 Status.iStatus); 1599 return VERR_GENERAL_FAILURE; 1600 } 1601 } 1602 if (RT_FAILURE(rc)) 1603 { 1604 RTMsgError("%s: error executing %s %s %s %s %s: %Rrc\n", 1605 pState->pszFilename, apszArgv[0], apszArgv[1], apszArgv[2], apszArgv[3], apszArgv[4], rc); 1606 return rc; 1607 } 1608 } 1609 1610 return VINF_SUCCESS; 1611 #else 1612 return VERR_NOT_IMPLEMENTED; 1613 #endif 1614 } 1615 1616 1617 /* -=-=-=-=-=- rewriters -=-=-=-=-=- */ 37 #include "scm.h" 38 1618 39 1619 40 … … 1626 47 * @param pSettings The settings. 1627 48 */ 1628 staticbool rewrite_StripTrailingBlanks(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings)49 bool rewrite_StripTrailingBlanks(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings) 1629 50 { 1630 51 if (!pSettings->fStripTrailingBlanks) … … 1665 86 * @param pSettings The settings. 1666 87 */ 1667 staticbool rewrite_ExpandTabs(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings)88 bool rewrite_ExpandTabs(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings) 1668 89 { 1669 90 if (!pSettings->fConvertTabs) … … 1752 173 /* Check svn:eol-style if appropriate */ 1753 174 if ( pSettings->fSetSvnEol 1754 && scmSvnIsInWorkingCopy(pState))175 && ScmSvnIsInWorkingCopy(pState)) 1755 176 { 1756 177 char *pszEol; 1757 int rc = scmSvnQueryProperty(pState, "svn:eol-style", &pszEol);178 int rc = ScmSvnQueryProperty(pState, "svn:eol-style", &pszEol); 1758 179 if ( (RT_SUCCESS(rc) && strcmp(pszEol, pszDesiredSvnEol)) 1759 180 || rc == VERR_NOT_FOUND) … … 1763 184 else 1764 185 ScmVerbose(pState, 2, " * Setting svn:eol-style to %s (was: %s)\n", pszDesiredSvnEol, pszEol); 1765 int rc2 = scmSvnSetProperty(pState, "svn:eol-style", pszDesiredSvnEol);186 int rc2 = ScmSvnSetProperty(pState, "svn:eol-style", pszDesiredSvnEol); 1766 187 if (RT_FAILURE(rc2)) 1767 RTMsgError(" scmSvnSetProperty: %Rrc\n", rc2); /** @todo propagate the error somehow... */188 RTMsgError("ScmSvnSetProperty: %Rrc\n", rc2); /** @todo propagate the error somehow... */ 1768 189 } 1769 190 if (RT_SUCCESS(rc)) … … 1783 204 * @param pSettings The settings. 1784 205 */ 1785 staticbool rewrite_ForceNativeEol(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings)206 bool rewrite_ForceNativeEol(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings) 1786 207 { 1787 208 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) … … 1800 221 * @param pSettings The settings. 1801 222 */ 1802 staticbool rewrite_ForceLF(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings)223 bool rewrite_ForceLF(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings) 1803 224 { 1804 225 return rewrite_ForceEol(pState, pIn, pOut, pSettings, SCMEOL_LF, "LF"); … … 1813 234 * @param pSettings The settings. 1814 235 */ 1815 staticbool rewrite_ForceCRLF(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings)236 bool rewrite_ForceCRLF(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings) 1816 237 { 1817 238 return rewrite_ForceEol(pState, pIn, pOut, pSettings, SCMEOL_CRLF, "CRLF"); … … 1829 250 * @remarks ASSUMES trailing white space has been removed already. 1830 251 */ 1831 staticbool rewrite_AdjustTrailingLines(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings)252 bool rewrite_AdjustTrailingLines(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings) 1832 253 { 1833 254 if ( !pSettings->fStripTrailingLines … … 1897 318 * @param pSettings The settings. 1898 319 */ 1899 staticbool rewrite_SvnNoExecutable(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings)320 bool rewrite_SvnNoExecutable(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings) 1900 321 { 1901 322 if ( !pSettings->fSetSvnExecutable 1902 || ! scmSvnIsInWorkingCopy(pState))1903 return false; 1904 1905 int rc = scmSvnQueryProperty(pState, "svn:executable", NULL);323 || !ScmSvnIsInWorkingCopy(pState)) 324 return false; 325 326 int rc = ScmSvnQueryProperty(pState, "svn:executable", NULL); 1906 327 if (RT_SUCCESS(rc)) 1907 328 { 1908 329 ScmVerbose(pState, 2, " * removing svn:executable\n"); 1909 rc = scmSvnDelProperty(pState, "svn:executable");330 rc = ScmSvnDelProperty(pState, "svn:executable"); 1910 331 if (RT_FAILURE(rc)) 1911 RTMsgError(" scmSvnSetProperty: %Rrc\n", rc); /** @todo error propagation here.. */332 RTMsgError("ScmSvnSetProperty: %Rrc\n", rc); /** @todo error propagation here.. */ 1912 333 } 1913 334 return false; … … 1923 344 * @param pSettings The settings. 1924 345 */ 1925 staticbool rewrite_SvnKeywords(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings)346 bool rewrite_SvnKeywords(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings) 1926 347 { 1927 348 if ( !pSettings->fSetSvnKeywords 1928 || ! scmSvnIsInWorkingCopy(pState))349 || !ScmSvnIsInWorkingCopy(pState)) 1929 350 return false; 1930 351 1931 352 char *pszKeywords; 1932 int rc = scmSvnQueryProperty(pState, "svn:keywords", &pszKeywords);353 int rc = ScmSvnQueryProperty(pState, "svn:keywords", &pszKeywords); 1933 354 if ( RT_SUCCESS(rc) 1934 355 && ( !strstr(pszKeywords, "Id") /** @todo need some function for finding a word in a string. */ … … 1944 365 { 1945 366 ScmVerbose(pState, 2, " * changing svn:keywords to '%s'\n", pszKeywords); 1946 rc = scmSvnSetProperty(pState, "svn:keywords", pszKeywords);367 rc = ScmSvnSetProperty(pState, "svn:keywords", pszKeywords); 1947 368 if (RT_FAILURE(rc)) 1948 RTMsgError(" scmSvnSetProperty: %Rrc\n", rc); /** @todo error propagation here.. */369 RTMsgError("ScmSvnSetProperty: %Rrc\n", rc); /** @todo error propagation here.. */ 1949 370 } 1950 371 else … … 1955 376 { 1956 377 ScmVerbose(pState, 2, " * setting svn:keywords to 'Id Revision'\n"); 1957 rc = scmSvnSetProperty(pState, "svn:keywords", "Id Revision");378 rc = ScmSvnSetProperty(pState, "svn:keywords", "Id Revision"); 1958 379 if (RT_FAILURE(rc)) 1959 RTMsgError(" scmSvnSetProperty: %Rrc\n", rc); /** @todo error propagation here.. */380 RTMsgError("ScmSvnSetProperty: %Rrc\n", rc); /** @todo error propagation here.. */ 1960 381 } 1961 382 else if (RT_SUCCESS(rc)) … … 1973 394 * @param pSettings The settings. 1974 395 */ 1975 staticbool rewrite_Makefile_kup(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings)396 bool rewrite_Makefile_kup(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings) 1976 397 { 1977 398 /* These files should be zero bytes. */ … … 1996 417 * - line continuation slashes should only be preceded by one space. 1997 418 */ 1998 staticbool rewrite_Makefile_kmk(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings)419 bool rewrite_Makefile_kmk(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings) 1999 420 { 2000 421 return false; … … 2032 453 * - string.h -> iprt/string.h, stdarg.h -> iprt/stdarg.h, etc. 2033 454 */ 2034 staticbool rewrite_C_and_CPP(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings)455 bool rewrite_C_and_CPP(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings) 2035 456 { 2036 457 … … 2038 459 } 2039 460 2040 /* -=-=-=-=-=- file and directory processing -=-=-=-=-=- */2041 2042 /**2043 * Processes a file.2044 *2045 * @returns IPRT status code.2046 * @param pState The rewriter state.2047 * @param pszFilename The file name.2048 * @param pszBasename The base name (pointer within @a pszFilename).2049 * @param cchBasename The length of the base name. (For passing to2050 * RTStrSimplePatternMultiMatch.)2051 * @param pBaseSettings The base settings to use. It's OK to modify2052 * these.2053 */2054 static int scmProcessFileInner(PSCMRWSTATE pState, const char *pszFilename, const char *pszBasename, size_t cchBasename,2055 PSCMSETTINGSBASE pBaseSettings)2056 {2057 /*2058 * Do the file level filtering.2059 */2060 if ( pBaseSettings->pszFilterFiles2061 && *pBaseSettings->pszFilterFiles2062 && !RTStrSimplePatternMultiMatch(pBaseSettings->pszFilterFiles, RTSTR_MAX, pszBasename, cchBasename, NULL))2063 {2064 ScmVerbose(NULL, 5, "skipping '%s': file filter mismatch\n", pszFilename);2065 return VINF_SUCCESS;2066 }2067 if ( pBaseSettings->pszFilterOutFiles2068 && *pBaseSettings->pszFilterOutFiles2069 && ( RTStrSimplePatternMultiMatch(pBaseSettings->pszFilterOutFiles, RTSTR_MAX, pszBasename, cchBasename, NULL)2070 || RTStrSimplePatternMultiMatch(pBaseSettings->pszFilterOutFiles, RTSTR_MAX, pszFilename, RTSTR_MAX, NULL)) )2071 {2072 ScmVerbose(NULL, 5, "skipping '%s': filterd out\n", pszFilename);2073 return VINF_SUCCESS;2074 }2075 if ( pBaseSettings->fOnlySvnFiles2076 && !scmSvnIsInWorkingCopy(pState))2077 {2078 ScmVerbose(NULL, 5, "skipping '%s': not in SVN WC\n", pszFilename);2079 return VINF_SUCCESS;2080 }2081 2082 /*2083 * Try find a matching rewrite config for this filename.2084 */2085 PCSCMCFGENTRY pCfg = NULL;2086 for (size_t iCfg = 0; iCfg < RT_ELEMENTS(g_aConfigs); iCfg++)2087 if (RTStrSimplePatternMultiMatch(g_aConfigs[iCfg].pszFilePattern, RTSTR_MAX, pszBasename, cchBasename, NULL))2088 {2089 pCfg = &g_aConfigs[iCfg];2090 break;2091 }2092 if (!pCfg)2093 {2094 ScmVerbose(NULL, 4, "skipping '%s': no rewriters configured\n", pszFilename);2095 return VINF_SUCCESS;2096 }2097 ScmVerbose(pState, 4, "matched \"%s\"\n", pCfg->pszFilePattern);2098 2099 /*2100 * Create an input stream from the file and check that it's text.2101 */2102 SCMSTREAM Stream1;2103 int rc = ScmStreamInitForReading(&Stream1, pszFilename);2104 if (RT_FAILURE(rc))2105 {2106 RTMsgError("Failed to read '%s': %Rrc\n", pszFilename, rc);2107 return rc;2108 }2109 if (ScmStreamIsText(&Stream1))2110 {2111 ScmVerbose(pState, 3, NULL);2112 2113 /*2114 * Gather SCM and editor settings from the stream.2115 */2116 rc = scmSettingsBaseLoadFromDocument(pBaseSettings, &Stream1);2117 if (RT_SUCCESS(rc))2118 {2119 ScmStreamRewindForReading(&Stream1);2120 2121 /*2122 * Create two more streams for output and push the text thru all the2123 * rewriters, switching the two streams around when something is2124 * actually rewritten. Stream1 remains unchanged.2125 */2126 SCMSTREAM Stream2;2127 rc = ScmStreamInitForWriting(&Stream2, &Stream1);2128 if (RT_SUCCESS(rc))2129 {2130 SCMSTREAM Stream3;2131 rc = ScmStreamInitForWriting(&Stream3, &Stream1);2132 if (RT_SUCCESS(rc))2133 {2134 bool fModified = false;2135 PSCMSTREAM pIn = &Stream1;2136 PSCMSTREAM pOut = &Stream2;2137 for (size_t iRw = 0; iRw < pCfg->cRewriters; iRw++)2138 {2139 bool fRc = pCfg->papfnRewriter[iRw](pState, pIn, pOut, pBaseSettings);2140 if (fRc)2141 {2142 PSCMSTREAM pTmp = pOut;2143 pOut = pIn == &Stream1 ? &Stream3 : pIn;2144 pIn = pTmp;2145 fModified = true;2146 }2147 ScmStreamRewindForReading(pIn);2148 ScmStreamRewindForWriting(pOut);2149 }2150 2151 rc = ScmStreamGetStatus(&Stream1);2152 if (RT_SUCCESS(rc))2153 rc = ScmStreamGetStatus(&Stream2);2154 if (RT_SUCCESS(rc))2155 rc = ScmStreamGetStatus(&Stream3);2156 if (RT_SUCCESS(rc))2157 {2158 /*2159 * If rewritten, write it back to disk.2160 */2161 if (fModified)2162 {2163 if (!g_fDryRun)2164 {2165 ScmVerbose(pState, 1, "writing modified file to \"%s%s\"\n", pszFilename, g_pszChangedSuff);2166 rc = ScmStreamWriteToFile(pIn, "%s%s", pszFilename, g_pszChangedSuff);2167 if (RT_FAILURE(rc))2168 RTMsgError("Error writing '%s%s': %Rrc\n", pszFilename, g_pszChangedSuff, rc);2169 }2170 else2171 {2172 ScmVerbose(pState, 1, NULL);2173 ScmDiffStreams(pszFilename, &Stream1, pIn, g_fDiffIgnoreEol, g_fDiffIgnoreLeadingWS,2174 g_fDiffIgnoreTrailingWS, g_fDiffSpecialChars, pBaseSettings->cchTab, g_pStdOut);2175 ScmVerbose(pState, 2, "would have modified the file \"%s%s\"\n", pszFilename, g_pszChangedSuff);2176 }2177 }2178 2179 /*2180 * If pending SVN property changes, apply them.2181 */2182 if (pState->cSvnPropChanges && RT_SUCCESS(rc))2183 {2184 if (!g_fDryRun)2185 {2186 rc = scmSvnApplyChanges(pState);2187 if (RT_FAILURE(rc))2188 RTMsgError("%s: failed to apply SVN property changes (%Rrc)\n", pszFilename, rc);2189 }2190 else2191 scmSvnDisplayChanges(pState);2192 }2193 2194 if (!fModified && !pState->cSvnPropChanges)2195 ScmVerbose(pState, 3, "no change\n", pszFilename);2196 }2197 else2198 RTMsgError("%s: stream error %Rrc\n", pszFilename);2199 ScmStreamDelete(&Stream3);2200 }2201 else2202 RTMsgError("Failed to init stream for writing: %Rrc\n", rc);2203 ScmStreamDelete(&Stream2);2204 }2205 else2206 RTMsgError("Failed to init stream for writing: %Rrc\n", rc);2207 }2208 else2209 RTMsgError("scmSettingsBaseLoadFromDocument: %Rrc\n", rc);2210 }2211 else2212 ScmVerbose(pState, 4, "not text file: \"%s\"\n", pszFilename);2213 ScmStreamDelete(&Stream1);2214 2215 return rc;2216 }2217 2218 /**2219 * Processes a file.2220 *2221 * This is just a wrapper for scmProcessFileInner for avoid wasting stack in the2222 * directory recursion method.2223 *2224 * @returns IPRT status code.2225 * @param pszFilename The file name.2226 * @param pszBasename The base name (pointer within @a pszFilename).2227 * @param cchBasename The length of the base name. (For passing to2228 * RTStrSimplePatternMultiMatch.)2229 * @param pSettingsStack The settings stack (pointer to the top element).2230 */2231 static int scmProcessFile(const char *pszFilename, const char *pszBasename, size_t cchBasename,2232 PSCMSETTINGS pSettingsStack)2233 {2234 SCMSETTINGSBASE Base;2235 int rc = scmSettingsStackMakeFileBase(pSettingsStack, pszFilename, pszBasename, cchBasename, &Base);2236 if (RT_SUCCESS(rc))2237 {2238 SCMRWSTATE State;2239 State.fFirst = false;2240 State.pszFilename = pszFilename;2241 State.cSvnPropChanges = 0;2242 State.paSvnPropChanges = NULL;2243 2244 rc = scmProcessFileInner(&State, pszFilename, pszBasename, cchBasename, &Base);2245 2246 size_t i = State.cSvnPropChanges;2247 while (i-- > 0)2248 {2249 RTStrFree(State.paSvnPropChanges[i].pszName);2250 RTStrFree(State.paSvnPropChanges[i].pszValue);2251 }2252 RTMemFree(State.paSvnPropChanges);2253 2254 scmSettingsBaseDelete(&Base);2255 }2256 return rc;2257 }2258 2259 2260 /**2261 * Tries to correct RTDIRENTRY_UNKNOWN.2262 *2263 * @returns Corrected type.2264 * @param pszPath The path to the object in question.2265 */2266 static RTDIRENTRYTYPE scmFigureUnknownType(const char *pszPath)2267 {2268 RTFSOBJINFO Info;2269 int rc = RTPathQueryInfo(pszPath, &Info, RTFSOBJATTRADD_NOTHING);2270 if (RT_FAILURE(rc))2271 return RTDIRENTRYTYPE_UNKNOWN;2272 if (RTFS_IS_DIRECTORY(Info.Attr.fMode))2273 return RTDIRENTRYTYPE_DIRECTORY;2274 if (RTFS_IS_FILE(Info.Attr.fMode))2275 return RTDIRENTRYTYPE_FILE;2276 return RTDIRENTRYTYPE_UNKNOWN;2277 }2278 2279 /**2280 * Recurse into a sub-directory and process all the files and directories.2281 *2282 * @returns IPRT status code.2283 * @param pszBuf Path buffer containing the directory path on2284 * entry. This ends with a dot. This is passed2285 * along when recursing in order to save stack space2286 * and avoid needless copying.2287 * @param cchDir Length of our path in pszbuf.2288 * @param pEntry Directory entry buffer. This is also passed2289 * along when recursing to save stack space.2290 * @param pSettingsStack The settings stack (pointer to the top element).2291 * @param iRecursion The recursion depth. This is used to restrict2292 * the recursions.2293 */2294 static int scmProcessDirTreeRecursion(char *pszBuf, size_t cchDir, PRTDIRENTRY pEntry,2295 PSCMSETTINGS pSettingsStack, unsigned iRecursion)2296 {2297 int rc;2298 Assert(cchDir > 1 && pszBuf[cchDir - 1] == '.');2299 2300 /*2301 * Make sure we stop somewhere.2302 */2303 if (iRecursion > 128)2304 {2305 RTMsgError("recursion too deep: %d\n", iRecursion);2306 return VINF_SUCCESS; /* ignore */2307 }2308 2309 /*2310 * Check if it's excluded by --only-svn-dir.2311 */2312 if (pSettingsStack->Base.fOnlySvnDirs)2313 {2314 rc = RTPathAppend(pszBuf, RTPATH_MAX, ".svn");2315 if (RT_FAILURE(rc))2316 {2317 RTMsgError("RTPathAppend: %Rrc\n", rc);2318 return rc;2319 }2320 if (!RTDirExists(pszBuf))2321 return VINF_SUCCESS;2322 2323 Assert(RTPATH_IS_SLASH(pszBuf[cchDir]));2324 pszBuf[cchDir] = '\0';2325 pszBuf[cchDir - 1] = '.';2326 }2327 2328 /*2329 * Try open and read the directory.2330 */2331 PRTDIR pDir;2332 rc = RTDirOpenFiltered(&pDir, pszBuf, RTDIRFILTER_NONE, 0);2333 if (RT_FAILURE(rc))2334 {2335 RTMsgError("Failed to enumerate directory '%s': %Rrc", pszBuf, rc);2336 return rc;2337 }2338 for (;;)2339 {2340 /* Read the next entry. */2341 rc = RTDirRead(pDir, pEntry, NULL);2342 if (RT_FAILURE(rc))2343 {2344 if (rc == VERR_NO_MORE_FILES)2345 rc = VINF_SUCCESS;2346 else2347 RTMsgError("RTDirRead -> %Rrc\n", rc);2348 break;2349 }2350 2351 /* Skip '.' and '..'. */2352 if ( pEntry->szName[0] == '.'2353 && ( pEntry->cbName == 12354 || ( pEntry->cbName == 22355 && pEntry->szName[1] == '.')))2356 continue;2357 2358 /* Enter it into the buffer so we've got a full name to work2359 with when needed. */2360 if (pEntry->cbName + cchDir >= RTPATH_MAX)2361 {2362 RTMsgError("Skipping too long entry: %s", pEntry->szName);2363 continue;2364 }2365 memcpy(&pszBuf[cchDir - 1], pEntry->szName, pEntry->cbName + 1);2366 2367 /* Figure the type. */2368 RTDIRENTRYTYPE enmType = pEntry->enmType;2369 if (enmType == RTDIRENTRYTYPE_UNKNOWN)2370 enmType = scmFigureUnknownType(pszBuf);2371 2372 /* Process the file or directory, skip the rest. */2373 if (enmType == RTDIRENTRYTYPE_FILE)2374 rc = scmProcessFile(pszBuf, pEntry->szName, pEntry->cbName, pSettingsStack);2375 else if (enmType == RTDIRENTRYTYPE_DIRECTORY)2376 {2377 /* Append the dot for the benefit of the pattern matching. */2378 if (pEntry->cbName + cchDir + 5 >= RTPATH_MAX)2379 {2380 RTMsgError("Skipping too deep dir entry: %s", pEntry->szName);2381 continue;2382 }2383 memcpy(&pszBuf[cchDir - 1 + pEntry->cbName], "/.", sizeof("/."));2384 size_t cchSubDir = cchDir - 1 + pEntry->cbName + sizeof("/.") - 1;2385 2386 if ( !pSettingsStack->Base.pszFilterOutDirs2387 || !*pSettingsStack->Base.pszFilterOutDirs2388 || ( !RTStrSimplePatternMultiMatch(pSettingsStack->Base.pszFilterOutDirs, RTSTR_MAX,2389 pEntry->szName, pEntry->cbName, NULL)2390 && !RTStrSimplePatternMultiMatch(pSettingsStack->Base.pszFilterOutDirs, RTSTR_MAX,2391 pszBuf, cchSubDir, NULL)2392 )2393 )2394 {2395 rc = scmSettingsStackPushDir(&pSettingsStack, pszBuf);2396 if (RT_SUCCESS(rc))2397 {2398 rc = scmProcessDirTreeRecursion(pszBuf, cchSubDir, pEntry, pSettingsStack, iRecursion + 1);2399 scmSettingsStackPopAndDestroy(&pSettingsStack);2400 }2401 }2402 }2403 if (RT_FAILURE(rc))2404 break;2405 }2406 RTDirClose(pDir);2407 return rc;2408 2409 }2410 2411 /**2412 * Process a directory tree.2413 *2414 * @returns IPRT status code.2415 * @param pszDir The directory to start with. This is pointer to2416 * a RTPATH_MAX sized buffer.2417 */2418 static int scmProcessDirTree(char *pszDir, PSCMSETTINGS pSettingsStack)2419 {2420 /*2421 * Setup the recursion.2422 */2423 int rc = RTPathAppend(pszDir, RTPATH_MAX, ".");2424 if (RT_SUCCESS(rc))2425 {2426 RTDIRENTRY Entry;2427 rc = scmProcessDirTreeRecursion(pszDir, strlen(pszDir), &Entry, pSettingsStack, 0);2428 }2429 else2430 RTMsgError("RTPathAppend: %Rrc\n", rc);2431 return rc;2432 }2433 2434 2435 /**2436 * Processes a file or directory specified as an command line argument.2437 *2438 * @returns IPRT status code2439 * @param pszSomething What we found in the command line arguments.2440 * @param pSettingsStack The settings stack (pointer to the top element).2441 */2442 static int scmProcessSomething(const char *pszSomething, PSCMSETTINGS pSettingsStack)2443 {2444 char szBuf[RTPATH_MAX];2445 int rc = RTPathAbs(pszSomething, szBuf, sizeof(szBuf));2446 if (RT_SUCCESS(rc))2447 {2448 RTPathChangeToUnixSlashes(szBuf, false /*fForce*/);2449 2450 PSCMSETTINGS pSettings;2451 rc = scmSettingsCreateForPath(&pSettings, &pSettingsStack->Base, szBuf);2452 if (RT_SUCCESS(rc))2453 {2454 scmSettingsStackPush(&pSettingsStack, pSettings);2455 2456 if (RTFileExists(szBuf))2457 {2458 const char *pszBasename = RTPathFilename(szBuf);2459 if (pszBasename)2460 {2461 size_t cchBasename = strlen(pszBasename);2462 rc = scmProcessFile(szBuf, pszBasename, cchBasename, pSettingsStack);2463 }2464 else2465 {2466 RTMsgError("RTPathFilename: NULL\n");2467 rc = VERR_IS_A_DIRECTORY;2468 }2469 }2470 else2471 rc = scmProcessDirTree(szBuf, pSettingsStack);2472 2473 PSCMSETTINGS pPopped = scmSettingsStackPop(&pSettingsStack);2474 Assert(pPopped == pSettings);2475 scmSettingsDestroy(pSettings);2476 }2477 else2478 RTMsgError("scmSettingsInitStack: %Rrc\n", rc);2479 }2480 else2481 RTMsgError("RTPathAbs: %Rrc\n", rc);2482 return rc;2483 }2484 2485 int main(int argc, char **argv)2486 {2487 int rc = RTR3InitExe(argc, &argv, 0);2488 if (RT_FAILURE(rc))2489 return 1;2490 2491 /*2492 * Init the settings.2493 */2494 PSCMSETTINGS pSettings;2495 rc = scmSettingsCreate(&pSettings, &g_Defaults);2496 if (RT_FAILURE(rc))2497 {2498 RTMsgError("scmSettingsCreate: %Rrc\n", rc);2499 return 1;2500 }2501 2502 /*2503 * Parse arguments and process input in order (because this is the only2504 * thing that works at the moment).2505 */2506 static RTGETOPTDEF s_aOpts[14 + RT_ELEMENTS(g_aScmOpts)] =2507 {2508 { "--dry-run", 'd', RTGETOPT_REQ_NOTHING },2509 { "--real-run", 'D', RTGETOPT_REQ_NOTHING },2510 { "--file-filter", 'f', RTGETOPT_REQ_STRING },2511 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },2512 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },2513 { "--diff-ignore-eol", SCMOPT_DIFF_IGNORE_EOL, RTGETOPT_REQ_NOTHING },2514 { "--diff-no-ignore-eol", SCMOPT_DIFF_NO_IGNORE_EOL, RTGETOPT_REQ_NOTHING },2515 { "--diff-ignore-space", SCMOPT_DIFF_IGNORE_SPACE, RTGETOPT_REQ_NOTHING },2516 { "--diff-no-ignore-space", SCMOPT_DIFF_NO_IGNORE_SPACE, RTGETOPT_REQ_NOTHING },2517 { "--diff-ignore-leading-space", SCMOPT_DIFF_IGNORE_LEADING_SPACE, RTGETOPT_REQ_NOTHING },2518 { "--diff-no-ignore-leading-space", SCMOPT_DIFF_NO_IGNORE_LEADING_SPACE, RTGETOPT_REQ_NOTHING },2519 { "--diff-ignore-trailing-space", SCMOPT_DIFF_IGNORE_TRAILING_SPACE, RTGETOPT_REQ_NOTHING },2520 { "--diff-no-ignore-trailing-space", SCMOPT_DIFF_NO_IGNORE_TRAILING_SPACE, RTGETOPT_REQ_NOTHING },2521 { "--diff-special-chars", SCMOPT_DIFF_SPECIAL_CHARS, RTGETOPT_REQ_NOTHING },2522 { "--diff-no-special-chars", SCMOPT_DIFF_NO_SPECIAL_CHARS, RTGETOPT_REQ_NOTHING },2523 };2524 memcpy(&s_aOpts[RT_ELEMENTS(s_aOpts) - RT_ELEMENTS(g_aScmOpts)], &g_aScmOpts[0], sizeof(g_aScmOpts));2525 2526 RTGETOPTUNION ValueUnion;2527 RTGETOPTSTATE GetOptState;2528 rc = RTGetOptInit(&GetOptState, argc, argv, &s_aOpts[0], RT_ELEMENTS(s_aOpts), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);2529 AssertReleaseRCReturn(rc, 1);2530 size_t cProcessed = 0;2531 2532 while ((rc = RTGetOpt(&GetOptState, &ValueUnion)) != 0)2533 {2534 switch (rc)2535 {2536 case 'd':2537 g_fDryRun = true;2538 break;2539 case 'D':2540 g_fDryRun = false;2541 break;2542 2543 case 'f':2544 g_pszFileFilter = ValueUnion.psz;2545 break;2546 2547 case 'h':2548 RTPrintf("VirtualBox Source Code Massager\n"2549 "\n"2550 "Usage: %s [options] <files & dirs>\n"2551 "\n"2552 "Options:\n", g_szProgName);2553 for (size_t i = 0; i < RT_ELEMENTS(s_aOpts); i++)2554 {2555 bool fAdvanceTwo = false;2556 if ((s_aOpts[i].fFlags & RTGETOPT_REQ_MASK) == RTGETOPT_REQ_NOTHING)2557 {2558 fAdvanceTwo = i + 1 < RT_ELEMENTS(s_aOpts)2559 && ( strstr(s_aOpts[i+1].pszLong, "-no-") != NULL2560 || strstr(s_aOpts[i+1].pszLong, "-not-") != NULL2561 || strstr(s_aOpts[i+1].pszLong, "-dont-") != NULL2562 );2563 if (fAdvanceTwo)2564 RTPrintf(" %s, %s\n", s_aOpts[i].pszLong, s_aOpts[i + 1].pszLong);2565 else2566 RTPrintf(" %s\n", s_aOpts[i].pszLong);2567 }2568 else if ((s_aOpts[i].fFlags & RTGETOPT_REQ_MASK) == RTGETOPT_REQ_STRING)2569 RTPrintf(" %s string\n", s_aOpts[i].pszLong);2570 else2571 RTPrintf(" %s value\n", s_aOpts[i].pszLong);2572 switch (s_aOpts[i].iShort)2573 {2574 case SCMOPT_CONVERT_EOL: RTPrintf(" Default: %RTbool\n", g_Defaults.fConvertEol); break;2575 case SCMOPT_CONVERT_TABS: RTPrintf(" Default: %RTbool\n", g_Defaults.fConvertTabs); break;2576 case SCMOPT_FORCE_FINAL_EOL: RTPrintf(" Default: %RTbool\n", g_Defaults.fForceFinalEol); break;2577 case SCMOPT_FORCE_TRAILING_LINE: RTPrintf(" Default: %RTbool\n", g_Defaults.fForceTrailingLine); break;2578 case SCMOPT_STRIP_TRAILING_BLANKS: RTPrintf(" Default: %RTbool\n", g_Defaults.fStripTrailingBlanks); break;2579 case SCMOPT_STRIP_TRAILING_LINES: RTPrintf(" Default: %RTbool\n", g_Defaults.fStripTrailingLines); break;2580 case SCMOPT_ONLY_SVN_DIRS: RTPrintf(" Default: %RTbool\n", g_Defaults.fOnlySvnDirs); break;2581 case SCMOPT_ONLY_SVN_FILES: RTPrintf(" Default: %RTbool\n", g_Defaults.fOnlySvnFiles); break;2582 case SCMOPT_SET_SVN_EOL: RTPrintf(" Default: %RTbool\n", g_Defaults.fSetSvnEol); break;2583 case SCMOPT_SET_SVN_EXECUTABLE: RTPrintf(" Default: %RTbool\n", g_Defaults.fSetSvnExecutable); break;2584 case SCMOPT_SET_SVN_KEYWORDS: RTPrintf(" Default: %RTbool\n", g_Defaults.fSetSvnKeywords); break;2585 case SCMOPT_TAB_SIZE: RTPrintf(" Default: %u\n", g_Defaults.cchTab); break;2586 case SCMOPT_FILTER_OUT_DIRS: RTPrintf(" Default: %s\n", g_Defaults.pszFilterOutDirs); break;2587 case SCMOPT_FILTER_FILES: RTPrintf(" Default: %s\n", g_Defaults.pszFilterFiles); break;2588 case SCMOPT_FILTER_OUT_FILES: RTPrintf(" Default: %s\n", g_Defaults.pszFilterOutFiles); break;2589 }2590 i += fAdvanceTwo;2591 }2592 return 1;2593 2594 case 'q':2595 g_iVerbosity = 0;2596 break;2597 2598 case 'v':2599 g_iVerbosity++;2600 break;2601 2602 case 'V':2603 {2604 /* The following is assuming that svn does it's job here. */2605 static const char s_szRev[] = "$Revision$";2606 const char *psz = RTStrStripL(strchr(s_szRev, ' '));2607 RTPrintf("r%.*s\n", strchr(psz, ' ') - psz, psz);2608 return 0;2609 }2610 2611 case SCMOPT_DIFF_IGNORE_EOL:2612 g_fDiffIgnoreEol = true;2613 break;2614 case SCMOPT_DIFF_NO_IGNORE_EOL:2615 g_fDiffIgnoreEol = false;2616 break;2617 2618 case SCMOPT_DIFF_IGNORE_SPACE:2619 g_fDiffIgnoreTrailingWS = g_fDiffIgnoreLeadingWS = true;2620 break;2621 case SCMOPT_DIFF_NO_IGNORE_SPACE:2622 g_fDiffIgnoreTrailingWS = g_fDiffIgnoreLeadingWS = false;2623 break;2624 2625 case SCMOPT_DIFF_IGNORE_LEADING_SPACE:2626 g_fDiffIgnoreLeadingWS = true;2627 break;2628 case SCMOPT_DIFF_NO_IGNORE_LEADING_SPACE:2629 g_fDiffIgnoreLeadingWS = false;2630 break;2631 2632 case SCMOPT_DIFF_IGNORE_TRAILING_SPACE:2633 g_fDiffIgnoreTrailingWS = true;2634 break;2635 case SCMOPT_DIFF_NO_IGNORE_TRAILING_SPACE:2636 g_fDiffIgnoreTrailingWS = false;2637 break;2638 2639 case SCMOPT_DIFF_SPECIAL_CHARS:2640 g_fDiffSpecialChars = true;2641 break;2642 case SCMOPT_DIFF_NO_SPECIAL_CHARS:2643 g_fDiffSpecialChars = false;2644 break;2645 2646 case VINF_GETOPT_NOT_OPTION:2647 {2648 if (!g_fDryRun)2649 {2650 if (!cProcessed)2651 {2652 RTPrintf("%s: Warning! This program will make changes to your source files and\n"2653 "%s: there is a slight risk that bugs or a full disk may cause\n"2654 "%s: LOSS OF DATA. So, please make sure you have checked in\n"2655 "%s: all your changes already. If you didn't, then don't blame\n"2656 "%s: anyone for not warning you!\n"2657 "%s:\n"2658 "%s: Press any key to continue...\n",2659 g_szProgName, g_szProgName, g_szProgName, g_szProgName, g_szProgName,2660 g_szProgName, g_szProgName);2661 RTStrmGetCh(g_pStdIn);2662 }2663 cProcessed++;2664 }2665 rc = scmProcessSomething(ValueUnion.psz, pSettings);2666 if (RT_FAILURE(rc))2667 return rc;2668 break;2669 }2670 2671 default:2672 {2673 int rc2 = scmSettingsBaseHandleOpt(&pSettings->Base, rc, &ValueUnion);2674 if (RT_SUCCESS(rc2))2675 break;2676 if (rc2 != VERR_GETOPT_UNKNOWN_OPTION)2677 return 2;2678 return RTGetOptPrintError(rc, &ValueUnion);2679 }2680 }2681 }2682 2683 scmSettingsDestroy(pSettings);2684 return 0;2685 }2686
Note:
See TracChangeset
for help on using the changeset viewer.