public virtual int parseGitHeaders(int ptr, int end) { while (ptr < end) { int eol = RawParseUtils.nextLF(Buffer, ptr); if (isHunkHdr(Buffer, ptr, eol) >= 1) { // First hunk header; break out and parse them later. break; } if (RawParseUtils.match(Buffer, ptr, OLD_NAME) >= 0) { ParseOldName(ptr, eol); } else if (RawParseUtils.match(Buffer, ptr, NEW_NAME) >= 0) { ParseNewName(ptr, eol); } else if (RawParseUtils.match(Buffer, ptr, OldModeString) >= 0) { _oldMode = ParseFileMode(ptr + OldModeString.Length, eol); } else if (RawParseUtils.match(Buffer, ptr, NewModeString) >= 0) { _newMode = ParseFileMode(ptr + NewModeString.Length, eol); } else if (RawParseUtils.match(Buffer, ptr, DeletedFileMode) >= 0) { _oldMode = ParseFileMode(ptr + DeletedFileMode.Length, eol); _newMode = FileMode.Missing; ChangeType = ChangeTypeEnum.DELETE; } else if (RawParseUtils.match(Buffer, ptr, NewFileMode) >= 0) { ParseNewFileMode(ptr, eol); } else if (RawParseUtils.match(Buffer, ptr, CopyFrom) >= 0) { oldName = ParseName(oldName, ptr + CopyFrom.Length, eol); ChangeType = ChangeTypeEnum.COPY; } else if (RawParseUtils.match(Buffer, ptr, CopyTo) >= 0) { newName = ParseName(newName, ptr + CopyTo.Length, eol); ChangeType = ChangeTypeEnum.COPY; } else if (RawParseUtils.match(Buffer, ptr, RenameOld) >= 0) { oldName = ParseName(oldName, ptr + RenameOld.Length, eol); ChangeType = ChangeTypeEnum.RENAME; } else if (RawParseUtils.match(Buffer, ptr, RenameNew) >= 0) { newName = ParseName(newName, ptr + RenameNew.Length, eol); ChangeType = ChangeTypeEnum.RENAME; } else if (RawParseUtils.match(Buffer, ptr, RenameFrom) >= 0) { oldName = ParseName(oldName, ptr + RenameFrom.Length, eol); ChangeType = ChangeTypeEnum.RENAME; } else if (RawParseUtils.match(Buffer, ptr, RenameTo) >= 0) { newName = ParseName(newName, ptr + RenameTo.Length, eol); ChangeType = ChangeTypeEnum.RENAME; } else if (RawParseUtils.match(Buffer, ptr, SimilarityIndex) >= 0) { _score = RawParseUtils.parseBase10(Buffer, ptr + SimilarityIndex.Length, null); } else if (RawParseUtils.match(Buffer, ptr, DissimilarityIndex) >= 0) { _score = RawParseUtils.parseBase10(Buffer, ptr + DissimilarityIndex.Length, null); } else if (RawParseUtils.match(Buffer, ptr, Index) >= 0) { ParseIndexLine(ptr + Index.Length, eol); } else { // Probably an empty patch (stat dirty). break; } ptr = eol; } return(ptr); }
private int ParseHunks(FileHeader fh, int c, int end) { byte[] buf = fh.Buffer; while (c < end) { // If we see a file header at this point, we have all of the // hunks for our current file. We should stop and report back // with this position so it can be parsed again later. // if (RawParseUtils.match(buf, c, DiffGit) >= 0) { break; } if (RawParseUtils.match(buf, c, DiffCc) >= 0) { break; } if (RawParseUtils.match(buf, c, DiffCombined) >= 0) { break; } if (RawParseUtils.match(buf, c, FileHeader.OLD_NAME) >= 0) { break; } if (RawParseUtils.match(buf, c, FileHeader.NEW_NAME) >= 0) { break; } if (FileHeader.isHunkHdr(buf, c, end) == fh.ParentCount) { HunkHeader h = fh.newHunkHeader(c); h.parseHeader(); c = h.parseBody(this, end); h.EndOffset = c; fh.addHunk(h); if (c < end) { switch (buf[c]) { case (byte)'@': case (byte)'d': case (byte)'\n': break; default: if (RawParseUtils.match(buf, c, SigFooter) < 0) { warn(buf, c, "Unexpected hunk trailer"); } break; } } continue; } int eol = RawParseUtils.nextLF(buf, c); if (fh.Hunks.isEmpty() && RawParseUtils.match(buf, c, GitBinary) >= 0) { fh.PatchType = FileHeader.PatchTypeEnum.GIT_BINARY; return(ParseGitBinary(fh, eol, end)); } if (fh.Hunks.isEmpty() && BinTrailer.Length < eol - c && RawParseUtils.match(buf, eol - BinTrailer.Length, BinTrailer) >= 0 && MatchAny(buf, c, BinHeaders)) { // The patch is a binary file diff, with no deltas. // fh.PatchType = FileHeader.PatchTypeEnum.BINARY; return(eol); } // Skip this line and move to the next. Its probably garbage // After the last hunk of a file. // c = eol; } if (fh.Hunks.isEmpty() && fh.getPatchType() == FileHeader.PatchTypeEnum.UNIFIED && !fh.hasMetaDataChanges()) { // Hmm, an empty patch? If there is no metadata here we // really have a binary patch that we didn't notice above. // fh.PatchType = FileHeader.PatchTypeEnum.BINARY; } return(c); }
/// <summary> /// Parse a "diff --git" or "diff --cc" line. /// </summary> /// <param name="ptr"> /// first character After the "diff --git " or "diff --cc " part. /// </param> /// <param name="end"> /// one past the last position to parse. /// </param> /// <returns> /// first character After the LF at the end of the line; -1 on error. /// </returns> public int parseGitFileName(int ptr, int end) { int eol = RawParseUtils.nextLF(Buffer, ptr); int bol = ptr; if (eol >= end) { return(-1); } // buffer[ptr..eol] looks like "a/foo b/foo\n". After the first // A regex to match this is "^[^/]+/(.*?) [^/+]+/\1\n$". There // is only one way to split the line such that text to the left // of the space matches the text to the right, excluding the part // before the first slash. // int aStart = RawParseUtils.nextLF(Buffer, ptr, (byte)'/'); if (aStart >= eol) { return(eol); } while (ptr < eol) { int sp = RawParseUtils.nextLF(Buffer, ptr, (byte)' '); if (sp >= eol) { // We can't split the header, it isn't valid. // This may be OK if this is a rename patch. // return(eol); } int bStart = RawParseUtils.nextLF(Buffer, sp, (byte)'/'); if (bStart >= eol) { return(eol); } // If buffer[aStart..sp - 1] = buffer[bStart..eol - 1] // we have a valid split. // if (Eq(aStart, sp - 1, bStart, eol - 1)) { if (Buffer[bol] == '"') { // We're a double quoted name. The region better end // in a double quote too, and we need to decode the // characters before reading the name. // if (Buffer[sp - 2] != '"') { return(eol); } oldName = QuotedString.GitPathStyle.GIT_PATH.dequote(Buffer, bol, sp - 1); oldName = P1(oldName); } else { oldName = RawParseUtils.decode(Constants.CHARSET, Buffer, aStart, sp - 1); } newName = oldName; return(eol); } // This split wasn't correct. Move past the space and try // another split as the space must be part of the file name. // ptr = sp; } return(eol); }
public void parseCanonical(RevWalk walk, byte[] raw) { MutableObjectId idBuffer = walk.IdBuffer; idBuffer.FromString(raw, 5); _tree = walk.lookupTree(idBuffer); int ptr = 46; if (Parents == null) { var pList = new RevCommit[1]; int nParents = 0; while (true) { if (raw[ptr] != (byte)'p') { break; } idBuffer.FromString(raw, ptr + 7); RevCommit p = walk.lookupCommit(idBuffer); if (nParents == 0) { pList[nParents++] = p; } else if (nParents == 1) { pList = new[] { pList[0], p }; nParents = 2; } else { if (pList.Length <= nParents) { RevCommit[] old = pList; pList = new RevCommit[pList.Length + 32]; Array.Copy(old, 0, pList, 0, nParents); } pList[nParents++] = p; } ptr += 48; } if (nParents != pList.Length) { RevCommit[] old = pList; pList = new RevCommit[nParents]; Array.Copy(old, 0, pList, 0, nParents); } Parents = pList; } // extract time from "committer " ptr = RawParseUtils.committer(raw, ptr); if (ptr > 0) { ptr = RawParseUtils.nextLF(raw, ptr, (byte)'>'); // In 2038 commitTime will overflow unless it is changed to long. CommitTime = RawParseUtils.parseBase10(raw, ptr, null); } if (walk.isRetainBody()) { _buffer = raw; } Flags |= PARSED; }
private int ParseFile(byte[] buf, int c, int end) { while (c < end) { if (FileHeader.isHunkHdr(buf, c, end) >= 1) { // If we find a disconnected hunk header we might // have missed a file header previously. The hunk // isn't valid without knowing where it comes from. // error(buf, c, "Hunk disconnected from file"); c = RawParseUtils.nextLF(buf, c); continue; } // Valid git style patch? // if (RawParseUtils.match(buf, c, DiffGit) >= 0) { return(ParseDiffGit(buf, c, end)); } if (RawParseUtils.match(buf, c, DiffCc) >= 0) { return(ParseDiffCombined(DiffCc, buf, c, end)); } if (RawParseUtils.match(buf, c, DiffCombined) >= 0) { return(ParseDiffCombined(DiffCombined, buf, c, end)); } // Junk between files? Leading junk? Traditional // (non-git generated) patch? // int n = RawParseUtils.nextLF(buf, c); if (n >= end) { // Patches cannot be only one line long. This must be // trailing junk that we should ignore. // return(end); } if (n - c < 6) { // A valid header must be at least 6 bytes on the // first line, e.g. "--- a/b\n". // c = n; continue; } if (RawParseUtils.match(buf, c, FileHeader.OLD_NAME) >= 0 && RawParseUtils.match(buf, n, FileHeader.NEW_NAME) >= 0) { // Probably a traditional patch. Ensure we have at least // a "@@ -0,0" smelling line next. We only check the "@@ -". // int f = RawParseUtils.nextLF(buf, n); if (f >= end) { return(end); } if (FileHeader.isHunkHdr(buf, f, end) == 1) { return(ParseTraditionalPatch(buf, c, end)); } } c = n; } return(c); }
/// <summary> /// Line of the patch script the error appears on. /// </summary> /// <returns></returns> public string getLineText() { int eol = RawParseUtils.nextLF(_buf, _offset); return(RawParseUtils.decode(Constants.CHARSET, _buf, _offset, eol)); }
public override void extractFileLines(StringBuilder sb, string[] text, int[] offsets) { byte[] buf = File.Buffer; int ptr = StartOffset; int eol = RawParseUtils.nextLF(buf, ptr); if (EndOffset <= eol) { return; } copyLine(sb, text, offsets, 0); for (ptr = eol; ptr < EndOffset; ptr = eol) { eol = RawParseUtils.nextLF(buf, ptr); if (eol - ptr < _old.Count + 1) { // Line isn't long enough to mention the state of each // ancestor. It must be the end of the hunk. break; } bool breakScan = false; switch (buf[ptr]) { case (byte)' ': case (byte)'-': case (byte)'+': break; default: // Line can't possibly be part of this hunk; the first // ancestor information isn't recognizable. // breakScan = true; break; } if (breakScan) { break; } bool copied = false; for (int ancestor = 0; ancestor < _old.Count; ancestor++) { switch (buf[ptr + ancestor]) { case (byte)' ': case (byte)'-': if (copied) { skipLine(text, offsets, ancestor); } else { copyLine(sb, text, offsets, ancestor); copied = true; } continue; case (byte)'+': continue; default: breakScan = true; break; } if (breakScan) { break; } } if (breakScan) { break; } if (!copied) { // If none of the ancestors caused the copy then this line // must be new across the board, so it only appears in the // text of the new file. // copyLine(sb, text, offsets, _old.Count); } } }
public void extractFileLines(Stream[] outStream) { byte[] buf = File.Buffer; int ptr = StartOffset; int eol = RawParseUtils.nextLF(buf, ptr); if (EndOffset <= eol) { return; } // Treat the hunk header as though it were from the ancestor, // as it may have a function header appearing After it which // was copied out of the ancestor file. // outStream[0].Write(buf, ptr, eol - ptr); //SCAN: for (ptr = eol; ptr < EndOffset; ptr = eol) { eol = RawParseUtils.nextLF(buf, ptr); if (eol - ptr < _old.Count + 1) { // Line isn't long enough to mention the state of each // ancestor. It must be the end of the hunk. break; } bool breakScan = false; switch (buf[ptr]) { case (byte)' ': case (byte)'-': case (byte)'+': break; default: // Line can't possibly be part of this hunk; the first // ancestor information isn't recognizable. // breakScan = true; break; } if (breakScan) { break; } int delcnt = 0; for (int ancestor = 0; ancestor < _old.Count; ancestor++) { switch (buf[ptr + ancestor]) { case (byte)'-': delcnt++; outStream[ancestor].Write(buf, ptr, eol - ptr); continue; case (byte)' ': outStream[ancestor].Write(buf, ptr, eol - ptr); continue; case (byte)'+': continue; default: breakScan = true; break; } if (breakScan) { break; } } if (breakScan) { break; } if (delcnt < _old.Count) { // This line appears in the new file if it wasn't deleted // relative to all ancestors. // outStream[_old.Count].Write(buf, ptr, eol - ptr); } } }
public override int parseBody(Patch script, int end) { byte[] buf = File.Buffer; int c = RawParseUtils.nextLF(buf, StartOffset); _old.ForEach(coi => { coi.LinesAdded = 0; coi.LinesDeleted = 0; coi.LinesContext = 0; }); LinesContext = 0; int nAdded = 0; for (int eol; c < end; c = eol) { eol = RawParseUtils.nextLF(buf, c); if (eol - c < _old.Count + 1) { // Line isn't long enough to mention the state of each // ancestor. It must be the end of the hunk. break; } bool break_scan = false; switch (buf[c]) { case (byte)' ': case (byte)'-': case (byte)'+': break; default: // Line can't possibly be part of this hunk; the first // ancestor information isn't recognizable. // break_scan = true; break; } if (break_scan) { break; } int localcontext = 0; for (int ancestor = 0; ancestor < _old.Count; ancestor++) { switch (buf[c + ancestor]) { case (byte)' ': localcontext++; _old[ancestor].LinesContext++; continue; case (byte)'-': _old[ancestor].LinesDeleted++; continue; case (byte)'+': _old[ancestor].LinesAdded++; nAdded++; continue; default: break_scan = true; break; } if (break_scan) { break; } } if (break_scan) { break; } if (localcontext == _old.Count) { LinesContext++; } } for (int ancestor = 0; ancestor < _old.Count; ancestor++) { CombinedOldImage o = _old[ancestor]; int cmp = o.LinesContext + o.LinesDeleted; if (cmp < o.LineCount) { int missingCnt = o.LineCount - cmp; script.error(buf, StartOffset, "Truncated hunk, at least " + missingCnt + " lines is missing for ancestor " + (ancestor + 1)); } } if (LinesContext + nAdded < NewLineCount) { int missingCount = NewLineCount - (LinesContext + nAdded); script.error(buf, StartOffset, "Truncated hunk, at least " + missingCount + " new lines is missing"); } return(c); }
public virtual int parseBody(Patch script, int end) { byte[] buf = _file.Buffer; int c = RawParseUtils.nextLF(buf, _startOffset), last = c; _oldImage.LinesDeleted = 0; _oldImage.LinesAdded = 0; for (; c < end; last = c, c = RawParseUtils.nextLF(buf, c)) { bool breakScan; switch (buf[c]) { case (byte)' ': case (byte)'\n': LinesContext++; continue; case (byte)'-': _oldImage.LinesDeleted++; continue; case (byte)'+': _oldImage.LinesAdded++; continue; case (byte)'\\': // Matches "\ No newline at end of file" continue; default: breakScan = true; break; } if (breakScan) { break; } } if (last < end && LinesContext + _oldImage.LinesDeleted - 1 == _oldImage.LineCount && LinesContext + _oldImage.LinesAdded == NewLineCount && RawParseUtils.match(buf, last, Patch.SigFooter) >= 0) { // This is an extremely common occurrence of "corruption". // Users add footers with their signatures After this mark, // and git diff adds the git executable version number. // Let it slide; the hunk otherwise looked sound. // _oldImage.LinesDeleted--; return(last); } if (LinesContext + _oldImage.LinesDeleted < _oldImage.LineCount) { int missingCount = _oldImage.LineCount - (LinesContext + _oldImage.LinesDeleted); script.error(buf, _startOffset, "Truncated hunk, at least " + missingCount + " old lines is missing"); } else if (LinesContext + _oldImage.LinesAdded < NewLineCount) { int missingCount = NewLineCount - (LinesContext + _oldImage.LinesAdded); script.error(buf, _startOffset, "Truncated hunk, at least " + missingCount + " new lines is missing"); } else if (LinesContext + _oldImage.LinesDeleted > _oldImage.LineCount || LinesContext + _oldImage.LinesAdded > NewLineCount) { string oldcnt = _oldImage.LineCount + ":" + NewLineCount; string newcnt = (LinesContext + _oldImage.LinesDeleted) + ":" + (LinesContext + _oldImage.LinesAdded); script.warn(buf, _startOffset, "Hunk header " + oldcnt + " does not match body line count of " + newcnt); } return(c); }