/** * Seeks a number of bytes relative to the current stream position. * * @returns IPRT status code. * @retval VERR_SEEK if the new stream position is the middle of an EOL marker. * This is a temporary restriction. * * @param pStream The stream. Must be in read mode. * @param offRelative The offset to seek to. A negative offset * rewinds and positive one fast forwards the * stream. Will quietly stop at the beginning and * end of the stream. */ int ScmStreamSeekRelative(PSCMSTREAM pStream, ssize_t offRelative) { size_t offAbsolute; if (offRelative >= 0) offAbsolute = pStream->off + offRelative; else if ((size_t)-offRelative <= pStream->off) offAbsolute = pStream->off + offRelative; else offAbsolute = 0; return ScmStreamSeekAbsolute(pStream, offAbsolute); }
/** * Reads @a cbToRead bytes into @a pvBuf. * * Will fail if end of stream is encountered before the entire read has been * completed. * * @returns IPRT status code. * @retval VERR_EOF if there isn't @a cbToRead bytes left to read. Stream * position will be unchanged. * * @param pStream The stream. Must be in read mode. * @param pvBuf The buffer to read into. * @param cbToRead The number of bytes to read. */ int ScmStreamRead(PSCMSTREAM pStream, void *pvBuf, size_t cbToRead) { AssertReturn(!pStream->fWriteOrRead, VERR_PERMISSION_DENIED); if (RT_FAILURE(pStream->rc)) return pStream->rc; /* If there isn't enough stream left, fail already. */ if (RT_UNLIKELY(pStream->cb - pStream->off < cbToRead)) return VERR_EOF; /* Copy the data and simply seek to the new stream position. */ memcpy(pvBuf, &pStream->pch[pStream->off], cbToRead); return ScmStreamSeekAbsolute(pStream, pStream->off + cbToRead); }
/** * Queries the value of an SVN property. * * This will automatically adjust for scheduled changes. * * @returns IPRT status code. * @retval VERR_INVALID_STATE if not a SVN WC file. * @retval VERR_NOT_FOUND if the property wasn't found. * @param pState The rewrite state to work on. * @param pszName The property name. * @param ppszValue Where to return the property value. Free this * using RTStrFree. Optional. */ int ScmSvnQueryProperty(PSCMRWSTATE pState, const char *pszName, char **ppszValue) { /* * Look it up in the scheduled changes. */ size_t i = pState->cSvnPropChanges; while (i-- > 0) if (!strcmp(pState->paSvnPropChanges[i].pszName, pszName)) { const char *pszValue = pState->paSvnPropChanges[i].pszValue; if (!pszValue) return VERR_NOT_FOUND; if (ppszValue) return RTStrDupEx(ppszValue, pszValue); return VINF_SUCCESS; } #ifdef SCM_WITHOUT_LIBSVN int rc; scmSvnFindSvnBinary(pState); if (g_enmSvnVersion < kScmSvnVersion_1_7) { /* * Hack: Read the .svn/props/<file>.svn-work file exists. */ char szPath[RTPATH_MAX]; rc = scmSvnConstructName(pState, ".svn/props/", ".svn-work", szPath); if (RT_SUCCESS(rc) && !RTFileExists(szPath)) rc = scmSvnConstructName(pState, ".svn/prop-base/", ".svn-base", szPath); if (RT_SUCCESS(rc)) { SCMSTREAM Stream; rc = ScmStreamInitForReading(&Stream, szPath); if (RT_SUCCESS(rc)) { /* * The current format is K len\n<name>\nV len\n<value>\n" ... END. */ rc = VERR_NOT_FOUND; size_t const cchName = strlen(pszName); SCMEOL enmEol; size_t cchLine; const char *pchLine; while ((pchLine = ScmStreamGetLine(&Stream, &cchLine, &enmEol)) != NULL) { /* * Parse the 'K num' / 'END' line. */ if ( cchLine == 3 && !memcmp(pchLine, "END", 3)) break; size_t cchKey; if ( cchLine < 3 || pchLine[0] != 'K' || pchLine[1] != ' ' || !scmSvnReadNumber(&pchLine[2], cchLine - 2, &cchKey) || cchKey == 0 || cchKey > 4096) { RTMsgError("%s:%u: Unexpected data '%.*s'\n", szPath, ScmStreamTellLine(&Stream), cchLine, pchLine); rc = VERR_PARSE_ERROR; break; } /* * Match the key and skip to the value line. Don't bother with * names containing EOL markers. */ size_t const offKey = ScmStreamTell(&Stream); bool fMatch = cchName == cchKey; if (fMatch) { pchLine = ScmStreamGetLine(&Stream, &cchLine, &enmEol); if (!pchLine) break; fMatch = cchLine == cchName && !memcmp(pchLine, pszName, cchName); } if (RT_FAILURE(ScmStreamSeekAbsolute(&Stream, offKey + cchKey))) break; if (RT_FAILURE(ScmStreamSeekByLine(&Stream, ScmStreamTellLine(&Stream) + 1))) break; /* * Read and Parse the 'V num' line. */ pchLine = ScmStreamGetLine(&Stream, &cchLine, &enmEol); if (!pchLine) break; size_t cchValue; if ( cchLine < 3 || pchLine[0] != 'V' || pchLine[1] != ' ' || !scmSvnReadNumber(&pchLine[2], cchLine - 2, &cchValue) || cchValue > _1M) { RTMsgError("%s:%u: Unexpected data '%.*s'\n", szPath, ScmStreamTellLine(&Stream), cchLine, pchLine); rc = VERR_PARSE_ERROR; break; } /* * If we have a match, allocate a return buffer and read the * value into it. Otherwise skip this value and continue * searching. */ if (fMatch) { if (!ppszValue) rc = VINF_SUCCESS; else { char *pszValue; rc = RTStrAllocEx(&pszValue, cchValue + 1); if (RT_SUCCESS(rc)) { rc = ScmStreamRead(&Stream, pszValue, cchValue); if (RT_SUCCESS(rc)) *ppszValue = pszValue; else RTStrFree(pszValue); } } break; } if (RT_FAILURE(ScmStreamSeekRelative(&Stream, cchValue))) break; if (RT_FAILURE(ScmStreamSeekByLine(&Stream, ScmStreamTellLine(&Stream) + 1))) break; } if (RT_FAILURE(ScmStreamGetStatus(&Stream))) { rc = ScmStreamGetStatus(&Stream); RTMsgError("%s: stream error %Rrc\n", szPath, rc); } ScmStreamDelete(&Stream); } } if (rc == VERR_FILE_NOT_FOUND) rc = VERR_NOT_FOUND; } else { const char *apszArgs[] = { g_szSvnPath, "propget", "--strict", pszName, pState->pszFilename, NULL }; char *pszValue; rc = scmSvnRunAndGetOutput(pState, apszArgs, false, &pszValue); if (RT_SUCCESS(rc)) { if (pszValue && *pszValue) { if (ppszValue) { *ppszValue = pszValue; pszValue = NULL; } } else rc = VERR_NOT_FOUND; RTStrFree(pszValue); } } return rc; #else NOREF(pState); #endif return VERR_NOT_FOUND; }