Inital commit
[patchwork] / apps / patchwork / bin / patchparser.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
25 def parse_patch(text):
26     patchbuf = ''
27     commentbuf = ''
28     buf = ''
29
30     # state specified the line we just saw, and what to expect next
31     state = 0
32     # 0: text
33     # 1: suspected patch header (diff, ====, Index:)
34     # 2: patch header line 1 (---)
35     # 3: patch header line 2 (+++)
36     # 4: patch hunk header line (@@ line)
37     # 5: patch hunk content
38     #
39     # valid transitions:
40     #  0 -> 1 (diff, ===, Index:)
41     #  0 -> 2 (---)
42     #  1 -> 2 (---)
43     #  2 -> 3 (+++)
44     #  3 -> 4 (@@ line)
45     #  4 -> 5 (patch content)
46     #  5 -> 1 (run out of lines from @@-specifed count)
47     #
48     # Suspected patch header is stored into buf, and appended to
49     # patchbuf if we find a following hunk. Otherwise, append to
50     # comment after parsing.
51
52     # line counts while parsing a patch hunk
53     lc = (0, 0)
54     hunk = 0
55
56     hunk_re = re.compile('^\@\@ -\d+(?:,(\d+))? \+\d+(?:,(\d+))? \@\@')
57
58     for line in text.split('\n'):
59         line += '\n'
60
61         if state == 0:
62             if line.startswith('diff') or line.startswith('===') \
63                     or line.startswith('Index: '):
64                 state = 1
65                 buf += line
66
67             elif line.startswith('--- '):
68                 state = 2
69                 buf += line
70
71             else:
72                 commentbuf += line
73
74         elif state == 1:
75             buf += line
76             if line.startswith('--- '):
77                 state = 2
78
79         elif state == 2:
80             if line.startswith('+++ '):
81                 state = 3
82                 buf += line
83
84             elif hunk:
85                 state = 1
86                 buf += line
87
88             else:
89                 state = 0
90                 commentbuf += buf + line
91                 buf = ''
92
93         elif state == 3:
94             match = hunk_re.match(line)
95             if match:
96
97                 def fn(x):
98                     if not x:
99                         return 1
100                     return int(x)
101
102                 lc = map(fn, match.groups())
103
104                 state = 4
105                 patchbuf += buf + line
106                 buf = ''
107
108             elif line.startswith('--- '):
109                 patchbuf += buf + line
110                 buf = ''
111                 state = 2
112
113             elif hunk:
114                 state = 1
115                 buf += line
116
117             else:
118                 state = 0
119                 commentbuf += buf + line
120                 buf = ''
121
122         elif state == 4 or state == 5:
123             if line.startswith('-'):
124                 lc[0] -= 1
125             elif line.startswith('+'):
126                 lc[1] -= 1
127             else:
128                 lc[0] -= 1
129                 lc[1] -= 1
130
131             patchbuf += line
132
133             if lc[0] <= 0 and lc[1] <= 0:
134                 state = 3
135                 hunk += 1
136             else:
137                 state = 5
138
139         else:
140             raise Exception("Unknown state %d! (line '%s')" % (state, line))
141
142     commentbuf += buf
143
144     if patchbuf == '':
145         patchbuf = None
146
147     if commentbuf == '':
148         commentbuf = None
149
150     return (patchbuf, commentbuf)
151
152 if __name__ == '__main__':
153     import sys
154     (patch, comment) = parse_patch(sys.stdin.read())
155     if patch:
156         print "Patch: ------\n" + patch
157     if comment:
158         print "Comment: ----\n" + comment