##// END OF EJS Templates
parsers: better bounds checking in fm1readmarkers...
Augie Fackler -
r41052:5c68b617 default
parent child Browse files
Show More
@@ -1,717 +1,728 b''
1 /*
1 /*
2 parsers.c - efficient content parsing
2 parsers.c - efficient content parsing
3
3
4 Copyright 2008 Matt Mackall <mpm@selenic.com> and others
4 Copyright 2008 Matt Mackall <mpm@selenic.com> and others
5
5
6 This software may be used and distributed according to the terms of
6 This software may be used and distributed according to the terms of
7 the GNU General Public License, incorporated herein by reference.
7 the GNU General Public License, incorporated herein by reference.
8 */
8 */
9
9
10 #include <Python.h>
10 #include <Python.h>
11 #include <ctype.h>
11 #include <ctype.h>
12 #include <stddef.h>
12 #include <stddef.h>
13 #include <string.h>
13 #include <string.h>
14
14
15 #include "bitmanipulation.h"
15 #include "bitmanipulation.h"
16 #include "charencode.h"
16 #include "charencode.h"
17 #include "util.h"
17 #include "util.h"
18
18
19 #ifdef IS_PY3K
19 #ifdef IS_PY3K
20 /* The mapping of Python types is meant to be temporary to get Python
20 /* The mapping of Python types is meant to be temporary to get Python
21 * 3 to compile. We should remove this once Python 3 support is fully
21 * 3 to compile. We should remove this once Python 3 support is fully
22 * supported and proper types are used in the extensions themselves. */
22 * supported and proper types are used in the extensions themselves. */
23 #define PyInt_Check PyLong_Check
23 #define PyInt_Check PyLong_Check
24 #define PyInt_FromLong PyLong_FromLong
24 #define PyInt_FromLong PyLong_FromLong
25 #define PyInt_FromSsize_t PyLong_FromSsize_t
25 #define PyInt_FromSsize_t PyLong_FromSsize_t
26 #define PyInt_AsLong PyLong_AsLong
26 #define PyInt_AsLong PyLong_AsLong
27 #endif
27 #endif
28
28
29 static const char *const versionerrortext = "Python minor version mismatch";
29 static const char *const versionerrortext = "Python minor version mismatch";
30
30
31 static PyObject *dict_new_presized(PyObject *self, PyObject *args)
31 static PyObject *dict_new_presized(PyObject *self, PyObject *args)
32 {
32 {
33 Py_ssize_t expected_size;
33 Py_ssize_t expected_size;
34
34
35 if (!PyArg_ParseTuple(args, "n:make_presized_dict", &expected_size))
35 if (!PyArg_ParseTuple(args, "n:make_presized_dict", &expected_size))
36 return NULL;
36 return NULL;
37
37
38 return _dict_new_presized(expected_size);
38 return _dict_new_presized(expected_size);
39 }
39 }
40
40
41 static inline dirstateTupleObject *make_dirstate_tuple(char state, int mode,
41 static inline dirstateTupleObject *make_dirstate_tuple(char state, int mode,
42 int size, int mtime)
42 int size, int mtime)
43 {
43 {
44 dirstateTupleObject *t =
44 dirstateTupleObject *t =
45 PyObject_New(dirstateTupleObject, &dirstateTupleType);
45 PyObject_New(dirstateTupleObject, &dirstateTupleType);
46 if (!t)
46 if (!t)
47 return NULL;
47 return NULL;
48 t->state = state;
48 t->state = state;
49 t->mode = mode;
49 t->mode = mode;
50 t->size = size;
50 t->size = size;
51 t->mtime = mtime;
51 t->mtime = mtime;
52 return t;
52 return t;
53 }
53 }
54
54
55 static PyObject *dirstate_tuple_new(PyTypeObject *subtype, PyObject *args,
55 static PyObject *dirstate_tuple_new(PyTypeObject *subtype, PyObject *args,
56 PyObject *kwds)
56 PyObject *kwds)
57 {
57 {
58 /* We do all the initialization here and not a tp_init function because
58 /* We do all the initialization here and not a tp_init function because
59 * dirstate_tuple is immutable. */
59 * dirstate_tuple is immutable. */
60 dirstateTupleObject *t;
60 dirstateTupleObject *t;
61 char state;
61 char state;
62 int size, mode, mtime;
62 int size, mode, mtime;
63 if (!PyArg_ParseTuple(args, "ciii", &state, &mode, &size, &mtime))
63 if (!PyArg_ParseTuple(args, "ciii", &state, &mode, &size, &mtime))
64 return NULL;
64 return NULL;
65
65
66 t = (dirstateTupleObject *)subtype->tp_alloc(subtype, 1);
66 t = (dirstateTupleObject *)subtype->tp_alloc(subtype, 1);
67 if (!t)
67 if (!t)
68 return NULL;
68 return NULL;
69 t->state = state;
69 t->state = state;
70 t->mode = mode;
70 t->mode = mode;
71 t->size = size;
71 t->size = size;
72 t->mtime = mtime;
72 t->mtime = mtime;
73
73
74 return (PyObject *)t;
74 return (PyObject *)t;
75 }
75 }
76
76
77 static void dirstate_tuple_dealloc(PyObject *o)
77 static void dirstate_tuple_dealloc(PyObject *o)
78 {
78 {
79 PyObject_Del(o);
79 PyObject_Del(o);
80 }
80 }
81
81
82 static Py_ssize_t dirstate_tuple_length(PyObject *o)
82 static Py_ssize_t dirstate_tuple_length(PyObject *o)
83 {
83 {
84 return 4;
84 return 4;
85 }
85 }
86
86
87 static PyObject *dirstate_tuple_item(PyObject *o, Py_ssize_t i)
87 static PyObject *dirstate_tuple_item(PyObject *o, Py_ssize_t i)
88 {
88 {
89 dirstateTupleObject *t = (dirstateTupleObject *)o;
89 dirstateTupleObject *t = (dirstateTupleObject *)o;
90 switch (i) {
90 switch (i) {
91 case 0:
91 case 0:
92 return PyBytes_FromStringAndSize(&t->state, 1);
92 return PyBytes_FromStringAndSize(&t->state, 1);
93 case 1:
93 case 1:
94 return PyInt_FromLong(t->mode);
94 return PyInt_FromLong(t->mode);
95 case 2:
95 case 2:
96 return PyInt_FromLong(t->size);
96 return PyInt_FromLong(t->size);
97 case 3:
97 case 3:
98 return PyInt_FromLong(t->mtime);
98 return PyInt_FromLong(t->mtime);
99 default:
99 default:
100 PyErr_SetString(PyExc_IndexError, "index out of range");
100 PyErr_SetString(PyExc_IndexError, "index out of range");
101 return NULL;
101 return NULL;
102 }
102 }
103 }
103 }
104
104
105 static PySequenceMethods dirstate_tuple_sq = {
105 static PySequenceMethods dirstate_tuple_sq = {
106 dirstate_tuple_length, /* sq_length */
106 dirstate_tuple_length, /* sq_length */
107 0, /* sq_concat */
107 0, /* sq_concat */
108 0, /* sq_repeat */
108 0, /* sq_repeat */
109 dirstate_tuple_item, /* sq_item */
109 dirstate_tuple_item, /* sq_item */
110 0, /* sq_ass_item */
110 0, /* sq_ass_item */
111 0, /* sq_contains */
111 0, /* sq_contains */
112 0, /* sq_inplace_concat */
112 0, /* sq_inplace_concat */
113 0 /* sq_inplace_repeat */
113 0 /* sq_inplace_repeat */
114 };
114 };
115
115
116 PyTypeObject dirstateTupleType = {
116 PyTypeObject dirstateTupleType = {
117 PyVarObject_HEAD_INIT(NULL, 0) /* header */
117 PyVarObject_HEAD_INIT(NULL, 0) /* header */
118 "dirstate_tuple", /* tp_name */
118 "dirstate_tuple", /* tp_name */
119 sizeof(dirstateTupleObject), /* tp_basicsize */
119 sizeof(dirstateTupleObject), /* tp_basicsize */
120 0, /* tp_itemsize */
120 0, /* tp_itemsize */
121 (destructor)dirstate_tuple_dealloc, /* tp_dealloc */
121 (destructor)dirstate_tuple_dealloc, /* tp_dealloc */
122 0, /* tp_print */
122 0, /* tp_print */
123 0, /* tp_getattr */
123 0, /* tp_getattr */
124 0, /* tp_setattr */
124 0, /* tp_setattr */
125 0, /* tp_compare */
125 0, /* tp_compare */
126 0, /* tp_repr */
126 0, /* tp_repr */
127 0, /* tp_as_number */
127 0, /* tp_as_number */
128 &dirstate_tuple_sq, /* tp_as_sequence */
128 &dirstate_tuple_sq, /* tp_as_sequence */
129 0, /* tp_as_mapping */
129 0, /* tp_as_mapping */
130 0, /* tp_hash */
130 0, /* tp_hash */
131 0, /* tp_call */
131 0, /* tp_call */
132 0, /* tp_str */
132 0, /* tp_str */
133 0, /* tp_getattro */
133 0, /* tp_getattro */
134 0, /* tp_setattro */
134 0, /* tp_setattro */
135 0, /* tp_as_buffer */
135 0, /* tp_as_buffer */
136 Py_TPFLAGS_DEFAULT, /* tp_flags */
136 Py_TPFLAGS_DEFAULT, /* tp_flags */
137 "dirstate tuple", /* tp_doc */
137 "dirstate tuple", /* tp_doc */
138 0, /* tp_traverse */
138 0, /* tp_traverse */
139 0, /* tp_clear */
139 0, /* tp_clear */
140 0, /* tp_richcompare */
140 0, /* tp_richcompare */
141 0, /* tp_weaklistoffset */
141 0, /* tp_weaklistoffset */
142 0, /* tp_iter */
142 0, /* tp_iter */
143 0, /* tp_iternext */
143 0, /* tp_iternext */
144 0, /* tp_methods */
144 0, /* tp_methods */
145 0, /* tp_members */
145 0, /* tp_members */
146 0, /* tp_getset */
146 0, /* tp_getset */
147 0, /* tp_base */
147 0, /* tp_base */
148 0, /* tp_dict */
148 0, /* tp_dict */
149 0, /* tp_descr_get */
149 0, /* tp_descr_get */
150 0, /* tp_descr_set */
150 0, /* tp_descr_set */
151 0, /* tp_dictoffset */
151 0, /* tp_dictoffset */
152 0, /* tp_init */
152 0, /* tp_init */
153 0, /* tp_alloc */
153 0, /* tp_alloc */
154 dirstate_tuple_new, /* tp_new */
154 dirstate_tuple_new, /* tp_new */
155 };
155 };
156
156
157 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
157 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
158 {
158 {
159 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
159 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
160 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
160 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
161 char state, *cur, *str, *cpos;
161 char state, *cur, *str, *cpos;
162 int mode, size, mtime;
162 int mode, size, mtime;
163 unsigned int flen, len, pos = 40;
163 unsigned int flen, len, pos = 40;
164 int readlen;
164 int readlen;
165
165
166 if (!PyArg_ParseTuple(
166 if (!PyArg_ParseTuple(
167 args, PY23("O!O!s#:parse_dirstate", "O!O!y#:parse_dirstate"),
167 args, PY23("O!O!s#:parse_dirstate", "O!O!y#:parse_dirstate"),
168 &PyDict_Type, &dmap, &PyDict_Type, &cmap, &str, &readlen))
168 &PyDict_Type, &dmap, &PyDict_Type, &cmap, &str, &readlen))
169 goto quit;
169 goto quit;
170
170
171 len = readlen;
171 len = readlen;
172
172
173 /* read parents */
173 /* read parents */
174 if (len < 40) {
174 if (len < 40) {
175 PyErr_SetString(PyExc_ValueError,
175 PyErr_SetString(PyExc_ValueError,
176 "too little data for parents");
176 "too little data for parents");
177 goto quit;
177 goto quit;
178 }
178 }
179
179
180 parents = Py_BuildValue(PY23("s#s#", "y#y#"), str, 20, str + 20, 20);
180 parents = Py_BuildValue(PY23("s#s#", "y#y#"), str, 20, str + 20, 20);
181 if (!parents)
181 if (!parents)
182 goto quit;
182 goto quit;
183
183
184 /* read filenames */
184 /* read filenames */
185 while (pos >= 40 && pos < len) {
185 while (pos >= 40 && pos < len) {
186 if (pos + 17 > len) {
186 if (pos + 17 > len) {
187 PyErr_SetString(PyExc_ValueError,
187 PyErr_SetString(PyExc_ValueError,
188 "overflow in dirstate");
188 "overflow in dirstate");
189 goto quit;
189 goto quit;
190 }
190 }
191 cur = str + pos;
191 cur = str + pos;
192 /* unpack header */
192 /* unpack header */
193 state = *cur;
193 state = *cur;
194 mode = getbe32(cur + 1);
194 mode = getbe32(cur + 1);
195 size = getbe32(cur + 5);
195 size = getbe32(cur + 5);
196 mtime = getbe32(cur + 9);
196 mtime = getbe32(cur + 9);
197 flen = getbe32(cur + 13);
197 flen = getbe32(cur + 13);
198 pos += 17;
198 pos += 17;
199 cur += 17;
199 cur += 17;
200 if (flen > len - pos) {
200 if (flen > len - pos) {
201 PyErr_SetString(PyExc_ValueError,
201 PyErr_SetString(PyExc_ValueError,
202 "overflow in dirstate");
202 "overflow in dirstate");
203 goto quit;
203 goto quit;
204 }
204 }
205
205
206 entry =
206 entry =
207 (PyObject *)make_dirstate_tuple(state, mode, size, mtime);
207 (PyObject *)make_dirstate_tuple(state, mode, size, mtime);
208 cpos = memchr(cur, 0, flen);
208 cpos = memchr(cur, 0, flen);
209 if (cpos) {
209 if (cpos) {
210 fname = PyBytes_FromStringAndSize(cur, cpos - cur);
210 fname = PyBytes_FromStringAndSize(cur, cpos - cur);
211 cname = PyBytes_FromStringAndSize(
211 cname = PyBytes_FromStringAndSize(
212 cpos + 1, flen - (cpos - cur) - 1);
212 cpos + 1, flen - (cpos - cur) - 1);
213 if (!fname || !cname ||
213 if (!fname || !cname ||
214 PyDict_SetItem(cmap, fname, cname) == -1 ||
214 PyDict_SetItem(cmap, fname, cname) == -1 ||
215 PyDict_SetItem(dmap, fname, entry) == -1)
215 PyDict_SetItem(dmap, fname, entry) == -1)
216 goto quit;
216 goto quit;
217 Py_DECREF(cname);
217 Py_DECREF(cname);
218 } else {
218 } else {
219 fname = PyBytes_FromStringAndSize(cur, flen);
219 fname = PyBytes_FromStringAndSize(cur, flen);
220 if (!fname || PyDict_SetItem(dmap, fname, entry) == -1)
220 if (!fname || PyDict_SetItem(dmap, fname, entry) == -1)
221 goto quit;
221 goto quit;
222 }
222 }
223 Py_DECREF(fname);
223 Py_DECREF(fname);
224 Py_DECREF(entry);
224 Py_DECREF(entry);
225 fname = cname = entry = NULL;
225 fname = cname = entry = NULL;
226 pos += flen;
226 pos += flen;
227 }
227 }
228
228
229 ret = parents;
229 ret = parents;
230 Py_INCREF(ret);
230 Py_INCREF(ret);
231 quit:
231 quit:
232 Py_XDECREF(fname);
232 Py_XDECREF(fname);
233 Py_XDECREF(cname);
233 Py_XDECREF(cname);
234 Py_XDECREF(entry);
234 Py_XDECREF(entry);
235 Py_XDECREF(parents);
235 Py_XDECREF(parents);
236 return ret;
236 return ret;
237 }
237 }
238
238
239 /*
239 /*
240 * Build a set of non-normal and other parent entries from the dirstate dmap
240 * Build a set of non-normal and other parent entries from the dirstate dmap
241 */
241 */
242 static PyObject *nonnormalotherparententries(PyObject *self, PyObject *args)
242 static PyObject *nonnormalotherparententries(PyObject *self, PyObject *args)
243 {
243 {
244 PyObject *dmap, *fname, *v;
244 PyObject *dmap, *fname, *v;
245 PyObject *nonnset = NULL, *otherpset = NULL, *result = NULL;
245 PyObject *nonnset = NULL, *otherpset = NULL, *result = NULL;
246 Py_ssize_t pos;
246 Py_ssize_t pos;
247
247
248 if (!PyArg_ParseTuple(args, "O!:nonnormalentries", &PyDict_Type, &dmap))
248 if (!PyArg_ParseTuple(args, "O!:nonnormalentries", &PyDict_Type, &dmap))
249 goto bail;
249 goto bail;
250
250
251 nonnset = PySet_New(NULL);
251 nonnset = PySet_New(NULL);
252 if (nonnset == NULL)
252 if (nonnset == NULL)
253 goto bail;
253 goto bail;
254
254
255 otherpset = PySet_New(NULL);
255 otherpset = PySet_New(NULL);
256 if (otherpset == NULL)
256 if (otherpset == NULL)
257 goto bail;
257 goto bail;
258
258
259 pos = 0;
259 pos = 0;
260 while (PyDict_Next(dmap, &pos, &fname, &v)) {
260 while (PyDict_Next(dmap, &pos, &fname, &v)) {
261 dirstateTupleObject *t;
261 dirstateTupleObject *t;
262 if (!dirstate_tuple_check(v)) {
262 if (!dirstate_tuple_check(v)) {
263 PyErr_SetString(PyExc_TypeError,
263 PyErr_SetString(PyExc_TypeError,
264 "expected a dirstate tuple");
264 "expected a dirstate tuple");
265 goto bail;
265 goto bail;
266 }
266 }
267 t = (dirstateTupleObject *)v;
267 t = (dirstateTupleObject *)v;
268
268
269 if (t->state == 'n' && t->size == -2) {
269 if (t->state == 'n' && t->size == -2) {
270 if (PySet_Add(otherpset, fname) == -1) {
270 if (PySet_Add(otherpset, fname) == -1) {
271 goto bail;
271 goto bail;
272 }
272 }
273 }
273 }
274
274
275 if (t->state == 'n' && t->mtime != -1)
275 if (t->state == 'n' && t->mtime != -1)
276 continue;
276 continue;
277 if (PySet_Add(nonnset, fname) == -1)
277 if (PySet_Add(nonnset, fname) == -1)
278 goto bail;
278 goto bail;
279 }
279 }
280
280
281 result = Py_BuildValue("(OO)", nonnset, otherpset);
281 result = Py_BuildValue("(OO)", nonnset, otherpset);
282 if (result == NULL)
282 if (result == NULL)
283 goto bail;
283 goto bail;
284 Py_DECREF(nonnset);
284 Py_DECREF(nonnset);
285 Py_DECREF(otherpset);
285 Py_DECREF(otherpset);
286 return result;
286 return result;
287 bail:
287 bail:
288 Py_XDECREF(nonnset);
288 Py_XDECREF(nonnset);
289 Py_XDECREF(otherpset);
289 Py_XDECREF(otherpset);
290 Py_XDECREF(result);
290 Py_XDECREF(result);
291 return NULL;
291 return NULL;
292 }
292 }
293
293
294 /*
294 /*
295 * Efficiently pack a dirstate object into its on-disk format.
295 * Efficiently pack a dirstate object into its on-disk format.
296 */
296 */
297 static PyObject *pack_dirstate(PyObject *self, PyObject *args)
297 static PyObject *pack_dirstate(PyObject *self, PyObject *args)
298 {
298 {
299 PyObject *packobj = NULL;
299 PyObject *packobj = NULL;
300 PyObject *map, *copymap, *pl, *mtime_unset = NULL;
300 PyObject *map, *copymap, *pl, *mtime_unset = NULL;
301 Py_ssize_t nbytes, pos, l;
301 Py_ssize_t nbytes, pos, l;
302 PyObject *k, *v = NULL, *pn;
302 PyObject *k, *v = NULL, *pn;
303 char *p, *s;
303 char *p, *s;
304 int now;
304 int now;
305
305
306 if (!PyArg_ParseTuple(args, "O!O!O!i:pack_dirstate", &PyDict_Type, &map,
306 if (!PyArg_ParseTuple(args, "O!O!O!i:pack_dirstate", &PyDict_Type, &map,
307 &PyDict_Type, &copymap, &PyTuple_Type, &pl, &now))
307 &PyDict_Type, &copymap, &PyTuple_Type, &pl, &now))
308 return NULL;
308 return NULL;
309
309
310 if (PyTuple_Size(pl) != 2) {
310 if (PyTuple_Size(pl) != 2) {
311 PyErr_SetString(PyExc_TypeError, "expected 2-element tuple");
311 PyErr_SetString(PyExc_TypeError, "expected 2-element tuple");
312 return NULL;
312 return NULL;
313 }
313 }
314
314
315 /* Figure out how much we need to allocate. */
315 /* Figure out how much we need to allocate. */
316 for (nbytes = 40, pos = 0; PyDict_Next(map, &pos, &k, &v);) {
316 for (nbytes = 40, pos = 0; PyDict_Next(map, &pos, &k, &v);) {
317 PyObject *c;
317 PyObject *c;
318 if (!PyBytes_Check(k)) {
318 if (!PyBytes_Check(k)) {
319 PyErr_SetString(PyExc_TypeError, "expected string key");
319 PyErr_SetString(PyExc_TypeError, "expected string key");
320 goto bail;
320 goto bail;
321 }
321 }
322 nbytes += PyBytes_GET_SIZE(k) + 17;
322 nbytes += PyBytes_GET_SIZE(k) + 17;
323 c = PyDict_GetItem(copymap, k);
323 c = PyDict_GetItem(copymap, k);
324 if (c) {
324 if (c) {
325 if (!PyBytes_Check(c)) {
325 if (!PyBytes_Check(c)) {
326 PyErr_SetString(PyExc_TypeError,
326 PyErr_SetString(PyExc_TypeError,
327 "expected string key");
327 "expected string key");
328 goto bail;
328 goto bail;
329 }
329 }
330 nbytes += PyBytes_GET_SIZE(c) + 1;
330 nbytes += PyBytes_GET_SIZE(c) + 1;
331 }
331 }
332 }
332 }
333
333
334 packobj = PyBytes_FromStringAndSize(NULL, nbytes);
334 packobj = PyBytes_FromStringAndSize(NULL, nbytes);
335 if (packobj == NULL)
335 if (packobj == NULL)
336 goto bail;
336 goto bail;
337
337
338 p = PyBytes_AS_STRING(packobj);
338 p = PyBytes_AS_STRING(packobj);
339
339
340 pn = PyTuple_GET_ITEM(pl, 0);
340 pn = PyTuple_GET_ITEM(pl, 0);
341 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
341 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
342 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
342 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
343 goto bail;
343 goto bail;
344 }
344 }
345 memcpy(p, s, l);
345 memcpy(p, s, l);
346 p += 20;
346 p += 20;
347 pn = PyTuple_GET_ITEM(pl, 1);
347 pn = PyTuple_GET_ITEM(pl, 1);
348 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
348 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
349 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
349 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
350 goto bail;
350 goto bail;
351 }
351 }
352 memcpy(p, s, l);
352 memcpy(p, s, l);
353 p += 20;
353 p += 20;
354
354
355 for (pos = 0; PyDict_Next(map, &pos, &k, &v);) {
355 for (pos = 0; PyDict_Next(map, &pos, &k, &v);) {
356 dirstateTupleObject *tuple;
356 dirstateTupleObject *tuple;
357 char state;
357 char state;
358 int mode, size, mtime;
358 int mode, size, mtime;
359 Py_ssize_t len, l;
359 Py_ssize_t len, l;
360 PyObject *o;
360 PyObject *o;
361 char *t;
361 char *t;
362
362
363 if (!dirstate_tuple_check(v)) {
363 if (!dirstate_tuple_check(v)) {
364 PyErr_SetString(PyExc_TypeError,
364 PyErr_SetString(PyExc_TypeError,
365 "expected a dirstate tuple");
365 "expected a dirstate tuple");
366 goto bail;
366 goto bail;
367 }
367 }
368 tuple = (dirstateTupleObject *)v;
368 tuple = (dirstateTupleObject *)v;
369
369
370 state = tuple->state;
370 state = tuple->state;
371 mode = tuple->mode;
371 mode = tuple->mode;
372 size = tuple->size;
372 size = tuple->size;
373 mtime = tuple->mtime;
373 mtime = tuple->mtime;
374 if (state == 'n' && mtime == now) {
374 if (state == 'n' && mtime == now) {
375 /* See pure/parsers.py:pack_dirstate for why we do
375 /* See pure/parsers.py:pack_dirstate for why we do
376 * this. */
376 * this. */
377 mtime = -1;
377 mtime = -1;
378 mtime_unset = (PyObject *)make_dirstate_tuple(
378 mtime_unset = (PyObject *)make_dirstate_tuple(
379 state, mode, size, mtime);
379 state, mode, size, mtime);
380 if (!mtime_unset)
380 if (!mtime_unset)
381 goto bail;
381 goto bail;
382 if (PyDict_SetItem(map, k, mtime_unset) == -1)
382 if (PyDict_SetItem(map, k, mtime_unset) == -1)
383 goto bail;
383 goto bail;
384 Py_DECREF(mtime_unset);
384 Py_DECREF(mtime_unset);
385 mtime_unset = NULL;
385 mtime_unset = NULL;
386 }
386 }
387 *p++ = state;
387 *p++ = state;
388 putbe32((uint32_t)mode, p);
388 putbe32((uint32_t)mode, p);
389 putbe32((uint32_t)size, p + 4);
389 putbe32((uint32_t)size, p + 4);
390 putbe32((uint32_t)mtime, p + 8);
390 putbe32((uint32_t)mtime, p + 8);
391 t = p + 12;
391 t = p + 12;
392 p += 16;
392 p += 16;
393 len = PyBytes_GET_SIZE(k);
393 len = PyBytes_GET_SIZE(k);
394 memcpy(p, PyBytes_AS_STRING(k), len);
394 memcpy(p, PyBytes_AS_STRING(k), len);
395 p += len;
395 p += len;
396 o = PyDict_GetItem(copymap, k);
396 o = PyDict_GetItem(copymap, k);
397 if (o) {
397 if (o) {
398 *p++ = '\0';
398 *p++ = '\0';
399 l = PyBytes_GET_SIZE(o);
399 l = PyBytes_GET_SIZE(o);
400 memcpy(p, PyBytes_AS_STRING(o), l);
400 memcpy(p, PyBytes_AS_STRING(o), l);
401 p += l;
401 p += l;
402 len += l + 1;
402 len += l + 1;
403 }
403 }
404 putbe32((uint32_t)len, t);
404 putbe32((uint32_t)len, t);
405 }
405 }
406
406
407 pos = p - PyBytes_AS_STRING(packobj);
407 pos = p - PyBytes_AS_STRING(packobj);
408 if (pos != nbytes) {
408 if (pos != nbytes) {
409 PyErr_Format(PyExc_SystemError, "bad dirstate size: %ld != %ld",
409 PyErr_Format(PyExc_SystemError, "bad dirstate size: %ld != %ld",
410 (long)pos, (long)nbytes);
410 (long)pos, (long)nbytes);
411 goto bail;
411 goto bail;
412 }
412 }
413
413
414 return packobj;
414 return packobj;
415 bail:
415 bail:
416 Py_XDECREF(mtime_unset);
416 Py_XDECREF(mtime_unset);
417 Py_XDECREF(packobj);
417 Py_XDECREF(packobj);
418 Py_XDECREF(v);
418 Py_XDECREF(v);
419 return NULL;
419 return NULL;
420 }
420 }
421
421
422 #define BUMPED_FIX 1
422 #define BUMPED_FIX 1
423 #define USING_SHA_256 2
423 #define USING_SHA_256 2
424 #define FM1_HEADER_SIZE (4 + 8 + 2 + 2 + 1 + 1 + 1)
424 #define FM1_HEADER_SIZE (4 + 8 + 2 + 2 + 1 + 1 + 1)
425
425
426 static PyObject *readshas(const char *source, unsigned char num,
426 static PyObject *readshas(const char *source, unsigned char num,
427 Py_ssize_t hashwidth)
427 Py_ssize_t hashwidth)
428 {
428 {
429 int i;
429 int i;
430 PyObject *list = PyTuple_New(num);
430 PyObject *list = PyTuple_New(num);
431 if (list == NULL) {
431 if (list == NULL) {
432 return NULL;
432 return NULL;
433 }
433 }
434 for (i = 0; i < num; i++) {
434 for (i = 0; i < num; i++) {
435 PyObject *hash = PyBytes_FromStringAndSize(source, hashwidth);
435 PyObject *hash = PyBytes_FromStringAndSize(source, hashwidth);
436 if (hash == NULL) {
436 if (hash == NULL) {
437 Py_DECREF(list);
437 Py_DECREF(list);
438 return NULL;
438 return NULL;
439 }
439 }
440 PyTuple_SET_ITEM(list, i, hash);
440 PyTuple_SET_ITEM(list, i, hash);
441 source += hashwidth;
441 source += hashwidth;
442 }
442 }
443 return list;
443 return list;
444 }
444 }
445
445
446 static PyObject *fm1readmarker(const char *databegin, const char *dataend,
446 static PyObject *fm1readmarker(const char *databegin, const char *dataend,
447 uint32_t *msize)
447 uint32_t *msize)
448 {
448 {
449 const char *data = databegin;
449 const char *data = databegin;
450 const char *meta;
450 const char *meta;
451
451
452 double mtime;
452 double mtime;
453 int16_t tz;
453 int16_t tz;
454 uint16_t flags;
454 uint16_t flags;
455 unsigned char nsuccs, nparents, nmetadata;
455 unsigned char nsuccs, nparents, nmetadata;
456 Py_ssize_t hashwidth = 20;
456 Py_ssize_t hashwidth = 20;
457
457
458 PyObject *prec = NULL, *parents = NULL, *succs = NULL;
458 PyObject *prec = NULL, *parents = NULL, *succs = NULL;
459 PyObject *metadata = NULL, *ret = NULL;
459 PyObject *metadata = NULL, *ret = NULL;
460 int i;
460 int i;
461
461
462 if (data + FM1_HEADER_SIZE > dataend) {
462 if (data + FM1_HEADER_SIZE > dataend) {
463 goto overflow;
463 goto overflow;
464 }
464 }
465
465
466 *msize = getbe32(data);
466 *msize = getbe32(data);
467 data += 4;
467 data += 4;
468 mtime = getbefloat64(data);
468 mtime = getbefloat64(data);
469 data += 8;
469 data += 8;
470 tz = getbeint16(data);
470 tz = getbeint16(data);
471 data += 2;
471 data += 2;
472 flags = getbeuint16(data);
472 flags = getbeuint16(data);
473 data += 2;
473 data += 2;
474
474
475 if (flags & USING_SHA_256) {
475 if (flags & USING_SHA_256) {
476 hashwidth = 32;
476 hashwidth = 32;
477 }
477 }
478
478
479 nsuccs = (unsigned char)(*data++);
479 nsuccs = (unsigned char)(*data++);
480 nparents = (unsigned char)(*data++);
480 nparents = (unsigned char)(*data++);
481 nmetadata = (unsigned char)(*data++);
481 nmetadata = (unsigned char)(*data++);
482
482
483 if (databegin + *msize > dataend) {
483 if (databegin + *msize > dataend) {
484 goto overflow;
484 goto overflow;
485 }
485 }
486 dataend = databegin + *msize; /* narrow down to marker size */
486 dataend = databegin + *msize; /* narrow down to marker size */
487
487
488 if (data + hashwidth > dataend) {
488 if (data + hashwidth > dataend) {
489 goto overflow;
489 goto overflow;
490 }
490 }
491 prec = PyBytes_FromStringAndSize(data, hashwidth);
491 prec = PyBytes_FromStringAndSize(data, hashwidth);
492 data += hashwidth;
492 data += hashwidth;
493 if (prec == NULL) {
493 if (prec == NULL) {
494 goto bail;
494 goto bail;
495 }
495 }
496
496
497 if (data + nsuccs * hashwidth > dataend) {
497 if (data + nsuccs * hashwidth > dataend) {
498 goto overflow;
498 goto overflow;
499 }
499 }
500 succs = readshas(data, nsuccs, hashwidth);
500 succs = readshas(data, nsuccs, hashwidth);
501 if (succs == NULL) {
501 if (succs == NULL) {
502 goto bail;
502 goto bail;
503 }
503 }
504 data += nsuccs * hashwidth;
504 data += nsuccs * hashwidth;
505
505
506 if (nparents == 1 || nparents == 2) {
506 if (nparents == 1 || nparents == 2) {
507 if (data + nparents * hashwidth > dataend) {
507 if (data + nparents * hashwidth > dataend) {
508 goto overflow;
508 goto overflow;
509 }
509 }
510 parents = readshas(data, nparents, hashwidth);
510 parents = readshas(data, nparents, hashwidth);
511 if (parents == NULL) {
511 if (parents == NULL) {
512 goto bail;
512 goto bail;
513 }
513 }
514 data += nparents * hashwidth;
514 data += nparents * hashwidth;
515 } else {
515 } else {
516 parents = Py_None;
516 parents = Py_None;
517 Py_INCREF(parents);
517 Py_INCREF(parents);
518 }
518 }
519
519
520 if (data + 2 * nmetadata > dataend) {
520 if (data + 2 * nmetadata > dataend) {
521 goto overflow;
521 goto overflow;
522 }
522 }
523 meta = data + (2 * nmetadata);
523 meta = data + (2 * nmetadata);
524 metadata = PyTuple_New(nmetadata);
524 metadata = PyTuple_New(nmetadata);
525 if (metadata == NULL) {
525 if (metadata == NULL) {
526 goto bail;
526 goto bail;
527 }
527 }
528 for (i = 0; i < nmetadata; i++) {
528 for (i = 0; i < nmetadata; i++) {
529 PyObject *tmp, *left = NULL, *right = NULL;
529 PyObject *tmp, *left = NULL, *right = NULL;
530 Py_ssize_t leftsize = (unsigned char)(*data++);
530 Py_ssize_t leftsize = (unsigned char)(*data++);
531 Py_ssize_t rightsize = (unsigned char)(*data++);
531 Py_ssize_t rightsize = (unsigned char)(*data++);
532 if (meta + leftsize + rightsize > dataend) {
532 if (meta + leftsize + rightsize > dataend) {
533 goto overflow;
533 goto overflow;
534 }
534 }
535 left = PyBytes_FromStringAndSize(meta, leftsize);
535 left = PyBytes_FromStringAndSize(meta, leftsize);
536 meta += leftsize;
536 meta += leftsize;
537 right = PyBytes_FromStringAndSize(meta, rightsize);
537 right = PyBytes_FromStringAndSize(meta, rightsize);
538 meta += rightsize;
538 meta += rightsize;
539 tmp = PyTuple_New(2);
539 tmp = PyTuple_New(2);
540 if (!left || !right || !tmp) {
540 if (!left || !right || !tmp) {
541 Py_XDECREF(left);
541 Py_XDECREF(left);
542 Py_XDECREF(right);
542 Py_XDECREF(right);
543 Py_XDECREF(tmp);
543 Py_XDECREF(tmp);
544 goto bail;
544 goto bail;
545 }
545 }
546 PyTuple_SET_ITEM(tmp, 0, left);
546 PyTuple_SET_ITEM(tmp, 0, left);
547 PyTuple_SET_ITEM(tmp, 1, right);
547 PyTuple_SET_ITEM(tmp, 1, right);
548 PyTuple_SET_ITEM(metadata, i, tmp);
548 PyTuple_SET_ITEM(metadata, i, tmp);
549 }
549 }
550 ret = Py_BuildValue("(OOHO(di)O)", prec, succs, flags, metadata, mtime,
550 ret = Py_BuildValue("(OOHO(di)O)", prec, succs, flags, metadata, mtime,
551 (int)tz * 60, parents);
551 (int)tz * 60, parents);
552 goto bail; /* return successfully */
552 goto bail; /* return successfully */
553
553
554 overflow:
554 overflow:
555 PyErr_SetString(PyExc_ValueError, "overflow in obsstore");
555 PyErr_SetString(PyExc_ValueError, "overflow in obsstore");
556 bail:
556 bail:
557 Py_XDECREF(prec);
557 Py_XDECREF(prec);
558 Py_XDECREF(succs);
558 Py_XDECREF(succs);
559 Py_XDECREF(metadata);
559 Py_XDECREF(metadata);
560 Py_XDECREF(parents);
560 Py_XDECREF(parents);
561 return ret;
561 return ret;
562 }
562 }
563
563
564 static PyObject *fm1readmarkers(PyObject *self, PyObject *args)
564 static PyObject *fm1readmarkers(PyObject *self, PyObject *args)
565 {
565 {
566 const char *data, *dataend;
566 const char *data, *dataend;
567 int datalen;
567 int datalen;
568 Py_ssize_t offset, stop;
568 Py_ssize_t offset, stop;
569 PyObject *markers = NULL;
569 PyObject *markers = NULL;
570
570
571 if (!PyArg_ParseTuple(args, PY23("s#nn", "y#nn"), &data, &datalen,
571 if (!PyArg_ParseTuple(args, PY23("s#nn", "y#nn"), &data, &datalen,
572 &offset, &stop)) {
572 &offset, &stop)) {
573 return NULL;
573 return NULL;
574 }
574 }
575 if (offset < 0) {
576 PyErr_SetString(PyExc_ValueError,
577 "invalid negative offset in fm1readmarkers");
578 return NULL;
579 }
580 if (stop > datalen) {
581 PyErr_SetString(
582 PyExc_ValueError,
583 "stop longer than data length in fm1readmarkers");
584 return NULL;
585 }
575 dataend = data + datalen;
586 dataend = data + datalen;
576 data += offset;
587 data += offset;
577 markers = PyList_New(0);
588 markers = PyList_New(0);
578 if (!markers) {
589 if (!markers) {
579 return NULL;
590 return NULL;
580 }
591 }
581 while (offset < stop) {
592 while (offset < stop) {
582 uint32_t msize;
593 uint32_t msize;
583 int error;
594 int error;
584 PyObject *record = fm1readmarker(data, dataend, &msize);
595 PyObject *record = fm1readmarker(data, dataend, &msize);
585 if (!record) {
596 if (!record) {
586 goto bail;
597 goto bail;
587 }
598 }
588 error = PyList_Append(markers, record);
599 error = PyList_Append(markers, record);
589 Py_DECREF(record);
600 Py_DECREF(record);
590 if (error) {
601 if (error) {
591 goto bail;
602 goto bail;
592 }
603 }
593 data += msize;
604 data += msize;
594 offset += msize;
605 offset += msize;
595 }
606 }
596 return markers;
607 return markers;
597 bail:
608 bail:
598 Py_DECREF(markers);
609 Py_DECREF(markers);
599 return NULL;
610 return NULL;
600 }
611 }
601
612
602 static char parsers_doc[] = "Efficient content parsing.";
613 static char parsers_doc[] = "Efficient content parsing.";
603
614
604 PyObject *encodedir(PyObject *self, PyObject *args);
615 PyObject *encodedir(PyObject *self, PyObject *args);
605 PyObject *pathencode(PyObject *self, PyObject *args);
616 PyObject *pathencode(PyObject *self, PyObject *args);
606 PyObject *lowerencode(PyObject *self, PyObject *args);
617 PyObject *lowerencode(PyObject *self, PyObject *args);
607 PyObject *parse_index2(PyObject *self, PyObject *args);
618 PyObject *parse_index2(PyObject *self, PyObject *args);
608
619
609 static PyMethodDef methods[] = {
620 static PyMethodDef methods[] = {
610 {"pack_dirstate", pack_dirstate, METH_VARARGS, "pack a dirstate\n"},
621 {"pack_dirstate", pack_dirstate, METH_VARARGS, "pack a dirstate\n"},
611 {"nonnormalotherparententries", nonnormalotherparententries, METH_VARARGS,
622 {"nonnormalotherparententries", nonnormalotherparententries, METH_VARARGS,
612 "create a set containing non-normal and other parent entries of given "
623 "create a set containing non-normal and other parent entries of given "
613 "dirstate\n"},
624 "dirstate\n"},
614 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
625 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
615 {"parse_index2", parse_index2, METH_VARARGS, "parse a revlog index\n"},
626 {"parse_index2", parse_index2, METH_VARARGS, "parse a revlog index\n"},
616 {"isasciistr", isasciistr, METH_VARARGS, "check if an ASCII string\n"},
627 {"isasciistr", isasciistr, METH_VARARGS, "check if an ASCII string\n"},
617 {"asciilower", asciilower, METH_VARARGS, "lowercase an ASCII string\n"},
628 {"asciilower", asciilower, METH_VARARGS, "lowercase an ASCII string\n"},
618 {"asciiupper", asciiupper, METH_VARARGS, "uppercase an ASCII string\n"},
629 {"asciiupper", asciiupper, METH_VARARGS, "uppercase an ASCII string\n"},
619 {"dict_new_presized", dict_new_presized, METH_VARARGS,
630 {"dict_new_presized", dict_new_presized, METH_VARARGS,
620 "construct a dict with an expected size\n"},
631 "construct a dict with an expected size\n"},
621 {"make_file_foldmap", make_file_foldmap, METH_VARARGS,
632 {"make_file_foldmap", make_file_foldmap, METH_VARARGS,
622 "make file foldmap\n"},
633 "make file foldmap\n"},
623 {"jsonescapeu8fast", jsonescapeu8fast, METH_VARARGS,
634 {"jsonescapeu8fast", jsonescapeu8fast, METH_VARARGS,
624 "escape a UTF-8 byte string to JSON (fast path)\n"},
635 "escape a UTF-8 byte string to JSON (fast path)\n"},
625 {"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"},
636 {"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"},
626 {"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"},
637 {"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"},
627 {"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"},
638 {"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"},
628 {"fm1readmarkers", fm1readmarkers, METH_VARARGS,
639 {"fm1readmarkers", fm1readmarkers, METH_VARARGS,
629 "parse v1 obsolete markers\n"},
640 "parse v1 obsolete markers\n"},
630 {NULL, NULL}};
641 {NULL, NULL}};
631
642
632 void dirs_module_init(PyObject *mod);
643 void dirs_module_init(PyObject *mod);
633 void manifest_module_init(PyObject *mod);
644 void manifest_module_init(PyObject *mod);
634 void revlog_module_init(PyObject *mod);
645 void revlog_module_init(PyObject *mod);
635
646
636 static const int version = 12;
647 static const int version = 12;
637
648
638 static void module_init(PyObject *mod)
649 static void module_init(PyObject *mod)
639 {
650 {
640 PyModule_AddIntConstant(mod, "version", version);
651 PyModule_AddIntConstant(mod, "version", version);
641
652
642 /* This module constant has two purposes. First, it lets us unit test
653 /* This module constant has two purposes. First, it lets us unit test
643 * the ImportError raised without hard-coding any error text. This
654 * the ImportError raised without hard-coding any error text. This
644 * means we can change the text in the future without breaking tests,
655 * means we can change the text in the future without breaking tests,
645 * even across changesets without a recompile. Second, its presence
656 * even across changesets without a recompile. Second, its presence
646 * can be used to determine whether the version-checking logic is
657 * can be used to determine whether the version-checking logic is
647 * present, which also helps in testing across changesets without a
658 * present, which also helps in testing across changesets without a
648 * recompile. Note that this means the pure-Python version of parsers
659 * recompile. Note that this means the pure-Python version of parsers
649 * should not have this module constant. */
660 * should not have this module constant. */
650 PyModule_AddStringConstant(mod, "versionerrortext", versionerrortext);
661 PyModule_AddStringConstant(mod, "versionerrortext", versionerrortext);
651
662
652 dirs_module_init(mod);
663 dirs_module_init(mod);
653 manifest_module_init(mod);
664 manifest_module_init(mod);
654 revlog_module_init(mod);
665 revlog_module_init(mod);
655
666
656 if (PyType_Ready(&dirstateTupleType) < 0)
667 if (PyType_Ready(&dirstateTupleType) < 0)
657 return;
668 return;
658 Py_INCREF(&dirstateTupleType);
669 Py_INCREF(&dirstateTupleType);
659 PyModule_AddObject(mod, "dirstatetuple",
670 PyModule_AddObject(mod, "dirstatetuple",
660 (PyObject *)&dirstateTupleType);
671 (PyObject *)&dirstateTupleType);
661 }
672 }
662
673
663 static int check_python_version(void)
674 static int check_python_version(void)
664 {
675 {
665 PyObject *sys = PyImport_ImportModule("sys"), *ver;
676 PyObject *sys = PyImport_ImportModule("sys"), *ver;
666 long hexversion;
677 long hexversion;
667 if (!sys)
678 if (!sys)
668 return -1;
679 return -1;
669 ver = PyObject_GetAttrString(sys, "hexversion");
680 ver = PyObject_GetAttrString(sys, "hexversion");
670 Py_DECREF(sys);
681 Py_DECREF(sys);
671 if (!ver)
682 if (!ver)
672 return -1;
683 return -1;
673 hexversion = PyInt_AsLong(ver);
684 hexversion = PyInt_AsLong(ver);
674 Py_DECREF(ver);
685 Py_DECREF(ver);
675 /* sys.hexversion is a 32-bit number by default, so the -1 case
686 /* sys.hexversion is a 32-bit number by default, so the -1 case
676 * should only occur in unusual circumstances (e.g. if sys.hexversion
687 * should only occur in unusual circumstances (e.g. if sys.hexversion
677 * is manually set to an invalid value). */
688 * is manually set to an invalid value). */
678 if ((hexversion == -1) || (hexversion >> 16 != PY_VERSION_HEX >> 16)) {
689 if ((hexversion == -1) || (hexversion >> 16 != PY_VERSION_HEX >> 16)) {
679 PyErr_Format(PyExc_ImportError,
690 PyErr_Format(PyExc_ImportError,
680 "%s: The Mercurial extension "
691 "%s: The Mercurial extension "
681 "modules were compiled with Python " PY_VERSION
692 "modules were compiled with Python " PY_VERSION
682 ", but "
693 ", but "
683 "Mercurial is currently using Python with "
694 "Mercurial is currently using Python with "
684 "sys.hexversion=%ld: "
695 "sys.hexversion=%ld: "
685 "Python %s\n at: %s",
696 "Python %s\n at: %s",
686 versionerrortext, hexversion, Py_GetVersion(),
697 versionerrortext, hexversion, Py_GetVersion(),
687 Py_GetProgramFullPath());
698 Py_GetProgramFullPath());
688 return -1;
699 return -1;
689 }
700 }
690 return 0;
701 return 0;
691 }
702 }
692
703
693 #ifdef IS_PY3K
704 #ifdef IS_PY3K
694 static struct PyModuleDef parsers_module = {PyModuleDef_HEAD_INIT, "parsers",
705 static struct PyModuleDef parsers_module = {PyModuleDef_HEAD_INIT, "parsers",
695 parsers_doc, -1, methods};
706 parsers_doc, -1, methods};
696
707
697 PyMODINIT_FUNC PyInit_parsers(void)
708 PyMODINIT_FUNC PyInit_parsers(void)
698 {
709 {
699 PyObject *mod;
710 PyObject *mod;
700
711
701 if (check_python_version() == -1)
712 if (check_python_version() == -1)
702 return NULL;
713 return NULL;
703 mod = PyModule_Create(&parsers_module);
714 mod = PyModule_Create(&parsers_module);
704 module_init(mod);
715 module_init(mod);
705 return mod;
716 return mod;
706 }
717 }
707 #else
718 #else
708 PyMODINIT_FUNC initparsers(void)
719 PyMODINIT_FUNC initparsers(void)
709 {
720 {
710 PyObject *mod;
721 PyObject *mod;
711
722
712 if (check_python_version() == -1)
723 if (check_python_version() == -1)
713 return;
724 return;
714 mod = Py_InitModule3("parsers", methods, parsers_doc);
725 mod = Py_InitModule3("parsers", methods, parsers_doc);
715 module_init(mod);
726 module_init(mod);
716 }
727 }
717 #endif
728 #endif
General Comments 0
You need to be logged in to leave comments. Login now