diff options
author | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
---|---|---|
committer | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
commit | 554fd8c5195424bdbcabf5de30fdc183aba391bd (patch) | |
tree | 976dc5ab7fddf506dadce60ae936f43f58787092 /libgo/go/patch/textdiff.go | |
download | cbb-gcc-4.6.4-15d2061ac0796199866debe9ac87130894b0cdd3.tar.bz2 cbb-gcc-4.6.4-15d2061ac0796199866debe9ac87130894b0cdd3.tar.xz |
obtained gcc-4.6.4.tar.bz2 from upstream website;upstream
verified gcc-4.6.4.tar.bz2.sig;
imported gcc-4.6.4 source tree from verified upstream tarball.
downloading a git-generated archive based on the 'upstream' tag
should provide you with a source tree that is binary identical
to the one extracted from the above tarball.
if you have obtained the source via the command 'git clone',
however, do note that line-endings of files in your working
directory might differ from line-endings of the respective
files in the upstream repository.
Diffstat (limited to 'libgo/go/patch/textdiff.go')
-rw-r--r-- | libgo/go/patch/textdiff.go | 171 |
1 files changed, 171 insertions, 0 deletions
diff --git a/libgo/go/patch/textdiff.go b/libgo/go/patch/textdiff.go new file mode 100644 index 000000000..c7e693fc6 --- /dev/null +++ b/libgo/go/patch/textdiff.go @@ -0,0 +1,171 @@ +package patch + +import ( + "bytes" + "os" +) + +type TextDiff []TextChunk + +// A TextChunk specifies an edit to a section of a file: +// the text beginning at Line, which should be exactly Old, +// is to be replaced with New. +type TextChunk struct { + Line int + Old []byte + New []byte +} + +func ParseTextDiff(raw []byte) (TextDiff, os.Error) { + // Copy raw so it is safe to keep references to slices. + _, chunks := sections(raw, "@@ -") + delta := 0 + diff := make(TextDiff, len(chunks)) + for i, raw := range chunks { + c := &diff[i] + + // Parse start line: @@ -oldLine,oldCount +newLine,newCount @@ junk + chunk := splitLines(raw) + chunkHeader := chunk[0] + var ok bool + var oldLine, oldCount, newLine, newCount int + s := chunkHeader + if oldLine, s, ok = atoi(s, "@@ -", 10); !ok { + ErrChunkHdr: + return nil, SyntaxError("unexpected chunk header line: " + string(chunkHeader)) + } + if len(s) == 0 || s[0] != ',' { + oldCount = 1 + } else if oldCount, s, ok = atoi(s, ",", 10); !ok { + goto ErrChunkHdr + } + if newLine, s, ok = atoi(s, " +", 10); !ok { + goto ErrChunkHdr + } + if len(s) == 0 || s[0] != ',' { + newCount = 1 + } else if newCount, s, ok = atoi(s, ",", 10); !ok { + goto ErrChunkHdr + } + if !hasPrefix(s, " @@") { + goto ErrChunkHdr + } + + // Special case: for created or deleted files, the empty half + // is given as starting at line 0. Translate to line 1. + if oldCount == 0 && oldLine == 0 { + oldLine = 1 + } + if newCount == 0 && newLine == 0 { + newLine = 1 + } + + // Count lines in text + var dropOldNL, dropNewNL bool + var nold, nnew int + var lastch byte + chunk = chunk[1:] + for _, l := range chunk { + if nold == oldCount && nnew == newCount && (len(l) == 0 || l[0] != '\\') { + if len(bytes.TrimSpace(l)) != 0 { + return nil, SyntaxError("too many chunk lines") + } + continue + } + if len(l) == 0 { + return nil, SyntaxError("empty chunk line") + } + switch l[0] { + case '+': + nnew++ + case '-': + nold++ + case ' ': + nnew++ + nold++ + case '\\': + if _, ok := skip(l, "\\ No newline at end of file"); ok { + switch lastch { + case '-': + dropOldNL = true + case '+': + dropNewNL = true + case ' ': + dropOldNL = true + dropNewNL = true + default: + return nil, SyntaxError("message `\\ No newline at end of file' out of context") + } + break + } + fallthrough + default: + return nil, SyntaxError("unexpected chunk line: " + string(l)) + } + lastch = l[0] + } + + // Does it match the header? + if nold != oldCount || nnew != newCount { + return nil, SyntaxError("chunk header does not match line count: " + string(chunkHeader)) + } + if oldLine+delta != newLine { + return nil, SyntaxError("chunk delta is out of sync with previous chunks") + } + delta += nnew - nold + c.Line = oldLine + + var old, new bytes.Buffer + nold = 0 + nnew = 0 + for _, l := range chunk { + if nold == oldCount && nnew == newCount { + break + } + ch, l := l[0], l[1:] + if ch == '\\' { + continue + } + if ch != '+' { + old.Write(l) + nold++ + } + if ch != '-' { + new.Write(l) + nnew++ + } + } + c.Old = old.Bytes() + c.New = new.Bytes() + if dropOldNL { + c.Old = c.Old[0 : len(c.Old)-1] + } + if dropNewNL { + c.New = c.New[0 : len(c.New)-1] + } + } + return diff, nil +} + +var ErrPatchFailure = os.NewError("patch did not apply cleanly") + +// Apply applies the changes listed in the diff +// to the data, returning the new version. +func (d TextDiff) Apply(data []byte) ([]byte, os.Error) { + var buf bytes.Buffer + line := 1 + for _, c := range d { + var ok bool + var prefix []byte + prefix, data, ok = getLine(data, c.Line-line) + if !ok || !bytes.HasPrefix(data, c.Old) { + return nil, ErrPatchFailure + } + buf.Write(prefix) + data = data[len(c.Old):] + buf.Write(c.New) + line = c.Line + bytes.Count(c.Old, newline) + } + buf.Write(data) + return buf.Bytes(), nil +} |