aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/patch/textdiff.go
diff options
context:
space:
mode:
authorIan Lance Taylor <ian@gcc.gnu.org>2010-12-03 04:34:57 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2010-12-03 04:34:57 +0000
commit7a9389330e91acc3ed05deac2d198af25d13cf3c (patch)
tree38fe54a4f38ede5d949c915d66191f24a6fe5153 /libgo/go/patch/textdiff.go
parent1aa6700378e5188a853c018256113ce6e1fb5c05 (diff)
downloadgcc-7a9389330e91acc3ed05deac2d198af25d13cf3c.zip
gcc-7a9389330e91acc3ed05deac2d198af25d13cf3c.tar.gz
gcc-7a9389330e91acc3ed05deac2d198af25d13cf3c.tar.bz2
Add Go frontend, libgo library, and Go testsuite.
gcc/: * gcc.c (default_compilers): Add entry for ".go". * common.opt: Add -static-libgo as a driver option. * doc/install.texi (Configuration): Mention libgo as an option for --enable-shared. Mention go as an option for --enable-languages. * doc/invoke.texi (Overall Options): Mention .go as a file name suffix. Mention go as a -x option. * doc/frontends.texi (G++ and GCC): Mention Go as a supported language. * doc/sourcebuild.texi (Top Level): Mention libgo. * doc/standards.texi (Standards): Add section on Go language. Move references for other languages into their own section. * doc/contrib.texi (Contributors): Mention that I contributed the Go frontend. gcc/testsuite/: * lib/go.exp: New file. * lib/go-dg.exp: New file. * lib/go-torture.exp: New file. * lib/target-supports.exp (check_compile): Match // Go. From-SVN: r167407
Diffstat (limited to 'libgo/go/patch/textdiff.go')
-rw-r--r--libgo/go/patch/textdiff.go171
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 0000000..c7e693f
--- /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
+}