VirtualBox

Changeset 26557 in vbox


Ignore:
Timestamp:
Feb 16, 2010 12:02:37 AM (15 years ago)
Author:
vboxsync
Message:

scm: Added an option for correcting the svn:eol-style property.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/.scm-settings

    r26516 r26557  
    3030# Global settings.
    3131#
     32# Only consider directories and files that are part of a SVN working set.
     33#
    3234--only-svn-dirs
     35--only-svn-files
     36--set-svn-eol
    3337
    3438#
  • trunk/src/bldprogs/scm.cpp

    r26521 r26557  
    3535#include <iprt/ctype.h>
    3636#include <iprt/dir.h>
     37#include <iprt/env.h>
    3738#include <iprt/file.h>
    3839#include <iprt/err.h>
     
    4344#include <iprt/param.h>
    4445#include <iprt/path.h>
     46#include <iprt/process.h>
    4547#include <iprt/stream.h>
    4648#include <iprt/string.h>
     
    127129
    128130/**
     131 * SVN property.
     132 */
     133typedef 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. */
     143typedef SCMSVNPROP *PSCMSVNPROP;
     144/** Pointer to a const  SVN property. */
     145typedef SCMSVNPROP const *PCSCMSVNPROP;
     146
     147
     148/**
    129149 * Rewriter state.
    130150 */
     
    136156     *  rewrite. */
    137157    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;
    138162} SCMRWSTATE;
    139163/** Pointer to the rewriter state. */
     
    208232    bool            fStripTrailingBlanks;
    209233    bool            fStripTrailingLines;
     234    /** Only process files that are part of a SVN working copy. */
     235    bool            fOnlySvnFiles;
    210236    /** Only recurse into directories containing an .svn dir.  */
    211237    bool            fOnlySvnDirs;
     238    /** Set svn:eol-style if missing or incorrect. */
     239    bool            fSetSvnEol;
    212240    /**  */
    213241    unsigned        cchTab;
     
    250278    SCMOPT_ONLY_SVN_DIRS,
    251279    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,
    252284    SCMOPT_TAB_SIZE,
    253285    SCMOPT_FILTER_OUT_DIRS,
     
    354386    /* .fStripTrailingBlanks = */   true,
    355387    /* .fStripTrailingLines = */    true,
     388    /* .fOnlySvnFiles = */          false,
    356389    /* .fOnlySvnDirs = */           false,
     390    /* .fSetSvnEol = */             false,
    357391    /* .cchTab = */                 8,
    358392    /* .pszFilterFiles = */         (char *)"",
     
    378412    { "--only-svn-dirs",                    SCMOPT_ONLY_SVN_DIRS,                   RTGETOPT_REQ_NOTHING },
    379413    { "--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 },
    380418    { "--tab-size",                         SCMOPT_TAB_SIZE,                        RTGETOPT_REQ_UINT8   },
    381419    { "--filter-out-dirs",                  SCMOPT_FILTER_OUT_DIRS,                 RTGETOPT_REQ_STRING  },
     
    831869
    832870/**
     871 * Get the current stream position as an byte offset.
     872 *
     873 * @returns The current byte offset
     874 * @param   pStream             The stream.
     875 */
     876size_t ScmStreamTell(PSCMSTREAM pStream)
     877{
     878    return pStream->off;
     879}
     880
     881/**
    833882 * Get the current stream position as a line number.
    834883 *
     
    839888{
    840889    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 */
     898size_t ScmStreamSize(PSCMSTREAM pStream)
     899{
     900    return pStream->cb;
    841901}
    842902
     
    855915
    856916/**
    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 */
     928int 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 */
     982int 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.
    858996 *
    859997 * @returns IPRT status code.
     
    8691007        return pStream->rc;
    8701008
    871     /* Must be fully lineated of course. */
     1009    /* Must be fully lineated. (lazy bird) */
    8721010    if (RT_UNLIKELY(!pStream->fFullyLineated))
    8731011    {
     
    9351073
    9361074    /* 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;
    9381076    pStream->iLine     = iLine + 1;
    9391077
     
    9581096static const char *ScmStreamGetLine(PSCMSTREAM pStream, size_t *pcchLine, PSCMEOL penmEol)
    9591097{
     1098    /** @todo this doesn't work when pStream->off !=
     1099     *        pStream->paLines[pStream->iLine-1].pff. */
    9601100    if (!pStream->fFullyLineated)
    9611101        return scmStreamGetLineInternal(pStream, pcchLine, penmEol);
    9621102    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 */
     1119static 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);
    9631132}
    9641133
     
    18422011        case SCMOPT_NOT_ONLY_SVN_DIRS:
    18432012            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:
    18442026            return VINF_SUCCESS;
    18452027
     
    23932575        if (pState && !pState->fFirst)
    23942576        {
    2395             RTPrintf("%s: info: Rewriting '%s'...\n", g_szProgName, pState->pszFilename);
     2577            RTPrintf("%s: info: --= Rewriting '%s' =--\n", g_szProgName, pState->pszFilename);
    23962578            pState->fFirst = true;
    23972579        }
    23982580        if (pszFormat)
    23992581        {
    2400             RTPrintf("%s: info: ", g_szProgName);
     2582            RTPrintf(pState
     2583                     ? "%s: info:   "
     2584                     : "%s: info: ",
     2585                     g_szProgName);
    24012586            va_list va;
    24022587            va_start(va, pszFormat);
     
    24052590        }
    24062591    }
     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 */
     2604static 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 */
     2632static 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 */
     2664static 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 */
     2697static 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 */
     2720static 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 */
     2750static 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 */
     2909static 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 */
     2970static 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 */
     2983static 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 */
     3006static 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
    24073065}
    24083066
     
    25163174 * @param   pSettings           The settings.
    25173175 * @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 */
     3178static bool rewrite_ForceEol(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings,
     3179                             SCMEOL enmDesiredEol, const char *pszDesiredSvnEol)
    25203180{
    25213181    if (!pSettings->fConvertEol)
     
    25413201        ScmVerbose(pState, 2, " * Converted EOL markers\n");
    25423202
     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
    25433222    /** @todo also check the subversion svn:eol-style state! */
    25443223    return fModified;
     
    25563235{
    25573236#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");
    25593238#else
    2560     return rewrite_ForceEol(pState, pIn, pOut, pSettings, SCMEOL_LF);
     3239    return rewrite_ForceEol(pState, pIn, pOut, pSettings, SCMEOL_LF,   "native");
    25613240#endif
    25623241}
     
    25723251static bool rewrite_ForceLF(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings)
    25733252{
    2574     return rewrite_ForceEol(pState, pIn, pOut, pSettings, SCMEOL_LF);
     3253    return rewrite_ForceEol(pState, pIn, pOut, pSettings, SCMEOL_LF, "LF");
    25753254}
    25763255
     
    25853264static bool rewrite_ForceCRLF(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings)
    25863265{
    2587     return rewrite_ForceEol(pState, pIn, pOut, pSettings, SCMEOL_CRLF);
     3266    return rewrite_ForceEol(pState, pIn, pOut, pSettings, SCMEOL_CRLF, "CRLF");
    25883267}
    25893268
     
    27363415 *
    27373416 * @returns IPRT status code.
     3417 * @param   pState              The rewriter state.
    27383418 * @param   pszFilename         The file name.
    27393419 * @param   pszBasename         The base name (pointer within @a pszFilename).
     
    27433423 *                              these.
    27443424 */
    2745 static int scmProcessFileInner(const char *pszFilename, const char *pszBasename, size_t cchBasename,
     3425static int scmProcessFileInner(PSCMRWSTATE pState, const char *pszFilename, const char *pszBasename, size_t cchBasename,
    27463426                               PSCMSETTINGSBASE pBaseSettings)
    27473427{
    2748     /*
    2749      * Init the rewriter state data.
    2750      */
    2751     SCMRWSTATE State;
    2752     State.fFirst      = false;
    2753     State.pszFilename = pszFilename;
    2754 
    27553428    /*
    27563429     * Do the file level filtering.
     
    27603433        && !RTStrSimplePatternMultiMatch(pBaseSettings->pszFilterFiles, RTSTR_MAX, pszBasename, cchBasename, NULL))
    27613434    {
    2762         ScmVerbose(NULL, 5, "file filter mismatch: \"%s\"\n", pszFilename);
     3435        ScmVerbose(NULL, 5, "skipping '%s': file filter mismatch\n", pszFilename);
    27633436        return VINF_SUCCESS;
    27643437    }
     
    27683441            || RTStrSimplePatternMultiMatch(pBaseSettings->pszFilterOutFiles, RTSTR_MAX, pszFilename, RTSTR_MAX, NULL)) )
    27693442    {
    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);
    27713450        return VINF_SUCCESS;
    27723451    }
     
    27843463    if (!pCfg)
    27853464    {
    2786         ScmVerbose(NULL, 4, "No rewriters configured for \"%s\"\n", pszFilename);
     3465        ScmVerbose(NULL, 4, "skipping '%s': no rewriters configured\n", pszFilename);
    27873466        return VINF_SUCCESS;
    27883467    }
    2789     ScmVerbose(&State, 4, "matched \"%s\"\n", pCfg->pszFilePattern);
     3468    ScmVerbose(pState, 4, "matched \"%s\"\n", pCfg->pszFilePattern);
    27903469
    27913470    /*
     
    28013480    if (ScmStreamIsText(&Stream1))
    28023481    {
    2803         ScmVerbose(&State, 3, NULL);
     3482        ScmVerbose(pState, 3, NULL);
    28043483
    28053484        /*
     
    28293508                    for (size_t iRw = 0; iRw < pCfg->cRewriters; iRw++)
    28303509                    {
    2831                         bool fRc = pCfg->papfnRewriter[iRw](&State, pIn, pOut, pBaseSettings);
     3510                        bool fRc = pCfg->papfnRewriter[iRw](pState, pIn, pOut, pBaseSettings);
    28323511                        if (fRc)
    28333512                        {
     
    28413520                    }
    28423521
    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))
    28473528                    {
    2848                         if (!g_fDryRun)
     3529                        /*
     3530                         * If rewritten, write it back to disk.
     3531                         */
     3532                        if (fModified)
    28493533                        {
    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                            }
    28543549                        }
    2855                         else
     3550
     3551                        /*
     3552                         * If pending SVN property changes, apply them.
     3553                         */
     3554                        if (pState->cSvnPropChanges && RT_SUCCESS(rc))
    28563555                        {
    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);
    28613564                        }
     3565
     3566                        if (!fModified && !pState->cSvnPropChanges);
     3567                            ScmVerbose(pState, 3, "no change\n", pszFilename);
    28623568                    }
    28633569                    else
    2864                         ScmVerbose(&State, 3, "No change\n", pszFilename);
    2865 
     3570                        RTMsgError("%s: stream error %Rrc\n", pszFilename);
    28663571                    ScmStreamDelete(&Stream3);
    28673572                }
     
    28773582    }
    28783583    else
    2879         ScmVerbose(&State, 4, "not text file: \"%s\"\n", pszFilename);
     3584        ScmVerbose(pState, 4, "not text file: \"%s\"\n", pszFilename);
    28803585    ScmStreamDelete(&Stream1);
    28813586
     
    29033608    if (RT_SUCCESS(rc))
    29043609    {
    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
    29063626        scmSettingsBaseDelete(&Base);
    29073627    }
     
    29923712        /* Read the next entry. */
    29933713        rc = RTDirRead(pDir, pEntry, NULL);
    2994         if (RT_FAILURE(rc) && rc != VERR_NO_MORE_FILES)
    2995             RTMsgError("RTDirRead -> %Rrc\n", rc);
    29963714        if (RT_FAILURE(rc))
     3715        {
     3716            if (rc == VERR_NO_MORE_FILES)
     3717                rc = VINF_SUCCESS;
     3718            else
     3719                RTMsgError("RTDirRead -> %Rrc\n", rc);
    29973720            break;
     3721        }
    29983722
    29993723        /* Skip '.' and '..'. */
     
    30533777    }
    30543778    RTDirClose(pDir);
    3055     return RT_SUCCESS(rc) ? 0 : 1;
     3779    return rc;
    30563780
    30573781}
     
    32063930                        fAdvanceTwo = i + 1 < RT_ELEMENTS(s_aOpts)
    32073931                                   && (   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                                      );
    32093935                        if (fAdvanceTwo)
    32103936                            RTPrintf("  %s, %s\n", s_aOpts[i].pszLong, s_aOpts[i + 1].pszLong);
     
    32253951                        case SCMOPT_STRIP_TRAILING_LINES:   RTPrintf("      Default: %RTbool\n", g_Defaults.fStripTrailingLines); break;
    32263952                        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;
    32273954                        case SCMOPT_TAB_SIZE:               RTPrintf("      Default: %u\n", g_Defaults.cchTab); break;
    32283955                        case SCMOPT_FILTER_OUT_DIRS:        RTPrintf("      Default: %s\n", g_Defaults.pszFilterOutDirs); break;
Note: See TracChangeset for help on using the changeset viewer.

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