Changeset 26557 in vbox
- Timestamp:
- Feb 16, 2010 12:02:37 AM (15 years ago)
- Location:
- trunk
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/.scm-settings
r26516 r26557 30 30 # Global settings. 31 31 # 32 # Only consider directories and files that are part of a SVN working set. 33 # 32 34 --only-svn-dirs 35 --only-svn-files 36 --set-svn-eol 33 37 34 38 # -
trunk/src/bldprogs/scm.cpp
r26521 r26557 35 35 #include <iprt/ctype.h> 36 36 #include <iprt/dir.h> 37 #include <iprt/env.h> 37 38 #include <iprt/file.h> 38 39 #include <iprt/err.h> … … 43 44 #include <iprt/param.h> 44 45 #include <iprt/path.h> 46 #include <iprt/process.h> 45 47 #include <iprt/stream.h> 46 48 #include <iprt/string.h> … … 127 129 128 130 /** 131 * SVN property. 132 */ 133 typedef struct SCMSVNPROP 134 { 135 /** The property. */ 136 char *pszName; 137 /** The value. 138 * When used to record updates, this can be set to NULL to trigger the 139 * deletion of the property. */ 140 char *pszValue; 141 } SCMSVNPROP; 142 /** Pointer to a SVN property. */ 143 typedef SCMSVNPROP *PSCMSVNPROP; 144 /** Pointer to a const SVN property. */ 145 typedef SCMSVNPROP const *PCSCMSVNPROP; 146 147 148 /** 129 149 * Rewriter state. 130 150 */ … … 136 156 * rewrite. */ 137 157 bool fFirst; 158 /** The number of SVN property changes. */ 159 size_t cSvnPropChanges; 160 /** Pointer to an array of SVN property changes. */ 161 PSCMSVNPROP paSvnPropChanges; 138 162 } SCMRWSTATE; 139 163 /** Pointer to the rewriter state. */ … … 208 232 bool fStripTrailingBlanks; 209 233 bool fStripTrailingLines; 234 /** Only process files that are part of a SVN working copy. */ 235 bool fOnlySvnFiles; 210 236 /** Only recurse into directories containing an .svn dir. */ 211 237 bool fOnlySvnDirs; 238 /** Set svn:eol-style if missing or incorrect. */ 239 bool fSetSvnEol; 212 240 /** */ 213 241 unsigned cchTab; … … 250 278 SCMOPT_ONLY_SVN_DIRS, 251 279 SCMOPT_NOT_ONLY_SVN_DIRS, 280 SCMOPT_ONLY_SVN_FILES, 281 SCMOPT_NOT_ONLY_SVN_FILES, 282 SCMOPT_SET_SVN_EOL, 283 SCMOPT_DONT_SET_SVN_EOL, 252 284 SCMOPT_TAB_SIZE, 253 285 SCMOPT_FILTER_OUT_DIRS, … … 354 386 /* .fStripTrailingBlanks = */ true, 355 387 /* .fStripTrailingLines = */ true, 388 /* .fOnlySvnFiles = */ false, 356 389 /* .fOnlySvnDirs = */ false, 390 /* .fSetSvnEol = */ false, 357 391 /* .cchTab = */ 8, 358 392 /* .pszFilterFiles = */ (char *)"", … … 378 412 { "--only-svn-dirs", SCMOPT_ONLY_SVN_DIRS, RTGETOPT_REQ_NOTHING }, 379 413 { "--not-only-svn-dirs", SCMOPT_NOT_ONLY_SVN_DIRS, RTGETOPT_REQ_NOTHING }, 414 { "--only-svn-files", SCMOPT_ONLY_SVN_FILES, RTGETOPT_REQ_NOTHING }, 415 { "--not-only-svn-files", SCMOPT_NOT_ONLY_SVN_FILES, RTGETOPT_REQ_NOTHING }, 416 { "--set-svn-eol", SCMOPT_SET_SVN_EOL, RTGETOPT_REQ_NOTHING }, 417 { "--dont-set-svn-eol", SCMOPT_DONT_SET_SVN_EOL, RTGETOPT_REQ_NOTHING }, 380 418 { "--tab-size", SCMOPT_TAB_SIZE, RTGETOPT_REQ_UINT8 }, 381 419 { "--filter-out-dirs", SCMOPT_FILTER_OUT_DIRS, RTGETOPT_REQ_STRING }, … … 831 869 832 870 /** 871 * Get the current stream position as an byte offset. 872 * 873 * @returns The current byte offset 874 * @param pStream The stream. 875 */ 876 size_t ScmStreamTell(PSCMSTREAM pStream) 877 { 878 return pStream->off; 879 } 880 881 /** 833 882 * Get the current stream position as a line number. 834 883 * … … 839 888 { 840 889 return pStream->iLine; 890 } 891 892 /** 893 * Get the current stream size in bytes. 894 * 895 * @returns Count of bytes. 896 * @param pStream The stream. 897 */ 898 size_t ScmStreamSize(PSCMSTREAM pStream) 899 { 900 return pStream->cb; 841 901 } 842 902 … … 855 915 856 916 /** 857 * Seeks to a given line in the tream. 917 * Seeks to a given byte offset in the stream. 918 * 919 * @returns IPRT status code. 920 * @retval VERR_SEEK if the new stream position is the middle of an EOL marker. 921 * This is a temporary restriction. 922 * 923 * @param pStream The stream. Must be in read mode. 924 * @param offAbsolute The offset to seek to. If this is beyond the 925 * end of the stream, the position is set to the 926 * end. 927 */ 928 int ScmStreamSeekAbsolute(PSCMSTREAM pStream, size_t offAbsolute) 929 { 930 AssertReturn(!pStream->fWriteOrRead, VERR_ACCESS_DENIED); 931 if (RT_FAILURE(pStream->rc)) 932 return pStream->rc; 933 934 /* Must be fully lineated. (lazy bird) */ 935 if (RT_UNLIKELY(!pStream->fFullyLineated)) 936 { 937 int rc = scmStreamLineate(pStream); 938 if (RT_FAILURE(rc)) 939 return rc; 940 } 941 942 /* Ok, do the job. */ 943 if (offAbsolute < pStream->cb) 944 { 945 /** @todo Should do a binary search here, but I'm too darn lazy tonight. */ 946 pStream->off = ~(size_t)0; 947 for (size_t i = 0; i < pStream->cLines; i++) 948 { 949 if (offAbsolute < pStream->paLines[i].off + pStream->paLines[i].cch + pStream->paLines[i].enmEol) 950 { 951 pStream->off = offAbsolute; 952 pStream->iLine = i; 953 if (offAbsolute > pStream->paLines[i].off + pStream->paLines[i].cch) 954 return pStream->rc = VERR_SEEK; 955 break; 956 } 957 } 958 AssertReturn(pStream->off != ~(size_t)0, pStream->rc = VERR_INTERNAL_ERROR_3); 959 } 960 else 961 { 962 pStream->off = pStream->cb; 963 pStream->iLine = pStream->cLines; 964 } 965 return VINF_SUCCESS; 966 } 967 968 969 /** 970 * Seeks a number of bytes relative to the current stream position. 971 * 972 * @returns IPRT status code. 973 * @retval VERR_SEEK if the new stream position is the middle of an EOL marker. 974 * This is a temporary restriction. 975 * 976 * @param pStream The stream. Must be in read mode. 977 * @param offRelative The offset to seek to. A negative offset 978 * rewinds and positive one fast forwards the 979 * stream. Will quietly stop at the begining and 980 * end of the stream. 981 */ 982 int ScmStreamSeekRelative(PSCMSTREAM pStream, ssize_t offRelative) 983 { 984 size_t offAbsolute; 985 if (offRelative >= 0) 986 offAbsolute = pStream->off + offRelative; 987 else if ((size_t)-offRelative <= pStream->off) 988 offAbsolute = pStream->off + offRelative; 989 else 990 offAbsolute = 0; 991 return ScmStreamSeekAbsolute(pStream, offAbsolute); 992 } 993 994 /** 995 * Seeks to a given line in the stream. 858 996 * 859 997 * @returns IPRT status code. … … 869 1007 return pStream->rc; 870 1008 871 /* Must be fully lineated of course.*/1009 /* Must be fully lineated. (lazy bird) */ 872 1010 if (RT_UNLIKELY(!pStream->fFullyLineated)) 873 1011 { … … 935 1073 936 1074 /* update the stream position. */ 937 pStream->off = pStream->paLines[iLine]. cch + pStream->paLines[iLine].enmEol;1075 pStream->off = pStream->paLines[iLine].off + pStream->paLines[iLine].cch + pStream->paLines[iLine].enmEol; 938 1076 pStream->iLine = iLine + 1; 939 1077 … … 958 1096 static const char *ScmStreamGetLine(PSCMSTREAM pStream, size_t *pcchLine, PSCMEOL penmEol) 959 1097 { 1098 /** @todo this doesn't work when pStream->off != 1099 * pStream->paLines[pStream->iLine-1].pff. */ 960 1100 if (!pStream->fFullyLineated) 961 1101 return scmStreamGetLineInternal(pStream, pcchLine, penmEol); 962 1102 return ScmStreamGetLineByNo(pStream, pStream->iLine, pcchLine, penmEol); 1103 } 1104 1105 /** 1106 * Reads @a cbToRead bytes into @a pvBuf. 1107 * 1108 * Will fail if end of stream is encountered before the entire read has been 1109 * completed. 1110 * 1111 * @returns IPRT status code. 1112 * @retval VERR_EOF if there isn't @a cbToRead bytes left to read. Stream 1113 * position will be unchanged. 1114 * 1115 * @param pStream The stream. Must be in read mode. 1116 * @param pvBuf The buffer to read into. 1117 * @param cbToRead The number of bytes to read. 1118 */ 1119 static int ScmStreamRead(PSCMSTREAM pStream, void *pvBuf, size_t cbToRead) 1120 { 1121 AssertReturn(!pStream->fWriteOrRead, NULL); 1122 if (RT_FAILURE(pStream->rc)) 1123 return NULL; 1124 1125 /* If there isn't enough stream left, fail already. */ 1126 if (RT_UNLIKELY(pStream->cb - pStream->cb < cbToRead)) 1127 return VERR_EOF; 1128 1129 /* Copy the data and simply seek to the new stream position. */ 1130 memcpy(pvBuf, &pStream->pch[pStream->off], cbToRead); 1131 return ScmStreamSeekAbsolute(pStream, pStream->off + cbToRead); 963 1132 } 964 1133 … … 1842 2011 case SCMOPT_NOT_ONLY_SVN_DIRS: 1843 2012 pSettings->fOnlySvnDirs = false; 2013 return VINF_SUCCESS; 2014 2015 case SCMOPT_ONLY_SVN_FILES: 2016 pSettings->fOnlySvnFiles = true; 2017 return VINF_SUCCESS; 2018 case SCMOPT_NOT_ONLY_SVN_FILES: 2019 pSettings->fOnlySvnFiles = false; 2020 return VINF_SUCCESS; 2021 2022 case SCMOPT_SET_SVN_EOL: 2023 pSettings->fSetSvnEol = true; 2024 return VINF_SUCCESS; 2025 case SCMOPT_DONT_SET_SVN_EOL: 1844 2026 return VINF_SUCCESS; 1845 2027 … … 2393 2575 if (pState && !pState->fFirst) 2394 2576 { 2395 RTPrintf("%s: info: Rewriting '%s'...\n", g_szProgName, pState->pszFilename);2577 RTPrintf("%s: info: --= Rewriting '%s' =--\n", g_szProgName, pState->pszFilename); 2396 2578 pState->fFirst = true; 2397 2579 } 2398 2580 if (pszFormat) 2399 2581 { 2400 RTPrintf("%s: info: ", g_szProgName); 2582 RTPrintf(pState 2583 ? "%s: info: " 2584 : "%s: info: ", 2585 g_szProgName); 2401 2586 va_list va; 2402 2587 va_start(va, pszFormat); … … 2405 2590 } 2406 2591 } 2592 } 2593 2594 2595 /* -=-=-=-=-=- subversion -=-=-=-=-=- */ 2596 2597 #define SCM_WITHOUT_LIBSVN 2598 2599 #ifdef SCM_WITHOUT_LIBSVN 2600 2601 /** 2602 * Callback that is call for each path to search. 2603 */ 2604 static DECLCALLBACK(int) scmSvnFindSvnBinaryCallback(char const *pchPath, size_t cchPath, void *pvUser1, void *pvUser2) 2605 { 2606 char *pszDst = (char *)pvUser1; 2607 size_t cchDst = (size_t)pvUser2; 2608 if (cchDst > cchPath) 2609 { 2610 memcpy(pszDst, pchPath, cchPath); 2611 pszDst[cchPath] = '\0'; 2612 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) 2613 int rc = RTPathAppend(pszDst, cchDst, "svn.exe"); 2614 #else 2615 int rc = RTPathAppend(pszDst, cchDst, "svn"); 2616 #endif 2617 if ( RT_SUCCESS(rc) 2618 && RTFileExists(pszDst)) 2619 return VINF_SUCCESS; 2620 } 2621 return VERR_TRY_AGAIN; 2622 } 2623 2624 2625 /** 2626 * Finds the svn binary. 2627 * 2628 * @param pszPath Where to store it. Worst case, we'll return 2629 * "svn" here. 2630 * @param cchPath The size of the buffer pointed to by @a pszPath. 2631 */ 2632 static void scmSvnFindSvnBinary(char *pszPath, size_t cchPath) 2633 { 2634 /** @todo code page fun... */ 2635 Assert(cchPath >= sizeof("svn")); 2636 #ifdef RT_OS_WINDOWS 2637 const char *pszEnvVar = RTEnvGet("Path"); 2638 #else 2639 const char *pszEnvVar = RTEnvGet("PATH"); 2640 #endif 2641 if (pszPath) 2642 { 2643 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) 2644 int rc = RTPathTraverseList(pszEnvVar, ';', scmSvnFindSvnBinaryCallback, pszPath, (void *)cchPath); 2645 #else 2646 int rc = RTPathTraverseList(pszEnvVar, ':', scmSvnFindSvnBinaryCallback, pszPath, (void *)cchPath); 2647 #endif 2648 if (RT_SUCCESS(rc)) 2649 return; 2650 } 2651 strcpy(pszPath, "svn"); 2652 } 2653 2654 2655 /** 2656 * Construct a dot svn filename for the file being rewritten. 2657 * 2658 * @returns IPRT status code. 2659 * @param pState The rewrite state (for the name). 2660 * @param pszDir The directory, including ".svn/". 2661 * @param pszSuff The filename suffix. 2662 * @param pszDst The output buffer. RTPATH_MAX in size. 2663 */ 2664 static int scmSvnConstructName(PSCMRWSTATE pState, const char *pszDir, const char *pszSuff, char *pszDst) 2665 { 2666 strcpy(pszDst, pState->pszFilename); /* ASSUMES sizeof(szBuf) <= sizeof(szPath) */ 2667 RTPathStripFilename(pszDst); 2668 2669 int rc = RTPathAppend(pszDst, RTPATH_MAX, pszDir); 2670 if (RT_SUCCESS(rc)) 2671 { 2672 rc = RTPathAppend(pszDst, RTPATH_MAX, RTPathFilename(pState->pszFilename)); 2673 if (RT_SUCCESS(rc)) 2674 { 2675 size_t cchDst = strlen(pszDst); 2676 size_t cchSuff = strlen(pszSuff); 2677 if (cchDst + cchSuff < RTPATH_MAX) 2678 { 2679 memcpy(&pszDst[cchDst], pszSuff, cchSuff + 1); 2680 return VINF_SUCCESS; 2681 } 2682 else 2683 rc = VERR_BUFFER_OVERFLOW; 2684 } 2685 } 2686 return rc; 2687 } 2688 2689 /** 2690 * Interprets the specified string as decimal numbers. 2691 * 2692 * @returns true if parsed successfully, false if not. 2693 * @param pch The string (not terminated). 2694 * @param cch The string length. 2695 * @param pu Where to return the value. 2696 */ 2697 static bool scmSvnReadNumber(const char *pch, size_t cch, size_t *pu) 2698 { 2699 size_t u = 0; 2700 while (cch-- > 0) 2701 { 2702 char ch = *pch++; 2703 if (ch < '0' || ch > '9') 2704 return false; 2705 u *= 10; 2706 u += ch - '0'; 2707 } 2708 *pu = u; 2709 return true; 2710 } 2711 2712 #endif /* SCM_WITHOUT_LIBSVN */ 2713 2714 /** 2715 * Checks if the file we're operating on is part of a SVN working copy. 2716 * 2717 * @returns true if it is, false if it isn't or we cannot tell. 2718 * @param pState The rewrite state to work on. 2719 */ 2720 static bool scmSvnIsInWorkingCopy(PSCMRWSTATE pState) 2721 { 2722 #ifdef SCM_WITHOUT_LIBSVN 2723 /* 2724 * Hack: check if the .svn/text-base/<file>.svn-base file exists. 2725 */ 2726 char szPath[RTPATH_MAX]; 2727 int rc = scmSvnConstructName(pState, ".svn/text-base/", ".svn-base", szPath); 2728 if (RT_SUCCESS(rc)) 2729 return RTFileExists(szPath); 2730 2731 #else 2732 NOREF(pState); 2733 #endif 2734 return false; 2735 } 2736 2737 /** 2738 * Queries the value of an SVN property. 2739 * 2740 * This will automatically adjust for scheduled changes. 2741 * 2742 * @returns IPRT status code. 2743 * @retval VERR_INVALID_STATE if not a SVN WC file. 2744 * @retval VERR_NOT_FOUND if the property wasn't found. 2745 * @param pState The rewrite state to work on. 2746 * @param pszName The property name. 2747 * @param ppszValue Where to return the property value. Free this 2748 * using RTStrFree. Optional. 2749 */ 2750 static int scmSvnQueryProperty(PSCMRWSTATE pState, const char *pszName, char **ppszValue) 2751 { 2752 /* 2753 * Look it up in the scheduled changes. 2754 */ 2755 uint32_t i = pState->cSvnPropChanges; 2756 while (i-- > 0) 2757 if (!strcmp(pState->paSvnPropChanges[i].pszName, pszName)) 2758 { 2759 const char *pszValue = pState->paSvnPropChanges[i].pszValue; 2760 if (!pszValue) 2761 return VERR_NOT_FOUND; 2762 if (ppszValue) 2763 return RTStrDupEx(ppszValue, pszValue); 2764 return VINF_SUCCESS; 2765 } 2766 2767 #ifdef SCM_WITHOUT_LIBSVN 2768 /* 2769 * Hack: Read the .svn/props/<file>.svn-work file exists. 2770 */ 2771 char szPath[RTPATH_MAX]; 2772 int rc = scmSvnConstructName(pState, ".svn/props/", ".svn-work", szPath); 2773 if (RT_SUCCESS(rc) && !RTFileExists(szPath)) 2774 rc = scmSvnConstructName(pState, ".svn/prop-base/", ".svn-base", szPath); 2775 if (RT_SUCCESS(rc)) 2776 { 2777 SCMSTREAM Stream; 2778 rc = ScmStreamInitForReading(&Stream, szPath); 2779 if (RT_SUCCESS(rc)) 2780 { 2781 /* 2782 * The current format is K len\n<name>\nV len\n<value>\n" ... END. 2783 */ 2784 rc = VERR_NOT_FOUND; 2785 size_t const cchName = strlen(pszName); 2786 SCMEOL enmEol; 2787 size_t cchLine; 2788 const char *pchLine; 2789 while ((pchLine = ScmStreamGetLine(&Stream, &cchLine, &enmEol)) != NULL) 2790 { 2791 /* 2792 * Parse the 'K num' / 'END' line. 2793 */ 2794 if ( cchLine == 3 2795 && !memcmp(pchLine, "END", 3)) 2796 break; 2797 size_t cchKey; 2798 if ( cchLine < 3 2799 || pchLine[0] != 'K' 2800 || pchLine[1] != ' ' 2801 || !scmSvnReadNumber(&pchLine[2], cchLine - 2, &cchKey) 2802 || cchKey == 0 2803 || cchKey > 4096) 2804 { 2805 RTMsgError("%s:%u: Unexpected data '%.*s'\n", szPath, ScmStreamTellLine(&Stream), cchLine, pchLine); 2806 rc = VERR_PARSE_ERROR; 2807 break; 2808 } 2809 2810 /* 2811 * Match the key and skip to the value line. Don't bother with 2812 * names containing EOL markers. 2813 */ 2814 size_t const offKey = ScmStreamTell(&Stream); 2815 bool fMatch = cchName == cchKey; 2816 if (fMatch) 2817 { 2818 pchLine = ScmStreamGetLine(&Stream, &cchLine, &enmEol); 2819 if (!pchLine) 2820 break; 2821 fMatch = cchLine == cchName 2822 && !memcmp(pchLine, pszName, cchName); 2823 } 2824 2825 if (RT_FAILURE(ScmStreamSeekAbsolute(&Stream, offKey + cchKey))) 2826 break; 2827 if (RT_FAILURE(ScmStreamSeekByLine(&Stream, ScmStreamTellLine(&Stream) + 1))) 2828 break; 2829 2830 /* 2831 * Read and Parse the 'V num' line. 2832 */ 2833 pchLine = ScmStreamGetLine(&Stream, &cchLine, &enmEol); 2834 if (!pchLine) 2835 break; 2836 size_t cchValue; 2837 if ( cchLine < 3 2838 || pchLine[0] != 'V' 2839 || pchLine[1] != ' ' 2840 || !scmSvnReadNumber(&pchLine[2], cchLine - 2, &cchValue) 2841 || cchValue == 0 2842 || cchValue > _1M) 2843 { 2844 RTMsgError("%s:%u: Unexpected data '%.*s'\n", szPath, ScmStreamTellLine(&Stream), cchLine, pchLine); 2845 rc = VERR_PARSE_ERROR; 2846 break; 2847 } 2848 2849 /* 2850 * If we have a match, allocate a return buffer and read the 2851 * value into it. Otherwise skip this value and continue 2852 * searching. 2853 */ 2854 if (fMatch) 2855 { 2856 if (!ppszValue) 2857 rc = VINF_SUCCESS; 2858 else 2859 { 2860 char *pszValue; 2861 rc = RTStrAllocEx(&pszValue, cchValue + 1); 2862 if (RT_SUCCESS(rc)) 2863 { 2864 rc = ScmStreamRead(&Stream, pszValue, cchValue); 2865 if (RT_SUCCESS(rc)) 2866 *ppszValue = pszValue; 2867 else 2868 RTStrFree(pszValue); 2869 } 2870 } 2871 break; 2872 } 2873 2874 if (RT_FAILURE(ScmStreamSeekRelative(&Stream, cchValue))) 2875 break; 2876 if (RT_FAILURE(ScmStreamSeekByLine(&Stream, ScmStreamTellLine(&Stream) + 1))) 2877 break; 2878 } 2879 2880 if (RT_FAILURE(ScmStreamGetStatus(&Stream))) 2881 { 2882 rc = ScmStreamGetStatus(&Stream); 2883 RTMsgError("%s: stream error %Rrc\n", szPath, rc); 2884 } 2885 ScmStreamDelete(&Stream); 2886 } 2887 } 2888 2889 if (rc == VERR_FILE_NOT_FOUND) 2890 rc = VERR_NOT_FOUND; 2891 return rc; 2892 2893 #else 2894 NOREF(pState); 2895 #endif 2896 return VERR_NOT_FOUND; 2897 } 2898 2899 2900 /** 2901 * Schedules the setting of a property. 2902 * 2903 * @returns IPRT status code. 2904 * @retval VERR_INVALID_STATE if not a SVN WC file. 2905 * @param pState The rewrite state to work on. 2906 * @param pszName The name of the property to set. 2907 * @param pszValue The value. NULL means deleting it. 2908 */ 2909 static int scmSvnSetProperty(PSCMRWSTATE pState, const char *pszName, const char *pszValue) 2910 { 2911 /* 2912 * Update any existing entry first. 2913 */ 2914 size_t i = pState->cSvnPropChanges; 2915 while (i-- > 0) 2916 if (!strcmp(pState->paSvnPropChanges[i].pszName, pszName)) 2917 { 2918 if (!pszValue) 2919 { 2920 RTStrFree(pState->paSvnPropChanges[i].pszValue); 2921 pState->paSvnPropChanges[i].pszValue = NULL; 2922 } 2923 else 2924 { 2925 char *pszCopy; 2926 int rc = RTStrDupEx(&pszCopy, pszValue); 2927 if (RT_FAILURE(rc)) 2928 return rc; 2929 pState->paSvnPropChanges[i].pszValue = pszCopy; 2930 } 2931 return VINF_SUCCESS; 2932 } 2933 2934 /* 2935 * Insert a new entry. 2936 */ 2937 i = pState->cSvnPropChanges; 2938 if ((i % 32) == 0) 2939 { 2940 void *pvNew = RTMemRealloc(pState->paSvnPropChanges, (i + 32) * sizeof(SCMSVNPROP)); 2941 if (!pvNew) 2942 return VERR_NO_MEMORY; 2943 pState->paSvnPropChanges = (PSCMSVNPROP)pvNew; 2944 } 2945 2946 pState->paSvnPropChanges[i].pszName = RTStrDup(pszName); 2947 pState->paSvnPropChanges[i].pszValue = pszValue ? RTStrDup(pszValue) : NULL; 2948 if ( pState->paSvnPropChanges[i].pszName 2949 && (pState->paSvnPropChanges[i].pszValue || !pszValue) ) 2950 pState->cSvnPropChanges = i + 1; 2951 else 2952 { 2953 RTStrFree(pState->paSvnPropChanges[i].pszName); 2954 pState->paSvnPropChanges[i].pszName = NULL; 2955 RTStrFree(pState->paSvnPropChanges[i].pszValue); 2956 pState->paSvnPropChanges[i].pszValue = NULL; 2957 return VERR_NO_MEMORY; 2958 } 2959 return VINF_SUCCESS; 2960 } 2961 2962 2963 /** 2964 * Schedules a property deletion. 2965 * 2966 * @returns IPRT status code. 2967 * @param pState The rewrite state to work on. 2968 * @param pszName The name of the property to delete. 2969 */ 2970 static int scmSvnDelProperty(PSCMRWSTATE pState, const char *pszName) 2971 { 2972 return scmSvnSetProperty(pState, pszName, NULL); 2973 } 2974 2975 2976 /** 2977 * Applies any SVN property changes to the work copy of the file. 2978 * 2979 * @returns IPRT status code. 2980 * @param pState The rewrite state which SVN property changes 2981 * should be applied. 2982 */ 2983 static int scmSvnDisplayChanges(PSCMRWSTATE pState) 2984 { 2985 size_t i = pState->cSvnPropChanges; 2986 while (i-- > 0) 2987 { 2988 const char *pszName = pState->paSvnPropChanges[i].pszName; 2989 const char *pszValue = pState->paSvnPropChanges[i].pszValue; 2990 if (pszValue) 2991 ScmVerbose(pState, 0, "svn ps '%s' '%s' %s\n", pszName, pszValue, pState->pszFilename); 2992 else 2993 ScmVerbose(pState, 0, "svn pd '%s' %s\n", pszName, pszValue, pState->pszFilename); 2994 } 2995 2996 return VINF_SUCCESS; 2997 } 2998 2999 /** 3000 * Applies any SVN property changes to the work copy of the file. 3001 * 3002 * @returns IPRT status code. 3003 * @param pState The rewrite state which SVN property changes 3004 * should be applied. 3005 */ 3006 static int scmSvnApplyChanges(PSCMRWSTATE pState) 3007 { 3008 #ifdef SCM_WITHOUT_LIBSVN 3009 /* 3010 * This sucks. We gotta find svn(.exe). 3011 */ 3012 static char s_szSvnPath[RTPATH_MAX]; 3013 if (s_szSvnPath[0] == '\0') 3014 scmSvnFindSvnBinary(s_szSvnPath, sizeof(s_szSvnPath)); 3015 3016 /* 3017 * Iterate thru the changes and apply them by starting the svn client. 3018 */ 3019 for (size_t i = 0; i <pState->cSvnPropChanges; i++) 3020 { 3021 const char *apszArgv[6]; 3022 apszArgv[0] = s_szSvnPath; 3023 apszArgv[1] = pState->paSvnPropChanges[i].pszValue ? "ps" : "pd"; 3024 apszArgv[2] = pState->paSvnPropChanges[i].pszName; 3025 int iArg = 3; 3026 if (pState->paSvnPropChanges[i].pszValue) 3027 apszArgv[iArg++] = pState->paSvnPropChanges[i].pszValue; 3028 apszArgv[iArg++] = pState->pszFilename; 3029 apszArgv[iArg++] = NULL; 3030 ScmVerbose(pState, 2, "executing: %s %s %s %s %s\n", 3031 apszArgv[0], apszArgv[1], apszArgv[2], apszArgv[3], apszArgv[4]); 3032 3033 RTPROCESS pid; 3034 int rc = RTProcCreate(s_szSvnPath, apszArgv, RTENV_DEFAULT, 0 /*fFlags*/, &pid); 3035 if (RT_SUCCESS(rc)) 3036 { 3037 RTPROCSTATUS Status; 3038 rc = RTProcWait(pid, RTPROCWAIT_FLAGS_BLOCK, &Status); 3039 if ( RT_SUCCESS(rc) 3040 && ( Status.enmReason != RTPROCEXITREASON_NORMAL 3041 || Status.iStatus != 0) ) 3042 { 3043 RTMsgError("%s: %s %s %s %s %s -> %s %u\n", 3044 pState->pszFilename, apszArgv[0], apszArgv[1], apszArgv[2], apszArgv[3], apszArgv[4], 3045 Status.enmReason == RTPROCEXITREASON_NORMAL ? "exit code" 3046 : Status.enmReason == RTPROCEXITREASON_SIGNAL ? "signal" 3047 : Status.enmReason == RTPROCEXITREASON_ABEND ? "abnormal end" 3048 : "abducted by alien", 3049 Status.iStatus); 3050 return VERR_GENERAL_FAILURE; 3051 } 3052 } 3053 if (RT_FAILURE(rc)) 3054 { 3055 RTMsgError("%s: error executing %s %s %s %s %s: %Rrc\n", 3056 pState->pszFilename, apszArgv[0], apszArgv[1], apszArgv[2], apszArgv[3], apszArgv[4], rc); 3057 return rc; 3058 } 3059 } 3060 3061 return VINF_SUCCESS; 3062 #else 3063 return VERR_NOT_IMPLEMENTED; 3064 #endif 2407 3065 } 2408 3066 … … 2516 3174 * @param pSettings The settings. 2517 3175 * @param enmDesiredEol The desired end of line indicator type. 2518 */ 2519 static bool rewrite_ForceEol(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings, SCMEOL enmDesiredEol) 3176 * @param pszDesiredSvnEol The desired svn:eol-style. 3177 */ 3178 static bool rewrite_ForceEol(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings, 3179 SCMEOL enmDesiredEol, const char *pszDesiredSvnEol) 2520 3180 { 2521 3181 if (!pSettings->fConvertEol) … … 2541 3201 ScmVerbose(pState, 2, " * Converted EOL markers\n"); 2542 3202 3203 /* Check svn:eol-style if appropriate */ 3204 if ( pSettings->fSetSvnEol 3205 && scmSvnIsInWorkingCopy(pState)) 3206 { 3207 char *pszEol; 3208 int rc = scmSvnQueryProperty(pState, "svn:eol-style", &pszEol); 3209 if ( (RT_SUCCESS(rc) && strcmp(pszEol, pszDesiredSvnEol)) 3210 || rc == VERR_NOT_FOUND) 3211 { 3212 if (rc == VERR_NOT_FOUND) 3213 ScmVerbose(pState, 2, " * Settings svn:eol-style to %s (missing)\n", pszDesiredSvnEol); 3214 else 3215 ScmVerbose(pState, 2, " * Settings svn:eol-style to %s (was: %s)\n", pszDesiredSvnEol, pszEol); 3216 rc = scmSvnSetProperty(pState, "svn:eol-style", pszDesiredSvnEol); 3217 if (RT_FAILURE(rc)) 3218 RTMsgError("scmSvnSetProperty: %Rrc\n", rc); /** @todo propagate the error somehow... */ 3219 } 3220 } 3221 2543 3222 /** @todo also check the subversion svn:eol-style state! */ 2544 3223 return fModified; … … 2556 3235 { 2557 3236 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) 2558 return rewrite_ForceEol(pState, pIn, pOut, pSettings, SCMEOL_CRLF );3237 return rewrite_ForceEol(pState, pIn, pOut, pSettings, SCMEOL_CRLF, "native"); 2559 3238 #else 2560 return rewrite_ForceEol(pState, pIn, pOut, pSettings, SCMEOL_LF );3239 return rewrite_ForceEol(pState, pIn, pOut, pSettings, SCMEOL_LF, "native"); 2561 3240 #endif 2562 3241 } … … 2572 3251 static bool rewrite_ForceLF(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings) 2573 3252 { 2574 return rewrite_ForceEol(pState, pIn, pOut, pSettings, SCMEOL_LF );3253 return rewrite_ForceEol(pState, pIn, pOut, pSettings, SCMEOL_LF, "LF"); 2575 3254 } 2576 3255 … … 2585 3264 static bool rewrite_ForceCRLF(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings) 2586 3265 { 2587 return rewrite_ForceEol(pState, pIn, pOut, pSettings, SCMEOL_CRLF );3266 return rewrite_ForceEol(pState, pIn, pOut, pSettings, SCMEOL_CRLF, "CRLF"); 2588 3267 } 2589 3268 … … 2736 3415 * 2737 3416 * @returns IPRT status code. 3417 * @param pState The rewriter state. 2738 3418 * @param pszFilename The file name. 2739 3419 * @param pszBasename The base name (pointer within @a pszFilename). … … 2743 3423 * these. 2744 3424 */ 2745 static int scmProcessFileInner( const char *pszFilename, const char *pszBasename, size_t cchBasename,3425 static int scmProcessFileInner(PSCMRWSTATE pState, const char *pszFilename, const char *pszBasename, size_t cchBasename, 2746 3426 PSCMSETTINGSBASE pBaseSettings) 2747 3427 { 2748 /*2749 * Init the rewriter state data.2750 */2751 SCMRWSTATE State;2752 State.fFirst = false;2753 State.pszFilename = pszFilename;2754 2755 3428 /* 2756 3429 * Do the file level filtering. … … 2760 3433 && !RTStrSimplePatternMultiMatch(pBaseSettings->pszFilterFiles, RTSTR_MAX, pszBasename, cchBasename, NULL)) 2761 3434 { 2762 ScmVerbose(NULL, 5, " file filter mismatch: \"%s\"\n", pszFilename);3435 ScmVerbose(NULL, 5, "skipping '%s': file filter mismatch\n", pszFilename); 2763 3436 return VINF_SUCCESS; 2764 3437 } … … 2768 3441 || RTStrSimplePatternMultiMatch(pBaseSettings->pszFilterOutFiles, RTSTR_MAX, pszFilename, RTSTR_MAX, NULL)) ) 2769 3442 { 2770 ScmVerbose(NULL, 5, "file filter out: \"%s\"\n", pszFilename); 3443 ScmVerbose(NULL, 5, "skipping '%s': filterd out\n", pszFilename); 3444 return VINF_SUCCESS; 3445 } 3446 if ( pBaseSettings->fOnlySvnFiles 3447 && !scmSvnIsInWorkingCopy(pState)) 3448 { 3449 ScmVerbose(NULL, 5, "skipping '%s': not in SVN WC\n", pszFilename); 2771 3450 return VINF_SUCCESS; 2772 3451 } … … 2784 3463 if (!pCfg) 2785 3464 { 2786 ScmVerbose(NULL, 4, " No rewriters configured for \"%s\"\n", pszFilename);3465 ScmVerbose(NULL, 4, "skipping '%s': no rewriters configured\n", pszFilename); 2787 3466 return VINF_SUCCESS; 2788 3467 } 2789 ScmVerbose( &State, 4, "matched \"%s\"\n", pCfg->pszFilePattern);3468 ScmVerbose(pState, 4, "matched \"%s\"\n", pCfg->pszFilePattern); 2790 3469 2791 3470 /* … … 2801 3480 if (ScmStreamIsText(&Stream1)) 2802 3481 { 2803 ScmVerbose( &State, 3, NULL);3482 ScmVerbose(pState, 3, NULL); 2804 3483 2805 3484 /* … … 2829 3508 for (size_t iRw = 0; iRw < pCfg->cRewriters; iRw++) 2830 3509 { 2831 bool fRc = pCfg->papfnRewriter[iRw]( &State, pIn, pOut, pBaseSettings);3510 bool fRc = pCfg->papfnRewriter[iRw](pState, pIn, pOut, pBaseSettings); 2832 3511 if (fRc) 2833 3512 { … … 2841 3520 } 2842 3521 2843 /* 2844 * If rewritten, write it back to disk. 2845 */ 2846 if (fModified) 3522 rc = ScmStreamGetStatus(&Stream1); 3523 if (RT_SUCCESS(rc)) 3524 rc = ScmStreamGetStatus(&Stream2); 3525 if (RT_SUCCESS(rc)) 3526 rc = ScmStreamGetStatus(&Stream3); 3527 if (RT_SUCCESS(rc)) 2847 3528 { 2848 if (!g_fDryRun) 3529 /* 3530 * If rewritten, write it back to disk. 3531 */ 3532 if (fModified) 2849 3533 { 2850 ScmVerbose(&State, 1, "Writing modified file to \"%s%s\"\n", pszFilename, g_pszChangedSuff); 2851 rc = ScmStreamWriteToFile(pIn, "%s%s", pszFilename, g_pszChangedSuff); 2852 if (RT_FAILURE(rc)) 2853 RTMsgError("Error writing '%s%s': %Rrc\n", pszFilename, g_pszChangedSuff, rc); 3534 if (!g_fDryRun) 3535 { 3536 ScmVerbose(pState, 1, "writing modified file to \"%s%s\"\n", pszFilename, g_pszChangedSuff); 3537 rc = ScmStreamWriteToFile(pIn, "%s%s", pszFilename, g_pszChangedSuff); 3538 if (RT_FAILURE(rc)) 3539 RTMsgError("Error writing '%s%s': %Rrc\n", pszFilename, g_pszChangedSuff, rc); 3540 } 3541 else 3542 { 3543 ScmVerbose(pState, 1, NULL); 3544 ScmDiffStreams(pszFilename, &Stream1, pIn, g_fDiffIgnoreEol, g_fDiffIgnoreLeadingWS, 3545 g_fDiffIgnoreTrailingWS, g_fDiffSpecialChars, pBaseSettings->cchTab, g_pStdOut); 3546 ScmVerbose(pState, 3, "would have modified the file \"%s%s\"\n", pszFilename, g_pszChangedSuff); 3547 scmSvnDisplayChanges(pState); 3548 } 2854 3549 } 2855 else 3550 3551 /* 3552 * If pending SVN property changes, apply them. 3553 */ 3554 if (pState->cSvnPropChanges && RT_SUCCESS(rc)) 2856 3555 { 2857 ScmVerbose(&State, 1, NULL); 2858 ScmDiffStreams(pszFilename, &Stream1, pIn, g_fDiffIgnoreEol, g_fDiffIgnoreLeadingWS, 2859 g_fDiffIgnoreTrailingWS, g_fDiffSpecialChars, pBaseSettings->cchTab, g_pStdOut); 2860 ScmVerbose(&State, 3, "Would have modified the file \"%s%s\"\n", pszFilename, g_pszChangedSuff); 3556 if (!g_fDryRun) 3557 { 3558 rc = scmSvnApplyChanges(pState); 3559 if (RT_FAILURE(rc)) 3560 RTMsgError("%s: failed to apply SVN property changes (%Rrc)\n", pszFilename, rc); 3561 } 3562 else 3563 scmSvnDisplayChanges(pState); 2861 3564 } 3565 3566 if (!fModified && !pState->cSvnPropChanges); 3567 ScmVerbose(pState, 3, "no change\n", pszFilename); 2862 3568 } 2863 3569 else 2864 ScmVerbose(&State, 3, "No change\n", pszFilename); 2865 3570 RTMsgError("%s: stream error %Rrc\n", pszFilename); 2866 3571 ScmStreamDelete(&Stream3); 2867 3572 } … … 2877 3582 } 2878 3583 else 2879 ScmVerbose( &State, 4, "not text file: \"%s\"\n", pszFilename);3584 ScmVerbose(pState, 4, "not text file: \"%s\"\n", pszFilename); 2880 3585 ScmStreamDelete(&Stream1); 2881 3586 … … 2903 3608 if (RT_SUCCESS(rc)) 2904 3609 { 2905 rc = scmProcessFileInner(pszFilename, pszBasename, cchBasename, &Base); 3610 SCMRWSTATE State; 3611 State.fFirst = false; 3612 State.pszFilename = pszFilename; 3613 State.cSvnPropChanges = 0; 3614 State.paSvnPropChanges = NULL; 3615 3616 rc = scmProcessFileInner(&State, pszFilename, pszBasename, cchBasename, &Base); 3617 3618 size_t i = State.cSvnPropChanges; 3619 while (i-- > 0) 3620 { 3621 RTStrFree(State.paSvnPropChanges[i].pszName); 3622 RTStrFree(State.paSvnPropChanges[i].pszValue); 3623 } 3624 RTMemFree(State.paSvnPropChanges); 3625 2906 3626 scmSettingsBaseDelete(&Base); 2907 3627 } … … 2992 3712 /* Read the next entry. */ 2993 3713 rc = RTDirRead(pDir, pEntry, NULL); 2994 if (RT_FAILURE(rc) && rc != VERR_NO_MORE_FILES)2995 RTMsgError("RTDirRead -> %Rrc\n", rc);2996 3714 if (RT_FAILURE(rc)) 3715 { 3716 if (rc == VERR_NO_MORE_FILES) 3717 rc = VINF_SUCCESS; 3718 else 3719 RTMsgError("RTDirRead -> %Rrc\n", rc); 2997 3720 break; 3721 } 2998 3722 2999 3723 /* Skip '.' and '..'. */ … … 3053 3777 } 3054 3778 RTDirClose(pDir); 3055 return RT_SUCCESS(rc) ? 0 : 1;3779 return rc; 3056 3780 3057 3781 } … … 3206 3930 fAdvanceTwo = i + 1 < RT_ELEMENTS(s_aOpts) 3207 3931 && ( strstr(s_aOpts[i+1].pszLong, "-no-") != NULL 3208 || strstr(s_aOpts[i+1].pszLong, "-not-") != NULL); 3932 || strstr(s_aOpts[i+1].pszLong, "-not-") != NULL 3933 || strstr(s_aOpts[i+1].pszLong, "-dont-") != NULL 3934 ); 3209 3935 if (fAdvanceTwo) 3210 3936 RTPrintf(" %s, %s\n", s_aOpts[i].pszLong, s_aOpts[i + 1].pszLong); … … 3225 3951 case SCMOPT_STRIP_TRAILING_LINES: RTPrintf(" Default: %RTbool\n", g_Defaults.fStripTrailingLines); break; 3226 3952 case SCMOPT_ONLY_SVN_DIRS: RTPrintf(" Default: %RTbool\n", g_Defaults.fOnlySvnDirs); break; 3953 case SCMOPT_ONLY_SVN_FILES: RTPrintf(" Default: %RTbool\n", g_Defaults.fOnlySvnFiles); break; 3227 3954 case SCMOPT_TAB_SIZE: RTPrintf(" Default: %u\n", g_Defaults.cchTab); break; 3228 3955 case SCMOPT_FILTER_OUT_DIRS: RTPrintf(" Default: %s\n", g_Defaults.pszFilterOutDirs); break;
Note:
See TracChangeset
for help on using the changeset viewer.