]> git.ozlabs.org Git - patchwork/blob - apps/patchwork/parser.py
Eliminate hashlib requirement
[patchwork] / apps / patchwork / parser.py
1 #!/usr/bin/python
2 #
3 # Patchwork - automated patch tracking system
4 # Copyright (C) 2008 Jeremy Kerr <jk@ozlabs.org>
5 #
6 # This file is part of the Patchwork package.
7 #
8 # Patchwork is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2 of the License, or
11 # (at your option) any later version.
12 #
13 # Patchwork is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the GNU General Public License
19 # along with Patchwork; if not, write to the Free Software
20 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21
22
23 import re
24 try:
25     import hashlib
26     sha1_hash = hashlib.sha1
27 except ImportError:
28     import sha
29     sha1_hash = sha.sha
30
31 _hunk_re = re.compile('^\@\@ -\d+(?:,(\d+))? \+\d+(?:,(\d+))? \@\@')
32 _filename_re = re.compile('^(---|\+\+\+) (\S+)')
33
34 def parse_patch(text):
35     patchbuf = ''
36     commentbuf = ''
37     buf = ''
38
39     # state specified the line we just saw, and what to expect next
40     state = 0
41     # 0: text
42     # 1: suspected patch header (diff, ====, Index:)
43     # 2: patch header line 1 (---)
44     # 3: patch header line 2 (+++)
45     # 4: patch hunk header line (@@ line)
46     # 5: patch hunk content
47     #
48     # valid transitions:
49     #  0 -> 1 (diff, ===, Index:)
50     #  0 -> 2 (---)
51     #  1 -> 2 (---)
52     #  2 -> 3 (+++)
53     #  3 -> 4 (@@ line)
54     #  4 -> 5 (patch content)
55     #  5 -> 1 (run out of lines from @@-specifed count)
56     #
57     # Suspected patch header is stored into buf, and appended to
58     # patchbuf if we find a following hunk. Otherwise, append to
59     # comment after parsing.
60
61     # line counts while parsing a patch hunk
62     lc = (0, 0)
63     hunk = 0
64
65
66     for line in text.split('\n'):
67         line += '\n'
68
69         if state == 0:
70             if line.startswith('diff') or line.startswith('===') \
71                     or line.startswith('Index: '):
72                 state = 1
73                 buf += line
74
75             elif line.startswith('--- '):
76                 state = 2
77                 buf += line
78
79             else:
80                 commentbuf += line
81
82         elif state == 1:
83             buf += line
84             if line.startswith('--- '):
85                 state = 2
86
87         elif state == 2:
88             if line.startswith('+++ '):
89                 state = 3
90                 buf += line
91
92             elif hunk:
93                 state = 1
94                 buf += line
95
96             else:
97                 state = 0
98                 commentbuf += buf + line
99                 buf = ''
100
101         elif state == 3:
102             match = _hunk_re.match(line)
103             if match:
104
105                 def fn(x):
106                     if not x:
107                         return 1
108                     return int(x)
109
110                 lc = map(fn, match.groups())
111
112                 state = 4
113                 patchbuf += buf + line
114                 buf = ''
115
116             elif line.startswith('--- '):
117                 patchbuf += buf + line
118                 buf = ''
119                 state = 2
120
121             elif hunk:
122                 state = 1
123                 buf += line
124
125             else:
126                 state = 0
127                 commentbuf += buf + line
128                 buf = ''
129
130         elif state == 4 or state == 5:
131             if line.startswith('-'):
132                 lc[0] -= 1
133             elif line.startswith('+'):
134                 lc[1] -= 1
135             else:
136                 lc[0] -= 1
137                 lc[1] -= 1
138
139             patchbuf += line
140
141             if lc[0] <= 0 and lc[1] <= 0:
142                 state = 3
143                 hunk += 1
144             else:
145                 state = 5
146
147         else:
148             raise Exception("Unknown state %d! (line '%s')" % (state, line))
149
150     commentbuf += buf
151
152     if patchbuf == '':
153         patchbuf = None
154
155     if commentbuf == '':
156         commentbuf = None
157
158     return (patchbuf, commentbuf)
159
160 def patch_hash(str):
161     str = str.replace('\r', '')
162     str = str.strip() + '\n'
163     lines = str.split('\n')
164
165     prefixes = ['-', '+', ' ']
166     hash = sha1_hash()
167
168     for line in str.split('\n'):
169
170         if len(line) <= 0:
171             continue
172
173         hunk_match = _hunk_re.match(line)
174         filename_match = _filename_re.match(line)
175
176         if filename_match:
177             # normalise -p1 top-directories
178             if filename_match.group(1) == '---':
179                 filename = 'a/'
180             else:
181                 filename = 'b/'
182             filename += '/'.join(filename_match.group(2).split('/')[1:])
183
184             line = filename_match.group(1) + ' ' + filename
185
186             
187         elif hunk_match:
188             # remove line numbers
189             def fn(x):
190                 if not x:
191                     return 1
192                 return int(x)
193             line_nos = map(fn, hunk_match.groups())
194             line = '@@ -%d +%d @@' % tuple(line_nos)
195
196         elif line[0] in prefixes:
197             pass
198
199         else:
200             continue
201
202         hash.update(line + '\n')
203
204 if __name__ == '__main__':
205     import sys
206 #    (patch, comment) = parse_patch(sys.stdin.read())
207 #    if patch:
208 #        print "Patch: ------\n" + patch
209 #    if comment:
210 #        print "Comment: ----\n" + comment
211     normalise_patch_content(sys.stdin.read())