/** * Compares two sets of lines from the two files. * * @returns true if they matches, false if they don't. * @param pState The diff state. * @param iLeft Where to start in the left stream. * @param iRight Where to start in the right stream. * @param cLines How many lines to compare. */ static bool scmDiffCompareLines(PSCMDIFFSTATE pState, size_t iLeft, size_t iRight, size_t cLines) { for (size_t iLine = 0; iLine < cLines; iLine++) { SCMEOL enmEolLeft; size_t cchLeft; const char *pchLeft = ScmStreamGetLineByNo(pState->pLeft, iLeft + iLine, &cchLeft, &enmEolLeft); SCMEOL enmEolRight; size_t cchRight; const char *pchRight = ScmStreamGetLineByNo(pState->pRight, iRight + iLine, &cchRight, &enmEolRight); if (!scmDiffCompare(pState, pchLeft, cchLeft, enmEolLeft, pchRight, cchRight, enmEolRight)) return false; } return true; }
/** * Prints a range of lines with a prefix. * * @param pState The diff state. * @param chPrefix The prefix. * @param pStream The stream to get the lines from. * @param iLine The first line. * @param cLines The number of lines. */ static void scmDiffPrintLines(PSCMDIFFSTATE pState, char chPrefix, PSCMSTREAM pStream, size_t iLine, size_t cLines) { while (cLines-- > 0) { SCMEOL enmEol; size_t cchLine; const char *pchLine = ScmStreamGetLineByNo(pStream, iLine, &cchLine, &enmEol); RTStrmPutCh(pState->pDiff, chPrefix); if (pchLine && cchLine) { if (!pState->fSpecialChars) RTStrmWrite(pState->pDiff, pchLine, cchLine); else { size_t offVir = 0; const char *pchStart = pchLine; const char *pchTab = (const char *)memchr(pchLine, '\t', cchLine); while (pchTab) { RTStrmWrite(pState->pDiff, pchStart, pchTab - pchStart); offVir += pchTab - pchStart; size_t cchTab = pState->cchTab - offVir % pState->cchTab; switch (cchTab) { case 1: RTStrmPutStr(pState->pDiff, "."); break; case 2: RTStrmPutStr(pState->pDiff, ".."); break; case 3: RTStrmPutStr(pState->pDiff, "[T]"); break; case 4: RTStrmPutStr(pState->pDiff, "[TA]"); break; case 5: RTStrmPutStr(pState->pDiff, "[TAB]"); break; default: RTStrmPrintf(pState->pDiff, "[TAB%.*s]", cchTab - 5, g_szTabSpaces); break; } offVir += cchTab; /* next */ pchStart = pchTab + 1; pchTab = (const char *)memchr(pchStart, '\t', cchLine - (pchStart - pchLine)); } size_t cchLeft = cchLine - (pchStart - pchLine); if (cchLeft) RTStrmWrite(pState->pDiff, pchStart, cchLeft); } } if (!pState->fSpecialChars) RTStrmPutCh(pState->pDiff, '\n'); else if (enmEol == SCMEOL_LF) RTStrmPutStr(pState->pDiff, "[LF]\n"); else if (enmEol == SCMEOL_CRLF) RTStrmPutStr(pState->pDiff, "[CRLF]\n"); else RTStrmPutStr(pState->pDiff, "[NONE]\n"); iLine++; } }
/** * Checks if the given line is empty or full of white space. * * @returns true if white space only, false if not (or if non-existant). * @param pStream The stream. Must be in read mode. * @param iLine The line in question. */ bool ScmStreamIsWhiteLine(PSCMSTREAM pStream, size_t iLine) { SCMEOL enmEol; size_t cchLine; const char *pchLine = ScmStreamGetLineByNo(pStream, iLine, &cchLine, &enmEol); if (!pchLine) return false; while (cchLine && RT_C_IS_SPACE(*pchLine)) pchLine++, cchLine--; return cchLine == 0; }
/** * Get a line from the stream. * * A line is always delimited by a LF character or the end of the stream. The * delimiter is not included in returned line length, but instead returned via * the @a penmEol indicator. * * @returns Pointer to the first character in the line, not NULL terminated. * NULL if the end of the stream has been reached or some problem * occurred. * * @param pStream The stream. Must be in read mode. * @param pcchLine The length. * @param penmEol Where to return the end of line type indicator. */ const char *ScmStreamGetLine(PSCMSTREAM pStream, size_t *pcchLine, PSCMEOL penmEol) { if (!pStream->fFullyLineated) return scmStreamGetLineInternal(pStream, pcchLine, penmEol); size_t offCur = pStream->off; size_t iCurLine = pStream->iLine; const char *pszLine = ScmStreamGetLineByNo(pStream, iCurLine, pcchLine, penmEol); if ( pszLine && offCur > pStream->paLines[iCurLine].off) { offCur -= pStream->paLines[iCurLine].off; Assert(offCur <= pStream->paLines[iCurLine].cch + pStream->paLines[iCurLine].enmEol); if (offCur < pStream->paLines[iCurLine].cch) *pcchLine -= offCur; else *pcchLine = 0; pszLine += offCur; } return pszLine; }
/** * Resynchronize the two streams and reports the difference. * * Upon return, the streams will be positioned after the block of @a cMatches * lines where it resynchronized them. * * @returns pState->cDiffs (just so we can use it in a return statement). * @param pState The state. * @param cMatches The number of lines that needs to match for the * stream to be considered synchronized again. */ static size_t scmDiffSynchronize(PSCMDIFFSTATE pState, size_t cMatches) { size_t const iStartLeft = ScmStreamTellLine(pState->pLeft) - 1; size_t const iStartRight = ScmStreamTellLine(pState->pRight) - 1; Assert(cMatches > 0); /* * Compare each new line from each of the streams will all the preceding * ones, including iStartLeft/Right. */ for (size_t iRange = 1; ; iRange++) { /* * Get the next line in the left stream and compare it against all the * preceding lines on the right side. */ SCMEOL enmEol; size_t cchLine; const char *pchLine = ScmStreamGetLineByNo(pState->pLeft, iStartLeft + iRange, &cchLine, &enmEol); if (!pchLine) return scmDiffReport(pState, 0, iStartLeft, ~(size_t)0, iStartRight, ~(size_t)0); for (size_t iRight = cMatches - 1; iRight < iRange; iRight++) { SCMEOL enmEolRight; size_t cchRight; const char *pchRight = ScmStreamGetLineByNo(pState->pRight, iStartRight + iRight, &cchRight, &enmEolRight); if ( scmDiffCompare(pState, pchLine, cchLine, enmEol, pchRight, cchRight, enmEolRight) && scmDiffCompareLines(pState, iStartLeft + iRange + 1 - cMatches, iStartRight + iRight + 1 - cMatches, cMatches - 1) ) return scmDiffReport(pState, cMatches, iStartLeft, iRange + 1 - cMatches, iStartRight, iRight + 1 - cMatches); } /* * Get the next line in the right stream and compare it against all the * lines on the right side. */ pchLine = ScmStreamGetLineByNo(pState->pRight, iStartRight + iRange, &cchLine, &enmEol); if (!pchLine) return scmDiffReport(pState, 0, iStartLeft, ~(size_t)0, iStartRight, ~(size_t)0); for (size_t iLeft = cMatches - 1; iLeft <= iRange; iLeft++) { SCMEOL enmEolLeft; size_t cchLeft; const char *pchLeft = ScmStreamGetLineByNo(pState->pLeft, iStartLeft + iLeft, &cchLeft, &enmEolLeft); if ( scmDiffCompare(pState, pchLeft, cchLeft, enmEolLeft, pchLine, cchLine, enmEol) && scmDiffCompareLines(pState, iStartLeft + iLeft + 1 - cMatches, iStartRight + iRange + 1 - cMatches, cMatches - 1) ) return scmDiffReport(pState, cMatches, iStartLeft, iLeft + 1 - cMatches, iStartRight, iRange + 1 - cMatches); } } }