OSDN Git Service

Add Go frontend, libgo library, and Go testsuite.
[pf3gnuchains/gcc-fork.git] / libgo / go / patch / git.go
1 // Copyright 2009 The Go Authors.  All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package patch
6
7 import (
8         "bytes"
9         "compress/zlib"
10         "crypto/sha1"
11         "encoding/git85"
12         "fmt"
13         "io"
14         "os"
15 )
16
17 func gitSHA1(data []byte) []byte {
18         if len(data) == 0 {
19                 // special case: 0 length is all zeros sum
20                 return make([]byte, 20)
21         }
22         h := sha1.New()
23         fmt.Fprintf(h, "blob %d\x00", len(data))
24         h.Write(data)
25         return h.Sum()
26 }
27
28 // BUG(rsc): The Git binary delta format is not implemented, only Git binary literals.
29
30 // GitBinaryLiteral represents a Git binary literal diff.
31 type GitBinaryLiteral struct {
32         OldSHA1 []byte // if non-empty, the SHA1 hash of the original
33         New     []byte // the new contents
34 }
35
36 // Apply implements the Diff interface's Apply method.
37 func (d *GitBinaryLiteral) Apply(old []byte) ([]byte, os.Error) {
38         if sum := gitSHA1(old); !bytes.HasPrefix(sum, d.OldSHA1) {
39                 return nil, ErrPatchFailure
40         }
41         return d.New, nil
42 }
43
44 func unhex(c byte) uint8 {
45         switch {
46         case '0' <= c && c <= '9':
47                 return c - '0'
48         case 'a' <= c && c <= 'f':
49                 return c - 'a' + 10
50         case 'A' <= c && c <= 'F':
51                 return c - 'A' + 10
52         }
53         return 255
54 }
55
56 func getHex(s []byte) (data []byte, rest []byte) {
57         n := 0
58         for n < len(s) && unhex(s[n]) != 255 {
59                 n++
60         }
61         n &^= 1 // Only take an even number of hex digits.
62         data = make([]byte, n/2)
63         for i := range data {
64                 data[i] = unhex(s[2*i])<<4 | unhex(s[2*i+1])
65         }
66         rest = s[n:]
67         return
68 }
69
70 // ParseGitBinary parses raw as a Git binary patch.
71 func ParseGitBinary(raw []byte) (Diff, os.Error) {
72         var oldSHA1, newSHA1 []byte
73         var sawBinary bool
74
75         for {
76                 var first []byte
77                 first, raw, _ = getLine(raw, 1)
78                 first = bytes.TrimSpace(first)
79                 if s, ok := skip(first, "index "); ok {
80                         oldSHA1, s = getHex(s)
81                         if s, ok = skip(s, ".."); !ok {
82                                 continue
83                         }
84                         newSHA1, s = getHex(s)
85                         continue
86                 }
87                 if _, ok := skip(first, "GIT binary patch"); ok {
88                         sawBinary = true
89                         continue
90                 }
91                 if n, _, ok := atoi(first, "literal ", 10); ok && sawBinary {
92                         data := make([]byte, n)
93                         d := git85.NewDecoder(bytes.NewBuffer(raw))
94                         z, err := zlib.NewReader(d)
95                         if err != nil {
96                                 return nil, err
97                         }
98                         defer z.Close()
99                         if _, err = io.ReadFull(z, data); err != nil {
100                                 if err == os.EOF {
101                                         err = io.ErrUnexpectedEOF
102                                 }
103                                 return nil, err
104                         }
105                         var buf [1]byte
106                         m, err := z.Read(buf[0:])
107                         if m != 0 || err != os.EOF {
108                                 return nil, os.NewError("Git binary literal longer than expected")
109                         }
110
111                         if sum := gitSHA1(data); !bytes.HasPrefix(sum, newSHA1) {
112                                 return nil, os.NewError("Git binary literal SHA1 mismatch")
113                         }
114                         return &GitBinaryLiteral{oldSHA1, data}, nil
115                 }
116                 if !sawBinary {
117                         return nil, os.NewError("unexpected Git patch header: " + string(first))
118                 }
119         }
120         panic("unreachable")
121 }