Changeset 41966 in vbox for trunk/src/bldprogs
- Timestamp:
- Jun 29, 2012 2:53:56 AM (13 years ago)
- svn:sync-xref-src-repo-rev:
- 78836
- Location:
- trunk/src/bldprogs
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/bldprogs/scm.cpp
r40534 r41966 1249 1249 if (pSettingsStack->Base.fOnlySvnDirs) 1250 1250 { 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; 1263 1253 } 1264 1254 -
trunk/src/bldprogs/scm.h
r40534 r41966 51 51 52 52 53 bool ScmSvnIsDirInWorkingCopy(const char *pszDir); 53 54 bool ScmSvnIsInWorkingCopy(PSCMRWSTATE pState); 54 55 int ScmSvnQueryProperty(PSCMRWSTATE pState, const char *pszName, char **ppszValue); -
trunk/src/bldprogs/scmsubversion.cpp
r40534 r41966 40 40 41 41 42 /******************************************************************************* 43 * Global Variables * 44 *******************************************************************************/ 45 static char g_szSvnPath[RTPATH_MAX]; 46 static enum 47 { 48 kScmSvnVersion_Ancient = 1, 49 kScmSvnVersion_1_6, 50 kScmSvnVersion_1_7, 51 kScmSvnVersion_End 52 } g_enmSvnVersion = kScmSvnVersion_Ancient; 53 42 54 43 55 #ifdef SCM_WITHOUT_LIBSVN … … 66 78 } 67 79 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 */ 95 static 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 */ 197 int 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 */ 378 int 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 */ 459 static 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 */ 515 static 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 */ 553 static void scmSvnFindSvnBinary(PSCMRWSTATE pState) 554 { 555 /* Already been called? */ 556 if (g_szSvnPath[0] != '\0') 557 return; 558 559 /* 560 * Locate it. 561 */ 78 562 /** @todo code page fun... */ 79 Assert(cchPath >= sizeof("svn"));80 563 #ifdef RT_OS_WINDOWS 81 564 const char *pszEnvVar = RTEnvGet("Path"); … … 83 566 const char *pszEnvVar = RTEnvGet("PATH"); 84 567 #endif 85 if (psz Path)568 if (pszEnvVar) 86 569 { 87 570 #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)); 89 572 #else 90 int rc = RTPathTraverseList(pszEnvVar, ':', scmSvnFindSvnBinaryCallback, pszPath, (void *)cchPath);573 int rc = RTPathTraverseList(pszEnvVar, ':', scmSvnFindSvnBinaryCallback, g_szSvnPath, (void *)sizeof(g_szSvnPath)); 91 574 #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; 96 600 } 97 601 … … 165 669 { 166 670 #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 */ 706 bool 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 } 174 731 175 732 #else … … 197 754 * Look it up in the scheduled changes. 198 755 */ 199 uint32_t i = pState->cSvnPropChanges;756 size_t i = pState->cSvnPropChanges; 200 757 while (i-- > 0) 201 758 if (!strcmp(pState->paSvnPropChanges[i].pszName, pszName)) … … 210 767 211 768 #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); 223 780 if (RT_SUCCESS(rc)) 224 781 { 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)) 234 785 { 235 786 /* 236 * Parse the 'K num' / 'END' line.787 * The current format is K len\n<name>\nV len\n<value>\n" ... END. 237 788 */ 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) 248 795 { 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 */ 262 838 pchLine = ScmStreamGetLine(&Stream, &cchLine, &enmEol); 263 839 if (!pchLine) 264 840 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; 267 882 } 268 883 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))) 286 885 { 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); 290 888 } 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) 298 906 { 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; 315 909 } 316 317 if (RT_FAILURE(ScmStreamSeekRelative(&Stream, cchValue)))318 break;319 if (RT_FAILURE(ScmStreamSeekByLine(&Stream, ScmStreamTellLine(&Stream) + 1)))320 break;321 910 } 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); 329 914 } 330 915 } 331 332 if (rc == VERR_FILE_NOT_FOUND)333 rc = VERR_NOT_FOUND;334 916 return rc; 335 917 … … 432 1014 const char *pszValue = pState->paSvnPropChanges[i].pszValue; 433 1015 if (pszValue) 434 ScmVerbose(pState, 0, "svn p s '%s' '%s'%s\n", pszName, pszValue, pState->pszFilename);1016 ScmVerbose(pState, 0, "svn propset '%s' '%s' %s\n", pszName, pszValue, pState->pszFilename); 435 1017 else 436 ScmVerbose(pState, 0, "svn p d '%s' %s\n", pszName, pszValue, pState->pszFilename);1018 ScmVerbose(pState, 0, "svn propdel '%s' %s\n", pszName, pState->pszFilename); 437 1019 } 438 1020 … … 450 1032 { 451 1033 #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); 458 1035 459 1036 /* … … 463 1040 { 464 1041 const char *apszArgv[6]; 465 apszArgv[0] = s_szSvnPath;466 apszArgv[1] = pState->paSvnPropChanges[i].pszValue ? "p s" : "pd";1042 apszArgv[0] = g_szSvnPath; 1043 apszArgv[1] = pState->paSvnPropChanges[i].pszValue ? "propset" : "propdel"; 467 1044 apszArgv[2] = pState->paSvnPropChanges[i].pszName; 468 1045 int iArg = 3; … … 471 1048 apszArgv[iArg++] = pState->pszFilename; 472 1049 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); 496 1052 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);500 1053 return rc; 501 }502 1054 } 503 1055
Note:
See TracChangeset
for help on using the changeset viewer.