VirtualBox

Changeset 41966 in vbox for trunk/src/bldprogs


Ignore:
Timestamp:
Jun 29, 2012 2:53:56 AM (13 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
78836
Message:

scm: For subversion v1.7, call the svn util to extract the info we need. This is slow, but it works.

Location:
trunk/src/bldprogs
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/bldprogs/scm.cpp

    r40534 r41966  
    12491249    if (pSettingsStack->Base.fOnlySvnDirs)
    12501250    {
    1251         rc = RTPathAppend(pszBuf, RTPATH_MAX, ".svn");
    1252         if (RT_FAILURE(rc))
    1253         {
    1254             RTMsgError("RTPathAppend: %Rrc\n", rc);
    1255             return rc;
    1256         }
    1257         if (!RTDirExists(pszBuf))
    1258             return VINF_SUCCESS;
    1259 
    1260         Assert(RTPATH_IS_SLASH(pszBuf[cchDir]));
    1261         pszBuf[cchDir]     = '\0';
    1262         pszBuf[cchDir - 1] = '.';
     1251        if (!ScmSvnIsDirInWorkingCopy(pszBuf))
     1252            return VINF_SUCCESS;
    12631253    }
    12641254
  • trunk/src/bldprogs/scm.h

    r40534 r41966  
    5151
    5252
     53bool ScmSvnIsDirInWorkingCopy(const char *pszDir);
    5354bool ScmSvnIsInWorkingCopy(PSCMRWSTATE pState);
    5455int  ScmSvnQueryProperty(PSCMRWSTATE pState, const char *pszName, char **ppszValue);
  • trunk/src/bldprogs/scmsubversion.cpp

    r40534 r41966  
    4040
    4141
     42/*******************************************************************************
     43*   Global Variables                                                           *
     44*******************************************************************************/
     45static char g_szSvnPath[RTPATH_MAX];
     46static enum
     47{
     48    kScmSvnVersion_Ancient = 1,
     49    kScmSvnVersion_1_6,
     50    kScmSvnVersion_1_7,
     51    kScmSvnVersion_End
     52}           g_enmSvnVersion = kScmSvnVersion_Ancient;
     53
    4254
    4355#ifdef SCM_WITHOUT_LIBSVN
     
    6678}
    6779
    68 
    69 /**
    70  * Finds the svn binary.
    71  *
    72  * @param   pszPath             Where to store it.  Worst case, we'll return
    73  *                              "svn" here.
    74  * @param   cchPath             The size of the buffer pointed to by @a pszPath.
    75  */
    76 static void scmSvnFindSvnBinary(char *pszPath, size_t cchPath)
    77 {
     80#include <iprt/handle.h>
     81#include <iprt/pipe.h>
     82#include <iprt/poll.h>
     83
     84/**
     85 * Reads from a pipe.
     86 *
     87 * @returns @a rc or other status code.
     88 * @param   rc              The current status of the operation.  Error status
     89 *                          are preserved and returned.
     90 * @param   phPipeR         Pointer to the pipe handle.
     91 * @param   pcbAllocated    Pointer to the buffer size variable.
     92 * @param   poffCur         Pointer to the buffer offset variable.
     93 * @param   ppszBuffer      Pointer to the buffer pointer variable.
     94 */
     95static int rtProcProcessOutput(int rc, PRTPIPE phPipeR, size_t *pcbAllocated, size_t *poffCur, char **ppszBuffer,
     96                               RTPOLLSET hPollSet, uint32_t idPollSet)
     97{
     98    size_t  cbRead;
     99    char    szTmp[_4K - 1];
     100    for (;;)
     101    {
     102        int rc2 = RTPipeRead(*phPipeR, szTmp, sizeof(szTmp), &cbRead);
     103        if (RT_SUCCESS(rc2) && cbRead)
     104        {
     105            /* Resize the buffer. */
     106            if (*poffCur + cbRead >= *pcbAllocated)
     107            {
     108                if (*pcbAllocated >= _1G)
     109                {
     110                    RTPollSetRemove(hPollSet, idPollSet);
     111                    rc2 = RTPipeClose(*phPipeR); AssertRC(rc2);
     112                    *phPipeR = NIL_RTPIPE;
     113                    return RT_SUCCESS(rc) ? VERR_TOO_MUCH_DATA : rc;
     114                }
     115
     116                size_t cbNew = *pcbAllocated ? *pcbAllocated * 2 : sizeof(szTmp) + 1;
     117                Assert(*poffCur + cbRead < cbNew);
     118                rc2 = RTStrRealloc(ppszBuffer, cbNew);
     119                if (RT_FAILURE(rc2))
     120                {
     121                    RTPollSetRemove(hPollSet, idPollSet);
     122                    rc2 = RTPipeClose(*phPipeR); AssertRC(rc2);
     123                    *phPipeR = NIL_RTPIPE;
     124                    return RT_SUCCESS(rc) ? rc2 : rc;
     125                }
     126                *pcbAllocated = cbNew;
     127            }
     128
     129            /* Append the new data, terminating it. */
     130            memcpy(*ppszBuffer + *poffCur, szTmp, cbRead);
     131            *poffCur += cbRead;
     132            (*ppszBuffer)[*poffCur] = '\0';
     133
     134            /* Check for null terminators in the string. */
     135            if (RT_SUCCESS(rc) && memchr(szTmp, '\0', cbRead))
     136                rc = VERR_NO_TRANSLATION;
     137
     138            /* If we read a full buffer, try read some more. */
     139            if (RT_SUCCESS(rc) && cbRead == sizeof(szTmp))
     140                continue;
     141        }
     142        else if (rc2 != VINF_TRY_AGAIN)
     143        {
     144            if (RT_FAILURE(rc) && rc2 != VERR_BROKEN_PIPE)
     145                rc = rc2;
     146            RTPollSetRemove(hPollSet, idPollSet);
     147            rc2 = RTPipeClose(*phPipeR); AssertRC(rc2);
     148            *phPipeR = NIL_RTPIPE;
     149        }
     150        return rc;
     151    }
     152}
     153
     154/** @name RTPROCEXEC_FLAGS_XXX - flags for RTProcExec and RTProcExecToString.
     155 * @{ */
     156/** Redirect /dev/null to standard input. */
     157#define RTPROCEXEC_FLAGS_STDIN_NULL             RT_BIT_32(0)
     158/** Redirect standard output to /dev/null. */
     159#define RTPROCEXEC_FLAGS_STDOUT_NULL            RT_BIT_32(1)
     160/** Redirect standard error to /dev/null. */
     161#define RTPROCEXEC_FLAGS_STDERR_NULL            RT_BIT_32(2)
     162/** Redirect all standard output to /dev/null as well as directing /dev/null
     163 * to standard input. */
     164#define RTPROCEXEC_FLAGS_STD_NULL               (  RTPROCEXEC_FLAGS_STDIN_NULL \
     165                                                 | RTPROCEXEC_FLAGS_STDOUT_NULL \
     166                                                 | RTPROCEXEC_FLAGS_STDERR_NULL)
     167/** Mask containing the valid flags. */
     168#define RTPROCEXEC_FLAGS_VALID_MASK             UINT32_C(0x00000007)
     169/** @} */
     170
     171/**
     172 * Runs a process, collecting the standard output and/or standard error.
     173 *
     174 *
     175 * @returns IPRT status code
     176 * @retval  VERR_NO_TRANSLATION if the output of the program isn't valid UTF-8
     177 *          or contains a nul character.
     178 * @retval  VERR_TOO_MUCH_DATA if the process produced too much data.
     179 *
     180 * @param   pszExec     Executable image to use to create the child process.
     181 * @param   papszArgs   Pointer to an array of arguments to the child.  The
     182 *                      array terminated by an entry containing NULL.
     183 * @param   hEnv        Handle to the environment block for the child.
     184 * @param   fFlags      A combination of RTPROCEXEC_FLAGS_XXX.  The @a
     185 *                      ppszStdOut and @a ppszStdErr parameters takes precedence
     186 *                      over redirection flags.
     187 * @param   pStatus     Where to return the status on success.
     188 * @param   ppszStdOut  Where to return the text written to standard output. If
     189 *                      NULL then standard output will not be collected and go
     190 *                      to the standard output handle of the process.
     191 *                      Free with RTStrFree, regardless of return status.
     192 * @param   ppszStdErr  Where to return the text written to standard error. If
     193 *                      NULL then standard output will not be collected and go
     194 *                      to the standard error handle of the process.
     195 *                      Free with RTStrFree, regardless of return status.
     196 */
     197int RTProcExecToString(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags,
     198                       PRTPROCSTATUS pStatus, char **ppszStdOut, char **ppszStdErr)
     199{
     200    int rc2;
     201
     202    /*
     203     * Clear output arguments (no returning failure here, simply crash!).
     204     */
     205    AssertPtr(pStatus);
     206    pStatus->enmReason = RTPROCEXITREASON_ABEND;
     207    pStatus->iStatus   = RTEXITCODE_FAILURE;
     208    AssertPtrNull(ppszStdOut);
     209    if (ppszStdOut)
     210        *ppszStdOut = NULL;
     211    AssertPtrNull(ppszStdOut);
     212    if (ppszStdErr)
     213        *ppszStdErr = NULL;
     214
     215    /*
     216     * Check input arguments.
     217     */
     218    AssertReturn(!(fFlags & ~RTPROCEXEC_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER);
     219
     220    /*
     221     * Do we need a standard input bitbucket?
     222     */
     223    int         rc = VINF_SUCCESS;
     224    PRTHANDLE   phChildStdIn = NULL;
     225    RTHANDLE    hChildStdIn;
     226    hChildStdIn.enmType = RTHANDLETYPE_FILE;
     227    hChildStdIn.u.hFile = NIL_RTFILE;
     228    if ((fFlags & RTPROCEXEC_FLAGS_STDIN_NULL) && RT_SUCCESS(rc))
     229    {
     230        phChildStdIn = &hChildStdIn;
     231        rc = RTFileOpenBitBucket(&hChildStdIn.u.hFile, RTFILE_O_READ);
     232    }
     233
     234    /*
     235     * Create the output pipes / bitbuckets.
     236     */
     237    RTPIPE      hPipeStdOutR  = NIL_RTPIPE;
     238    PRTHANDLE   phChildStdOut = NULL;
     239    RTHANDLE    hChildStdOut;
     240    hChildStdOut.enmType = RTHANDLETYPE_PIPE;
     241    hChildStdOut.u.hPipe = NIL_RTPIPE;
     242    if (ppszStdOut && RT_SUCCESS(rc))
     243    {
     244        phChildStdOut = &hChildStdOut;
     245        rc = RTPipeCreate(&hPipeStdOutR, &hChildStdOut.u.hPipe, 0 /*fFlags*/);
     246    }
     247    else if ((fFlags & RTPROCEXEC_FLAGS_STDOUT_NULL) && RT_SUCCESS(rc))
     248    {
     249        phChildStdOut = &hChildStdOut;
     250        hChildStdOut.enmType = RTHANDLETYPE_FILE;
     251        hChildStdOut.u.hFile = NIL_RTFILE;
     252        rc = RTFileOpenBitBucket(&hChildStdOut.u.hFile, RTFILE_O_WRITE);
     253    }
     254
     255    RTPIPE      hPipeStdErrR  = NIL_RTPIPE;
     256    PRTHANDLE   phChildStdErr = NULL;
     257    RTHANDLE    hChildStdErr;
     258    hChildStdErr.enmType = RTHANDLETYPE_PIPE;
     259    hChildStdErr.u.hPipe = NIL_RTPIPE;
     260    if (ppszStdErr && RT_SUCCESS(rc))
     261    {
     262        phChildStdErr = &hChildStdErr;
     263        rc = RTPipeCreate(&hPipeStdErrR, &hChildStdErr.u.hPipe, 0 /*fFlags*/);
     264    }
     265    else if ((fFlags & RTPROCEXEC_FLAGS_STDERR_NULL) && RT_SUCCESS(rc))
     266    {
     267        phChildStdErr = &hChildStdErr;
     268        hChildStdErr.enmType = RTHANDLETYPE_FILE;
     269        hChildStdErr.u.hFile = NIL_RTFILE;
     270        rc = RTFileOpenBitBucket(&hChildStdErr.u.hFile, RTFILE_O_WRITE);
     271    }
     272
     273    if (RT_SUCCESS(rc))
     274    {
     275        RTPOLLSET hPollSet;
     276        rc = RTPollSetCreate(&hPollSet);
     277        if (RT_SUCCESS(rc))
     278        {
     279            if (hPipeStdOutR != NIL_RTPIPE && RT_SUCCESS(rc))
     280                rc = RTPollSetAddPipe(hPollSet, hPipeStdOutR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, 1);
     281            if (hPipeStdErrR != NIL_RTPIPE)
     282                rc = RTPollSetAddPipe(hPollSet, hPipeStdErrR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, 2);
     283        }
     284        if (RT_SUCCESS(rc))
     285        {
     286            /*
     287             * Create the process.
     288             */
     289            RTPROCESS hProc;
     290            rc = RTProcCreateEx(g_szSvnPath,
     291                                papszArgs,
     292                                RTENV_DEFAULT,
     293                                0 /*fFlags*/,
     294                                NULL /*phStdIn*/,
     295                                phChildStdOut,
     296                                phChildStdErr,
     297                                NULL /*pszAsUser*/,
     298                                NULL /*pszPassword*/,
     299                                &hProc);
     300            rc2 = RTHandleClose(&hChildStdErr); AssertRC(rc2);
     301            rc2 = RTHandleClose(&hChildStdOut); AssertRC(rc2);
     302
     303            if (RT_SUCCESS(rc))
     304            {
     305                /*
     306                 * Process output and wait for the process to finish.
     307                 */
     308                size_t cbStdOut  = 0;
     309                size_t offStdOut = 0;
     310                size_t cbStdErr  = 0;
     311                size_t offStdErr = 0;
     312                for (;;)
     313                {
     314                    if (hPipeStdOutR != NIL_RTPIPE)
     315                        rc = rtProcProcessOutput(rc, &hPipeStdOutR, &cbStdOut, &offStdOut, ppszStdOut, hPollSet, 1);
     316                    if (hPipeStdErrR != NIL_RTPIPE)
     317                        rc = rtProcProcessOutput(rc, &hPipeStdErrR, &cbStdErr, &offStdErr, ppszStdErr, hPollSet, 2);
     318                    if (hPipeStdOutR == NIL_RTPIPE && hPipeStdErrR == NIL_RTPIPE)
     319                        break;
     320
     321                    if (hProc != NIL_RTPROCESS)
     322                    {
     323                        rc2 = RTProcWait(hProc, RTPROCWAIT_FLAGS_NOBLOCK, pStatus);
     324                        if (rc2 != VERR_PROCESS_RUNNING)
     325                        {
     326                            if (RT_FAILURE(rc2))
     327                                rc = rc2;
     328                            hProc = NIL_RTPROCESS;
     329                        }
     330                    }
     331
     332                    rc2 = RTPoll(hPollSet, 10000, NULL, NULL);
     333                    Assert(RT_SUCCESS(rc2) || rc2 == VERR_TIMEOUT);
     334                }
     335
     336                if (RT_SUCCESS(rc))
     337                {
     338                    if (   (ppszStdOut && *ppszStdOut && !RTStrIsValidEncoding(*ppszStdOut))
     339                        || (ppszStdErr && *ppszStdErr && !RTStrIsValidEncoding(*ppszStdErr)) )
     340                        rc = VERR_NO_TRANSLATION;
     341                }
     342
     343                /*
     344                 * No more output, just wait for it to finish.
     345                 */
     346                if (hProc != NIL_RTPROCESS)
     347                {
     348                    rc2 = RTProcWait(hProc, RTPROCWAIT_FLAGS_BLOCK, pStatus);
     349                    if (RT_FAILURE(rc2))
     350                        rc = rc2;
     351                }
     352            }
     353            RTPollSetDestroy(hPollSet);
     354        }
     355    }
     356
     357    rc2 = RTHandleClose(&hChildStdErr); AssertRC(rc2);
     358    rc2 = RTHandleClose(&hChildStdOut); AssertRC(rc2);
     359    rc2 = RTHandleClose(&hChildStdIn);  AssertRC(rc2);
     360    rc2 = RTPipeClose(hPipeStdErrR);    AssertRC(rc2);
     361    rc2 = RTPipeClose(hPipeStdOutR);    AssertRC(rc2);
     362    return rc;
     363}
     364
     365
     366/**
     367 * Runs a process, waiting for it to complete.
     368 *
     369 * @returns IPRT status code
     370 *
     371 * @param   pszExec     Executable image to use to create the child process.
     372 * @param   papszArgs   Pointer to an array of arguments to the child.  The
     373 *                      array terminated by an entry containing NULL.
     374 * @param   hEnv        Handle to the environment block for the child.
     375 * @param   fFlags      A combination of RTPROCEXEC_FLAGS_XXX.
     376 * @param   pStatus     Where to return the status on success.
     377 */
     378int RTProcExec(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags,
     379               PRTPROCSTATUS pStatus)
     380{
     381    int rc;
     382
     383    /*
     384     * Clear output argument (no returning failure here, simply crash!).
     385     */
     386    AssertPtr(pStatus);
     387    pStatus->enmReason = RTPROCEXITREASON_ABEND;
     388    pStatus->iStatus   = RTEXITCODE_FAILURE;
     389
     390    /*
     391     * Check input arguments.
     392     */
     393    AssertReturn(!(fFlags & ~RTPROCEXEC_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER);
     394
     395    /*
     396     * Set up /dev/null redirections.
     397     */
     398    PRTHANDLE   aph[3] = { NULL, NULL, NULL };
     399    RTHANDLE    ah[3];
     400    for (uint32_t i = 0; i < 3; i++)
     401    {
     402        ah[i].enmType = RTHANDLETYPE_FILE;
     403        ah[i].u.hFile = NIL_RTFILE;
     404    }
     405    rc = VINF_SUCCESS;
     406    if ((fFlags & RTPROCEXEC_FLAGS_STDIN_NULL) && RT_SUCCESS(rc))
     407    {
     408        aph[0] = &ah[0];
     409        rc = RTFileOpenBitBucket(&ah[0].u.hFile, RTFILE_O_READ);
     410    }
     411    if ((fFlags & RTPROCEXEC_FLAGS_STDOUT_NULL) && RT_SUCCESS(rc))
     412    {
     413        aph[1] = &ah[1];
     414        rc = RTFileOpenBitBucket(&ah[1].u.hFile, RTFILE_O_WRITE);
     415    }
     416    if ((fFlags & RTPROCEXEC_FLAGS_STDERR_NULL) && RT_SUCCESS(rc))
     417    {
     418        aph[2] = &ah[2];
     419        rc = RTFileOpenBitBucket(&ah[2].u.hFile, RTFILE_O_WRITE);
     420    }
     421
     422    /*
     423     * Create the process.
     424     */
     425    RTPROCESS hProc;
     426    if (RT_SUCCESS(rc))
     427        rc = RTProcCreateEx(g_szSvnPath,
     428                            papszArgs,
     429                            RTENV_DEFAULT,
     430                            0 /*fFlags*/,
     431                            aph[0],
     432                            aph[1],
     433                            aph[2],
     434                            NULL /*pszAsUser*/,
     435                            NULL /*pszPassword*/,
     436                            &hProc);
     437
     438    for (uint32_t i = 0; i < 3; i++)
     439        RTFileClose(ah[i].u.hFile);
     440
     441    if (RT_SUCCESS(rc))
     442        rc = RTProcWait(hProc, RTPROCWAIT_FLAGS_BLOCK, pStatus);
     443    return rc;
     444}
     445
     446
     447
     448/**
     449 * Executes SVN and gets the output.
     450 *
     451 * Standard error is suppressed.
     452 *
     453 * @returns VINF_SUCCESS if the command executed successfully.
     454 * @param   pState              The rewrite state to work on.  Can be NULL.
     455 * @param   papszArgs           The SVN argument.
     456 * @param   fNormalFailureOk    Whether normal failure is ok.
     457 * @param   ppszStdOut          Where to return the output on success.
     458 */
     459static int scmSvnRunAndGetOutput(PSCMRWSTATE pState, const char **papszArgs, bool fNormalFailureOk, char **ppszStdOut)
     460{
     461    *ppszStdOut = NULL;
     462
     463    char *pszCmdLine = NULL;
     464    int rc = RTGetOptArgvToString(&pszCmdLine, papszArgs, RTGETOPTARGV_CNV_QUOTE_BOURNE_SH);
     465    if (RT_FAILURE(rc))
     466        return rc;
     467    ScmVerbose(pState, 2, "executing: %s\n", pszCmdLine);
     468
     469    RTPROCSTATUS Status;
     470    rc = RTProcExecToString(g_szSvnPath, papszArgs, RTENV_DEFAULT,
     471                            RTPROCEXEC_FLAGS_STD_NULL, &Status, ppszStdOut, NULL);
     472
     473    if (    RT_SUCCESS(rc)
     474        &&  (   Status.enmReason != RTPROCEXITREASON_NORMAL
     475             || Status.iStatus != 0) )
     476    {
     477        if (fNormalFailureOk || Status.enmReason != RTPROCEXITREASON_NORMAL)
     478            RTMsgError("%s: %s -> %s %u\n",
     479                       pszCmdLine,
     480                       Status.enmReason == RTPROCEXITREASON_NORMAL   ? "exit code"
     481                       : Status.enmReason == RTPROCEXITREASON_SIGNAL ? "signal"
     482                       : Status.enmReason == RTPROCEXITREASON_ABEND  ? "abnormal end"
     483                       : "abducted by alien",
     484                       Status.iStatus);
     485        rc = VERR_GENERAL_FAILURE;
     486    }
     487    else if (RT_FAILURE(rc))
     488    {
     489        if (pState)
     490            RTMsgError("%s: executing: %s => %Rrc\n", pState->pszFilename, pszCmdLine, rc);
     491        else
     492            RTMsgError("executing: %s => %Rrc\n", pszCmdLine, rc);
     493    }
     494
     495    if (RT_FAILURE(rc))
     496    {
     497        RTStrFree(*ppszStdOut);
     498        *ppszStdOut = NULL;
     499    }
     500    RTStrFree(pszCmdLine);
     501    return rc;
     502}
     503
     504
     505/**
     506 * Executes SVN.
     507 *
     508 * Standard error and standard output is suppressed.
     509 *
     510 * @returns VINF_SUCCESS if the command executed successfully.
     511 * @param   pState              The rewrite state to work on.
     512 * @param   papszArgs           The SVN argument.
     513 * @param   fNormalFailureOk    Whether normal failure is ok.
     514 */
     515static int scmSvnRun(PSCMRWSTATE pState, const char **papszArgs, bool fNormalFailureOk)
     516{
     517    char *pszCmdLine = NULL;
     518    int rc = RTGetOptArgvToString(&pszCmdLine, papszArgs, RTGETOPTARGV_CNV_QUOTE_BOURNE_SH);
     519    if (RT_FAILURE(rc))
     520        return rc;
     521    ScmVerbose(pState, 2, "executing: %s\n", pszCmdLine);
     522
     523    /* Lazy bird uses RTProcExecToString. */
     524    RTPROCSTATUS Status;
     525    rc = RTProcExec(g_szSvnPath, papszArgs, RTENV_DEFAULT, RTPROCEXEC_FLAGS_STD_NULL, &Status);
     526
     527    if (    RT_SUCCESS(rc)
     528        &&  (   Status.enmReason != RTPROCEXITREASON_NORMAL
     529             || Status.iStatus != 0) )
     530    {
     531        if (fNormalFailureOk || Status.enmReason != RTPROCEXITREASON_NORMAL)
     532            RTMsgError("%s: %s -> %s %u\n",
     533                       pState->pszFilename,
     534                       pszCmdLine,
     535                       Status.enmReason == RTPROCEXITREASON_NORMAL   ? "exit code"
     536                       : Status.enmReason == RTPROCEXITREASON_SIGNAL ? "signal"
     537                       : Status.enmReason == RTPROCEXITREASON_ABEND  ? "abnormal end"
     538                       : "abducted by alien",
     539                       Status.iStatus);
     540        rc = VERR_GENERAL_FAILURE;
     541    }
     542    else if (RT_FAILURE(rc))
     543        RTMsgError("%s: %s -> %Rrc\n", pState->pszFilename, pszCmdLine, rc);
     544
     545    RTStrFree(pszCmdLine);
     546    return rc;
     547}
     548
     549
     550/**
     551 * Finds the svn binary, updating g_szSvnPath and g_enmSvnVersion.
     552 */
     553static void scmSvnFindSvnBinary(PSCMRWSTATE pState)
     554{
     555    /* Already been called? */
     556    if (g_szSvnPath[0] != '\0')
     557        return;
     558
     559    /*
     560     * Locate it.
     561     */
    78562    /** @todo code page fun... */
    79     Assert(cchPath >= sizeof("svn"));
    80563#ifdef RT_OS_WINDOWS
    81564    const char *pszEnvVar = RTEnvGet("Path");
     
    83566    const char *pszEnvVar = RTEnvGet("PATH");
    84567#endif
    85     if (pszPath)
     568    if (pszEnvVar)
    86569    {
    87570#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
    88         int rc = RTPathTraverseList(pszEnvVar, ';', scmSvnFindSvnBinaryCallback, pszPath, (void *)cchPath);
     571        int rc = RTPathTraverseList(pszEnvVar, ';', scmSvnFindSvnBinaryCallback, g_szSvnPath, (void *)sizeof(g_szSvnPath));
    89572#else
    90         int rc = RTPathTraverseList(pszEnvVar, ':', scmSvnFindSvnBinaryCallback, pszPath, (void *)cchPath);
     573        int rc = RTPathTraverseList(pszEnvVar, ':', scmSvnFindSvnBinaryCallback, g_szSvnPath, (void *)sizeof(g_szSvnPath));
    91574#endif
    92         if (RT_SUCCESS(rc))
    93             return;
    94     }
    95     strcpy(pszPath, "svn");
     575        if (RT_FAILURE(rc))
     576            strcpy(g_szSvnPath, "svn");
     577    }
     578    else
     579        strcpy(g_szSvnPath, "svn");
     580
     581    /*
     582     * Check the version.
     583     */
     584    const char *apszArgs[] = { g_szSvnPath, "--version", "--quiet", NULL };
     585    char *pszVersion;
     586    int rc = scmSvnRunAndGetOutput(pState, apszArgs, false, &pszVersion);
     587    if (RT_SUCCESS(rc))
     588    {
     589        char *pszStripped = RTStrStrip(pszVersion);
     590        if (RTStrVersionCompare(pszVersion, "1.7") >= 0)
     591            g_enmSvnVersion = kScmSvnVersion_1_7;
     592        else if (RTStrVersionCompare(pszVersion, "1.6") >= 0)
     593            g_enmSvnVersion = kScmSvnVersion_1_6;
     594        else
     595            g_enmSvnVersion = kScmSvnVersion_Ancient;
     596        RTStrFree(pszVersion);
     597    }
     598    else
     599        g_enmSvnVersion = kScmSvnVersion_Ancient;
    96600}
    97601
     
    165669{
    166670#ifdef SCM_WITHOUT_LIBSVN
    167     /*
    168      * Hack: check if the .svn/text-base/<file>.svn-base file exists.
    169      */
    170     char szPath[RTPATH_MAX];
    171     int rc = scmSvnConstructName(pState, ".svn/text-base/", ".svn-base", szPath);
    172     if (RT_SUCCESS(rc))
    173         return RTFileExists(szPath);
     671    scmSvnFindSvnBinary(pState);
     672    if (g_enmSvnVersion < kScmSvnVersion_1_7)
     673    {
     674        /*
     675         * Hack: check if the .svn/text-base/<file>.svn-base file exists.
     676         */
     677        char szPath[RTPATH_MAX];
     678        int rc = scmSvnConstructName(pState, ".svn/text-base/", ".svn-base", szPath);
     679        if (RT_SUCCESS(rc))
     680            return RTFileExists(szPath);
     681    }
     682    else
     683    {
     684        const char *apszArgs[] = { g_szSvnPath, "propget", "svn:no-such-property", pState->pszFilename, NULL };
     685        char       *pszValue;
     686        int rc = scmSvnRunAndGetOutput(pState, apszArgs, true, &pszValue);
     687        if (RT_SUCCESS(rc))
     688        {
     689            RTStrFree(pszValue);
     690            return true;
     691        }
     692    }
     693
     694#else
     695    NOREF(pState);
     696#endif
     697    return false;
     698}
     699
     700/**
     701 * Checks if the specified directory is part of a SVN working copy.
     702 *
     703 * @returns true if it is, false if it isn't or we cannot tell.
     704 * @param   pszDir              The directory in question.
     705 */
     706bool ScmSvnIsDirInWorkingCopy(const char *pszDir)
     707{
     708#ifdef SCM_WITHOUT_LIBSVN
     709    scmSvnFindSvnBinary(NULL);
     710    if (g_enmSvnVersion < kScmSvnVersion_1_7)
     711    {
     712        /*
     713         * Hack: check if the .svn/ dir exists.
     714         */
     715        char szPath[RTPATH_MAX];
     716        int rc = RTPathJoin(szPath, sizeof(szPath), pszDir, ".svn");
     717        if (RT_SUCCESS(rc))
     718            return RTDirExists(szPath);
     719    }
     720    else
     721    {
     722        const char *apszArgs[] = { g_szSvnPath, "propget", "svn:no-such-property", pszDir, NULL };
     723        char       *pszValue;
     724        int rc = scmSvnRunAndGetOutput(NULL, apszArgs, true, &pszValue);
     725        if (RT_SUCCESS(rc))
     726        {
     727            RTStrFree(pszValue);
     728            return true;
     729        }
     730    }
    174731
    175732#else
     
    197754     * Look it up in the scheduled changes.
    198755     */
    199     uint32_t i = pState->cSvnPropChanges;
     756    size_t i = pState->cSvnPropChanges;
    200757    while (i-- > 0)
    201758        if (!strcmp(pState->paSvnPropChanges[i].pszName, pszName))
     
    210767
    211768#ifdef SCM_WITHOUT_LIBSVN
    212     /*
    213      * Hack: Read the .svn/props/<file>.svn-work file exists.
    214      */
    215     char szPath[RTPATH_MAX];
    216     int rc = scmSvnConstructName(pState, ".svn/props/", ".svn-work", szPath);
    217     if (RT_SUCCESS(rc) && !RTFileExists(szPath))
    218         rc = scmSvnConstructName(pState, ".svn/prop-base/", ".svn-base", szPath);
    219     if (RT_SUCCESS(rc))
    220     {
    221         SCMSTREAM Stream;
    222         rc = ScmStreamInitForReading(&Stream, szPath);
     769    int rc;
     770    scmSvnFindSvnBinary(pState);
     771    if (g_enmSvnVersion < kScmSvnVersion_1_7)
     772    {
     773        /*
     774         * Hack: Read the .svn/props/<file>.svn-work file exists.
     775         */
     776        char szPath[RTPATH_MAX];
     777        rc = scmSvnConstructName(pState, ".svn/props/", ".svn-work", szPath);
     778        if (RT_SUCCESS(rc) && !RTFileExists(szPath))
     779            rc = scmSvnConstructName(pState, ".svn/prop-base/", ".svn-base", szPath);
    223780        if (RT_SUCCESS(rc))
    224781        {
    225             /*
    226              * The current format is K len\n<name>\nV len\n<value>\n" ... END.
    227              */
    228             rc = VERR_NOT_FOUND;
    229             size_t const    cchName = strlen(pszName);
    230             SCMEOL          enmEol;
    231             size_t          cchLine;
    232             const char     *pchLine;
    233             while ((pchLine = ScmStreamGetLine(&Stream, &cchLine, &enmEol)) != NULL)
     782            SCMSTREAM Stream;
     783            rc = ScmStreamInitForReading(&Stream, szPath);
     784            if (RT_SUCCESS(rc))
    234785            {
    235786                /*
    236                  * Parse the 'K num' / 'END' line.
     787                 * The current format is K len\n<name>\nV len\n<value>\n" ... END.
    237788                 */
    238                 if (   cchLine == 3
    239                     && !memcmp(pchLine, "END", 3))
    240                     break;
    241                 size_t cchKey;
    242                 if (   cchLine < 3
    243                     || pchLine[0] != 'K'
    244                     || pchLine[1] != ' '
    245                     || !scmSvnReadNumber(&pchLine[2], cchLine - 2, &cchKey)
    246                     || cchKey == 0
    247                     || cchKey > 4096)
     789                rc = VERR_NOT_FOUND;
     790                size_t const    cchName = strlen(pszName);
     791                SCMEOL          enmEol;
     792                size_t          cchLine;
     793                const char     *pchLine;
     794                while ((pchLine = ScmStreamGetLine(&Stream, &cchLine, &enmEol)) != NULL)
    248795                {
    249                     RTMsgError("%s:%u: Unexpected data '%.*s'\n", szPath, ScmStreamTellLine(&Stream), cchLine, pchLine);
    250                     rc = VERR_PARSE_ERROR;
    251                     break;
    252                 }
    253 
    254                 /*
    255                  * Match the key and skip to the value line.  Don't bother with
    256                  * names containing EOL markers.
    257                  */
    258                 size_t const offKey = ScmStreamTell(&Stream);
    259                 bool fMatch = cchName == cchKey;
    260                 if (fMatch)
    261                 {
     796                    /*
     797                     * Parse the 'K num' / 'END' line.
     798                     */
     799                    if (   cchLine == 3
     800                        && !memcmp(pchLine, "END", 3))
     801                        break;
     802                    size_t cchKey;
     803                    if (   cchLine < 3
     804                        || pchLine[0] != 'K'
     805                        || pchLine[1] != ' '
     806                        || !scmSvnReadNumber(&pchLine[2], cchLine - 2, &cchKey)
     807                        || cchKey == 0
     808                        || cchKey > 4096)
     809                    {
     810                        RTMsgError("%s:%u: Unexpected data '%.*s'\n", szPath, ScmStreamTellLine(&Stream), cchLine, pchLine);
     811                        rc = VERR_PARSE_ERROR;
     812                        break;
     813                    }
     814
     815                    /*
     816                     * Match the key and skip to the value line.  Don't bother with
     817                     * names containing EOL markers.
     818                     */
     819                    size_t const offKey = ScmStreamTell(&Stream);
     820                    bool fMatch = cchName == cchKey;
     821                    if (fMatch)
     822                    {
     823                        pchLine = ScmStreamGetLine(&Stream, &cchLine, &enmEol);
     824                        if (!pchLine)
     825                            break;
     826                        fMatch = cchLine == cchName
     827                              && !memcmp(pchLine, pszName, cchName);
     828                    }
     829
     830                    if (RT_FAILURE(ScmStreamSeekAbsolute(&Stream, offKey + cchKey)))
     831                        break;
     832                    if (RT_FAILURE(ScmStreamSeekByLine(&Stream, ScmStreamTellLine(&Stream) + 1)))
     833                        break;
     834
     835                    /*
     836                     * Read and Parse the 'V num' line.
     837                     */
    262838                    pchLine = ScmStreamGetLine(&Stream, &cchLine, &enmEol);
    263839                    if (!pchLine)
    264840                        break;
    265                     fMatch = cchLine == cchName
    266                           && !memcmp(pchLine, pszName, cchName);
     841                    size_t cchValue;
     842                    if (   cchLine < 3
     843                        || pchLine[0] != 'V'
     844                        || pchLine[1] != ' '
     845                        || !scmSvnReadNumber(&pchLine[2], cchLine - 2, &cchValue)
     846                        || cchValue > _1M)
     847                    {
     848                        RTMsgError("%s:%u: Unexpected data '%.*s'\n", szPath, ScmStreamTellLine(&Stream), cchLine, pchLine);
     849                        rc = VERR_PARSE_ERROR;
     850                        break;
     851                    }
     852
     853                    /*
     854                     * If we have a match, allocate a return buffer and read the
     855                     * value into it.  Otherwise skip this value and continue
     856                     * searching.
     857                     */
     858                    if (fMatch)
     859                    {
     860                        if (!ppszValue)
     861                            rc = VINF_SUCCESS;
     862                        else
     863                        {
     864                            char *pszValue;
     865                            rc = RTStrAllocEx(&pszValue, cchValue + 1);
     866                            if (RT_SUCCESS(rc))
     867                            {
     868                                rc = ScmStreamRead(&Stream, pszValue, cchValue);
     869                                if (RT_SUCCESS(rc))
     870                                    *ppszValue = pszValue;
     871                                else
     872                                    RTStrFree(pszValue);
     873                            }
     874                        }
     875                        break;
     876                    }
     877
     878                    if (RT_FAILURE(ScmStreamSeekRelative(&Stream, cchValue)))
     879                        break;
     880                    if (RT_FAILURE(ScmStreamSeekByLine(&Stream, ScmStreamTellLine(&Stream) + 1)))
     881                        break;
    267882                }
    268883
    269                 if (RT_FAILURE(ScmStreamSeekAbsolute(&Stream, offKey + cchKey)))
    270                     break;
    271                 if (RT_FAILURE(ScmStreamSeekByLine(&Stream, ScmStreamTellLine(&Stream) + 1)))
    272                     break;
    273 
    274                 /*
    275                  * Read and Parse the 'V num' line.
    276                  */
    277                 pchLine = ScmStreamGetLine(&Stream, &cchLine, &enmEol);
    278                 if (!pchLine)
    279                     break;
    280                 size_t cchValue;
    281                 if (   cchLine < 3
    282                     || pchLine[0] != 'V'
    283                     || pchLine[1] != ' '
    284                     || !scmSvnReadNumber(&pchLine[2], cchLine - 2, &cchValue)
    285                     || cchValue > _1M)
     884                if (RT_FAILURE(ScmStreamGetStatus(&Stream)))
    286885                {
    287                     RTMsgError("%s:%u: Unexpected data '%.*s'\n", szPath, ScmStreamTellLine(&Stream), cchLine, pchLine);
    288                     rc = VERR_PARSE_ERROR;
    289                     break;
     886                    rc = ScmStreamGetStatus(&Stream);
     887                    RTMsgError("%s: stream error %Rrc\n", szPath, rc);
    290888                }
    291 
    292                 /*
    293                  * If we have a match, allocate a return buffer and read the
    294                  * value into it.  Otherwise skip this value and continue
    295                  * searching.
    296                  */
    297                 if (fMatch)
     889                ScmStreamDelete(&Stream);
     890            }
     891        }
     892
     893        if (rc == VERR_FILE_NOT_FOUND)
     894            rc = VERR_NOT_FOUND;
     895    }
     896    else
     897    {
     898        const char *apszArgs[] = { g_szSvnPath, "propget", "--strict", pszName, pState->pszFilename, NULL };
     899        char       *pszValue;
     900        rc = scmSvnRunAndGetOutput(pState, apszArgs, false, &pszValue);
     901        if (RT_SUCCESS(rc))
     902        {
     903            if (pszValue && *pszValue)
     904            {
     905                if (ppszValue)
    298906                {
    299                     if (!ppszValue)
    300                         rc = VINF_SUCCESS;
    301                     else
    302                     {
    303                         char *pszValue;
    304                         rc = RTStrAllocEx(&pszValue, cchValue + 1);
    305                         if (RT_SUCCESS(rc))
    306                         {
    307                             rc = ScmStreamRead(&Stream, pszValue, cchValue);
    308                             if (RT_SUCCESS(rc))
    309                                 *ppszValue = pszValue;
    310                             else
    311                                 RTStrFree(pszValue);
    312                         }
    313                     }
    314                     break;
     907                    *ppszValue = pszValue;
     908                    pszValue = NULL;
    315909                }
    316 
    317                 if (RT_FAILURE(ScmStreamSeekRelative(&Stream, cchValue)))
    318                     break;
    319                 if (RT_FAILURE(ScmStreamSeekByLine(&Stream, ScmStreamTellLine(&Stream) + 1)))
    320                     break;
    321910            }
    322 
    323             if (RT_FAILURE(ScmStreamGetStatus(&Stream)))
    324             {
    325                 rc = ScmStreamGetStatus(&Stream);
    326                 RTMsgError("%s: stream error %Rrc\n", szPath, rc);
    327             }
    328             ScmStreamDelete(&Stream);
     911            else
     912                rc = VERR_NOT_FOUND;
     913            RTStrFree(pszValue);
    329914        }
    330915    }
    331 
    332     if (rc == VERR_FILE_NOT_FOUND)
    333         rc = VERR_NOT_FOUND;
    334916    return rc;
    335917
     
    4321014        const char *pszValue = pState->paSvnPropChanges[i].pszValue;
    4331015        if (pszValue)
    434             ScmVerbose(pState, 0, "svn ps '%s' '%s' %s\n", pszName, pszValue, pState->pszFilename);
     1016            ScmVerbose(pState, 0, "svn propset '%s' '%s' %s\n", pszName, pszValue, pState->pszFilename);
    4351017        else
    436             ScmVerbose(pState, 0, "svn pd '%s'  %s\n", pszName, pszValue, pState->pszFilename);
     1018            ScmVerbose(pState, 0, "svn propdel '%s' %s\n", pszName, pState->pszFilename);
    4371019    }
    4381020
     
    4501032{
    4511033#ifdef SCM_WITHOUT_LIBSVN
    452     /*
    453      * This sucks. We gotta find svn(.exe).
    454      */
    455     static char s_szSvnPath[RTPATH_MAX];
    456     if (s_szSvnPath[0] == '\0')
    457         scmSvnFindSvnBinary(s_szSvnPath, sizeof(s_szSvnPath));
     1034    scmSvnFindSvnBinary(pState);
    4581035
    4591036    /*
     
    4631040    {
    4641041        const char *apszArgv[6];
    465         apszArgv[0] = s_szSvnPath;
    466         apszArgv[1] = pState->paSvnPropChanges[i].pszValue ? "ps" : "pd";
     1042        apszArgv[0] = g_szSvnPath;
     1043        apszArgv[1] = pState->paSvnPropChanges[i].pszValue ? "propset" : "propdel";
    4671044        apszArgv[2] = pState->paSvnPropChanges[i].pszName;
    4681045        int iArg = 3;
     
    4711048        apszArgv[iArg++] = pState->pszFilename;
    4721049        apszArgv[iArg++] = NULL;
    473         ScmVerbose(pState, 2, "executing: %s %s %s %s %s\n",
    474                    apszArgv[0], apszArgv[1], apszArgv[2], apszArgv[3], apszArgv[4]);
    475 
    476         RTPROCESS pid;
    477         int rc = RTProcCreate(s_szSvnPath, apszArgv, RTENV_DEFAULT, 0 /*fFlags*/, &pid);
    478         if (RT_SUCCESS(rc))
    479         {
    480             RTPROCSTATUS Status;
    481             rc = RTProcWait(pid, RTPROCWAIT_FLAGS_BLOCK, &Status);
    482             if (    RT_SUCCESS(rc)
    483                 &&  (   Status.enmReason != RTPROCEXITREASON_NORMAL
    484                      || Status.iStatus != 0) )
    485             {
    486                 RTMsgError("%s: %s %s %s %s %s -> %s %u\n",
    487                            pState->pszFilename, apszArgv[0], apszArgv[1], apszArgv[2], apszArgv[3], apszArgv[4],
    488                            Status.enmReason == RTPROCEXITREASON_NORMAL   ? "exit code"
    489                            : Status.enmReason == RTPROCEXITREASON_SIGNAL ? "signal"
    490                            : Status.enmReason == RTPROCEXITREASON_ABEND  ? "abnormal end"
    491                            : "abducted by alien",
    492                            Status.iStatus);
    493                 return VERR_GENERAL_FAILURE;
    494             }
    495         }
     1050
     1051        int rc = scmSvnRun(pState, apszArgv, false);
    4961052        if (RT_FAILURE(rc))
    497         {
    498             RTMsgError("%s: error executing %s %s %s %s %s: %Rrc\n",
    499                        pState->pszFilename, apszArgv[0], apszArgv[1], apszArgv[2], apszArgv[3], apszArgv[4], rc);
    5001053            return rc;
    501         }
    5021054    }
    5031055
Note: See TracChangeset for help on using the changeset viewer.

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