38 % dst) |
38 % dst) |
39 |
39 |
40 util.copyfile(abssrc, absdst) |
40 util.copyfile(abssrc, absdst) |
41 |
41 |
42 # public functions |
42 # public functions |
|
43 |
|
44 def split(stream): |
|
45 '''return an iterator of individual patches from a stream''' |
|
46 def isheader(line, inheader): |
|
47 if inheader and line[0] in (' ', '\t'): |
|
48 # continuation |
|
49 return True |
|
50 l = line.split(': ', 1) |
|
51 return len(l) == 2 and ' ' not in l[0] |
|
52 |
|
53 def chunk(lines): |
|
54 return cStringIO.StringIO(''.join(lines)) |
|
55 |
|
56 def hgsplit(stream, cur): |
|
57 inheader = True |
|
58 |
|
59 for line in stream: |
|
60 if not line.strip(): |
|
61 inheader = False |
|
62 if not inheader and line.startswith('# HG changeset patch'): |
|
63 yield chunk(cur) |
|
64 cur = [] |
|
65 inheader = True |
|
66 |
|
67 cur.append(line) |
|
68 |
|
69 if cur: |
|
70 yield chunk(cur) |
|
71 |
|
72 def mboxsplit(stream, cur): |
|
73 for line in stream: |
|
74 if line.startswith('From '): |
|
75 for c in split(chunk(cur[1:])): |
|
76 yield c |
|
77 cur = [] |
|
78 |
|
79 cur.append(line) |
|
80 |
|
81 if cur: |
|
82 for c in split(chunk(cur[1:])): |
|
83 yield c |
|
84 |
|
85 def mimesplit(stream, cur): |
|
86 def msgfp(m): |
|
87 fp = cStringIO.StringIO() |
|
88 g = email.Generator.Generator(fp, mangle_from_=False) |
|
89 g.flatten(m) |
|
90 fp.seek(0) |
|
91 return fp |
|
92 |
|
93 for line in stream: |
|
94 cur.append(line) |
|
95 c = chunk(cur) |
|
96 |
|
97 m = email.Parser.Parser().parse(c) |
|
98 if not m.is_multipart(): |
|
99 yield msgfp(m) |
|
100 else: |
|
101 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch') |
|
102 for part in m.walk(): |
|
103 ct = part.get_content_type() |
|
104 if ct not in ok_types: |
|
105 continue |
|
106 yield msgfp(part) |
|
107 |
|
108 def headersplit(stream, cur): |
|
109 inheader = False |
|
110 |
|
111 for line in stream: |
|
112 if not inheader and isheader(line, inheader): |
|
113 yield chunk(cur) |
|
114 cur = [] |
|
115 inheader = True |
|
116 if inheader and not isheader(line, inheader): |
|
117 inheader = False |
|
118 |
|
119 cur.append(line) |
|
120 |
|
121 if cur: |
|
122 yield chunk(cur) |
|
123 |
|
124 def remainder(cur): |
|
125 yield chunk(cur) |
|
126 |
|
127 class fiter(object): |
|
128 def __init__(self, fp): |
|
129 self.fp = fp |
|
130 |
|
131 def __iter__(self): |
|
132 return self |
|
133 |
|
134 def next(self): |
|
135 l = self.fp.readline() |
|
136 if not l: |
|
137 raise StopIteration |
|
138 return l |
|
139 |
|
140 inheader = False |
|
141 cur = [] |
|
142 |
|
143 mimeheaders = ['content-type'] |
|
144 |
|
145 if not hasattr(stream, 'next'): |
|
146 # http responses, for example, have readline but not next |
|
147 stream = fiter(stream) |
|
148 |
|
149 for line in stream: |
|
150 cur.append(line) |
|
151 if line.startswith('# HG changeset patch'): |
|
152 return hgsplit(stream, cur) |
|
153 elif line.startswith('From '): |
|
154 return mboxsplit(stream, cur) |
|
155 elif isheader(line, inheader): |
|
156 inheader = True |
|
157 if line.split(':', 1)[0].lower() in mimeheaders: |
|
158 # let email parser handle this |
|
159 return mimesplit(stream, cur) |
|
160 elif inheader: |
|
161 # No evil headers seen, split by hand |
|
162 return headersplit(stream, cur) |
|
163 # Not enough info, keep reading |
|
164 |
|
165 # if we are here, we have a very plain patch |
|
166 return remainder(cur) |
43 |
167 |
44 def extract(ui, fileobj): |
168 def extract(ui, fileobj): |
45 '''extract patch from data read from fileobj. |
169 '''extract patch from data read from fileobj. |
46 |
170 |
47 patch can be a normal patch or contained in an email message. |
171 patch can be a normal patch or contained in an email message. |