##// END OF EJS Templates
cext: split character encoding functions to new compilation unit...
Yuya Nishihara -
r33752:e9996bd7 default
parent child Browse files
Show More
This diff has been collapsed as it changes many lines, (796 lines changed) Show them Hide them
@@ -1,997 +1,209
1 /*
1 /*
2 parsers.c - efficient content parsing
2 charencode.c - miscellaneous character encoding
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>
12 #include <stddef.h>
13 #include <string.h>
14
11
15 #include "util.h"
12 #include "util.h"
16 #include "bitmanipulation.h"
17
18 #ifdef IS_PY3K
19 /* The mapping of Python types is meant to be temporary to get Python
20 * 3 to compile. We should remove this once Python 3 support is fully
21 * supported and proper types are used in the extensions themselves. */
22 #define PyInt_Type PyLong_Type
23 #define PyInt_Check PyLong_Check
24 #define PyInt_FromLong PyLong_FromLong
25 #define PyInt_FromSsize_t PyLong_FromSsize_t
26 #define PyInt_AS_LONG PyLong_AS_LONG
27 #define PyInt_AsLong PyLong_AsLong
28 #endif
29
30 static const char *const versionerrortext = "Python minor version mismatch";
31
13
32 static const char lowertable[128] = {
14 static const char lowertable[128] = {
33 '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
15 '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
34 '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f',
16 '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f',
35 '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
17 '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
36 '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f',
18 '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f',
37 '\x20', '\x21', '\x22', '\x23', '\x24', '\x25', '\x26', '\x27',
19 '\x20', '\x21', '\x22', '\x23', '\x24', '\x25', '\x26', '\x27',
38 '\x28', '\x29', '\x2a', '\x2b', '\x2c', '\x2d', '\x2e', '\x2f',
20 '\x28', '\x29', '\x2a', '\x2b', '\x2c', '\x2d', '\x2e', '\x2f',
39 '\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37',
21 '\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37',
40 '\x38', '\x39', '\x3a', '\x3b', '\x3c', '\x3d', '\x3e', '\x3f',
22 '\x38', '\x39', '\x3a', '\x3b', '\x3c', '\x3d', '\x3e', '\x3f',
41 '\x40',
23 '\x40',
42 '\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67', /* A-G */
24 '\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67', /* A-G */
43 '\x68', '\x69', '\x6a', '\x6b', '\x6c', '\x6d', '\x6e', '\x6f', /* H-O */
25 '\x68', '\x69', '\x6a', '\x6b', '\x6c', '\x6d', '\x6e', '\x6f', /* H-O */
44 '\x70', '\x71', '\x72', '\x73', '\x74', '\x75', '\x76', '\x77', /* P-W */
26 '\x70', '\x71', '\x72', '\x73', '\x74', '\x75', '\x76', '\x77', /* P-W */
45 '\x78', '\x79', '\x7a', /* X-Z */
27 '\x78', '\x79', '\x7a', /* X-Z */
46 '\x5b', '\x5c', '\x5d', '\x5e', '\x5f',
28 '\x5b', '\x5c', '\x5d', '\x5e', '\x5f',
47 '\x60', '\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67',
29 '\x60', '\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67',
48 '\x68', '\x69', '\x6a', '\x6b', '\x6c', '\x6d', '\x6e', '\x6f',
30 '\x68', '\x69', '\x6a', '\x6b', '\x6c', '\x6d', '\x6e', '\x6f',
49 '\x70', '\x71', '\x72', '\x73', '\x74', '\x75', '\x76', '\x77',
31 '\x70', '\x71', '\x72', '\x73', '\x74', '\x75', '\x76', '\x77',
50 '\x78', '\x79', '\x7a', '\x7b', '\x7c', '\x7d', '\x7e', '\x7f'
32 '\x78', '\x79', '\x7a', '\x7b', '\x7c', '\x7d', '\x7e', '\x7f'
51 };
33 };
52
34
53 static const char uppertable[128] = {
35 static const char uppertable[128] = {
54 '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
36 '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
55 '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f',
37 '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f',
56 '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
38 '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
57 '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f',
39 '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f',
58 '\x20', '\x21', '\x22', '\x23', '\x24', '\x25', '\x26', '\x27',
40 '\x20', '\x21', '\x22', '\x23', '\x24', '\x25', '\x26', '\x27',
59 '\x28', '\x29', '\x2a', '\x2b', '\x2c', '\x2d', '\x2e', '\x2f',
41 '\x28', '\x29', '\x2a', '\x2b', '\x2c', '\x2d', '\x2e', '\x2f',
60 '\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37',
42 '\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37',
61 '\x38', '\x39', '\x3a', '\x3b', '\x3c', '\x3d', '\x3e', '\x3f',
43 '\x38', '\x39', '\x3a', '\x3b', '\x3c', '\x3d', '\x3e', '\x3f',
62 '\x40', '\x41', '\x42', '\x43', '\x44', '\x45', '\x46', '\x47',
44 '\x40', '\x41', '\x42', '\x43', '\x44', '\x45', '\x46', '\x47',
63 '\x48', '\x49', '\x4a', '\x4b', '\x4c', '\x4d', '\x4e', '\x4f',
45 '\x48', '\x49', '\x4a', '\x4b', '\x4c', '\x4d', '\x4e', '\x4f',
64 '\x50', '\x51', '\x52', '\x53', '\x54', '\x55', '\x56', '\x57',
46 '\x50', '\x51', '\x52', '\x53', '\x54', '\x55', '\x56', '\x57',
65 '\x58', '\x59', '\x5a', '\x5b', '\x5c', '\x5d', '\x5e', '\x5f',
47 '\x58', '\x59', '\x5a', '\x5b', '\x5c', '\x5d', '\x5e', '\x5f',
66 '\x60',
48 '\x60',
67 '\x41', '\x42', '\x43', '\x44', '\x45', '\x46', '\x47', /* a-g */
49 '\x41', '\x42', '\x43', '\x44', '\x45', '\x46', '\x47', /* a-g */
68 '\x48', '\x49', '\x4a', '\x4b', '\x4c', '\x4d', '\x4e', '\x4f', /* h-o */
50 '\x48', '\x49', '\x4a', '\x4b', '\x4c', '\x4d', '\x4e', '\x4f', /* h-o */
69 '\x50', '\x51', '\x52', '\x53', '\x54', '\x55', '\x56', '\x57', /* p-w */
51 '\x50', '\x51', '\x52', '\x53', '\x54', '\x55', '\x56', '\x57', /* p-w */
70 '\x58', '\x59', '\x5a', /* x-z */
52 '\x58', '\x59', '\x5a', /* x-z */
71 '\x7b', '\x7c', '\x7d', '\x7e', '\x7f'
53 '\x7b', '\x7c', '\x7d', '\x7e', '\x7f'
72 };
54 };
73
55
74 /*
56 /*
75 * Turn a hex-encoded string into binary.
57 * Turn a hex-encoded string into binary.
76 */
58 */
77 PyObject *unhexlify(const char *str, int len)
59 PyObject *unhexlify(const char *str, int len)
78 {
60 {
79 PyObject *ret;
61 PyObject *ret;
80 char *d;
62 char *d;
81 int i;
63 int i;
82
64
83 ret = PyBytes_FromStringAndSize(NULL, len / 2);
65 ret = PyBytes_FromStringAndSize(NULL, len / 2);
84
66
85 if (!ret)
67 if (!ret)
86 return NULL;
68 return NULL;
87
69
88 d = PyBytes_AsString(ret);
70 d = PyBytes_AsString(ret);
89
71
90 for (i = 0; i < len;) {
72 for (i = 0; i < len;) {
91 int hi = hexdigit(str, i++);
73 int hi = hexdigit(str, i++);
92 int lo = hexdigit(str, i++);
74 int lo = hexdigit(str, i++);
93 *d++ = (hi << 4) | lo;
75 *d++ = (hi << 4) | lo;
94 }
76 }
95
77
96 return ret;
78 return ret;
97 }
79 }
98
80
99 static inline PyObject *_asciitransform(PyObject *str_obj,
81 static inline PyObject *_asciitransform(PyObject *str_obj,
100 const char table[128],
82 const char table[128],
101 PyObject *fallback_fn)
83 PyObject *fallback_fn)
102 {
84 {
103 char *str, *newstr;
85 char *str, *newstr;
104 Py_ssize_t i, len;
86 Py_ssize_t i, len;
105 PyObject *newobj = NULL;
87 PyObject *newobj = NULL;
106 PyObject *ret = NULL;
88 PyObject *ret = NULL;
107
89
108 str = PyBytes_AS_STRING(str_obj);
90 str = PyBytes_AS_STRING(str_obj);
109 len = PyBytes_GET_SIZE(str_obj);
91 len = PyBytes_GET_SIZE(str_obj);
110
92
111 newobj = PyBytes_FromStringAndSize(NULL, len);
93 newobj = PyBytes_FromStringAndSize(NULL, len);
112 if (!newobj)
94 if (!newobj)
113 goto quit;
95 goto quit;
114
96
115 newstr = PyBytes_AS_STRING(newobj);
97 newstr = PyBytes_AS_STRING(newobj);
116
98
117 for (i = 0; i < len; i++) {
99 for (i = 0; i < len; i++) {
118 char c = str[i];
100 char c = str[i];
119 if (c & 0x80) {
101 if (c & 0x80) {
120 if (fallback_fn != NULL) {
102 if (fallback_fn != NULL) {
121 ret = PyObject_CallFunctionObjArgs(fallback_fn,
103 ret = PyObject_CallFunctionObjArgs(fallback_fn,
122 str_obj, NULL);
104 str_obj, NULL);
123 } else {
105 } else {
124 PyObject *err = PyUnicodeDecodeError_Create(
106 PyObject *err = PyUnicodeDecodeError_Create(
125 "ascii", str, len, i, (i + 1),
107 "ascii", str, len, i, (i + 1),
126 "unexpected code byte");
108 "unexpected code byte");
127 PyErr_SetObject(PyExc_UnicodeDecodeError, err);
109 PyErr_SetObject(PyExc_UnicodeDecodeError, err);
128 Py_XDECREF(err);
110 Py_XDECREF(err);
129 }
111 }
130 goto quit;
112 goto quit;
131 }
113 }
132 newstr[i] = table[(unsigned char)c];
114 newstr[i] = table[(unsigned char)c];
133 }
115 }
134
116
135 ret = newobj;
117 ret = newobj;
136 Py_INCREF(ret);
118 Py_INCREF(ret);
137 quit:
119 quit:
138 Py_XDECREF(newobj);
120 Py_XDECREF(newobj);
139 return ret;
121 return ret;
140 }
122 }
141
123
142 static PyObject *asciilower(PyObject *self, PyObject *args)
124 PyObject *asciilower(PyObject *self, PyObject *args)
143 {
125 {
144 PyObject *str_obj;
126 PyObject *str_obj;
145 if (!PyArg_ParseTuple(args, "O!:asciilower", &PyBytes_Type, &str_obj))
127 if (!PyArg_ParseTuple(args, "O!:asciilower", &PyBytes_Type, &str_obj))
146 return NULL;
128 return NULL;
147 return _asciitransform(str_obj, lowertable, NULL);
129 return _asciitransform(str_obj, lowertable, NULL);
148 }
130 }
149
131
150 static PyObject *asciiupper(PyObject *self, PyObject *args)
132 PyObject *asciiupper(PyObject *self, PyObject *args)
151 {
133 {
152 PyObject *str_obj;
134 PyObject *str_obj;
153 if (!PyArg_ParseTuple(args, "O!:asciiupper", &PyBytes_Type, &str_obj))
135 if (!PyArg_ParseTuple(args, "O!:asciiupper", &PyBytes_Type, &str_obj))
154 return NULL;
136 return NULL;
155 return _asciitransform(str_obj, uppertable, NULL);
137 return _asciitransform(str_obj, uppertable, NULL);
156 }
138 }
157
139
158 static PyObject *dict_new_presized(PyObject *self, PyObject *args)
140 PyObject *make_file_foldmap(PyObject *self, PyObject *args)
159 {
160 Py_ssize_t expected_size;
161
162 if (!PyArg_ParseTuple(args, "n:make_presized_dict", &expected_size))
163 return NULL;
164
165 return _dict_new_presized(expected_size);
166 }
167
168 static PyObject *make_file_foldmap(PyObject *self, PyObject *args)
169 {
141 {
170 PyObject *dmap, *spec_obj, *normcase_fallback;
142 PyObject *dmap, *spec_obj, *normcase_fallback;
171 PyObject *file_foldmap = NULL;
143 PyObject *file_foldmap = NULL;
172 enum normcase_spec spec;
144 enum normcase_spec spec;
173 PyObject *k, *v;
145 PyObject *k, *v;
174 dirstateTupleObject *tuple;
146 dirstateTupleObject *tuple;
175 Py_ssize_t pos = 0;
147 Py_ssize_t pos = 0;
176 const char *table;
148 const char *table;
177
149
178 if (!PyArg_ParseTuple(args, "O!O!O!:make_file_foldmap",
150 if (!PyArg_ParseTuple(args, "O!O!O!:make_file_foldmap",
179 &PyDict_Type, &dmap,
151 &PyDict_Type, &dmap,
180 &PyInt_Type, &spec_obj,
152 &PyInt_Type, &spec_obj,
181 &PyFunction_Type, &normcase_fallback))
153 &PyFunction_Type, &normcase_fallback))
182 goto quit;
154 goto quit;
183
155
184 spec = (int)PyInt_AS_LONG(spec_obj);
156 spec = (int)PyInt_AS_LONG(spec_obj);
185 switch (spec) {
157 switch (spec) {
186 case NORMCASE_LOWER:
158 case NORMCASE_LOWER:
187 table = lowertable;
159 table = lowertable;
188 break;
160 break;
189 case NORMCASE_UPPER:
161 case NORMCASE_UPPER:
190 table = uppertable;
162 table = uppertable;
191 break;
163 break;
192 case NORMCASE_OTHER:
164 case NORMCASE_OTHER:
193 table = NULL;
165 table = NULL;
194 break;
166 break;
195 default:
167 default:
196 PyErr_SetString(PyExc_TypeError, "invalid normcasespec");
168 PyErr_SetString(PyExc_TypeError, "invalid normcasespec");
197 goto quit;
169 goto quit;
198 }
170 }
199
171
200 /* Add some more entries to deal with additions outside this
172 /* Add some more entries to deal with additions outside this
201 function. */
173 function. */
202 file_foldmap = _dict_new_presized((PyDict_Size(dmap) / 10) * 11);
174 file_foldmap = _dict_new_presized((PyDict_Size(dmap) / 10) * 11);
203 if (file_foldmap == NULL)
175 if (file_foldmap == NULL)
204 goto quit;
176 goto quit;
205
177
206 while (PyDict_Next(dmap, &pos, &k, &v)) {
178 while (PyDict_Next(dmap, &pos, &k, &v)) {
207 if (!dirstate_tuple_check(v)) {
179 if (!dirstate_tuple_check(v)) {
208 PyErr_SetString(PyExc_TypeError,
180 PyErr_SetString(PyExc_TypeError,
209 "expected a dirstate tuple");
181 "expected a dirstate tuple");
210 goto quit;
182 goto quit;
211 }
183 }
212
184
213 tuple = (dirstateTupleObject *)v;
185 tuple = (dirstateTupleObject *)v;
214 if (tuple->state != 'r') {
186 if (tuple->state != 'r') {
215 PyObject *normed;
187 PyObject *normed;
216 if (table != NULL) {
188 if (table != NULL) {
217 normed = _asciitransform(k, table,
189 normed = _asciitransform(k, table,
218 normcase_fallback);
190 normcase_fallback);
219 } else {
191 } else {
220 normed = PyObject_CallFunctionObjArgs(
192 normed = PyObject_CallFunctionObjArgs(
221 normcase_fallback, k, NULL);
193 normcase_fallback, k, NULL);
222 }
194 }
223
195
224 if (normed == NULL)
196 if (normed == NULL)
225 goto quit;
197 goto quit;
226 if (PyDict_SetItem(file_foldmap, normed, k) == -1) {
198 if (PyDict_SetItem(file_foldmap, normed, k) == -1) {
227 Py_DECREF(normed);
199 Py_DECREF(normed);
228 goto quit;
200 goto quit;
229 }
201 }
230 Py_DECREF(normed);
202 Py_DECREF(normed);
231 }
203 }
232 }
204 }
233 return file_foldmap;
205 return file_foldmap;
234 quit:
206 quit:
235 Py_XDECREF(file_foldmap);
207 Py_XDECREF(file_foldmap);
236 return NULL;
208 return NULL;
237 }
209 }
238
239 /*
240 * This code assumes that a manifest is stitched together with newline
241 * ('\n') characters.
242 */
243 static PyObject *parse_manifest(PyObject *self, PyObject *args)
244 {
245 PyObject *mfdict, *fdict;
246 char *str, *start, *end;
247 int len;
248
249 if (!PyArg_ParseTuple(args, "O!O!s#:parse_manifest",
250 &PyDict_Type, &mfdict,
251 &PyDict_Type, &fdict,
252 &str, &len))
253 goto quit;
254
255 start = str;
256 end = str + len;
257 while (start < end) {
258 PyObject *file = NULL, *node = NULL;
259 PyObject *flags = NULL;
260 char *zero = NULL, *newline = NULL;
261 ptrdiff_t nlen;
262
263 zero = memchr(start, '\0', end - start);
264 if (!zero) {
265 PyErr_SetString(PyExc_ValueError,
266 "manifest entry has no separator");
267 goto quit;
268 }
269
270 newline = memchr(zero + 1, '\n', end - (zero + 1));
271 if (!newline) {
272 PyErr_SetString(PyExc_ValueError,
273 "manifest contains trailing garbage");
274 goto quit;
275 }
276
277 file = PyBytes_FromStringAndSize(start, zero - start);
278
279 if (!file)
280 goto bail;
281
282 nlen = newline - zero - 1;
283
284 node = unhexlify(zero + 1, nlen > 40 ? 40 : (int)nlen);
285 if (!node)
286 goto bail;
287
288 if (nlen > 40) {
289 flags = PyBytes_FromStringAndSize(zero + 41,
290 nlen - 40);
291 if (!flags)
292 goto bail;
293
294 if (PyDict_SetItem(fdict, file, flags) == -1)
295 goto bail;
296 }
297
298 if (PyDict_SetItem(mfdict, file, node) == -1)
299 goto bail;
300
301 start = newline + 1;
302
303 Py_XDECREF(flags);
304 Py_XDECREF(node);
305 Py_XDECREF(file);
306 continue;
307 bail:
308 Py_XDECREF(flags);
309 Py_XDECREF(node);
310 Py_XDECREF(file);
311 goto quit;
312 }
313
314 Py_INCREF(Py_None);
315 return Py_None;
316 quit:
317 return NULL;
318 }
319
320 static inline dirstateTupleObject *make_dirstate_tuple(char state, int mode,
321 int size, int mtime)
322 {
323 dirstateTupleObject *t = PyObject_New(dirstateTupleObject,
324 &dirstateTupleType);
325 if (!t)
326 return NULL;
327 t->state = state;
328 t->mode = mode;
329 t->size = size;
330 t->mtime = mtime;
331 return t;
332 }
333
334 static PyObject *dirstate_tuple_new(PyTypeObject *subtype, PyObject *args,
335 PyObject *kwds)
336 {
337 /* We do all the initialization here and not a tp_init function because
338 * dirstate_tuple is immutable. */
339 dirstateTupleObject *t;
340 char state;
341 int size, mode, mtime;
342 if (!PyArg_ParseTuple(args, "ciii", &state, &mode, &size, &mtime))
343 return NULL;
344
345 t = (dirstateTupleObject *)subtype->tp_alloc(subtype, 1);
346 if (!t)
347 return NULL;
348 t->state = state;
349 t->mode = mode;
350 t->size = size;
351 t->mtime = mtime;
352
353 return (PyObject *)t;
354 }
355
356 static void dirstate_tuple_dealloc(PyObject *o)
357 {
358 PyObject_Del(o);
359 }
360
361 static Py_ssize_t dirstate_tuple_length(PyObject *o)
362 {
363 return 4;
364 }
365
366 static PyObject *dirstate_tuple_item(PyObject *o, Py_ssize_t i)
367 {
368 dirstateTupleObject *t = (dirstateTupleObject *)o;
369 switch (i) {
370 case 0:
371 return PyBytes_FromStringAndSize(&t->state, 1);
372 case 1:
373 return PyInt_FromLong(t->mode);
374 case 2:
375 return PyInt_FromLong(t->size);
376 case 3:
377 return PyInt_FromLong(t->mtime);
378 default:
379 PyErr_SetString(PyExc_IndexError, "index out of range");
380 return NULL;
381 }
382 }
383
384 static PySequenceMethods dirstate_tuple_sq = {
385 dirstate_tuple_length, /* sq_length */
386 0, /* sq_concat */
387 0, /* sq_repeat */
388 dirstate_tuple_item, /* sq_item */
389 0, /* sq_ass_item */
390 0, /* sq_contains */
391 0, /* sq_inplace_concat */
392 0 /* sq_inplace_repeat */
393 };
394
395 PyTypeObject dirstateTupleType = {
396 PyVarObject_HEAD_INIT(NULL, 0)
397 "dirstate_tuple", /* tp_name */
398 sizeof(dirstateTupleObject),/* tp_basicsize */
399 0, /* tp_itemsize */
400 (destructor)dirstate_tuple_dealloc, /* tp_dealloc */
401 0, /* tp_print */
402 0, /* tp_getattr */
403 0, /* tp_setattr */
404 0, /* tp_compare */
405 0, /* tp_repr */
406 0, /* tp_as_number */
407 &dirstate_tuple_sq, /* tp_as_sequence */
408 0, /* tp_as_mapping */
409 0, /* tp_hash */
410 0, /* tp_call */
411 0, /* tp_str */
412 0, /* tp_getattro */
413 0, /* tp_setattro */
414 0, /* tp_as_buffer */
415 Py_TPFLAGS_DEFAULT, /* tp_flags */
416 "dirstate tuple", /* tp_doc */
417 0, /* tp_traverse */
418 0, /* tp_clear */
419 0, /* tp_richcompare */
420 0, /* tp_weaklistoffset */
421 0, /* tp_iter */
422 0, /* tp_iternext */
423 0, /* tp_methods */
424 0, /* tp_members */
425 0, /* tp_getset */
426 0, /* tp_base */
427 0, /* tp_dict */
428 0, /* tp_descr_get */
429 0, /* tp_descr_set */
430 0, /* tp_dictoffset */
431 0, /* tp_init */
432 0, /* tp_alloc */
433 dirstate_tuple_new, /* tp_new */
434 };
435
436 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
437 {
438 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
439 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
440 char state, *cur, *str, *cpos;
441 int mode, size, mtime;
442 unsigned int flen, len, pos = 40;
443 int readlen;
444
445 if (!PyArg_ParseTuple(args, "O!O!s#:parse_dirstate",
446 &PyDict_Type, &dmap,
447 &PyDict_Type, &cmap,
448 &str, &readlen))
449 goto quit;
450
451 len = readlen;
452
453 /* read parents */
454 if (len < 40) {
455 PyErr_SetString(
456 PyExc_ValueError, "too little data for parents");
457 goto quit;
458 }
459
460 parents = Py_BuildValue("s#s#", str, 20, str + 20, 20);
461 if (!parents)
462 goto quit;
463
464 /* read filenames */
465 while (pos >= 40 && pos < len) {
466 if (pos + 17 > len) {
467 PyErr_SetString(PyExc_ValueError,
468 "overflow in dirstate");
469 goto quit;
470 }
471 cur = str + pos;
472 /* unpack header */
473 state = *cur;
474 mode = getbe32(cur + 1);
475 size = getbe32(cur + 5);
476 mtime = getbe32(cur + 9);
477 flen = getbe32(cur + 13);
478 pos += 17;
479 cur += 17;
480 if (flen > len - pos) {
481 PyErr_SetString(PyExc_ValueError, "overflow in dirstate");
482 goto quit;
483 }
484
485 entry = (PyObject *)make_dirstate_tuple(state, mode, size,
486 mtime);
487 cpos = memchr(cur, 0, flen);
488 if (cpos) {
489 fname = PyBytes_FromStringAndSize(cur, cpos - cur);
490 cname = PyBytes_FromStringAndSize(cpos + 1,
491 flen - (cpos - cur) - 1);
492 if (!fname || !cname ||
493 PyDict_SetItem(cmap, fname, cname) == -1 ||
494 PyDict_SetItem(dmap, fname, entry) == -1)
495 goto quit;
496 Py_DECREF(cname);
497 } else {
498 fname = PyBytes_FromStringAndSize(cur, flen);
499 if (!fname ||
500 PyDict_SetItem(dmap, fname, entry) == -1)
501 goto quit;
502 }
503 Py_DECREF(fname);
504 Py_DECREF(entry);
505 fname = cname = entry = NULL;
506 pos += flen;
507 }
508
509 ret = parents;
510 Py_INCREF(ret);
511 quit:
512 Py_XDECREF(fname);
513 Py_XDECREF(cname);
514 Py_XDECREF(entry);
515 Py_XDECREF(parents);
516 return ret;
517 }
518
519 /*
520 * Build a set of non-normal and other parent entries from the dirstate dmap
521 */
522 static PyObject *nonnormalotherparententries(PyObject *self, PyObject *args) {
523 PyObject *dmap, *fname, *v;
524 PyObject *nonnset = NULL, *otherpset = NULL, *result = NULL;
525 Py_ssize_t pos;
526
527 if (!PyArg_ParseTuple(args, "O!:nonnormalentries",
528 &PyDict_Type, &dmap))
529 goto bail;
530
531 nonnset = PySet_New(NULL);
532 if (nonnset == NULL)
533 goto bail;
534
535 otherpset = PySet_New(NULL);
536 if (otherpset == NULL)
537 goto bail;
538
539 pos = 0;
540 while (PyDict_Next(dmap, &pos, &fname, &v)) {
541 dirstateTupleObject *t;
542 if (!dirstate_tuple_check(v)) {
543 PyErr_SetString(PyExc_TypeError,
544 "expected a dirstate tuple");
545 goto bail;
546 }
547 t = (dirstateTupleObject *)v;
548
549 if (t->state == 'n' && t->size == -2) {
550 if (PySet_Add(otherpset, fname) == -1) {
551 goto bail;
552 }
553 }
554
555 if (t->state == 'n' && t->mtime != -1)
556 continue;
557 if (PySet_Add(nonnset, fname) == -1)
558 goto bail;
559 }
560
561 result = Py_BuildValue("(OO)", nonnset, otherpset);
562 if (result == NULL)
563 goto bail;
564 Py_DECREF(nonnset);
565 Py_DECREF(otherpset);
566 return result;
567 bail:
568 Py_XDECREF(nonnset);
569 Py_XDECREF(otherpset);
570 Py_XDECREF(result);
571 return NULL;
572 }
573
574 /*
575 * Efficiently pack a dirstate object into its on-disk format.
576 */
577 static PyObject *pack_dirstate(PyObject *self, PyObject *args)
578 {
579 PyObject *packobj = NULL;
580 PyObject *map, *copymap, *pl, *mtime_unset = NULL;
581 Py_ssize_t nbytes, pos, l;
582 PyObject *k, *v = NULL, *pn;
583 char *p, *s;
584 int now;
585
586 if (!PyArg_ParseTuple(args, "O!O!Oi:pack_dirstate",
587 &PyDict_Type, &map, &PyDict_Type, &copymap,
588 &pl, &now))
589 return NULL;
590
591 if (!PySequence_Check(pl) || PySequence_Size(pl) != 2) {
592 PyErr_SetString(PyExc_TypeError, "expected 2-element sequence");
593 return NULL;
594 }
595
596 /* Figure out how much we need to allocate. */
597 for (nbytes = 40, pos = 0; PyDict_Next(map, &pos, &k, &v);) {
598 PyObject *c;
599 if (!PyBytes_Check(k)) {
600 PyErr_SetString(PyExc_TypeError, "expected string key");
601 goto bail;
602 }
603 nbytes += PyBytes_GET_SIZE(k) + 17;
604 c = PyDict_GetItem(copymap, k);
605 if (c) {
606 if (!PyBytes_Check(c)) {
607 PyErr_SetString(PyExc_TypeError,
608 "expected string key");
609 goto bail;
610 }
611 nbytes += PyBytes_GET_SIZE(c) + 1;
612 }
613 }
614
615 packobj = PyBytes_FromStringAndSize(NULL, nbytes);
616 if (packobj == NULL)
617 goto bail;
618
619 p = PyBytes_AS_STRING(packobj);
620
621 pn = PySequence_ITEM(pl, 0);
622 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
623 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
624 goto bail;
625 }
626 memcpy(p, s, l);
627 p += 20;
628 pn = PySequence_ITEM(pl, 1);
629 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
630 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
631 goto bail;
632 }
633 memcpy(p, s, l);
634 p += 20;
635
636 for (pos = 0; PyDict_Next(map, &pos, &k, &v); ) {
637 dirstateTupleObject *tuple;
638 char state;
639 int mode, size, mtime;
640 Py_ssize_t len, l;
641 PyObject *o;
642 char *t;
643
644 if (!dirstate_tuple_check(v)) {
645 PyErr_SetString(PyExc_TypeError,
646 "expected a dirstate tuple");
647 goto bail;
648 }
649 tuple = (dirstateTupleObject *)v;
650
651 state = tuple->state;
652 mode = tuple->mode;
653 size = tuple->size;
654 mtime = tuple->mtime;
655 if (state == 'n' && mtime == now) {
656 /* See pure/parsers.py:pack_dirstate for why we do
657 * this. */
658 mtime = -1;
659 mtime_unset = (PyObject *)make_dirstate_tuple(
660 state, mode, size, mtime);
661 if (!mtime_unset)
662 goto bail;
663 if (PyDict_SetItem(map, k, mtime_unset) == -1)
664 goto bail;
665 Py_DECREF(mtime_unset);
666 mtime_unset = NULL;
667 }
668 *p++ = state;
669 putbe32((uint32_t)mode, p);
670 putbe32((uint32_t)size, p + 4);
671 putbe32((uint32_t)mtime, p + 8);
672 t = p + 12;
673 p += 16;
674 len = PyBytes_GET_SIZE(k);
675 memcpy(p, PyBytes_AS_STRING(k), len);
676 p += len;
677 o = PyDict_GetItem(copymap, k);
678 if (o) {
679 *p++ = '\0';
680 l = PyBytes_GET_SIZE(o);
681 memcpy(p, PyBytes_AS_STRING(o), l);
682 p += l;
683 len += l + 1;
684 }
685 putbe32((uint32_t)len, t);
686 }
687
688 pos = p - PyBytes_AS_STRING(packobj);
689 if (pos != nbytes) {
690 PyErr_Format(PyExc_SystemError, "bad dirstate size: %ld != %ld",
691 (long)pos, (long)nbytes);
692 goto bail;
693 }
694
695 return packobj;
696 bail:
697 Py_XDECREF(mtime_unset);
698 Py_XDECREF(packobj);
699 Py_XDECREF(v);
700 return NULL;
701 }
702
703 #define BUMPED_FIX 1
704 #define USING_SHA_256 2
705 #define FM1_HEADER_SIZE (4 + 8 + 2 + 2 + 1 + 1 + 1)
706
707 static PyObject *readshas(
708 const char *source, unsigned char num, Py_ssize_t hashwidth)
709 {
710 int i;
711 PyObject *list = PyTuple_New(num);
712 if (list == NULL) {
713 return NULL;
714 }
715 for (i = 0; i < num; i++) {
716 PyObject *hash = PyBytes_FromStringAndSize(source, hashwidth);
717 if (hash == NULL) {
718 Py_DECREF(list);
719 return NULL;
720 }
721 PyTuple_SET_ITEM(list, i, hash);
722 source += hashwidth;
723 }
724 return list;
725 }
726
727 static PyObject *fm1readmarker(const char *databegin, const char *dataend,
728 uint32_t *msize)
729 {
730 const char *data = databegin;
731 const char *meta;
732
733 double mtime;
734 int16_t tz;
735 uint16_t flags;
736 unsigned char nsuccs, nparents, nmetadata;
737 Py_ssize_t hashwidth = 20;
738
739 PyObject *prec = NULL, *parents = NULL, *succs = NULL;
740 PyObject *metadata = NULL, *ret = NULL;
741 int i;
742
743 if (data + FM1_HEADER_SIZE > dataend) {
744 goto overflow;
745 }
746
747 *msize = getbe32(data);
748 data += 4;
749 mtime = getbefloat64(data);
750 data += 8;
751 tz = getbeint16(data);
752 data += 2;
753 flags = getbeuint16(data);
754 data += 2;
755
756 if (flags & USING_SHA_256) {
757 hashwidth = 32;
758 }
759
760 nsuccs = (unsigned char)(*data++);
761 nparents = (unsigned char)(*data++);
762 nmetadata = (unsigned char)(*data++);
763
764 if (databegin + *msize > dataend) {
765 goto overflow;
766 }
767 dataend = databegin + *msize; /* narrow down to marker size */
768
769 if (data + hashwidth > dataend) {
770 goto overflow;
771 }
772 prec = PyBytes_FromStringAndSize(data, hashwidth);
773 data += hashwidth;
774 if (prec == NULL) {
775 goto bail;
776 }
777
778 if (data + nsuccs * hashwidth > dataend) {
779 goto overflow;
780 }
781 succs = readshas(data, nsuccs, hashwidth);
782 if (succs == NULL) {
783 goto bail;
784 }
785 data += nsuccs * hashwidth;
786
787 if (nparents == 1 || nparents == 2) {
788 if (data + nparents * hashwidth > dataend) {
789 goto overflow;
790 }
791 parents = readshas(data, nparents, hashwidth);
792 if (parents == NULL) {
793 goto bail;
794 }
795 data += nparents * hashwidth;
796 } else {
797 parents = Py_None;
798 Py_INCREF(parents);
799 }
800
801 if (data + 2 * nmetadata > dataend) {
802 goto overflow;
803 }
804 meta = data + (2 * nmetadata);
805 metadata = PyTuple_New(nmetadata);
806 if (metadata == NULL) {
807 goto bail;
808 }
809 for (i = 0; i < nmetadata; i++) {
810 PyObject *tmp, *left = NULL, *right = NULL;
811 Py_ssize_t leftsize = (unsigned char)(*data++);
812 Py_ssize_t rightsize = (unsigned char)(*data++);
813 if (meta + leftsize + rightsize > dataend) {
814 goto overflow;
815 }
816 left = PyBytes_FromStringAndSize(meta, leftsize);
817 meta += leftsize;
818 right = PyBytes_FromStringAndSize(meta, rightsize);
819 meta += rightsize;
820 tmp = PyTuple_New(2);
821 if (!left || !right || !tmp) {
822 Py_XDECREF(left);
823 Py_XDECREF(right);
824 Py_XDECREF(tmp);
825 goto bail;
826 }
827 PyTuple_SET_ITEM(tmp, 0, left);
828 PyTuple_SET_ITEM(tmp, 1, right);
829 PyTuple_SET_ITEM(metadata, i, tmp);
830 }
831 ret = Py_BuildValue("(OOHO(di)O)", prec, succs, flags,
832 metadata, mtime, (int)tz * 60, parents);
833 goto bail; /* return successfully */
834
835 overflow:
836 PyErr_SetString(PyExc_ValueError, "overflow in obsstore");
837 bail:
838 Py_XDECREF(prec);
839 Py_XDECREF(succs);
840 Py_XDECREF(metadata);
841 Py_XDECREF(parents);
842 return ret;
843 }
844
845
846 static PyObject *fm1readmarkers(PyObject *self, PyObject *args) {
847 const char *data, *dataend;
848 int datalen;
849 Py_ssize_t offset, stop;
850 PyObject *markers = NULL;
851
852 if (!PyArg_ParseTuple(args, "s#nn", &data, &datalen, &offset, &stop)) {
853 return NULL;
854 }
855 dataend = data + datalen;
856 data += offset;
857 markers = PyList_New(0);
858 if (!markers) {
859 return NULL;
860 }
861 while (offset < stop) {
862 uint32_t msize;
863 int error;
864 PyObject *record = fm1readmarker(data, dataend, &msize);
865 if (!record) {
866 goto bail;
867 }
868 error = PyList_Append(markers, record);
869 Py_DECREF(record);
870 if (error) {
871 goto bail;
872 }
873 data += msize;
874 offset += msize;
875 }
876 return markers;
877 bail:
878 Py_DECREF(markers);
879 return NULL;
880 }
881
882 static char parsers_doc[] = "Efficient content parsing.";
883
884 PyObject *encodedir(PyObject *self, PyObject *args);
885 PyObject *pathencode(PyObject *self, PyObject *args);
886 PyObject *lowerencode(PyObject *self, PyObject *args);
887 PyObject *parse_index2(PyObject *self, PyObject *args);
888
889 static PyMethodDef methods[] = {
890 {"pack_dirstate", pack_dirstate, METH_VARARGS, "pack a dirstate\n"},
891 {"nonnormalotherparententries", nonnormalotherparententries, METH_VARARGS,
892 "create a set containing non-normal and other parent entries of given "
893 "dirstate\n"},
894 {"parse_manifest", parse_manifest, METH_VARARGS, "parse a manifest\n"},
895 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
896 {"parse_index2", parse_index2, METH_VARARGS, "parse a revlog index\n"},
897 {"asciilower", asciilower, METH_VARARGS, "lowercase an ASCII string\n"},
898 {"asciiupper", asciiupper, METH_VARARGS, "uppercase an ASCII string\n"},
899 {"dict_new_presized", dict_new_presized, METH_VARARGS,
900 "construct a dict with an expected size\n"},
901 {"make_file_foldmap", make_file_foldmap, METH_VARARGS,
902 "make file foldmap\n"},
903 {"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"},
904 {"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"},
905 {"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"},
906 {"fm1readmarkers", fm1readmarkers, METH_VARARGS,
907 "parse v1 obsolete markers\n"},
908 {NULL, NULL}
909 };
910
911 void dirs_module_init(PyObject *mod);
912 void manifest_module_init(PyObject *mod);
913 void revlog_module_init(PyObject *mod);
914
915 static const int version = 1;
916
917 static void module_init(PyObject *mod)
918 {
919 PyModule_AddIntConstant(mod, "version", version);
920
921 /* This module constant has two purposes. First, it lets us unit test
922 * the ImportError raised without hard-coding any error text. This
923 * means we can change the text in the future without breaking tests,
924 * even across changesets without a recompile. Second, its presence
925 * can be used to determine whether the version-checking logic is
926 * present, which also helps in testing across changesets without a
927 * recompile. Note that this means the pure-Python version of parsers
928 * should not have this module constant. */
929 PyModule_AddStringConstant(mod, "versionerrortext", versionerrortext);
930
931 dirs_module_init(mod);
932 manifest_module_init(mod);
933 revlog_module_init(mod);
934
935 if (PyType_Ready(&dirstateTupleType) < 0)
936 return;
937 Py_INCREF(&dirstateTupleType);
938 PyModule_AddObject(mod, "dirstatetuple",
939 (PyObject *)&dirstateTupleType);
940 }
941
942 static int check_python_version(void)
943 {
944 PyObject *sys = PyImport_ImportModule("sys"), *ver;
945 long hexversion;
946 if (!sys)
947 return -1;
948 ver = PyObject_GetAttrString(sys, "hexversion");
949 Py_DECREF(sys);
950 if (!ver)
951 return -1;
952 hexversion = PyInt_AsLong(ver);
953 Py_DECREF(ver);
954 /* sys.hexversion is a 32-bit number by default, so the -1 case
955 * should only occur in unusual circumstances (e.g. if sys.hexversion
956 * is manually set to an invalid value). */
957 if ((hexversion == -1) || (hexversion >> 16 != PY_VERSION_HEX >> 16)) {
958 PyErr_Format(PyExc_ImportError, "%s: The Mercurial extension "
959 "modules were compiled with Python " PY_VERSION ", but "
960 "Mercurial is currently using Python with sys.hexversion=%ld: "
961 "Python %s\n at: %s", versionerrortext, hexversion,
962 Py_GetVersion(), Py_GetProgramFullPath());
963 return -1;
964 }
965 return 0;
966 }
967
968 #ifdef IS_PY3K
969 static struct PyModuleDef parsers_module = {
970 PyModuleDef_HEAD_INIT,
971 "parsers",
972 parsers_doc,
973 -1,
974 methods
975 };
976
977 PyMODINIT_FUNC PyInit_parsers(void)
978 {
979 PyObject *mod;
980
981 if (check_python_version() == -1)
982 return NULL;
983 mod = PyModule_Create(&parsers_module);
984 module_init(mod);
985 return mod;
986 }
987 #else
988 PyMODINIT_FUNC initparsers(void)
989 {
990 PyObject *mod;
991
992 if (check_python_version() == -1)
993 return;
994 mod = Py_InitModule3("parsers", methods, parsers_doc);
995 module_init(mod);
996 }
997 #endif
@@ -1,939 +1,939
1 /*
1 /*
2 * manifest.c - manifest type that does on-demand parsing.
2 * manifest.c - manifest type that does on-demand parsing.
3 *
3 *
4 * Copyright 2015, Google Inc.
4 * Copyright 2015, Google Inc.
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 #include <Python.h>
9 #include <Python.h>
10
10
11 #include <assert.h>
11 #include <assert.h>
12 #include <string.h>
12 #include <string.h>
13 #include <stdlib.h>
13 #include <stdlib.h>
14
14
15 #include "util.h"
15 #include "util.h"
16
16
17 #define DEFAULT_LINES 100000
17 #define DEFAULT_LINES 100000
18
18
19 typedef struct {
19 typedef struct {
20 char *start;
20 char *start;
21 Py_ssize_t len; /* length of line including terminal newline */
21 Py_ssize_t len; /* length of line including terminal newline */
22 char hash_suffix;
22 char hash_suffix;
23 bool from_malloc;
23 bool from_malloc;
24 bool deleted;
24 bool deleted;
25 } line;
25 } line;
26
26
27 typedef struct {
27 typedef struct {
28 PyObject_HEAD
28 PyObject_HEAD
29 PyObject *pydata;
29 PyObject *pydata;
30 line *lines;
30 line *lines;
31 int numlines; /* number of line entries */
31 int numlines; /* number of line entries */
32 int livelines; /* number of non-deleted lines */
32 int livelines; /* number of non-deleted lines */
33 int maxlines; /* allocated number of lines */
33 int maxlines; /* allocated number of lines */
34 bool dirty;
34 bool dirty;
35 } lazymanifest;
35 } lazymanifest;
36
36
37 #define MANIFEST_OOM -1
37 #define MANIFEST_OOM -1
38 #define MANIFEST_NOT_SORTED -2
38 #define MANIFEST_NOT_SORTED -2
39 #define MANIFEST_MALFORMED -3
39 #define MANIFEST_MALFORMED -3
40
40
41 /* defined in parsers.c */
41 /* defined in charencode.c */
42 PyObject *unhexlify(const char *str, int len);
42 PyObject *unhexlify(const char *str, int len);
43
43
44 /* get the length of the path for a line */
44 /* get the length of the path for a line */
45 static size_t pathlen(line *l) {
45 static size_t pathlen(line *l) {
46 return strlen(l->start);
46 return strlen(l->start);
47 }
47 }
48
48
49 /* get the node value of a single line */
49 /* get the node value of a single line */
50 static PyObject *nodeof(line *l) {
50 static PyObject *nodeof(line *l) {
51 char *s = l->start;
51 char *s = l->start;
52 ssize_t llen = pathlen(l);
52 ssize_t llen = pathlen(l);
53 PyObject *hash = unhexlify(s + llen + 1, 40);
53 PyObject *hash = unhexlify(s + llen + 1, 40);
54 if (!hash) {
54 if (!hash) {
55 return NULL;
55 return NULL;
56 }
56 }
57 if (l->hash_suffix != '\0') {
57 if (l->hash_suffix != '\0') {
58 char newhash[21];
58 char newhash[21];
59 memcpy(newhash, PyBytes_AsString(hash), 20);
59 memcpy(newhash, PyBytes_AsString(hash), 20);
60 Py_DECREF(hash);
60 Py_DECREF(hash);
61 newhash[20] = l->hash_suffix;
61 newhash[20] = l->hash_suffix;
62 hash = PyBytes_FromStringAndSize(newhash, 21);
62 hash = PyBytes_FromStringAndSize(newhash, 21);
63 }
63 }
64 return hash;
64 return hash;
65 }
65 }
66
66
67 /* get the node hash and flags of a line as a tuple */
67 /* get the node hash and flags of a line as a tuple */
68 static PyObject *hashflags(line *l)
68 static PyObject *hashflags(line *l)
69 {
69 {
70 char *s = l->start;
70 char *s = l->start;
71 size_t plen = pathlen(l);
71 size_t plen = pathlen(l);
72 PyObject *hash = nodeof(l);
72 PyObject *hash = nodeof(l);
73
73
74 /* 40 for hash, 1 for null byte, 1 for newline */
74 /* 40 for hash, 1 for null byte, 1 for newline */
75 size_t hplen = plen + 42;
75 size_t hplen = plen + 42;
76 Py_ssize_t flen = l->len - hplen;
76 Py_ssize_t flen = l->len - hplen;
77 PyObject *flags;
77 PyObject *flags;
78 PyObject *tup;
78 PyObject *tup;
79
79
80 if (!hash)
80 if (!hash)
81 return NULL;
81 return NULL;
82 flags = PyBytes_FromStringAndSize(s + hplen - 1, flen);
82 flags = PyBytes_FromStringAndSize(s + hplen - 1, flen);
83 if (!flags) {
83 if (!flags) {
84 Py_DECREF(hash);
84 Py_DECREF(hash);
85 return NULL;
85 return NULL;
86 }
86 }
87 tup = PyTuple_Pack(2, hash, flags);
87 tup = PyTuple_Pack(2, hash, flags);
88 Py_DECREF(flags);
88 Py_DECREF(flags);
89 Py_DECREF(hash);
89 Py_DECREF(hash);
90 return tup;
90 return tup;
91 }
91 }
92
92
93 /* if we're about to run out of space in the line index, add more */
93 /* if we're about to run out of space in the line index, add more */
94 static bool realloc_if_full(lazymanifest *self)
94 static bool realloc_if_full(lazymanifest *self)
95 {
95 {
96 if (self->numlines == self->maxlines) {
96 if (self->numlines == self->maxlines) {
97 self->maxlines *= 2;
97 self->maxlines *= 2;
98 self->lines = realloc(self->lines, self->maxlines * sizeof(line));
98 self->lines = realloc(self->lines, self->maxlines * sizeof(line));
99 }
99 }
100 return !!self->lines;
100 return !!self->lines;
101 }
101 }
102
102
103 /*
103 /*
104 * Find the line boundaries in the manifest that 'data' points to and store
104 * Find the line boundaries in the manifest that 'data' points to and store
105 * information about each line in 'self'.
105 * information about each line in 'self'.
106 */
106 */
107 static int find_lines(lazymanifest *self, char *data, Py_ssize_t len)
107 static int find_lines(lazymanifest *self, char *data, Py_ssize_t len)
108 {
108 {
109 char *prev = NULL;
109 char *prev = NULL;
110 while (len > 0) {
110 while (len > 0) {
111 line *l;
111 line *l;
112 char *next = memchr(data, '\n', len);
112 char *next = memchr(data, '\n', len);
113 if (!next) {
113 if (!next) {
114 return MANIFEST_MALFORMED;
114 return MANIFEST_MALFORMED;
115 }
115 }
116 next++; /* advance past newline */
116 next++; /* advance past newline */
117 if (!realloc_if_full(self)) {
117 if (!realloc_if_full(self)) {
118 return MANIFEST_OOM; /* no memory */
118 return MANIFEST_OOM; /* no memory */
119 }
119 }
120 if (prev && strcmp(prev, data) > -1) {
120 if (prev && strcmp(prev, data) > -1) {
121 /* This data isn't sorted, so we have to abort. */
121 /* This data isn't sorted, so we have to abort. */
122 return MANIFEST_NOT_SORTED;
122 return MANIFEST_NOT_SORTED;
123 }
123 }
124 l = self->lines + ((self->numlines)++);
124 l = self->lines + ((self->numlines)++);
125 l->start = data;
125 l->start = data;
126 l->len = next - data;
126 l->len = next - data;
127 l->hash_suffix = '\0';
127 l->hash_suffix = '\0';
128 l->from_malloc = false;
128 l->from_malloc = false;
129 l->deleted = false;
129 l->deleted = false;
130 len = len - l->len;
130 len = len - l->len;
131 prev = data;
131 prev = data;
132 data = next;
132 data = next;
133 }
133 }
134 self->livelines = self->numlines;
134 self->livelines = self->numlines;
135 return 0;
135 return 0;
136 }
136 }
137
137
138 static int lazymanifest_init(lazymanifest *self, PyObject *args)
138 static int lazymanifest_init(lazymanifest *self, PyObject *args)
139 {
139 {
140 char *data;
140 char *data;
141 Py_ssize_t len;
141 Py_ssize_t len;
142 int err, ret;
142 int err, ret;
143 PyObject *pydata;
143 PyObject *pydata;
144 if (!PyArg_ParseTuple(args, "S", &pydata)) {
144 if (!PyArg_ParseTuple(args, "S", &pydata)) {
145 return -1;
145 return -1;
146 }
146 }
147 err = PyBytes_AsStringAndSize(pydata, &data, &len);
147 err = PyBytes_AsStringAndSize(pydata, &data, &len);
148
148
149 self->dirty = false;
149 self->dirty = false;
150 if (err == -1)
150 if (err == -1)
151 return -1;
151 return -1;
152 self->pydata = pydata;
152 self->pydata = pydata;
153 Py_INCREF(self->pydata);
153 Py_INCREF(self->pydata);
154 Py_BEGIN_ALLOW_THREADS
154 Py_BEGIN_ALLOW_THREADS
155 self->lines = malloc(DEFAULT_LINES * sizeof(line));
155 self->lines = malloc(DEFAULT_LINES * sizeof(line));
156 self->maxlines = DEFAULT_LINES;
156 self->maxlines = DEFAULT_LINES;
157 self->numlines = 0;
157 self->numlines = 0;
158 if (!self->lines)
158 if (!self->lines)
159 ret = MANIFEST_OOM;
159 ret = MANIFEST_OOM;
160 else
160 else
161 ret = find_lines(self, data, len);
161 ret = find_lines(self, data, len);
162 Py_END_ALLOW_THREADS
162 Py_END_ALLOW_THREADS
163 switch (ret) {
163 switch (ret) {
164 case 0:
164 case 0:
165 break;
165 break;
166 case MANIFEST_OOM:
166 case MANIFEST_OOM:
167 PyErr_NoMemory();
167 PyErr_NoMemory();
168 break;
168 break;
169 case MANIFEST_NOT_SORTED:
169 case MANIFEST_NOT_SORTED:
170 PyErr_Format(PyExc_ValueError,
170 PyErr_Format(PyExc_ValueError,
171 "Manifest lines not in sorted order.");
171 "Manifest lines not in sorted order.");
172 break;
172 break;
173 case MANIFEST_MALFORMED:
173 case MANIFEST_MALFORMED:
174 PyErr_Format(PyExc_ValueError,
174 PyErr_Format(PyExc_ValueError,
175 "Manifest did not end in a newline.");
175 "Manifest did not end in a newline.");
176 break;
176 break;
177 default:
177 default:
178 PyErr_Format(PyExc_ValueError,
178 PyErr_Format(PyExc_ValueError,
179 "Unknown problem parsing manifest.");
179 "Unknown problem parsing manifest.");
180 }
180 }
181 return ret == 0 ? 0 : -1;
181 return ret == 0 ? 0 : -1;
182 }
182 }
183
183
184 static void lazymanifest_dealloc(lazymanifest *self)
184 static void lazymanifest_dealloc(lazymanifest *self)
185 {
185 {
186 /* free any extra lines we had to allocate */
186 /* free any extra lines we had to allocate */
187 int i;
187 int i;
188 for (i = 0; i < self->numlines; i++) {
188 for (i = 0; i < self->numlines; i++) {
189 if (self->lines[i].from_malloc) {
189 if (self->lines[i].from_malloc) {
190 free(self->lines[i].start);
190 free(self->lines[i].start);
191 }
191 }
192 }
192 }
193 if (self->lines) {
193 if (self->lines) {
194 free(self->lines);
194 free(self->lines);
195 self->lines = NULL;
195 self->lines = NULL;
196 }
196 }
197 if (self->pydata) {
197 if (self->pydata) {
198 Py_DECREF(self->pydata);
198 Py_DECREF(self->pydata);
199 self->pydata = NULL;
199 self->pydata = NULL;
200 }
200 }
201 PyObject_Del(self);
201 PyObject_Del(self);
202 }
202 }
203
203
204 /* iteration support */
204 /* iteration support */
205
205
206 typedef struct {
206 typedef struct {
207 PyObject_HEAD lazymanifest *m;
207 PyObject_HEAD lazymanifest *m;
208 Py_ssize_t pos;
208 Py_ssize_t pos;
209 } lmIter;
209 } lmIter;
210
210
211 static void lmiter_dealloc(PyObject *o)
211 static void lmiter_dealloc(PyObject *o)
212 {
212 {
213 lmIter *self = (lmIter *)o;
213 lmIter *self = (lmIter *)o;
214 Py_DECREF(self->m);
214 Py_DECREF(self->m);
215 PyObject_Del(self);
215 PyObject_Del(self);
216 }
216 }
217
217
218 static line *lmiter_nextline(lmIter *self)
218 static line *lmiter_nextline(lmIter *self)
219 {
219 {
220 do {
220 do {
221 self->pos++;
221 self->pos++;
222 if (self->pos >= self->m->numlines) {
222 if (self->pos >= self->m->numlines) {
223 return NULL;
223 return NULL;
224 }
224 }
225 /* skip over deleted manifest entries */
225 /* skip over deleted manifest entries */
226 } while (self->m->lines[self->pos].deleted);
226 } while (self->m->lines[self->pos].deleted);
227 return self->m->lines + self->pos;
227 return self->m->lines + self->pos;
228 }
228 }
229
229
230 static PyObject *lmiter_iterentriesnext(PyObject *o)
230 static PyObject *lmiter_iterentriesnext(PyObject *o)
231 {
231 {
232 size_t pl;
232 size_t pl;
233 line *l;
233 line *l;
234 Py_ssize_t consumed;
234 Py_ssize_t consumed;
235 PyObject *ret = NULL, *path = NULL, *hash = NULL, *flags = NULL;
235 PyObject *ret = NULL, *path = NULL, *hash = NULL, *flags = NULL;
236 l = lmiter_nextline((lmIter *)o);
236 l = lmiter_nextline((lmIter *)o);
237 if (!l) {
237 if (!l) {
238 goto done;
238 goto done;
239 }
239 }
240 pl = pathlen(l);
240 pl = pathlen(l);
241 path = PyBytes_FromStringAndSize(l->start, pl);
241 path = PyBytes_FromStringAndSize(l->start, pl);
242 hash = nodeof(l);
242 hash = nodeof(l);
243 consumed = pl + 41;
243 consumed = pl + 41;
244 flags = PyBytes_FromStringAndSize(l->start + consumed,
244 flags = PyBytes_FromStringAndSize(l->start + consumed,
245 l->len - consumed - 1);
245 l->len - consumed - 1);
246 if (!path || !hash || !flags) {
246 if (!path || !hash || !flags) {
247 goto done;
247 goto done;
248 }
248 }
249 ret = PyTuple_Pack(3, path, hash, flags);
249 ret = PyTuple_Pack(3, path, hash, flags);
250 done:
250 done:
251 Py_XDECREF(path);
251 Py_XDECREF(path);
252 Py_XDECREF(hash);
252 Py_XDECREF(hash);
253 Py_XDECREF(flags);
253 Py_XDECREF(flags);
254 return ret;
254 return ret;
255 }
255 }
256
256
257 #ifdef IS_PY3K
257 #ifdef IS_PY3K
258 #define LAZYMANIFESTENTRIESITERATOR_TPFLAGS Py_TPFLAGS_DEFAULT
258 #define LAZYMANIFESTENTRIESITERATOR_TPFLAGS Py_TPFLAGS_DEFAULT
259 #else
259 #else
260 #define LAZYMANIFESTENTRIESITERATOR_TPFLAGS Py_TPFLAGS_DEFAULT \
260 #define LAZYMANIFESTENTRIESITERATOR_TPFLAGS Py_TPFLAGS_DEFAULT \
261 | Py_TPFLAGS_HAVE_ITER
261 | Py_TPFLAGS_HAVE_ITER
262 #endif
262 #endif
263
263
264 static PyTypeObject lazymanifestEntriesIterator = {
264 static PyTypeObject lazymanifestEntriesIterator = {
265 PyVarObject_HEAD_INIT(NULL, 0)
265 PyVarObject_HEAD_INIT(NULL, 0)
266 "parsers.lazymanifest.entriesiterator", /*tp_name */
266 "parsers.lazymanifest.entriesiterator", /*tp_name */
267 sizeof(lmIter), /*tp_basicsize */
267 sizeof(lmIter), /*tp_basicsize */
268 0, /*tp_itemsize */
268 0, /*tp_itemsize */
269 lmiter_dealloc, /*tp_dealloc */
269 lmiter_dealloc, /*tp_dealloc */
270 0, /*tp_print */
270 0, /*tp_print */
271 0, /*tp_getattr */
271 0, /*tp_getattr */
272 0, /*tp_setattr */
272 0, /*tp_setattr */
273 0, /*tp_compare */
273 0, /*tp_compare */
274 0, /*tp_repr */
274 0, /*tp_repr */
275 0, /*tp_as_number */
275 0, /*tp_as_number */
276 0, /*tp_as_sequence */
276 0, /*tp_as_sequence */
277 0, /*tp_as_mapping */
277 0, /*tp_as_mapping */
278 0, /*tp_hash */
278 0, /*tp_hash */
279 0, /*tp_call */
279 0, /*tp_call */
280 0, /*tp_str */
280 0, /*tp_str */
281 0, /*tp_getattro */
281 0, /*tp_getattro */
282 0, /*tp_setattro */
282 0, /*tp_setattro */
283 0, /*tp_as_buffer */
283 0, /*tp_as_buffer */
284 LAZYMANIFESTENTRIESITERATOR_TPFLAGS, /* tp_flags */
284 LAZYMANIFESTENTRIESITERATOR_TPFLAGS, /* tp_flags */
285 "Iterator for 3-tuples in a lazymanifest.", /* tp_doc */
285 "Iterator for 3-tuples in a lazymanifest.", /* tp_doc */
286 0, /* tp_traverse */
286 0, /* tp_traverse */
287 0, /* tp_clear */
287 0, /* tp_clear */
288 0, /* tp_richcompare */
288 0, /* tp_richcompare */
289 0, /* tp_weaklistoffset */
289 0, /* tp_weaklistoffset */
290 PyObject_SelfIter, /* tp_iter: __iter__() method */
290 PyObject_SelfIter, /* tp_iter: __iter__() method */
291 lmiter_iterentriesnext, /* tp_iternext: next() method */
291 lmiter_iterentriesnext, /* tp_iternext: next() method */
292 };
292 };
293
293
294 static PyObject *lmiter_iterkeysnext(PyObject *o)
294 static PyObject *lmiter_iterkeysnext(PyObject *o)
295 {
295 {
296 size_t pl;
296 size_t pl;
297 line *l = lmiter_nextline((lmIter *)o);
297 line *l = lmiter_nextline((lmIter *)o);
298 if (!l) {
298 if (!l) {
299 return NULL;
299 return NULL;
300 }
300 }
301 pl = pathlen(l);
301 pl = pathlen(l);
302 return PyBytes_FromStringAndSize(l->start, pl);
302 return PyBytes_FromStringAndSize(l->start, pl);
303 }
303 }
304
304
305 #ifdef IS_PY3K
305 #ifdef IS_PY3K
306 #define LAZYMANIFESTKEYSITERATOR_TPFLAGS Py_TPFLAGS_DEFAULT
306 #define LAZYMANIFESTKEYSITERATOR_TPFLAGS Py_TPFLAGS_DEFAULT
307 #else
307 #else
308 #define LAZYMANIFESTKEYSITERATOR_TPFLAGS Py_TPFLAGS_DEFAULT \
308 #define LAZYMANIFESTKEYSITERATOR_TPFLAGS Py_TPFLAGS_DEFAULT \
309 | Py_TPFLAGS_HAVE_ITER
309 | Py_TPFLAGS_HAVE_ITER
310 #endif
310 #endif
311
311
312 static PyTypeObject lazymanifestKeysIterator = {
312 static PyTypeObject lazymanifestKeysIterator = {
313 PyVarObject_HEAD_INIT(NULL, 0)
313 PyVarObject_HEAD_INIT(NULL, 0)
314 "parsers.lazymanifest.keysiterator", /*tp_name */
314 "parsers.lazymanifest.keysiterator", /*tp_name */
315 sizeof(lmIter), /*tp_basicsize */
315 sizeof(lmIter), /*tp_basicsize */
316 0, /*tp_itemsize */
316 0, /*tp_itemsize */
317 lmiter_dealloc, /*tp_dealloc */
317 lmiter_dealloc, /*tp_dealloc */
318 0, /*tp_print */
318 0, /*tp_print */
319 0, /*tp_getattr */
319 0, /*tp_getattr */
320 0, /*tp_setattr */
320 0, /*tp_setattr */
321 0, /*tp_compare */
321 0, /*tp_compare */
322 0, /*tp_repr */
322 0, /*tp_repr */
323 0, /*tp_as_number */
323 0, /*tp_as_number */
324 0, /*tp_as_sequence */
324 0, /*tp_as_sequence */
325 0, /*tp_as_mapping */
325 0, /*tp_as_mapping */
326 0, /*tp_hash */
326 0, /*tp_hash */
327 0, /*tp_call */
327 0, /*tp_call */
328 0, /*tp_str */
328 0, /*tp_str */
329 0, /*tp_getattro */
329 0, /*tp_getattro */
330 0, /*tp_setattro */
330 0, /*tp_setattro */
331 0, /*tp_as_buffer */
331 0, /*tp_as_buffer */
332 LAZYMANIFESTKEYSITERATOR_TPFLAGS, /* tp_flags */
332 LAZYMANIFESTKEYSITERATOR_TPFLAGS, /* tp_flags */
333 "Keys iterator for a lazymanifest.", /* tp_doc */
333 "Keys iterator for a lazymanifest.", /* tp_doc */
334 0, /* tp_traverse */
334 0, /* tp_traverse */
335 0, /* tp_clear */
335 0, /* tp_clear */
336 0, /* tp_richcompare */
336 0, /* tp_richcompare */
337 0, /* tp_weaklistoffset */
337 0, /* tp_weaklistoffset */
338 PyObject_SelfIter, /* tp_iter: __iter__() method */
338 PyObject_SelfIter, /* tp_iter: __iter__() method */
339 lmiter_iterkeysnext, /* tp_iternext: next() method */
339 lmiter_iterkeysnext, /* tp_iternext: next() method */
340 };
340 };
341
341
342 static lazymanifest *lazymanifest_copy(lazymanifest *self);
342 static lazymanifest *lazymanifest_copy(lazymanifest *self);
343
343
344 static PyObject *lazymanifest_getentriesiter(lazymanifest *self)
344 static PyObject *lazymanifest_getentriesiter(lazymanifest *self)
345 {
345 {
346 lmIter *i = NULL;
346 lmIter *i = NULL;
347 lazymanifest *t = lazymanifest_copy(self);
347 lazymanifest *t = lazymanifest_copy(self);
348 if (!t) {
348 if (!t) {
349 PyErr_NoMemory();
349 PyErr_NoMemory();
350 return NULL;
350 return NULL;
351 }
351 }
352 i = PyObject_New(lmIter, &lazymanifestEntriesIterator);
352 i = PyObject_New(lmIter, &lazymanifestEntriesIterator);
353 if (i) {
353 if (i) {
354 i->m = t;
354 i->m = t;
355 i->pos = -1;
355 i->pos = -1;
356 } else {
356 } else {
357 Py_DECREF(t);
357 Py_DECREF(t);
358 PyErr_NoMemory();
358 PyErr_NoMemory();
359 }
359 }
360 return (PyObject *)i;
360 return (PyObject *)i;
361 }
361 }
362
362
363 static PyObject *lazymanifest_getkeysiter(lazymanifest *self)
363 static PyObject *lazymanifest_getkeysiter(lazymanifest *self)
364 {
364 {
365 lmIter *i = NULL;
365 lmIter *i = NULL;
366 lazymanifest *t = lazymanifest_copy(self);
366 lazymanifest *t = lazymanifest_copy(self);
367 if (!t) {
367 if (!t) {
368 PyErr_NoMemory();
368 PyErr_NoMemory();
369 return NULL;
369 return NULL;
370 }
370 }
371 i = PyObject_New(lmIter, &lazymanifestKeysIterator);
371 i = PyObject_New(lmIter, &lazymanifestKeysIterator);
372 if (i) {
372 if (i) {
373 i->m = t;
373 i->m = t;
374 i->pos = -1;
374 i->pos = -1;
375 } else {
375 } else {
376 Py_DECREF(t);
376 Py_DECREF(t);
377 PyErr_NoMemory();
377 PyErr_NoMemory();
378 }
378 }
379 return (PyObject *)i;
379 return (PyObject *)i;
380 }
380 }
381
381
382 /* __getitem__ and __setitem__ support */
382 /* __getitem__ and __setitem__ support */
383
383
384 static Py_ssize_t lazymanifest_size(lazymanifest *self)
384 static Py_ssize_t lazymanifest_size(lazymanifest *self)
385 {
385 {
386 return self->livelines;
386 return self->livelines;
387 }
387 }
388
388
389 static int linecmp(const void *left, const void *right)
389 static int linecmp(const void *left, const void *right)
390 {
390 {
391 return strcmp(((const line *)left)->start,
391 return strcmp(((const line *)left)->start,
392 ((const line *)right)->start);
392 ((const line *)right)->start);
393 }
393 }
394
394
395 static PyObject *lazymanifest_getitem(lazymanifest *self, PyObject *key)
395 static PyObject *lazymanifest_getitem(lazymanifest *self, PyObject *key)
396 {
396 {
397 line needle;
397 line needle;
398 line *hit;
398 line *hit;
399 if (!PyBytes_Check(key)) {
399 if (!PyBytes_Check(key)) {
400 PyErr_Format(PyExc_TypeError,
400 PyErr_Format(PyExc_TypeError,
401 "getitem: manifest keys must be a string.");
401 "getitem: manifest keys must be a string.");
402 return NULL;
402 return NULL;
403 }
403 }
404 needle.start = PyBytes_AsString(key);
404 needle.start = PyBytes_AsString(key);
405 hit = bsearch(&needle, self->lines, self->numlines, sizeof(line),
405 hit = bsearch(&needle, self->lines, self->numlines, sizeof(line),
406 &linecmp);
406 &linecmp);
407 if (!hit || hit->deleted) {
407 if (!hit || hit->deleted) {
408 PyErr_Format(PyExc_KeyError, "No such manifest entry.");
408 PyErr_Format(PyExc_KeyError, "No such manifest entry.");
409 return NULL;
409 return NULL;
410 }
410 }
411 return hashflags(hit);
411 return hashflags(hit);
412 }
412 }
413
413
414 static int lazymanifest_delitem(lazymanifest *self, PyObject *key)
414 static int lazymanifest_delitem(lazymanifest *self, PyObject *key)
415 {
415 {
416 line needle;
416 line needle;
417 line *hit;
417 line *hit;
418 if (!PyBytes_Check(key)) {
418 if (!PyBytes_Check(key)) {
419 PyErr_Format(PyExc_TypeError,
419 PyErr_Format(PyExc_TypeError,
420 "delitem: manifest keys must be a string.");
420 "delitem: manifest keys must be a string.");
421 return -1;
421 return -1;
422 }
422 }
423 needle.start = PyBytes_AsString(key);
423 needle.start = PyBytes_AsString(key);
424 hit = bsearch(&needle, self->lines, self->numlines, sizeof(line),
424 hit = bsearch(&needle, self->lines, self->numlines, sizeof(line),
425 &linecmp);
425 &linecmp);
426 if (!hit || hit->deleted) {
426 if (!hit || hit->deleted) {
427 PyErr_Format(PyExc_KeyError,
427 PyErr_Format(PyExc_KeyError,
428 "Tried to delete nonexistent manifest entry.");
428 "Tried to delete nonexistent manifest entry.");
429 return -1;
429 return -1;
430 }
430 }
431 self->dirty = true;
431 self->dirty = true;
432 hit->deleted = true;
432 hit->deleted = true;
433 self->livelines--;
433 self->livelines--;
434 return 0;
434 return 0;
435 }
435 }
436
436
437 /* Do a binary search for the insertion point for new, creating the
437 /* Do a binary search for the insertion point for new, creating the
438 * new entry if needed. */
438 * new entry if needed. */
439 static int internalsetitem(lazymanifest *self, line *new) {
439 static int internalsetitem(lazymanifest *self, line *new) {
440 int start = 0, end = self->numlines;
440 int start = 0, end = self->numlines;
441 while (start < end) {
441 while (start < end) {
442 int pos = start + (end - start) / 2;
442 int pos = start + (end - start) / 2;
443 int c = linecmp(new, self->lines + pos);
443 int c = linecmp(new, self->lines + pos);
444 if (c < 0)
444 if (c < 0)
445 end = pos;
445 end = pos;
446 else if (c > 0)
446 else if (c > 0)
447 start = pos + 1;
447 start = pos + 1;
448 else {
448 else {
449 if (self->lines[pos].deleted)
449 if (self->lines[pos].deleted)
450 self->livelines++;
450 self->livelines++;
451 if (self->lines[pos].from_malloc)
451 if (self->lines[pos].from_malloc)
452 free(self->lines[pos].start);
452 free(self->lines[pos].start);
453 start = pos;
453 start = pos;
454 goto finish;
454 goto finish;
455 }
455 }
456 }
456 }
457 /* being here means we need to do an insert */
457 /* being here means we need to do an insert */
458 if (!realloc_if_full(self)) {
458 if (!realloc_if_full(self)) {
459 PyErr_NoMemory();
459 PyErr_NoMemory();
460 return -1;
460 return -1;
461 }
461 }
462 memmove(self->lines + start + 1, self->lines + start,
462 memmove(self->lines + start + 1, self->lines + start,
463 (self->numlines - start) * sizeof(line));
463 (self->numlines - start) * sizeof(line));
464 self->numlines++;
464 self->numlines++;
465 self->livelines++;
465 self->livelines++;
466 finish:
466 finish:
467 self->lines[start] = *new;
467 self->lines[start] = *new;
468 self->dirty = true;
468 self->dirty = true;
469 return 0;
469 return 0;
470 }
470 }
471
471
472 static int lazymanifest_setitem(
472 static int lazymanifest_setitem(
473 lazymanifest *self, PyObject *key, PyObject *value)
473 lazymanifest *self, PyObject *key, PyObject *value)
474 {
474 {
475 char *path;
475 char *path;
476 Py_ssize_t plen;
476 Py_ssize_t plen;
477 PyObject *pyhash;
477 PyObject *pyhash;
478 Py_ssize_t hlen;
478 Py_ssize_t hlen;
479 char *hash;
479 char *hash;
480 PyObject *pyflags;
480 PyObject *pyflags;
481 char *flags;
481 char *flags;
482 Py_ssize_t flen;
482 Py_ssize_t flen;
483 size_t dlen;
483 size_t dlen;
484 char *dest;
484 char *dest;
485 int i;
485 int i;
486 line new;
486 line new;
487 if (!PyBytes_Check(key)) {
487 if (!PyBytes_Check(key)) {
488 PyErr_Format(PyExc_TypeError,
488 PyErr_Format(PyExc_TypeError,
489 "setitem: manifest keys must be a string.");
489 "setitem: manifest keys must be a string.");
490 return -1;
490 return -1;
491 }
491 }
492 if (!value) {
492 if (!value) {
493 return lazymanifest_delitem(self, key);
493 return lazymanifest_delitem(self, key);
494 }
494 }
495 if (!PyTuple_Check(value) || PyTuple_Size(value) != 2) {
495 if (!PyTuple_Check(value) || PyTuple_Size(value) != 2) {
496 PyErr_Format(PyExc_TypeError,
496 PyErr_Format(PyExc_TypeError,
497 "Manifest values must be a tuple of (node, flags).");
497 "Manifest values must be a tuple of (node, flags).");
498 return -1;
498 return -1;
499 }
499 }
500 if (PyBytes_AsStringAndSize(key, &path, &plen) == -1) {
500 if (PyBytes_AsStringAndSize(key, &path, &plen) == -1) {
501 return -1;
501 return -1;
502 }
502 }
503
503
504 pyhash = PyTuple_GetItem(value, 0);
504 pyhash = PyTuple_GetItem(value, 0);
505 if (!PyBytes_Check(pyhash)) {
505 if (!PyBytes_Check(pyhash)) {
506 PyErr_Format(PyExc_TypeError,
506 PyErr_Format(PyExc_TypeError,
507 "node must be a 20-byte string");
507 "node must be a 20-byte string");
508 return -1;
508 return -1;
509 }
509 }
510 hlen = PyBytes_Size(pyhash);
510 hlen = PyBytes_Size(pyhash);
511 /* Some parts of the codebase try and set 21 or 22
511 /* Some parts of the codebase try and set 21 or 22
512 * byte "hash" values in order to perturb things for
512 * byte "hash" values in order to perturb things for
513 * status. We have to preserve at least the 21st
513 * status. We have to preserve at least the 21st
514 * byte. Sigh. If there's a 22nd byte, we drop it on
514 * byte. Sigh. If there's a 22nd byte, we drop it on
515 * the floor, which works fine.
515 * the floor, which works fine.
516 */
516 */
517 if (hlen != 20 && hlen != 21 && hlen != 22) {
517 if (hlen != 20 && hlen != 21 && hlen != 22) {
518 PyErr_Format(PyExc_TypeError,
518 PyErr_Format(PyExc_TypeError,
519 "node must be a 20-byte string");
519 "node must be a 20-byte string");
520 return -1;
520 return -1;
521 }
521 }
522 hash = PyBytes_AsString(pyhash);
522 hash = PyBytes_AsString(pyhash);
523
523
524 pyflags = PyTuple_GetItem(value, 1);
524 pyflags = PyTuple_GetItem(value, 1);
525 if (!PyBytes_Check(pyflags) || PyBytes_Size(pyflags) > 1) {
525 if (!PyBytes_Check(pyflags) || PyBytes_Size(pyflags) > 1) {
526 PyErr_Format(PyExc_TypeError,
526 PyErr_Format(PyExc_TypeError,
527 "flags must a 0 or 1 byte string");
527 "flags must a 0 or 1 byte string");
528 return -1;
528 return -1;
529 }
529 }
530 if (PyBytes_AsStringAndSize(pyflags, &flags, &flen) == -1) {
530 if (PyBytes_AsStringAndSize(pyflags, &flags, &flen) == -1) {
531 return -1;
531 return -1;
532 }
532 }
533 /* one null byte and one newline */
533 /* one null byte and one newline */
534 dlen = plen + 41 + flen + 1;
534 dlen = plen + 41 + flen + 1;
535 dest = malloc(dlen);
535 dest = malloc(dlen);
536 if (!dest) {
536 if (!dest) {
537 PyErr_NoMemory();
537 PyErr_NoMemory();
538 return -1;
538 return -1;
539 }
539 }
540 memcpy(dest, path, plen + 1);
540 memcpy(dest, path, plen + 1);
541 for (i = 0; i < 20; i++) {
541 for (i = 0; i < 20; i++) {
542 /* Cast to unsigned, so it will not get sign-extended when promoted
542 /* Cast to unsigned, so it will not get sign-extended when promoted
543 * to int (as is done when passing to a variadic function)
543 * to int (as is done when passing to a variadic function)
544 */
544 */
545 sprintf(dest + plen + 1 + (i * 2), "%02x", (unsigned char)hash[i]);
545 sprintf(dest + plen + 1 + (i * 2), "%02x", (unsigned char)hash[i]);
546 }
546 }
547 memcpy(dest + plen + 41, flags, flen);
547 memcpy(dest + plen + 41, flags, flen);
548 dest[plen + 41 + flen] = '\n';
548 dest[plen + 41 + flen] = '\n';
549 new.start = dest;
549 new.start = dest;
550 new.len = dlen;
550 new.len = dlen;
551 new.hash_suffix = '\0';
551 new.hash_suffix = '\0';
552 if (hlen > 20) {
552 if (hlen > 20) {
553 new.hash_suffix = hash[20];
553 new.hash_suffix = hash[20];
554 }
554 }
555 new.from_malloc = true; /* is `start` a pointer we allocated? */
555 new.from_malloc = true; /* is `start` a pointer we allocated? */
556 new.deleted = false; /* is this entry deleted? */
556 new.deleted = false; /* is this entry deleted? */
557 if (internalsetitem(self, &new)) {
557 if (internalsetitem(self, &new)) {
558 return -1;
558 return -1;
559 }
559 }
560 return 0;
560 return 0;
561 }
561 }
562
562
563 static PyMappingMethods lazymanifest_mapping_methods = {
563 static PyMappingMethods lazymanifest_mapping_methods = {
564 (lenfunc)lazymanifest_size, /* mp_length */
564 (lenfunc)lazymanifest_size, /* mp_length */
565 (binaryfunc)lazymanifest_getitem, /* mp_subscript */
565 (binaryfunc)lazymanifest_getitem, /* mp_subscript */
566 (objobjargproc)lazymanifest_setitem, /* mp_ass_subscript */
566 (objobjargproc)lazymanifest_setitem, /* mp_ass_subscript */
567 };
567 };
568
568
569 /* sequence methods (important or __contains__ builds an iterator) */
569 /* sequence methods (important or __contains__ builds an iterator) */
570
570
571 static int lazymanifest_contains(lazymanifest *self, PyObject *key)
571 static int lazymanifest_contains(lazymanifest *self, PyObject *key)
572 {
572 {
573 line needle;
573 line needle;
574 line *hit;
574 line *hit;
575 if (!PyBytes_Check(key)) {
575 if (!PyBytes_Check(key)) {
576 /* Our keys are always strings, so if the contains
576 /* Our keys are always strings, so if the contains
577 * check is for a non-string, just return false. */
577 * check is for a non-string, just return false. */
578 return 0;
578 return 0;
579 }
579 }
580 needle.start = PyBytes_AsString(key);
580 needle.start = PyBytes_AsString(key);
581 hit = bsearch(&needle, self->lines, self->numlines, sizeof(line),
581 hit = bsearch(&needle, self->lines, self->numlines, sizeof(line),
582 &linecmp);
582 &linecmp);
583 if (!hit || hit->deleted) {
583 if (!hit || hit->deleted) {
584 return 0;
584 return 0;
585 }
585 }
586 return 1;
586 return 1;
587 }
587 }
588
588
589 static PySequenceMethods lazymanifest_seq_meths = {
589 static PySequenceMethods lazymanifest_seq_meths = {
590 (lenfunc)lazymanifest_size, /* sq_length */
590 (lenfunc)lazymanifest_size, /* sq_length */
591 0, /* sq_concat */
591 0, /* sq_concat */
592 0, /* sq_repeat */
592 0, /* sq_repeat */
593 0, /* sq_item */
593 0, /* sq_item */
594 0, /* sq_slice */
594 0, /* sq_slice */
595 0, /* sq_ass_item */
595 0, /* sq_ass_item */
596 0, /* sq_ass_slice */
596 0, /* sq_ass_slice */
597 (objobjproc)lazymanifest_contains, /* sq_contains */
597 (objobjproc)lazymanifest_contains, /* sq_contains */
598 0, /* sq_inplace_concat */
598 0, /* sq_inplace_concat */
599 0, /* sq_inplace_repeat */
599 0, /* sq_inplace_repeat */
600 };
600 };
601
601
602
602
603 /* Other methods (copy, diff, etc) */
603 /* Other methods (copy, diff, etc) */
604 static PyTypeObject lazymanifestType;
604 static PyTypeObject lazymanifestType;
605
605
606 /* If the manifest has changes, build the new manifest text and reindex it. */
606 /* If the manifest has changes, build the new manifest text and reindex it. */
607 static int compact(lazymanifest *self) {
607 static int compact(lazymanifest *self) {
608 int i;
608 int i;
609 ssize_t need = 0;
609 ssize_t need = 0;
610 char *data;
610 char *data;
611 line *src, *dst;
611 line *src, *dst;
612 PyObject *pydata;
612 PyObject *pydata;
613 if (!self->dirty)
613 if (!self->dirty)
614 return 0;
614 return 0;
615 for (i = 0; i < self->numlines; i++) {
615 for (i = 0; i < self->numlines; i++) {
616 if (!self->lines[i].deleted) {
616 if (!self->lines[i].deleted) {
617 need += self->lines[i].len;
617 need += self->lines[i].len;
618 }
618 }
619 }
619 }
620 pydata = PyBytes_FromStringAndSize(NULL, need);
620 pydata = PyBytes_FromStringAndSize(NULL, need);
621 if (!pydata)
621 if (!pydata)
622 return -1;
622 return -1;
623 data = PyBytes_AsString(pydata);
623 data = PyBytes_AsString(pydata);
624 if (!data) {
624 if (!data) {
625 return -1;
625 return -1;
626 }
626 }
627 src = self->lines;
627 src = self->lines;
628 dst = self->lines;
628 dst = self->lines;
629 for (i = 0; i < self->numlines; i++, src++) {
629 for (i = 0; i < self->numlines; i++, src++) {
630 char *tofree = NULL;
630 char *tofree = NULL;
631 if (src->from_malloc) {
631 if (src->from_malloc) {
632 tofree = src->start;
632 tofree = src->start;
633 }
633 }
634 if (!src->deleted) {
634 if (!src->deleted) {
635 memcpy(data, src->start, src->len);
635 memcpy(data, src->start, src->len);
636 *dst = *src;
636 *dst = *src;
637 dst->start = data;
637 dst->start = data;
638 dst->from_malloc = false;
638 dst->from_malloc = false;
639 data += dst->len;
639 data += dst->len;
640 dst++;
640 dst++;
641 }
641 }
642 free(tofree);
642 free(tofree);
643 }
643 }
644 Py_DECREF(self->pydata);
644 Py_DECREF(self->pydata);
645 self->pydata = pydata;
645 self->pydata = pydata;
646 self->numlines = self->livelines;
646 self->numlines = self->livelines;
647 self->dirty = false;
647 self->dirty = false;
648 return 0;
648 return 0;
649 }
649 }
650
650
651 static PyObject *lazymanifest_text(lazymanifest *self)
651 static PyObject *lazymanifest_text(lazymanifest *self)
652 {
652 {
653 if (compact(self) != 0) {
653 if (compact(self) != 0) {
654 PyErr_NoMemory();
654 PyErr_NoMemory();
655 return NULL;
655 return NULL;
656 }
656 }
657 Py_INCREF(self->pydata);
657 Py_INCREF(self->pydata);
658 return self->pydata;
658 return self->pydata;
659 }
659 }
660
660
661 static lazymanifest *lazymanifest_copy(lazymanifest *self)
661 static lazymanifest *lazymanifest_copy(lazymanifest *self)
662 {
662 {
663 lazymanifest *copy = NULL;
663 lazymanifest *copy = NULL;
664 if (compact(self) != 0) {
664 if (compact(self) != 0) {
665 goto nomem;
665 goto nomem;
666 }
666 }
667 copy = PyObject_New(lazymanifest, &lazymanifestType);
667 copy = PyObject_New(lazymanifest, &lazymanifestType);
668 if (!copy) {
668 if (!copy) {
669 goto nomem;
669 goto nomem;
670 }
670 }
671 copy->numlines = self->numlines;
671 copy->numlines = self->numlines;
672 copy->livelines = self->livelines;
672 copy->livelines = self->livelines;
673 copy->dirty = false;
673 copy->dirty = false;
674 copy->lines = malloc(self->maxlines *sizeof(line));
674 copy->lines = malloc(self->maxlines *sizeof(line));
675 if (!copy->lines) {
675 if (!copy->lines) {
676 goto nomem;
676 goto nomem;
677 }
677 }
678 memcpy(copy->lines, self->lines, self->numlines * sizeof(line));
678 memcpy(copy->lines, self->lines, self->numlines * sizeof(line));
679 copy->maxlines = self->maxlines;
679 copy->maxlines = self->maxlines;
680 copy->pydata = self->pydata;
680 copy->pydata = self->pydata;
681 Py_INCREF(copy->pydata);
681 Py_INCREF(copy->pydata);
682 return copy;
682 return copy;
683 nomem:
683 nomem:
684 PyErr_NoMemory();
684 PyErr_NoMemory();
685 Py_XDECREF(copy);
685 Py_XDECREF(copy);
686 return NULL;
686 return NULL;
687 }
687 }
688
688
689 static lazymanifest *lazymanifest_filtercopy(
689 static lazymanifest *lazymanifest_filtercopy(
690 lazymanifest *self, PyObject *matchfn)
690 lazymanifest *self, PyObject *matchfn)
691 {
691 {
692 lazymanifest *copy = NULL;
692 lazymanifest *copy = NULL;
693 int i;
693 int i;
694 if (!PyCallable_Check(matchfn)) {
694 if (!PyCallable_Check(matchfn)) {
695 PyErr_SetString(PyExc_TypeError, "matchfn must be callable");
695 PyErr_SetString(PyExc_TypeError, "matchfn must be callable");
696 return NULL;
696 return NULL;
697 }
697 }
698 /* compact ourselves first to avoid double-frees later when we
698 /* compact ourselves first to avoid double-frees later when we
699 * compact tmp so that it doesn't have random pointers to our
699 * compact tmp so that it doesn't have random pointers to our
700 * underlying from_malloc-data (self->pydata is safe) */
700 * underlying from_malloc-data (self->pydata is safe) */
701 if (compact(self) != 0) {
701 if (compact(self) != 0) {
702 goto nomem;
702 goto nomem;
703 }
703 }
704 copy = PyObject_New(lazymanifest, &lazymanifestType);
704 copy = PyObject_New(lazymanifest, &lazymanifestType);
705 if (!copy) {
705 if (!copy) {
706 goto nomem;
706 goto nomem;
707 }
707 }
708 copy->dirty = true;
708 copy->dirty = true;
709 copy->lines = malloc(self->maxlines * sizeof(line));
709 copy->lines = malloc(self->maxlines * sizeof(line));
710 if (!copy->lines) {
710 if (!copy->lines) {
711 goto nomem;
711 goto nomem;
712 }
712 }
713 copy->maxlines = self->maxlines;
713 copy->maxlines = self->maxlines;
714 copy->numlines = 0;
714 copy->numlines = 0;
715 copy->pydata = self->pydata;
715 copy->pydata = self->pydata;
716 Py_INCREF(self->pydata);
716 Py_INCREF(self->pydata);
717 for (i = 0; i < self->numlines; i++) {
717 for (i = 0; i < self->numlines; i++) {
718 PyObject *arglist = NULL, *result = NULL;
718 PyObject *arglist = NULL, *result = NULL;
719 arglist = Py_BuildValue("(s)", self->lines[i].start);
719 arglist = Py_BuildValue("(s)", self->lines[i].start);
720 if (!arglist) {
720 if (!arglist) {
721 return NULL;
721 return NULL;
722 }
722 }
723 result = PyObject_CallObject(matchfn, arglist);
723 result = PyObject_CallObject(matchfn, arglist);
724 Py_DECREF(arglist);
724 Py_DECREF(arglist);
725 /* if the callback raised an exception, just let it
725 /* if the callback raised an exception, just let it
726 * through and give up */
726 * through and give up */
727 if (!result) {
727 if (!result) {
728 free(copy->lines);
728 free(copy->lines);
729 Py_DECREF(self->pydata);
729 Py_DECREF(self->pydata);
730 return NULL;
730 return NULL;
731 }
731 }
732 if (PyObject_IsTrue(result)) {
732 if (PyObject_IsTrue(result)) {
733 assert(!(self->lines[i].from_malloc));
733 assert(!(self->lines[i].from_malloc));
734 copy->lines[copy->numlines++] = self->lines[i];
734 copy->lines[copy->numlines++] = self->lines[i];
735 }
735 }
736 Py_DECREF(result);
736 Py_DECREF(result);
737 }
737 }
738 copy->livelines = copy->numlines;
738 copy->livelines = copy->numlines;
739 return copy;
739 return copy;
740 nomem:
740 nomem:
741 PyErr_NoMemory();
741 PyErr_NoMemory();
742 Py_XDECREF(copy);
742 Py_XDECREF(copy);
743 return NULL;
743 return NULL;
744 }
744 }
745
745
746 static PyObject *lazymanifest_diff(lazymanifest *self, PyObject *args)
746 static PyObject *lazymanifest_diff(lazymanifest *self, PyObject *args)
747 {
747 {
748 lazymanifest *other;
748 lazymanifest *other;
749 PyObject *pyclean = NULL;
749 PyObject *pyclean = NULL;
750 bool listclean;
750 bool listclean;
751 PyObject *emptyTup = NULL, *ret = NULL;
751 PyObject *emptyTup = NULL, *ret = NULL;
752 PyObject *es;
752 PyObject *es;
753 int sneedle = 0, oneedle = 0;
753 int sneedle = 0, oneedle = 0;
754 if (!PyArg_ParseTuple(args, "O!|O", &lazymanifestType, &other, &pyclean)) {
754 if (!PyArg_ParseTuple(args, "O!|O", &lazymanifestType, &other, &pyclean)) {
755 return NULL;
755 return NULL;
756 }
756 }
757 listclean = (!pyclean) ? false : PyObject_IsTrue(pyclean);
757 listclean = (!pyclean) ? false : PyObject_IsTrue(pyclean);
758 es = PyBytes_FromString("");
758 es = PyBytes_FromString("");
759 if (!es) {
759 if (!es) {
760 goto nomem;
760 goto nomem;
761 }
761 }
762 emptyTup = PyTuple_Pack(2, Py_None, es);
762 emptyTup = PyTuple_Pack(2, Py_None, es);
763 Py_DECREF(es);
763 Py_DECREF(es);
764 if (!emptyTup) {
764 if (!emptyTup) {
765 goto nomem;
765 goto nomem;
766 }
766 }
767 ret = PyDict_New();
767 ret = PyDict_New();
768 if (!ret) {
768 if (!ret) {
769 goto nomem;
769 goto nomem;
770 }
770 }
771 while (sneedle != self->numlines || oneedle != other->numlines) {
771 while (sneedle != self->numlines || oneedle != other->numlines) {
772 line *left = self->lines + sneedle;
772 line *left = self->lines + sneedle;
773 line *right = other->lines + oneedle;
773 line *right = other->lines + oneedle;
774 int result;
774 int result;
775 PyObject *key;
775 PyObject *key;
776 PyObject *outer;
776 PyObject *outer;
777 /* If we're looking at a deleted entry and it's not
777 /* If we're looking at a deleted entry and it's not
778 * the end of the manifest, just skip it. */
778 * the end of the manifest, just skip it. */
779 if (left->deleted && sneedle < self->numlines) {
779 if (left->deleted && sneedle < self->numlines) {
780 sneedle++;
780 sneedle++;
781 continue;
781 continue;
782 }
782 }
783 if (right->deleted && oneedle < other->numlines) {
783 if (right->deleted && oneedle < other->numlines) {
784 oneedle++;
784 oneedle++;
785 continue;
785 continue;
786 }
786 }
787 /* if we're at the end of either manifest, then we
787 /* if we're at the end of either manifest, then we
788 * know the remaining items are adds so we can skip
788 * know the remaining items are adds so we can skip
789 * the strcmp. */
789 * the strcmp. */
790 if (sneedle == self->numlines) {
790 if (sneedle == self->numlines) {
791 result = 1;
791 result = 1;
792 } else if (oneedle == other->numlines) {
792 } else if (oneedle == other->numlines) {
793 result = -1;
793 result = -1;
794 } else {
794 } else {
795 result = linecmp(left, right);
795 result = linecmp(left, right);
796 }
796 }
797 key = result <= 0 ?
797 key = result <= 0 ?
798 PyBytes_FromString(left->start) :
798 PyBytes_FromString(left->start) :
799 PyBytes_FromString(right->start);
799 PyBytes_FromString(right->start);
800 if (!key)
800 if (!key)
801 goto nomem;
801 goto nomem;
802 if (result < 0) {
802 if (result < 0) {
803 PyObject *l = hashflags(left);
803 PyObject *l = hashflags(left);
804 if (!l) {
804 if (!l) {
805 goto nomem;
805 goto nomem;
806 }
806 }
807 outer = PyTuple_Pack(2, l, emptyTup);
807 outer = PyTuple_Pack(2, l, emptyTup);
808 Py_DECREF(l);
808 Py_DECREF(l);
809 if (!outer) {
809 if (!outer) {
810 goto nomem;
810 goto nomem;
811 }
811 }
812 PyDict_SetItem(ret, key, outer);
812 PyDict_SetItem(ret, key, outer);
813 Py_DECREF(outer);
813 Py_DECREF(outer);
814 sneedle++;
814 sneedle++;
815 } else if (result > 0) {
815 } else if (result > 0) {
816 PyObject *r = hashflags(right);
816 PyObject *r = hashflags(right);
817 if (!r) {
817 if (!r) {
818 goto nomem;
818 goto nomem;
819 }
819 }
820 outer = PyTuple_Pack(2, emptyTup, r);
820 outer = PyTuple_Pack(2, emptyTup, r);
821 Py_DECREF(r);
821 Py_DECREF(r);
822 if (!outer) {
822 if (!outer) {
823 goto nomem;
823 goto nomem;
824 }
824 }
825 PyDict_SetItem(ret, key, outer);
825 PyDict_SetItem(ret, key, outer);
826 Py_DECREF(outer);
826 Py_DECREF(outer);
827 oneedle++;
827 oneedle++;
828 } else {
828 } else {
829 /* file exists in both manifests */
829 /* file exists in both manifests */
830 if (left->len != right->len
830 if (left->len != right->len
831 || memcmp(left->start, right->start, left->len)
831 || memcmp(left->start, right->start, left->len)
832 || left->hash_suffix != right->hash_suffix) {
832 || left->hash_suffix != right->hash_suffix) {
833 PyObject *l = hashflags(left);
833 PyObject *l = hashflags(left);
834 PyObject *r;
834 PyObject *r;
835 if (!l) {
835 if (!l) {
836 goto nomem;
836 goto nomem;
837 }
837 }
838 r = hashflags(right);
838 r = hashflags(right);
839 if (!r) {
839 if (!r) {
840 Py_DECREF(l);
840 Py_DECREF(l);
841 goto nomem;
841 goto nomem;
842 }
842 }
843 outer = PyTuple_Pack(2, l, r);
843 outer = PyTuple_Pack(2, l, r);
844 Py_DECREF(l);
844 Py_DECREF(l);
845 Py_DECREF(r);
845 Py_DECREF(r);
846 if (!outer) {
846 if (!outer) {
847 goto nomem;
847 goto nomem;
848 }
848 }
849 PyDict_SetItem(ret, key, outer);
849 PyDict_SetItem(ret, key, outer);
850 Py_DECREF(outer);
850 Py_DECREF(outer);
851 } else if (listclean) {
851 } else if (listclean) {
852 PyDict_SetItem(ret, key, Py_None);
852 PyDict_SetItem(ret, key, Py_None);
853 }
853 }
854 sneedle++;
854 sneedle++;
855 oneedle++;
855 oneedle++;
856 }
856 }
857 Py_DECREF(key);
857 Py_DECREF(key);
858 }
858 }
859 Py_DECREF(emptyTup);
859 Py_DECREF(emptyTup);
860 return ret;
860 return ret;
861 nomem:
861 nomem:
862 PyErr_NoMemory();
862 PyErr_NoMemory();
863 Py_XDECREF(ret);
863 Py_XDECREF(ret);
864 Py_XDECREF(emptyTup);
864 Py_XDECREF(emptyTup);
865 return NULL;
865 return NULL;
866 }
866 }
867
867
868 static PyMethodDef lazymanifest_methods[] = {
868 static PyMethodDef lazymanifest_methods[] = {
869 {"iterkeys", (PyCFunction)lazymanifest_getkeysiter, METH_NOARGS,
869 {"iterkeys", (PyCFunction)lazymanifest_getkeysiter, METH_NOARGS,
870 "Iterate over file names in this lazymanifest."},
870 "Iterate over file names in this lazymanifest."},
871 {"iterentries", (PyCFunction)lazymanifest_getentriesiter, METH_NOARGS,
871 {"iterentries", (PyCFunction)lazymanifest_getentriesiter, METH_NOARGS,
872 "Iterate over (path, nodeid, flags) tuples in this lazymanifest."},
872 "Iterate over (path, nodeid, flags) tuples in this lazymanifest."},
873 {"copy", (PyCFunction)lazymanifest_copy, METH_NOARGS,
873 {"copy", (PyCFunction)lazymanifest_copy, METH_NOARGS,
874 "Make a copy of this lazymanifest."},
874 "Make a copy of this lazymanifest."},
875 {"filtercopy", (PyCFunction)lazymanifest_filtercopy, METH_O,
875 {"filtercopy", (PyCFunction)lazymanifest_filtercopy, METH_O,
876 "Make a copy of this manifest filtered by matchfn."},
876 "Make a copy of this manifest filtered by matchfn."},
877 {"diff", (PyCFunction)lazymanifest_diff, METH_VARARGS,
877 {"diff", (PyCFunction)lazymanifest_diff, METH_VARARGS,
878 "Compare this lazymanifest to another one."},
878 "Compare this lazymanifest to another one."},
879 {"text", (PyCFunction)lazymanifest_text, METH_NOARGS,
879 {"text", (PyCFunction)lazymanifest_text, METH_NOARGS,
880 "Encode this manifest to text."},
880 "Encode this manifest to text."},
881 {NULL},
881 {NULL},
882 };
882 };
883
883
884 #ifdef IS_PY3K
884 #ifdef IS_PY3K
885 #define LAZYMANIFEST_TPFLAGS Py_TPFLAGS_DEFAULT
885 #define LAZYMANIFEST_TPFLAGS Py_TPFLAGS_DEFAULT
886 #else
886 #else
887 #define LAZYMANIFEST_TPFLAGS Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_SEQUENCE_IN
887 #define LAZYMANIFEST_TPFLAGS Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_SEQUENCE_IN
888 #endif
888 #endif
889
889
890 static PyTypeObject lazymanifestType = {
890 static PyTypeObject lazymanifestType = {
891 PyVarObject_HEAD_INIT(NULL, 0)
891 PyVarObject_HEAD_INIT(NULL, 0)
892 "parsers.lazymanifest", /* tp_name */
892 "parsers.lazymanifest", /* tp_name */
893 sizeof(lazymanifest), /* tp_basicsize */
893 sizeof(lazymanifest), /* tp_basicsize */
894 0, /* tp_itemsize */
894 0, /* tp_itemsize */
895 (destructor)lazymanifest_dealloc, /* tp_dealloc */
895 (destructor)lazymanifest_dealloc, /* tp_dealloc */
896 0, /* tp_print */
896 0, /* tp_print */
897 0, /* tp_getattr */
897 0, /* tp_getattr */
898 0, /* tp_setattr */
898 0, /* tp_setattr */
899 0, /* tp_compare */
899 0, /* tp_compare */
900 0, /* tp_repr */
900 0, /* tp_repr */
901 0, /* tp_as_number */
901 0, /* tp_as_number */
902 &lazymanifest_seq_meths, /* tp_as_sequence */
902 &lazymanifest_seq_meths, /* tp_as_sequence */
903 &lazymanifest_mapping_methods, /* tp_as_mapping */
903 &lazymanifest_mapping_methods, /* tp_as_mapping */
904 0, /* tp_hash */
904 0, /* tp_hash */
905 0, /* tp_call */
905 0, /* tp_call */
906 0, /* tp_str */
906 0, /* tp_str */
907 0, /* tp_getattro */
907 0, /* tp_getattro */
908 0, /* tp_setattro */
908 0, /* tp_setattro */
909 0, /* tp_as_buffer */
909 0, /* tp_as_buffer */
910 LAZYMANIFEST_TPFLAGS, /* tp_flags */
910 LAZYMANIFEST_TPFLAGS, /* tp_flags */
911 "TODO(augie)", /* tp_doc */
911 "TODO(augie)", /* tp_doc */
912 0, /* tp_traverse */
912 0, /* tp_traverse */
913 0, /* tp_clear */
913 0, /* tp_clear */
914 0, /* tp_richcompare */
914 0, /* tp_richcompare */
915 0, /* tp_weaklistoffset */
915 0, /* tp_weaklistoffset */
916 (getiterfunc)lazymanifest_getkeysiter, /* tp_iter */
916 (getiterfunc)lazymanifest_getkeysiter, /* tp_iter */
917 0, /* tp_iternext */
917 0, /* tp_iternext */
918 lazymanifest_methods, /* tp_methods */
918 lazymanifest_methods, /* tp_methods */
919 0, /* tp_members */
919 0, /* tp_members */
920 0, /* tp_getset */
920 0, /* tp_getset */
921 0, /* tp_base */
921 0, /* tp_base */
922 0, /* tp_dict */
922 0, /* tp_dict */
923 0, /* tp_descr_get */
923 0, /* tp_descr_get */
924 0, /* tp_descr_set */
924 0, /* tp_descr_set */
925 0, /* tp_dictoffset */
925 0, /* tp_dictoffset */
926 (initproc)lazymanifest_init, /* tp_init */
926 (initproc)lazymanifest_init, /* tp_init */
927 0, /* tp_alloc */
927 0, /* tp_alloc */
928 };
928 };
929
929
930 void manifest_module_init(PyObject * mod)
930 void manifest_module_init(PyObject * mod)
931 {
931 {
932 lazymanifestType.tp_new = PyType_GenericNew;
932 lazymanifestType.tp_new = PyType_GenericNew;
933 if (PyType_Ready(&lazymanifestType) < 0)
933 if (PyType_Ready(&lazymanifestType) < 0)
934 return;
934 return;
935 Py_INCREF(&lazymanifestType);
935 Py_INCREF(&lazymanifestType);
936
936
937 PyModule_AddObject(mod, "lazymanifest",
937 PyModule_AddObject(mod, "lazymanifest",
938 (PyObject *)&lazymanifestType);
938 (PyObject *)&lazymanifestType);
939 }
939 }
@@ -1,997 +1,806
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 "util.h"
15 #include "util.h"
16 #include "bitmanipulation.h"
16 #include "bitmanipulation.h"
17
17
18 #ifdef IS_PY3K
18 #ifdef IS_PY3K
19 /* The mapping of Python types is meant to be temporary to get Python
19 /* The mapping of Python types is meant to be temporary to get Python
20 * 3 to compile. We should remove this once Python 3 support is fully
20 * 3 to compile. We should remove this once Python 3 support is fully
21 * supported and proper types are used in the extensions themselves. */
21 * supported and proper types are used in the extensions themselves. */
22 #define PyInt_Type PyLong_Type
22 #define PyInt_Type PyLong_Type
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_AS_LONG PyLong_AS_LONG
26 #define PyInt_AS_LONG PyLong_AS_LONG
27 #define PyInt_AsLong PyLong_AsLong
27 #define PyInt_AsLong PyLong_AsLong
28 #endif
28 #endif
29
29
30 static const char *const versionerrortext = "Python minor version mismatch";
30 static const char *const versionerrortext = "Python minor version mismatch";
31
31
32 static const char lowertable[128] = {
32 /* defined in charencode.c */
33 '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
33 PyObject *unhexlify(const char *str, int len);
34 '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f',
34 PyObject *asciilower(PyObject *self, PyObject *args);
35 '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
35 PyObject *asciiupper(PyObject *self, PyObject *args);
36 '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f',
36 PyObject *make_file_foldmap(PyObject *self, PyObject *args);
37 '\x20', '\x21', '\x22', '\x23', '\x24', '\x25', '\x26', '\x27',
38 '\x28', '\x29', '\x2a', '\x2b', '\x2c', '\x2d', '\x2e', '\x2f',
39 '\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37',
40 '\x38', '\x39', '\x3a', '\x3b', '\x3c', '\x3d', '\x3e', '\x3f',
41 '\x40',
42 '\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67', /* A-G */
43 '\x68', '\x69', '\x6a', '\x6b', '\x6c', '\x6d', '\x6e', '\x6f', /* H-O */
44 '\x70', '\x71', '\x72', '\x73', '\x74', '\x75', '\x76', '\x77', /* P-W */
45 '\x78', '\x79', '\x7a', /* X-Z */
46 '\x5b', '\x5c', '\x5d', '\x5e', '\x5f',
47 '\x60', '\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67',
48 '\x68', '\x69', '\x6a', '\x6b', '\x6c', '\x6d', '\x6e', '\x6f',
49 '\x70', '\x71', '\x72', '\x73', '\x74', '\x75', '\x76', '\x77',
50 '\x78', '\x79', '\x7a', '\x7b', '\x7c', '\x7d', '\x7e', '\x7f'
51 };
52
53 static const char uppertable[128] = {
54 '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
55 '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f',
56 '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
57 '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f',
58 '\x20', '\x21', '\x22', '\x23', '\x24', '\x25', '\x26', '\x27',
59 '\x28', '\x29', '\x2a', '\x2b', '\x2c', '\x2d', '\x2e', '\x2f',
60 '\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37',
61 '\x38', '\x39', '\x3a', '\x3b', '\x3c', '\x3d', '\x3e', '\x3f',
62 '\x40', '\x41', '\x42', '\x43', '\x44', '\x45', '\x46', '\x47',
63 '\x48', '\x49', '\x4a', '\x4b', '\x4c', '\x4d', '\x4e', '\x4f',
64 '\x50', '\x51', '\x52', '\x53', '\x54', '\x55', '\x56', '\x57',
65 '\x58', '\x59', '\x5a', '\x5b', '\x5c', '\x5d', '\x5e', '\x5f',
66 '\x60',
67 '\x41', '\x42', '\x43', '\x44', '\x45', '\x46', '\x47', /* a-g */
68 '\x48', '\x49', '\x4a', '\x4b', '\x4c', '\x4d', '\x4e', '\x4f', /* h-o */
69 '\x50', '\x51', '\x52', '\x53', '\x54', '\x55', '\x56', '\x57', /* p-w */
70 '\x58', '\x59', '\x5a', /* x-z */
71 '\x7b', '\x7c', '\x7d', '\x7e', '\x7f'
72 };
73
74 /*
75 * Turn a hex-encoded string into binary.
76 */
77 PyObject *unhexlify(const char *str, int len)
78 {
79 PyObject *ret;
80 char *d;
81 int i;
82
83 ret = PyBytes_FromStringAndSize(NULL, len / 2);
84
85 if (!ret)
86 return NULL;
87
88 d = PyBytes_AsString(ret);
89
90 for (i = 0; i < len;) {
91 int hi = hexdigit(str, i++);
92 int lo = hexdigit(str, i++);
93 *d++ = (hi << 4) | lo;
94 }
95
96 return ret;
97 }
98
99 static inline PyObject *_asciitransform(PyObject *str_obj,
100 const char table[128],
101 PyObject *fallback_fn)
102 {
103 char *str, *newstr;
104 Py_ssize_t i, len;
105 PyObject *newobj = NULL;
106 PyObject *ret = NULL;
107
108 str = PyBytes_AS_STRING(str_obj);
109 len = PyBytes_GET_SIZE(str_obj);
110
111 newobj = PyBytes_FromStringAndSize(NULL, len);
112 if (!newobj)
113 goto quit;
114
115 newstr = PyBytes_AS_STRING(newobj);
116
117 for (i = 0; i < len; i++) {
118 char c = str[i];
119 if (c & 0x80) {
120 if (fallback_fn != NULL) {
121 ret = PyObject_CallFunctionObjArgs(fallback_fn,
122 str_obj, NULL);
123 } else {
124 PyObject *err = PyUnicodeDecodeError_Create(
125 "ascii", str, len, i, (i + 1),
126 "unexpected code byte");
127 PyErr_SetObject(PyExc_UnicodeDecodeError, err);
128 Py_XDECREF(err);
129 }
130 goto quit;
131 }
132 newstr[i] = table[(unsigned char)c];
133 }
134
135 ret = newobj;
136 Py_INCREF(ret);
137 quit:
138 Py_XDECREF(newobj);
139 return ret;
140 }
141
142 static PyObject *asciilower(PyObject *self, PyObject *args)
143 {
144 PyObject *str_obj;
145 if (!PyArg_ParseTuple(args, "O!:asciilower", &PyBytes_Type, &str_obj))
146 return NULL;
147 return _asciitransform(str_obj, lowertable, NULL);
148 }
149
150 static PyObject *asciiupper(PyObject *self, PyObject *args)
151 {
152 PyObject *str_obj;
153 if (!PyArg_ParseTuple(args, "O!:asciiupper", &PyBytes_Type, &str_obj))
154 return NULL;
155 return _asciitransform(str_obj, uppertable, NULL);
156 }
157
37
158 static PyObject *dict_new_presized(PyObject *self, PyObject *args)
38 static PyObject *dict_new_presized(PyObject *self, PyObject *args)
159 {
39 {
160 Py_ssize_t expected_size;
40 Py_ssize_t expected_size;
161
41
162 if (!PyArg_ParseTuple(args, "n:make_presized_dict", &expected_size))
42 if (!PyArg_ParseTuple(args, "n:make_presized_dict", &expected_size))
163 return NULL;
43 return NULL;
164
44
165 return _dict_new_presized(expected_size);
45 return _dict_new_presized(expected_size);
166 }
46 }
167
47
168 static PyObject *make_file_foldmap(PyObject *self, PyObject *args)
169 {
170 PyObject *dmap, *spec_obj, *normcase_fallback;
171 PyObject *file_foldmap = NULL;
172 enum normcase_spec spec;
173 PyObject *k, *v;
174 dirstateTupleObject *tuple;
175 Py_ssize_t pos = 0;
176 const char *table;
177
178 if (!PyArg_ParseTuple(args, "O!O!O!:make_file_foldmap",
179 &PyDict_Type, &dmap,
180 &PyInt_Type, &spec_obj,
181 &PyFunction_Type, &normcase_fallback))
182 goto quit;
183
184 spec = (int)PyInt_AS_LONG(spec_obj);
185 switch (spec) {
186 case NORMCASE_LOWER:
187 table = lowertable;
188 break;
189 case NORMCASE_UPPER:
190 table = uppertable;
191 break;
192 case NORMCASE_OTHER:
193 table = NULL;
194 break;
195 default:
196 PyErr_SetString(PyExc_TypeError, "invalid normcasespec");
197 goto quit;
198 }
199
200 /* Add some more entries to deal with additions outside this
201 function. */
202 file_foldmap = _dict_new_presized((PyDict_Size(dmap) / 10) * 11);
203 if (file_foldmap == NULL)
204 goto quit;
205
206 while (PyDict_Next(dmap, &pos, &k, &v)) {
207 if (!dirstate_tuple_check(v)) {
208 PyErr_SetString(PyExc_TypeError,
209 "expected a dirstate tuple");
210 goto quit;
211 }
212
213 tuple = (dirstateTupleObject *)v;
214 if (tuple->state != 'r') {
215 PyObject *normed;
216 if (table != NULL) {
217 normed = _asciitransform(k, table,
218 normcase_fallback);
219 } else {
220 normed = PyObject_CallFunctionObjArgs(
221 normcase_fallback, k, NULL);
222 }
223
224 if (normed == NULL)
225 goto quit;
226 if (PyDict_SetItem(file_foldmap, normed, k) == -1) {
227 Py_DECREF(normed);
228 goto quit;
229 }
230 Py_DECREF(normed);
231 }
232 }
233 return file_foldmap;
234 quit:
235 Py_XDECREF(file_foldmap);
236 return NULL;
237 }
238
239 /*
48 /*
240 * This code assumes that a manifest is stitched together with newline
49 * This code assumes that a manifest is stitched together with newline
241 * ('\n') characters.
50 * ('\n') characters.
242 */
51 */
243 static PyObject *parse_manifest(PyObject *self, PyObject *args)
52 static PyObject *parse_manifest(PyObject *self, PyObject *args)
244 {
53 {
245 PyObject *mfdict, *fdict;
54 PyObject *mfdict, *fdict;
246 char *str, *start, *end;
55 char *str, *start, *end;
247 int len;
56 int len;
248
57
249 if (!PyArg_ParseTuple(args, "O!O!s#:parse_manifest",
58 if (!PyArg_ParseTuple(args, "O!O!s#:parse_manifest",
250 &PyDict_Type, &mfdict,
59 &PyDict_Type, &mfdict,
251 &PyDict_Type, &fdict,
60 &PyDict_Type, &fdict,
252 &str, &len))
61 &str, &len))
253 goto quit;
62 goto quit;
254
63
255 start = str;
64 start = str;
256 end = str + len;
65 end = str + len;
257 while (start < end) {
66 while (start < end) {
258 PyObject *file = NULL, *node = NULL;
67 PyObject *file = NULL, *node = NULL;
259 PyObject *flags = NULL;
68 PyObject *flags = NULL;
260 char *zero = NULL, *newline = NULL;
69 char *zero = NULL, *newline = NULL;
261 ptrdiff_t nlen;
70 ptrdiff_t nlen;
262
71
263 zero = memchr(start, '\0', end - start);
72 zero = memchr(start, '\0', end - start);
264 if (!zero) {
73 if (!zero) {
265 PyErr_SetString(PyExc_ValueError,
74 PyErr_SetString(PyExc_ValueError,
266 "manifest entry has no separator");
75 "manifest entry has no separator");
267 goto quit;
76 goto quit;
268 }
77 }
269
78
270 newline = memchr(zero + 1, '\n', end - (zero + 1));
79 newline = memchr(zero + 1, '\n', end - (zero + 1));
271 if (!newline) {
80 if (!newline) {
272 PyErr_SetString(PyExc_ValueError,
81 PyErr_SetString(PyExc_ValueError,
273 "manifest contains trailing garbage");
82 "manifest contains trailing garbage");
274 goto quit;
83 goto quit;
275 }
84 }
276
85
277 file = PyBytes_FromStringAndSize(start, zero - start);
86 file = PyBytes_FromStringAndSize(start, zero - start);
278
87
279 if (!file)
88 if (!file)
280 goto bail;
89 goto bail;
281
90
282 nlen = newline - zero - 1;
91 nlen = newline - zero - 1;
283
92
284 node = unhexlify(zero + 1, nlen > 40 ? 40 : (int)nlen);
93 node = unhexlify(zero + 1, nlen > 40 ? 40 : (int)nlen);
285 if (!node)
94 if (!node)
286 goto bail;
95 goto bail;
287
96
288 if (nlen > 40) {
97 if (nlen > 40) {
289 flags = PyBytes_FromStringAndSize(zero + 41,
98 flags = PyBytes_FromStringAndSize(zero + 41,
290 nlen - 40);
99 nlen - 40);
291 if (!flags)
100 if (!flags)
292 goto bail;
101 goto bail;
293
102
294 if (PyDict_SetItem(fdict, file, flags) == -1)
103 if (PyDict_SetItem(fdict, file, flags) == -1)
295 goto bail;
104 goto bail;
296 }
105 }
297
106
298 if (PyDict_SetItem(mfdict, file, node) == -1)
107 if (PyDict_SetItem(mfdict, file, node) == -1)
299 goto bail;
108 goto bail;
300
109
301 start = newline + 1;
110 start = newline + 1;
302
111
303 Py_XDECREF(flags);
112 Py_XDECREF(flags);
304 Py_XDECREF(node);
113 Py_XDECREF(node);
305 Py_XDECREF(file);
114 Py_XDECREF(file);
306 continue;
115 continue;
307 bail:
116 bail:
308 Py_XDECREF(flags);
117 Py_XDECREF(flags);
309 Py_XDECREF(node);
118 Py_XDECREF(node);
310 Py_XDECREF(file);
119 Py_XDECREF(file);
311 goto quit;
120 goto quit;
312 }
121 }
313
122
314 Py_INCREF(Py_None);
123 Py_INCREF(Py_None);
315 return Py_None;
124 return Py_None;
316 quit:
125 quit:
317 return NULL;
126 return NULL;
318 }
127 }
319
128
320 static inline dirstateTupleObject *make_dirstate_tuple(char state, int mode,
129 static inline dirstateTupleObject *make_dirstate_tuple(char state, int mode,
321 int size, int mtime)
130 int size, int mtime)
322 {
131 {
323 dirstateTupleObject *t = PyObject_New(dirstateTupleObject,
132 dirstateTupleObject *t = PyObject_New(dirstateTupleObject,
324 &dirstateTupleType);
133 &dirstateTupleType);
325 if (!t)
134 if (!t)
326 return NULL;
135 return NULL;
327 t->state = state;
136 t->state = state;
328 t->mode = mode;
137 t->mode = mode;
329 t->size = size;
138 t->size = size;
330 t->mtime = mtime;
139 t->mtime = mtime;
331 return t;
140 return t;
332 }
141 }
333
142
334 static PyObject *dirstate_tuple_new(PyTypeObject *subtype, PyObject *args,
143 static PyObject *dirstate_tuple_new(PyTypeObject *subtype, PyObject *args,
335 PyObject *kwds)
144 PyObject *kwds)
336 {
145 {
337 /* We do all the initialization here and not a tp_init function because
146 /* We do all the initialization here and not a tp_init function because
338 * dirstate_tuple is immutable. */
147 * dirstate_tuple is immutable. */
339 dirstateTupleObject *t;
148 dirstateTupleObject *t;
340 char state;
149 char state;
341 int size, mode, mtime;
150 int size, mode, mtime;
342 if (!PyArg_ParseTuple(args, "ciii", &state, &mode, &size, &mtime))
151 if (!PyArg_ParseTuple(args, "ciii", &state, &mode, &size, &mtime))
343 return NULL;
152 return NULL;
344
153
345 t = (dirstateTupleObject *)subtype->tp_alloc(subtype, 1);
154 t = (dirstateTupleObject *)subtype->tp_alloc(subtype, 1);
346 if (!t)
155 if (!t)
347 return NULL;
156 return NULL;
348 t->state = state;
157 t->state = state;
349 t->mode = mode;
158 t->mode = mode;
350 t->size = size;
159 t->size = size;
351 t->mtime = mtime;
160 t->mtime = mtime;
352
161
353 return (PyObject *)t;
162 return (PyObject *)t;
354 }
163 }
355
164
356 static void dirstate_tuple_dealloc(PyObject *o)
165 static void dirstate_tuple_dealloc(PyObject *o)
357 {
166 {
358 PyObject_Del(o);
167 PyObject_Del(o);
359 }
168 }
360
169
361 static Py_ssize_t dirstate_tuple_length(PyObject *o)
170 static Py_ssize_t dirstate_tuple_length(PyObject *o)
362 {
171 {
363 return 4;
172 return 4;
364 }
173 }
365
174
366 static PyObject *dirstate_tuple_item(PyObject *o, Py_ssize_t i)
175 static PyObject *dirstate_tuple_item(PyObject *o, Py_ssize_t i)
367 {
176 {
368 dirstateTupleObject *t = (dirstateTupleObject *)o;
177 dirstateTupleObject *t = (dirstateTupleObject *)o;
369 switch (i) {
178 switch (i) {
370 case 0:
179 case 0:
371 return PyBytes_FromStringAndSize(&t->state, 1);
180 return PyBytes_FromStringAndSize(&t->state, 1);
372 case 1:
181 case 1:
373 return PyInt_FromLong(t->mode);
182 return PyInt_FromLong(t->mode);
374 case 2:
183 case 2:
375 return PyInt_FromLong(t->size);
184 return PyInt_FromLong(t->size);
376 case 3:
185 case 3:
377 return PyInt_FromLong(t->mtime);
186 return PyInt_FromLong(t->mtime);
378 default:
187 default:
379 PyErr_SetString(PyExc_IndexError, "index out of range");
188 PyErr_SetString(PyExc_IndexError, "index out of range");
380 return NULL;
189 return NULL;
381 }
190 }
382 }
191 }
383
192
384 static PySequenceMethods dirstate_tuple_sq = {
193 static PySequenceMethods dirstate_tuple_sq = {
385 dirstate_tuple_length, /* sq_length */
194 dirstate_tuple_length, /* sq_length */
386 0, /* sq_concat */
195 0, /* sq_concat */
387 0, /* sq_repeat */
196 0, /* sq_repeat */
388 dirstate_tuple_item, /* sq_item */
197 dirstate_tuple_item, /* sq_item */
389 0, /* sq_ass_item */
198 0, /* sq_ass_item */
390 0, /* sq_contains */
199 0, /* sq_contains */
391 0, /* sq_inplace_concat */
200 0, /* sq_inplace_concat */
392 0 /* sq_inplace_repeat */
201 0 /* sq_inplace_repeat */
393 };
202 };
394
203
395 PyTypeObject dirstateTupleType = {
204 PyTypeObject dirstateTupleType = {
396 PyVarObject_HEAD_INIT(NULL, 0)
205 PyVarObject_HEAD_INIT(NULL, 0)
397 "dirstate_tuple", /* tp_name */
206 "dirstate_tuple", /* tp_name */
398 sizeof(dirstateTupleObject),/* tp_basicsize */
207 sizeof(dirstateTupleObject),/* tp_basicsize */
399 0, /* tp_itemsize */
208 0, /* tp_itemsize */
400 (destructor)dirstate_tuple_dealloc, /* tp_dealloc */
209 (destructor)dirstate_tuple_dealloc, /* tp_dealloc */
401 0, /* tp_print */
210 0, /* tp_print */
402 0, /* tp_getattr */
211 0, /* tp_getattr */
403 0, /* tp_setattr */
212 0, /* tp_setattr */
404 0, /* tp_compare */
213 0, /* tp_compare */
405 0, /* tp_repr */
214 0, /* tp_repr */
406 0, /* tp_as_number */
215 0, /* tp_as_number */
407 &dirstate_tuple_sq, /* tp_as_sequence */
216 &dirstate_tuple_sq, /* tp_as_sequence */
408 0, /* tp_as_mapping */
217 0, /* tp_as_mapping */
409 0, /* tp_hash */
218 0, /* tp_hash */
410 0, /* tp_call */
219 0, /* tp_call */
411 0, /* tp_str */
220 0, /* tp_str */
412 0, /* tp_getattro */
221 0, /* tp_getattro */
413 0, /* tp_setattro */
222 0, /* tp_setattro */
414 0, /* tp_as_buffer */
223 0, /* tp_as_buffer */
415 Py_TPFLAGS_DEFAULT, /* tp_flags */
224 Py_TPFLAGS_DEFAULT, /* tp_flags */
416 "dirstate tuple", /* tp_doc */
225 "dirstate tuple", /* tp_doc */
417 0, /* tp_traverse */
226 0, /* tp_traverse */
418 0, /* tp_clear */
227 0, /* tp_clear */
419 0, /* tp_richcompare */
228 0, /* tp_richcompare */
420 0, /* tp_weaklistoffset */
229 0, /* tp_weaklistoffset */
421 0, /* tp_iter */
230 0, /* tp_iter */
422 0, /* tp_iternext */
231 0, /* tp_iternext */
423 0, /* tp_methods */
232 0, /* tp_methods */
424 0, /* tp_members */
233 0, /* tp_members */
425 0, /* tp_getset */
234 0, /* tp_getset */
426 0, /* tp_base */
235 0, /* tp_base */
427 0, /* tp_dict */
236 0, /* tp_dict */
428 0, /* tp_descr_get */
237 0, /* tp_descr_get */
429 0, /* tp_descr_set */
238 0, /* tp_descr_set */
430 0, /* tp_dictoffset */
239 0, /* tp_dictoffset */
431 0, /* tp_init */
240 0, /* tp_init */
432 0, /* tp_alloc */
241 0, /* tp_alloc */
433 dirstate_tuple_new, /* tp_new */
242 dirstate_tuple_new, /* tp_new */
434 };
243 };
435
244
436 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
245 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
437 {
246 {
438 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
247 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
439 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
248 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
440 char state, *cur, *str, *cpos;
249 char state, *cur, *str, *cpos;
441 int mode, size, mtime;
250 int mode, size, mtime;
442 unsigned int flen, len, pos = 40;
251 unsigned int flen, len, pos = 40;
443 int readlen;
252 int readlen;
444
253
445 if (!PyArg_ParseTuple(args, "O!O!s#:parse_dirstate",
254 if (!PyArg_ParseTuple(args, "O!O!s#:parse_dirstate",
446 &PyDict_Type, &dmap,
255 &PyDict_Type, &dmap,
447 &PyDict_Type, &cmap,
256 &PyDict_Type, &cmap,
448 &str, &readlen))
257 &str, &readlen))
449 goto quit;
258 goto quit;
450
259
451 len = readlen;
260 len = readlen;
452
261
453 /* read parents */
262 /* read parents */
454 if (len < 40) {
263 if (len < 40) {
455 PyErr_SetString(
264 PyErr_SetString(
456 PyExc_ValueError, "too little data for parents");
265 PyExc_ValueError, "too little data for parents");
457 goto quit;
266 goto quit;
458 }
267 }
459
268
460 parents = Py_BuildValue("s#s#", str, 20, str + 20, 20);
269 parents = Py_BuildValue("s#s#", str, 20, str + 20, 20);
461 if (!parents)
270 if (!parents)
462 goto quit;
271 goto quit;
463
272
464 /* read filenames */
273 /* read filenames */
465 while (pos >= 40 && pos < len) {
274 while (pos >= 40 && pos < len) {
466 if (pos + 17 > len) {
275 if (pos + 17 > len) {
467 PyErr_SetString(PyExc_ValueError,
276 PyErr_SetString(PyExc_ValueError,
468 "overflow in dirstate");
277 "overflow in dirstate");
469 goto quit;
278 goto quit;
470 }
279 }
471 cur = str + pos;
280 cur = str + pos;
472 /* unpack header */
281 /* unpack header */
473 state = *cur;
282 state = *cur;
474 mode = getbe32(cur + 1);
283 mode = getbe32(cur + 1);
475 size = getbe32(cur + 5);
284 size = getbe32(cur + 5);
476 mtime = getbe32(cur + 9);
285 mtime = getbe32(cur + 9);
477 flen = getbe32(cur + 13);
286 flen = getbe32(cur + 13);
478 pos += 17;
287 pos += 17;
479 cur += 17;
288 cur += 17;
480 if (flen > len - pos) {
289 if (flen > len - pos) {
481 PyErr_SetString(PyExc_ValueError, "overflow in dirstate");
290 PyErr_SetString(PyExc_ValueError, "overflow in dirstate");
482 goto quit;
291 goto quit;
483 }
292 }
484
293
485 entry = (PyObject *)make_dirstate_tuple(state, mode, size,
294 entry = (PyObject *)make_dirstate_tuple(state, mode, size,
486 mtime);
295 mtime);
487 cpos = memchr(cur, 0, flen);
296 cpos = memchr(cur, 0, flen);
488 if (cpos) {
297 if (cpos) {
489 fname = PyBytes_FromStringAndSize(cur, cpos - cur);
298 fname = PyBytes_FromStringAndSize(cur, cpos - cur);
490 cname = PyBytes_FromStringAndSize(cpos + 1,
299 cname = PyBytes_FromStringAndSize(cpos + 1,
491 flen - (cpos - cur) - 1);
300 flen - (cpos - cur) - 1);
492 if (!fname || !cname ||
301 if (!fname || !cname ||
493 PyDict_SetItem(cmap, fname, cname) == -1 ||
302 PyDict_SetItem(cmap, fname, cname) == -1 ||
494 PyDict_SetItem(dmap, fname, entry) == -1)
303 PyDict_SetItem(dmap, fname, entry) == -1)
495 goto quit;
304 goto quit;
496 Py_DECREF(cname);
305 Py_DECREF(cname);
497 } else {
306 } else {
498 fname = PyBytes_FromStringAndSize(cur, flen);
307 fname = PyBytes_FromStringAndSize(cur, flen);
499 if (!fname ||
308 if (!fname ||
500 PyDict_SetItem(dmap, fname, entry) == -1)
309 PyDict_SetItem(dmap, fname, entry) == -1)
501 goto quit;
310 goto quit;
502 }
311 }
503 Py_DECREF(fname);
312 Py_DECREF(fname);
504 Py_DECREF(entry);
313 Py_DECREF(entry);
505 fname = cname = entry = NULL;
314 fname = cname = entry = NULL;
506 pos += flen;
315 pos += flen;
507 }
316 }
508
317
509 ret = parents;
318 ret = parents;
510 Py_INCREF(ret);
319 Py_INCREF(ret);
511 quit:
320 quit:
512 Py_XDECREF(fname);
321 Py_XDECREF(fname);
513 Py_XDECREF(cname);
322 Py_XDECREF(cname);
514 Py_XDECREF(entry);
323 Py_XDECREF(entry);
515 Py_XDECREF(parents);
324 Py_XDECREF(parents);
516 return ret;
325 return ret;
517 }
326 }
518
327
519 /*
328 /*
520 * Build a set of non-normal and other parent entries from the dirstate dmap
329 * Build a set of non-normal and other parent entries from the dirstate dmap
521 */
330 */
522 static PyObject *nonnormalotherparententries(PyObject *self, PyObject *args) {
331 static PyObject *nonnormalotherparententries(PyObject *self, PyObject *args) {
523 PyObject *dmap, *fname, *v;
332 PyObject *dmap, *fname, *v;
524 PyObject *nonnset = NULL, *otherpset = NULL, *result = NULL;
333 PyObject *nonnset = NULL, *otherpset = NULL, *result = NULL;
525 Py_ssize_t pos;
334 Py_ssize_t pos;
526
335
527 if (!PyArg_ParseTuple(args, "O!:nonnormalentries",
336 if (!PyArg_ParseTuple(args, "O!:nonnormalentries",
528 &PyDict_Type, &dmap))
337 &PyDict_Type, &dmap))
529 goto bail;
338 goto bail;
530
339
531 nonnset = PySet_New(NULL);
340 nonnset = PySet_New(NULL);
532 if (nonnset == NULL)
341 if (nonnset == NULL)
533 goto bail;
342 goto bail;
534
343
535 otherpset = PySet_New(NULL);
344 otherpset = PySet_New(NULL);
536 if (otherpset == NULL)
345 if (otherpset == NULL)
537 goto bail;
346 goto bail;
538
347
539 pos = 0;
348 pos = 0;
540 while (PyDict_Next(dmap, &pos, &fname, &v)) {
349 while (PyDict_Next(dmap, &pos, &fname, &v)) {
541 dirstateTupleObject *t;
350 dirstateTupleObject *t;
542 if (!dirstate_tuple_check(v)) {
351 if (!dirstate_tuple_check(v)) {
543 PyErr_SetString(PyExc_TypeError,
352 PyErr_SetString(PyExc_TypeError,
544 "expected a dirstate tuple");
353 "expected a dirstate tuple");
545 goto bail;
354 goto bail;
546 }
355 }
547 t = (dirstateTupleObject *)v;
356 t = (dirstateTupleObject *)v;
548
357
549 if (t->state == 'n' && t->size == -2) {
358 if (t->state == 'n' && t->size == -2) {
550 if (PySet_Add(otherpset, fname) == -1) {
359 if (PySet_Add(otherpset, fname) == -1) {
551 goto bail;
360 goto bail;
552 }
361 }
553 }
362 }
554
363
555 if (t->state == 'n' && t->mtime != -1)
364 if (t->state == 'n' && t->mtime != -1)
556 continue;
365 continue;
557 if (PySet_Add(nonnset, fname) == -1)
366 if (PySet_Add(nonnset, fname) == -1)
558 goto bail;
367 goto bail;
559 }
368 }
560
369
561 result = Py_BuildValue("(OO)", nonnset, otherpset);
370 result = Py_BuildValue("(OO)", nonnset, otherpset);
562 if (result == NULL)
371 if (result == NULL)
563 goto bail;
372 goto bail;
564 Py_DECREF(nonnset);
373 Py_DECREF(nonnset);
565 Py_DECREF(otherpset);
374 Py_DECREF(otherpset);
566 return result;
375 return result;
567 bail:
376 bail:
568 Py_XDECREF(nonnset);
377 Py_XDECREF(nonnset);
569 Py_XDECREF(otherpset);
378 Py_XDECREF(otherpset);
570 Py_XDECREF(result);
379 Py_XDECREF(result);
571 return NULL;
380 return NULL;
572 }
381 }
573
382
574 /*
383 /*
575 * Efficiently pack a dirstate object into its on-disk format.
384 * Efficiently pack a dirstate object into its on-disk format.
576 */
385 */
577 static PyObject *pack_dirstate(PyObject *self, PyObject *args)
386 static PyObject *pack_dirstate(PyObject *self, PyObject *args)
578 {
387 {
579 PyObject *packobj = NULL;
388 PyObject *packobj = NULL;
580 PyObject *map, *copymap, *pl, *mtime_unset = NULL;
389 PyObject *map, *copymap, *pl, *mtime_unset = NULL;
581 Py_ssize_t nbytes, pos, l;
390 Py_ssize_t nbytes, pos, l;
582 PyObject *k, *v = NULL, *pn;
391 PyObject *k, *v = NULL, *pn;
583 char *p, *s;
392 char *p, *s;
584 int now;
393 int now;
585
394
586 if (!PyArg_ParseTuple(args, "O!O!Oi:pack_dirstate",
395 if (!PyArg_ParseTuple(args, "O!O!Oi:pack_dirstate",
587 &PyDict_Type, &map, &PyDict_Type, &copymap,
396 &PyDict_Type, &map, &PyDict_Type, &copymap,
588 &pl, &now))
397 &pl, &now))
589 return NULL;
398 return NULL;
590
399
591 if (!PySequence_Check(pl) || PySequence_Size(pl) != 2) {
400 if (!PySequence_Check(pl) || PySequence_Size(pl) != 2) {
592 PyErr_SetString(PyExc_TypeError, "expected 2-element sequence");
401 PyErr_SetString(PyExc_TypeError, "expected 2-element sequence");
593 return NULL;
402 return NULL;
594 }
403 }
595
404
596 /* Figure out how much we need to allocate. */
405 /* Figure out how much we need to allocate. */
597 for (nbytes = 40, pos = 0; PyDict_Next(map, &pos, &k, &v);) {
406 for (nbytes = 40, pos = 0; PyDict_Next(map, &pos, &k, &v);) {
598 PyObject *c;
407 PyObject *c;
599 if (!PyBytes_Check(k)) {
408 if (!PyBytes_Check(k)) {
600 PyErr_SetString(PyExc_TypeError, "expected string key");
409 PyErr_SetString(PyExc_TypeError, "expected string key");
601 goto bail;
410 goto bail;
602 }
411 }
603 nbytes += PyBytes_GET_SIZE(k) + 17;
412 nbytes += PyBytes_GET_SIZE(k) + 17;
604 c = PyDict_GetItem(copymap, k);
413 c = PyDict_GetItem(copymap, k);
605 if (c) {
414 if (c) {
606 if (!PyBytes_Check(c)) {
415 if (!PyBytes_Check(c)) {
607 PyErr_SetString(PyExc_TypeError,
416 PyErr_SetString(PyExc_TypeError,
608 "expected string key");
417 "expected string key");
609 goto bail;
418 goto bail;
610 }
419 }
611 nbytes += PyBytes_GET_SIZE(c) + 1;
420 nbytes += PyBytes_GET_SIZE(c) + 1;
612 }
421 }
613 }
422 }
614
423
615 packobj = PyBytes_FromStringAndSize(NULL, nbytes);
424 packobj = PyBytes_FromStringAndSize(NULL, nbytes);
616 if (packobj == NULL)
425 if (packobj == NULL)
617 goto bail;
426 goto bail;
618
427
619 p = PyBytes_AS_STRING(packobj);
428 p = PyBytes_AS_STRING(packobj);
620
429
621 pn = PySequence_ITEM(pl, 0);
430 pn = PySequence_ITEM(pl, 0);
622 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
431 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
623 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
432 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
624 goto bail;
433 goto bail;
625 }
434 }
626 memcpy(p, s, l);
435 memcpy(p, s, l);
627 p += 20;
436 p += 20;
628 pn = PySequence_ITEM(pl, 1);
437 pn = PySequence_ITEM(pl, 1);
629 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
438 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
630 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
439 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
631 goto bail;
440 goto bail;
632 }
441 }
633 memcpy(p, s, l);
442 memcpy(p, s, l);
634 p += 20;
443 p += 20;
635
444
636 for (pos = 0; PyDict_Next(map, &pos, &k, &v); ) {
445 for (pos = 0; PyDict_Next(map, &pos, &k, &v); ) {
637 dirstateTupleObject *tuple;
446 dirstateTupleObject *tuple;
638 char state;
447 char state;
639 int mode, size, mtime;
448 int mode, size, mtime;
640 Py_ssize_t len, l;
449 Py_ssize_t len, l;
641 PyObject *o;
450 PyObject *o;
642 char *t;
451 char *t;
643
452
644 if (!dirstate_tuple_check(v)) {
453 if (!dirstate_tuple_check(v)) {
645 PyErr_SetString(PyExc_TypeError,
454 PyErr_SetString(PyExc_TypeError,
646 "expected a dirstate tuple");
455 "expected a dirstate tuple");
647 goto bail;
456 goto bail;
648 }
457 }
649 tuple = (dirstateTupleObject *)v;
458 tuple = (dirstateTupleObject *)v;
650
459
651 state = tuple->state;
460 state = tuple->state;
652 mode = tuple->mode;
461 mode = tuple->mode;
653 size = tuple->size;
462 size = tuple->size;
654 mtime = tuple->mtime;
463 mtime = tuple->mtime;
655 if (state == 'n' && mtime == now) {
464 if (state == 'n' && mtime == now) {
656 /* See pure/parsers.py:pack_dirstate for why we do
465 /* See pure/parsers.py:pack_dirstate for why we do
657 * this. */
466 * this. */
658 mtime = -1;
467 mtime = -1;
659 mtime_unset = (PyObject *)make_dirstate_tuple(
468 mtime_unset = (PyObject *)make_dirstate_tuple(
660 state, mode, size, mtime);
469 state, mode, size, mtime);
661 if (!mtime_unset)
470 if (!mtime_unset)
662 goto bail;
471 goto bail;
663 if (PyDict_SetItem(map, k, mtime_unset) == -1)
472 if (PyDict_SetItem(map, k, mtime_unset) == -1)
664 goto bail;
473 goto bail;
665 Py_DECREF(mtime_unset);
474 Py_DECREF(mtime_unset);
666 mtime_unset = NULL;
475 mtime_unset = NULL;
667 }
476 }
668 *p++ = state;
477 *p++ = state;
669 putbe32((uint32_t)mode, p);
478 putbe32((uint32_t)mode, p);
670 putbe32((uint32_t)size, p + 4);
479 putbe32((uint32_t)size, p + 4);
671 putbe32((uint32_t)mtime, p + 8);
480 putbe32((uint32_t)mtime, p + 8);
672 t = p + 12;
481 t = p + 12;
673 p += 16;
482 p += 16;
674 len = PyBytes_GET_SIZE(k);
483 len = PyBytes_GET_SIZE(k);
675 memcpy(p, PyBytes_AS_STRING(k), len);
484 memcpy(p, PyBytes_AS_STRING(k), len);
676 p += len;
485 p += len;
677 o = PyDict_GetItem(copymap, k);
486 o = PyDict_GetItem(copymap, k);
678 if (o) {
487 if (o) {
679 *p++ = '\0';
488 *p++ = '\0';
680 l = PyBytes_GET_SIZE(o);
489 l = PyBytes_GET_SIZE(o);
681 memcpy(p, PyBytes_AS_STRING(o), l);
490 memcpy(p, PyBytes_AS_STRING(o), l);
682 p += l;
491 p += l;
683 len += l + 1;
492 len += l + 1;
684 }
493 }
685 putbe32((uint32_t)len, t);
494 putbe32((uint32_t)len, t);
686 }
495 }
687
496
688 pos = p - PyBytes_AS_STRING(packobj);
497 pos = p - PyBytes_AS_STRING(packobj);
689 if (pos != nbytes) {
498 if (pos != nbytes) {
690 PyErr_Format(PyExc_SystemError, "bad dirstate size: %ld != %ld",
499 PyErr_Format(PyExc_SystemError, "bad dirstate size: %ld != %ld",
691 (long)pos, (long)nbytes);
500 (long)pos, (long)nbytes);
692 goto bail;
501 goto bail;
693 }
502 }
694
503
695 return packobj;
504 return packobj;
696 bail:
505 bail:
697 Py_XDECREF(mtime_unset);
506 Py_XDECREF(mtime_unset);
698 Py_XDECREF(packobj);
507 Py_XDECREF(packobj);
699 Py_XDECREF(v);
508 Py_XDECREF(v);
700 return NULL;
509 return NULL;
701 }
510 }
702
511
703 #define BUMPED_FIX 1
512 #define BUMPED_FIX 1
704 #define USING_SHA_256 2
513 #define USING_SHA_256 2
705 #define FM1_HEADER_SIZE (4 + 8 + 2 + 2 + 1 + 1 + 1)
514 #define FM1_HEADER_SIZE (4 + 8 + 2 + 2 + 1 + 1 + 1)
706
515
707 static PyObject *readshas(
516 static PyObject *readshas(
708 const char *source, unsigned char num, Py_ssize_t hashwidth)
517 const char *source, unsigned char num, Py_ssize_t hashwidth)
709 {
518 {
710 int i;
519 int i;
711 PyObject *list = PyTuple_New(num);
520 PyObject *list = PyTuple_New(num);
712 if (list == NULL) {
521 if (list == NULL) {
713 return NULL;
522 return NULL;
714 }
523 }
715 for (i = 0; i < num; i++) {
524 for (i = 0; i < num; i++) {
716 PyObject *hash = PyBytes_FromStringAndSize(source, hashwidth);
525 PyObject *hash = PyBytes_FromStringAndSize(source, hashwidth);
717 if (hash == NULL) {
526 if (hash == NULL) {
718 Py_DECREF(list);
527 Py_DECREF(list);
719 return NULL;
528 return NULL;
720 }
529 }
721 PyTuple_SET_ITEM(list, i, hash);
530 PyTuple_SET_ITEM(list, i, hash);
722 source += hashwidth;
531 source += hashwidth;
723 }
532 }
724 return list;
533 return list;
725 }
534 }
726
535
727 static PyObject *fm1readmarker(const char *databegin, const char *dataend,
536 static PyObject *fm1readmarker(const char *databegin, const char *dataend,
728 uint32_t *msize)
537 uint32_t *msize)
729 {
538 {
730 const char *data = databegin;
539 const char *data = databegin;
731 const char *meta;
540 const char *meta;
732
541
733 double mtime;
542 double mtime;
734 int16_t tz;
543 int16_t tz;
735 uint16_t flags;
544 uint16_t flags;
736 unsigned char nsuccs, nparents, nmetadata;
545 unsigned char nsuccs, nparents, nmetadata;
737 Py_ssize_t hashwidth = 20;
546 Py_ssize_t hashwidth = 20;
738
547
739 PyObject *prec = NULL, *parents = NULL, *succs = NULL;
548 PyObject *prec = NULL, *parents = NULL, *succs = NULL;
740 PyObject *metadata = NULL, *ret = NULL;
549 PyObject *metadata = NULL, *ret = NULL;
741 int i;
550 int i;
742
551
743 if (data + FM1_HEADER_SIZE > dataend) {
552 if (data + FM1_HEADER_SIZE > dataend) {
744 goto overflow;
553 goto overflow;
745 }
554 }
746
555
747 *msize = getbe32(data);
556 *msize = getbe32(data);
748 data += 4;
557 data += 4;
749 mtime = getbefloat64(data);
558 mtime = getbefloat64(data);
750 data += 8;
559 data += 8;
751 tz = getbeint16(data);
560 tz = getbeint16(data);
752 data += 2;
561 data += 2;
753 flags = getbeuint16(data);
562 flags = getbeuint16(data);
754 data += 2;
563 data += 2;
755
564
756 if (flags & USING_SHA_256) {
565 if (flags & USING_SHA_256) {
757 hashwidth = 32;
566 hashwidth = 32;
758 }
567 }
759
568
760 nsuccs = (unsigned char)(*data++);
569 nsuccs = (unsigned char)(*data++);
761 nparents = (unsigned char)(*data++);
570 nparents = (unsigned char)(*data++);
762 nmetadata = (unsigned char)(*data++);
571 nmetadata = (unsigned char)(*data++);
763
572
764 if (databegin + *msize > dataend) {
573 if (databegin + *msize > dataend) {
765 goto overflow;
574 goto overflow;
766 }
575 }
767 dataend = databegin + *msize; /* narrow down to marker size */
576 dataend = databegin + *msize; /* narrow down to marker size */
768
577
769 if (data + hashwidth > dataend) {
578 if (data + hashwidth > dataend) {
770 goto overflow;
579 goto overflow;
771 }
580 }
772 prec = PyBytes_FromStringAndSize(data, hashwidth);
581 prec = PyBytes_FromStringAndSize(data, hashwidth);
773 data += hashwidth;
582 data += hashwidth;
774 if (prec == NULL) {
583 if (prec == NULL) {
775 goto bail;
584 goto bail;
776 }
585 }
777
586
778 if (data + nsuccs * hashwidth > dataend) {
587 if (data + nsuccs * hashwidth > dataend) {
779 goto overflow;
588 goto overflow;
780 }
589 }
781 succs = readshas(data, nsuccs, hashwidth);
590 succs = readshas(data, nsuccs, hashwidth);
782 if (succs == NULL) {
591 if (succs == NULL) {
783 goto bail;
592 goto bail;
784 }
593 }
785 data += nsuccs * hashwidth;
594 data += nsuccs * hashwidth;
786
595
787 if (nparents == 1 || nparents == 2) {
596 if (nparents == 1 || nparents == 2) {
788 if (data + nparents * hashwidth > dataend) {
597 if (data + nparents * hashwidth > dataend) {
789 goto overflow;
598 goto overflow;
790 }
599 }
791 parents = readshas(data, nparents, hashwidth);
600 parents = readshas(data, nparents, hashwidth);
792 if (parents == NULL) {
601 if (parents == NULL) {
793 goto bail;
602 goto bail;
794 }
603 }
795 data += nparents * hashwidth;
604 data += nparents * hashwidth;
796 } else {
605 } else {
797 parents = Py_None;
606 parents = Py_None;
798 Py_INCREF(parents);
607 Py_INCREF(parents);
799 }
608 }
800
609
801 if (data + 2 * nmetadata > dataend) {
610 if (data + 2 * nmetadata > dataend) {
802 goto overflow;
611 goto overflow;
803 }
612 }
804 meta = data + (2 * nmetadata);
613 meta = data + (2 * nmetadata);
805 metadata = PyTuple_New(nmetadata);
614 metadata = PyTuple_New(nmetadata);
806 if (metadata == NULL) {
615 if (metadata == NULL) {
807 goto bail;
616 goto bail;
808 }
617 }
809 for (i = 0; i < nmetadata; i++) {
618 for (i = 0; i < nmetadata; i++) {
810 PyObject *tmp, *left = NULL, *right = NULL;
619 PyObject *tmp, *left = NULL, *right = NULL;
811 Py_ssize_t leftsize = (unsigned char)(*data++);
620 Py_ssize_t leftsize = (unsigned char)(*data++);
812 Py_ssize_t rightsize = (unsigned char)(*data++);
621 Py_ssize_t rightsize = (unsigned char)(*data++);
813 if (meta + leftsize + rightsize > dataend) {
622 if (meta + leftsize + rightsize > dataend) {
814 goto overflow;
623 goto overflow;
815 }
624 }
816 left = PyBytes_FromStringAndSize(meta, leftsize);
625 left = PyBytes_FromStringAndSize(meta, leftsize);
817 meta += leftsize;
626 meta += leftsize;
818 right = PyBytes_FromStringAndSize(meta, rightsize);
627 right = PyBytes_FromStringAndSize(meta, rightsize);
819 meta += rightsize;
628 meta += rightsize;
820 tmp = PyTuple_New(2);
629 tmp = PyTuple_New(2);
821 if (!left || !right || !tmp) {
630 if (!left || !right || !tmp) {
822 Py_XDECREF(left);
631 Py_XDECREF(left);
823 Py_XDECREF(right);
632 Py_XDECREF(right);
824 Py_XDECREF(tmp);
633 Py_XDECREF(tmp);
825 goto bail;
634 goto bail;
826 }
635 }
827 PyTuple_SET_ITEM(tmp, 0, left);
636 PyTuple_SET_ITEM(tmp, 0, left);
828 PyTuple_SET_ITEM(tmp, 1, right);
637 PyTuple_SET_ITEM(tmp, 1, right);
829 PyTuple_SET_ITEM(metadata, i, tmp);
638 PyTuple_SET_ITEM(metadata, i, tmp);
830 }
639 }
831 ret = Py_BuildValue("(OOHO(di)O)", prec, succs, flags,
640 ret = Py_BuildValue("(OOHO(di)O)", prec, succs, flags,
832 metadata, mtime, (int)tz * 60, parents);
641 metadata, mtime, (int)tz * 60, parents);
833 goto bail; /* return successfully */
642 goto bail; /* return successfully */
834
643
835 overflow:
644 overflow:
836 PyErr_SetString(PyExc_ValueError, "overflow in obsstore");
645 PyErr_SetString(PyExc_ValueError, "overflow in obsstore");
837 bail:
646 bail:
838 Py_XDECREF(prec);
647 Py_XDECREF(prec);
839 Py_XDECREF(succs);
648 Py_XDECREF(succs);
840 Py_XDECREF(metadata);
649 Py_XDECREF(metadata);
841 Py_XDECREF(parents);
650 Py_XDECREF(parents);
842 return ret;
651 return ret;
843 }
652 }
844
653
845
654
846 static PyObject *fm1readmarkers(PyObject *self, PyObject *args) {
655 static PyObject *fm1readmarkers(PyObject *self, PyObject *args) {
847 const char *data, *dataend;
656 const char *data, *dataend;
848 int datalen;
657 int datalen;
849 Py_ssize_t offset, stop;
658 Py_ssize_t offset, stop;
850 PyObject *markers = NULL;
659 PyObject *markers = NULL;
851
660
852 if (!PyArg_ParseTuple(args, "s#nn", &data, &datalen, &offset, &stop)) {
661 if (!PyArg_ParseTuple(args, "s#nn", &data, &datalen, &offset, &stop)) {
853 return NULL;
662 return NULL;
854 }
663 }
855 dataend = data + datalen;
664 dataend = data + datalen;
856 data += offset;
665 data += offset;
857 markers = PyList_New(0);
666 markers = PyList_New(0);
858 if (!markers) {
667 if (!markers) {
859 return NULL;
668 return NULL;
860 }
669 }
861 while (offset < stop) {
670 while (offset < stop) {
862 uint32_t msize;
671 uint32_t msize;
863 int error;
672 int error;
864 PyObject *record = fm1readmarker(data, dataend, &msize);
673 PyObject *record = fm1readmarker(data, dataend, &msize);
865 if (!record) {
674 if (!record) {
866 goto bail;
675 goto bail;
867 }
676 }
868 error = PyList_Append(markers, record);
677 error = PyList_Append(markers, record);
869 Py_DECREF(record);
678 Py_DECREF(record);
870 if (error) {
679 if (error) {
871 goto bail;
680 goto bail;
872 }
681 }
873 data += msize;
682 data += msize;
874 offset += msize;
683 offset += msize;
875 }
684 }
876 return markers;
685 return markers;
877 bail:
686 bail:
878 Py_DECREF(markers);
687 Py_DECREF(markers);
879 return NULL;
688 return NULL;
880 }
689 }
881
690
882 static char parsers_doc[] = "Efficient content parsing.";
691 static char parsers_doc[] = "Efficient content parsing.";
883
692
884 PyObject *encodedir(PyObject *self, PyObject *args);
693 PyObject *encodedir(PyObject *self, PyObject *args);
885 PyObject *pathencode(PyObject *self, PyObject *args);
694 PyObject *pathencode(PyObject *self, PyObject *args);
886 PyObject *lowerencode(PyObject *self, PyObject *args);
695 PyObject *lowerencode(PyObject *self, PyObject *args);
887 PyObject *parse_index2(PyObject *self, PyObject *args);
696 PyObject *parse_index2(PyObject *self, PyObject *args);
888
697
889 static PyMethodDef methods[] = {
698 static PyMethodDef methods[] = {
890 {"pack_dirstate", pack_dirstate, METH_VARARGS, "pack a dirstate\n"},
699 {"pack_dirstate", pack_dirstate, METH_VARARGS, "pack a dirstate\n"},
891 {"nonnormalotherparententries", nonnormalotherparententries, METH_VARARGS,
700 {"nonnormalotherparententries", nonnormalotherparententries, METH_VARARGS,
892 "create a set containing non-normal and other parent entries of given "
701 "create a set containing non-normal and other parent entries of given "
893 "dirstate\n"},
702 "dirstate\n"},
894 {"parse_manifest", parse_manifest, METH_VARARGS, "parse a manifest\n"},
703 {"parse_manifest", parse_manifest, METH_VARARGS, "parse a manifest\n"},
895 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
704 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
896 {"parse_index2", parse_index2, METH_VARARGS, "parse a revlog index\n"},
705 {"parse_index2", parse_index2, METH_VARARGS, "parse a revlog index\n"},
897 {"asciilower", asciilower, METH_VARARGS, "lowercase an ASCII string\n"},
706 {"asciilower", asciilower, METH_VARARGS, "lowercase an ASCII string\n"},
898 {"asciiupper", asciiupper, METH_VARARGS, "uppercase an ASCII string\n"},
707 {"asciiupper", asciiupper, METH_VARARGS, "uppercase an ASCII string\n"},
899 {"dict_new_presized", dict_new_presized, METH_VARARGS,
708 {"dict_new_presized", dict_new_presized, METH_VARARGS,
900 "construct a dict with an expected size\n"},
709 "construct a dict with an expected size\n"},
901 {"make_file_foldmap", make_file_foldmap, METH_VARARGS,
710 {"make_file_foldmap", make_file_foldmap, METH_VARARGS,
902 "make file foldmap\n"},
711 "make file foldmap\n"},
903 {"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"},
712 {"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"},
904 {"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"},
713 {"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"},
905 {"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"},
714 {"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"},
906 {"fm1readmarkers", fm1readmarkers, METH_VARARGS,
715 {"fm1readmarkers", fm1readmarkers, METH_VARARGS,
907 "parse v1 obsolete markers\n"},
716 "parse v1 obsolete markers\n"},
908 {NULL, NULL}
717 {NULL, NULL}
909 };
718 };
910
719
911 void dirs_module_init(PyObject *mod);
720 void dirs_module_init(PyObject *mod);
912 void manifest_module_init(PyObject *mod);
721 void manifest_module_init(PyObject *mod);
913 void revlog_module_init(PyObject *mod);
722 void revlog_module_init(PyObject *mod);
914
723
915 static const int version = 1;
724 static const int version = 1;
916
725
917 static void module_init(PyObject *mod)
726 static void module_init(PyObject *mod)
918 {
727 {
919 PyModule_AddIntConstant(mod, "version", version);
728 PyModule_AddIntConstant(mod, "version", version);
920
729
921 /* This module constant has two purposes. First, it lets us unit test
730 /* This module constant has two purposes. First, it lets us unit test
922 * the ImportError raised without hard-coding any error text. This
731 * the ImportError raised without hard-coding any error text. This
923 * means we can change the text in the future without breaking tests,
732 * means we can change the text in the future without breaking tests,
924 * even across changesets without a recompile. Second, its presence
733 * even across changesets without a recompile. Second, its presence
925 * can be used to determine whether the version-checking logic is
734 * can be used to determine whether the version-checking logic is
926 * present, which also helps in testing across changesets without a
735 * present, which also helps in testing across changesets without a
927 * recompile. Note that this means the pure-Python version of parsers
736 * recompile. Note that this means the pure-Python version of parsers
928 * should not have this module constant. */
737 * should not have this module constant. */
929 PyModule_AddStringConstant(mod, "versionerrortext", versionerrortext);
738 PyModule_AddStringConstant(mod, "versionerrortext", versionerrortext);
930
739
931 dirs_module_init(mod);
740 dirs_module_init(mod);
932 manifest_module_init(mod);
741 manifest_module_init(mod);
933 revlog_module_init(mod);
742 revlog_module_init(mod);
934
743
935 if (PyType_Ready(&dirstateTupleType) < 0)
744 if (PyType_Ready(&dirstateTupleType) < 0)
936 return;
745 return;
937 Py_INCREF(&dirstateTupleType);
746 Py_INCREF(&dirstateTupleType);
938 PyModule_AddObject(mod, "dirstatetuple",
747 PyModule_AddObject(mod, "dirstatetuple",
939 (PyObject *)&dirstateTupleType);
748 (PyObject *)&dirstateTupleType);
940 }
749 }
941
750
942 static int check_python_version(void)
751 static int check_python_version(void)
943 {
752 {
944 PyObject *sys = PyImport_ImportModule("sys"), *ver;
753 PyObject *sys = PyImport_ImportModule("sys"), *ver;
945 long hexversion;
754 long hexversion;
946 if (!sys)
755 if (!sys)
947 return -1;
756 return -1;
948 ver = PyObject_GetAttrString(sys, "hexversion");
757 ver = PyObject_GetAttrString(sys, "hexversion");
949 Py_DECREF(sys);
758 Py_DECREF(sys);
950 if (!ver)
759 if (!ver)
951 return -1;
760 return -1;
952 hexversion = PyInt_AsLong(ver);
761 hexversion = PyInt_AsLong(ver);
953 Py_DECREF(ver);
762 Py_DECREF(ver);
954 /* sys.hexversion is a 32-bit number by default, so the -1 case
763 /* sys.hexversion is a 32-bit number by default, so the -1 case
955 * should only occur in unusual circumstances (e.g. if sys.hexversion
764 * should only occur in unusual circumstances (e.g. if sys.hexversion
956 * is manually set to an invalid value). */
765 * is manually set to an invalid value). */
957 if ((hexversion == -1) || (hexversion >> 16 != PY_VERSION_HEX >> 16)) {
766 if ((hexversion == -1) || (hexversion >> 16 != PY_VERSION_HEX >> 16)) {
958 PyErr_Format(PyExc_ImportError, "%s: The Mercurial extension "
767 PyErr_Format(PyExc_ImportError, "%s: The Mercurial extension "
959 "modules were compiled with Python " PY_VERSION ", but "
768 "modules were compiled with Python " PY_VERSION ", but "
960 "Mercurial is currently using Python with sys.hexversion=%ld: "
769 "Mercurial is currently using Python with sys.hexversion=%ld: "
961 "Python %s\n at: %s", versionerrortext, hexversion,
770 "Python %s\n at: %s", versionerrortext, hexversion,
962 Py_GetVersion(), Py_GetProgramFullPath());
771 Py_GetVersion(), Py_GetProgramFullPath());
963 return -1;
772 return -1;
964 }
773 }
965 return 0;
774 return 0;
966 }
775 }
967
776
968 #ifdef IS_PY3K
777 #ifdef IS_PY3K
969 static struct PyModuleDef parsers_module = {
778 static struct PyModuleDef parsers_module = {
970 PyModuleDef_HEAD_INIT,
779 PyModuleDef_HEAD_INIT,
971 "parsers",
780 "parsers",
972 parsers_doc,
781 parsers_doc,
973 -1,
782 -1,
974 methods
783 methods
975 };
784 };
976
785
977 PyMODINIT_FUNC PyInit_parsers(void)
786 PyMODINIT_FUNC PyInit_parsers(void)
978 {
787 {
979 PyObject *mod;
788 PyObject *mod;
980
789
981 if (check_python_version() == -1)
790 if (check_python_version() == -1)
982 return NULL;
791 return NULL;
983 mod = PyModule_Create(&parsers_module);
792 mod = PyModule_Create(&parsers_module);
984 module_init(mod);
793 module_init(mod);
985 return mod;
794 return mod;
986 }
795 }
987 #else
796 #else
988 PyMODINIT_FUNC initparsers(void)
797 PyMODINIT_FUNC initparsers(void)
989 {
798 {
990 PyObject *mod;
799 PyObject *mod;
991
800
992 if (check_python_version() == -1)
801 if (check_python_version() == -1)
993 return;
802 return;
994 mod = Py_InitModule3("parsers", methods, parsers_doc);
803 mod = Py_InitModule3("parsers", methods, parsers_doc);
995 module_init(mod);
804 module_init(mod);
996 }
805 }
997 #endif
806 #endif
@@ -1,952 +1,953
1 #
1 #
2 # This is the mercurial setup script.
2 # This is the mercurial setup script.
3 #
3 #
4 # 'python setup.py install', or
4 # 'python setup.py install', or
5 # 'python setup.py --help' for more options
5 # 'python setup.py --help' for more options
6
6
7 import os
7 import os
8
8
9 supportedpy = '~= 2.7'
9 supportedpy = '~= 2.7'
10 if os.environ.get('HGALLOWPYTHON3', ''):
10 if os.environ.get('HGALLOWPYTHON3', ''):
11 # Mercurial will never work on Python 3 before 3.5 due to a lack
11 # Mercurial will never work on Python 3 before 3.5 due to a lack
12 # of % formatting on bytestrings, and can't work on 3.6.0 or 3.6.1
12 # of % formatting on bytestrings, and can't work on 3.6.0 or 3.6.1
13 # due to a bug in % formatting in bytestrings.
13 # due to a bug in % formatting in bytestrings.
14 #
14 #
15 # TODO: when we actually work on Python 3, use this string as the
15 # TODO: when we actually work on Python 3, use this string as the
16 # actual supportedpy string.
16 # actual supportedpy string.
17 supportedpy = ','.join([
17 supportedpy = ','.join([
18 '>=2.7',
18 '>=2.7',
19 '!=3.0.*',
19 '!=3.0.*',
20 '!=3.1.*',
20 '!=3.1.*',
21 '!=3.2.*',
21 '!=3.2.*',
22 '!=3.3.*',
22 '!=3.3.*',
23 '!=3.4.*',
23 '!=3.4.*',
24 '!=3.6.0',
24 '!=3.6.0',
25 '!=3.6.1',
25 '!=3.6.1',
26 ])
26 ])
27
27
28 import sys, platform
28 import sys, platform
29 if sys.version_info[0] >= 3:
29 if sys.version_info[0] >= 3:
30 printf = eval('print')
30 printf = eval('print')
31 libdir_escape = 'unicode_escape'
31 libdir_escape = 'unicode_escape'
32 else:
32 else:
33 libdir_escape = 'string_escape'
33 libdir_escape = 'string_escape'
34 def printf(*args, **kwargs):
34 def printf(*args, **kwargs):
35 f = kwargs.get('file', sys.stdout)
35 f = kwargs.get('file', sys.stdout)
36 end = kwargs.get('end', '\n')
36 end = kwargs.get('end', '\n')
37 f.write(b' '.join(args) + end)
37 f.write(b' '.join(args) + end)
38
38
39 # Attempt to guide users to a modern pip - this means that 2.6 users
39 # Attempt to guide users to a modern pip - this means that 2.6 users
40 # should have a chance of getting a 4.2 release, and when we ratchet
40 # should have a chance of getting a 4.2 release, and when we ratchet
41 # the version requirement forward again hopefully everyone will get
41 # the version requirement forward again hopefully everyone will get
42 # something that works for them.
42 # something that works for them.
43 if sys.version_info < (2, 7, 0, 'final'):
43 if sys.version_info < (2, 7, 0, 'final'):
44 pip_message = ('This may be due to an out of date pip. '
44 pip_message = ('This may be due to an out of date pip. '
45 'Make sure you have pip >= 9.0.1.')
45 'Make sure you have pip >= 9.0.1.')
46 try:
46 try:
47 import pip
47 import pip
48 pip_version = tuple([int(x) for x in pip.__version__.split('.')[:3]])
48 pip_version = tuple([int(x) for x in pip.__version__.split('.')[:3]])
49 if pip_version < (9, 0, 1) :
49 if pip_version < (9, 0, 1) :
50 pip_message = (
50 pip_message = (
51 'Your pip version is out of date, please install '
51 'Your pip version is out of date, please install '
52 'pip >= 9.0.1. pip {} detected.'.format(pip.__version__))
52 'pip >= 9.0.1. pip {} detected.'.format(pip.__version__))
53 else:
53 else:
54 # pip is new enough - it must be something else
54 # pip is new enough - it must be something else
55 pip_message = ''
55 pip_message = ''
56 except Exception:
56 except Exception:
57 pass
57 pass
58 error = """
58 error = """
59 Mercurial does not support Python older than 2.7.
59 Mercurial does not support Python older than 2.7.
60 Python {py} detected.
60 Python {py} detected.
61 {pip}
61 {pip}
62 """.format(py=sys.version_info, pip=pip_message)
62 """.format(py=sys.version_info, pip=pip_message)
63 printf(error, file=sys.stderr)
63 printf(error, file=sys.stderr)
64 sys.exit(1)
64 sys.exit(1)
65
65
66 # Solaris Python packaging brain damage
66 # Solaris Python packaging brain damage
67 try:
67 try:
68 import hashlib
68 import hashlib
69 sha = hashlib.sha1()
69 sha = hashlib.sha1()
70 except ImportError:
70 except ImportError:
71 try:
71 try:
72 import sha
72 import sha
73 sha.sha # silence unused import warning
73 sha.sha # silence unused import warning
74 except ImportError:
74 except ImportError:
75 raise SystemExit(
75 raise SystemExit(
76 "Couldn't import standard hashlib (incomplete Python install).")
76 "Couldn't import standard hashlib (incomplete Python install).")
77
77
78 try:
78 try:
79 import zlib
79 import zlib
80 zlib.compressobj # silence unused import warning
80 zlib.compressobj # silence unused import warning
81 except ImportError:
81 except ImportError:
82 raise SystemExit(
82 raise SystemExit(
83 "Couldn't import standard zlib (incomplete Python install).")
83 "Couldn't import standard zlib (incomplete Python install).")
84
84
85 # The base IronPython distribution (as of 2.7.1) doesn't support bz2
85 # The base IronPython distribution (as of 2.7.1) doesn't support bz2
86 isironpython = False
86 isironpython = False
87 try:
87 try:
88 isironpython = (platform.python_implementation()
88 isironpython = (platform.python_implementation()
89 .lower().find("ironpython") != -1)
89 .lower().find("ironpython") != -1)
90 except AttributeError:
90 except AttributeError:
91 pass
91 pass
92
92
93 if isironpython:
93 if isironpython:
94 sys.stderr.write("warning: IronPython detected (no bz2 support)\n")
94 sys.stderr.write("warning: IronPython detected (no bz2 support)\n")
95 else:
95 else:
96 try:
96 try:
97 import bz2
97 import bz2
98 bz2.BZ2Compressor # silence unused import warning
98 bz2.BZ2Compressor # silence unused import warning
99 except ImportError:
99 except ImportError:
100 raise SystemExit(
100 raise SystemExit(
101 "Couldn't import standard bz2 (incomplete Python install).")
101 "Couldn't import standard bz2 (incomplete Python install).")
102
102
103 ispypy = "PyPy" in sys.version
103 ispypy = "PyPy" in sys.version
104
104
105 import ctypes
105 import ctypes
106 import stat, subprocess, time
106 import stat, subprocess, time
107 import re
107 import re
108 import shutil
108 import shutil
109 import tempfile
109 import tempfile
110 from distutils import log
110 from distutils import log
111 # We have issues with setuptools on some platforms and builders. Until
111 # We have issues with setuptools on some platforms and builders. Until
112 # those are resolved, setuptools is opt-in except for platforms where
112 # those are resolved, setuptools is opt-in except for platforms where
113 # we don't have issues.
113 # we don't have issues.
114 issetuptools = (os.name == 'nt' or 'FORCE_SETUPTOOLS' in os.environ)
114 issetuptools = (os.name == 'nt' or 'FORCE_SETUPTOOLS' in os.environ)
115 if issetuptools:
115 if issetuptools:
116 from setuptools import setup
116 from setuptools import setup
117 else:
117 else:
118 from distutils.core import setup
118 from distutils.core import setup
119 from distutils.ccompiler import new_compiler
119 from distutils.ccompiler import new_compiler
120 from distutils.core import Command, Extension
120 from distutils.core import Command, Extension
121 from distutils.dist import Distribution
121 from distutils.dist import Distribution
122 from distutils.command.build import build
122 from distutils.command.build import build
123 from distutils.command.build_ext import build_ext
123 from distutils.command.build_ext import build_ext
124 from distutils.command.build_py import build_py
124 from distutils.command.build_py import build_py
125 from distutils.command.build_scripts import build_scripts
125 from distutils.command.build_scripts import build_scripts
126 from distutils.command.install import install
126 from distutils.command.install import install
127 from distutils.command.install_lib import install_lib
127 from distutils.command.install_lib import install_lib
128 from distutils.command.install_scripts import install_scripts
128 from distutils.command.install_scripts import install_scripts
129 from distutils.spawn import spawn, find_executable
129 from distutils.spawn import spawn, find_executable
130 from distutils import file_util
130 from distutils import file_util
131 from distutils.errors import (
131 from distutils.errors import (
132 CCompilerError,
132 CCompilerError,
133 DistutilsError,
133 DistutilsError,
134 DistutilsExecError,
134 DistutilsExecError,
135 )
135 )
136 from distutils.sysconfig import get_python_inc, get_config_var
136 from distutils.sysconfig import get_python_inc, get_config_var
137 from distutils.version import StrictVersion
137 from distutils.version import StrictVersion
138
138
139 scripts = ['hg']
139 scripts = ['hg']
140 if os.name == 'nt':
140 if os.name == 'nt':
141 # We remove hg.bat if we are able to build hg.exe.
141 # We remove hg.bat if we are able to build hg.exe.
142 scripts.append('contrib/win32/hg.bat')
142 scripts.append('contrib/win32/hg.bat')
143
143
144 def cancompile(cc, code):
144 def cancompile(cc, code):
145 tmpdir = tempfile.mkdtemp(prefix='hg-install-')
145 tmpdir = tempfile.mkdtemp(prefix='hg-install-')
146 devnull = oldstderr = None
146 devnull = oldstderr = None
147 try:
147 try:
148 fname = os.path.join(tmpdir, 'testcomp.c')
148 fname = os.path.join(tmpdir, 'testcomp.c')
149 f = open(fname, 'w')
149 f = open(fname, 'w')
150 f.write(code)
150 f.write(code)
151 f.close()
151 f.close()
152 # Redirect stderr to /dev/null to hide any error messages
152 # Redirect stderr to /dev/null to hide any error messages
153 # from the compiler.
153 # from the compiler.
154 # This will have to be changed if we ever have to check
154 # This will have to be changed if we ever have to check
155 # for a function on Windows.
155 # for a function on Windows.
156 devnull = open('/dev/null', 'w')
156 devnull = open('/dev/null', 'w')
157 oldstderr = os.dup(sys.stderr.fileno())
157 oldstderr = os.dup(sys.stderr.fileno())
158 os.dup2(devnull.fileno(), sys.stderr.fileno())
158 os.dup2(devnull.fileno(), sys.stderr.fileno())
159 objects = cc.compile([fname], output_dir=tmpdir)
159 objects = cc.compile([fname], output_dir=tmpdir)
160 cc.link_executable(objects, os.path.join(tmpdir, "a.out"))
160 cc.link_executable(objects, os.path.join(tmpdir, "a.out"))
161 return True
161 return True
162 except Exception:
162 except Exception:
163 return False
163 return False
164 finally:
164 finally:
165 if oldstderr is not None:
165 if oldstderr is not None:
166 os.dup2(oldstderr, sys.stderr.fileno())
166 os.dup2(oldstderr, sys.stderr.fileno())
167 if devnull is not None:
167 if devnull is not None:
168 devnull.close()
168 devnull.close()
169 shutil.rmtree(tmpdir)
169 shutil.rmtree(tmpdir)
170
170
171 # simplified version of distutils.ccompiler.CCompiler.has_function
171 # simplified version of distutils.ccompiler.CCompiler.has_function
172 # that actually removes its temporary files.
172 # that actually removes its temporary files.
173 def hasfunction(cc, funcname):
173 def hasfunction(cc, funcname):
174 code = 'int main(void) { %s(); }\n' % funcname
174 code = 'int main(void) { %s(); }\n' % funcname
175 return cancompile(cc, code)
175 return cancompile(cc, code)
176
176
177 def hasheader(cc, headername):
177 def hasheader(cc, headername):
178 code = '#include <%s>\nint main(void) { return 0; }\n' % headername
178 code = '#include <%s>\nint main(void) { return 0; }\n' % headername
179 return cancompile(cc, code)
179 return cancompile(cc, code)
180
180
181 # py2exe needs to be installed to work
181 # py2exe needs to be installed to work
182 try:
182 try:
183 import py2exe
183 import py2exe
184 py2exe.Distribution # silence unused import warning
184 py2exe.Distribution # silence unused import warning
185 py2exeloaded = True
185 py2exeloaded = True
186 # import py2exe's patched Distribution class
186 # import py2exe's patched Distribution class
187 from distutils.core import Distribution
187 from distutils.core import Distribution
188 except ImportError:
188 except ImportError:
189 py2exeloaded = False
189 py2exeloaded = False
190
190
191 def runcmd(cmd, env):
191 def runcmd(cmd, env):
192 p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
192 p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
193 stderr=subprocess.PIPE, env=env)
193 stderr=subprocess.PIPE, env=env)
194 out, err = p.communicate()
194 out, err = p.communicate()
195 return p.returncode, out, err
195 return p.returncode, out, err
196
196
197 class hgcommand(object):
197 class hgcommand(object):
198 def __init__(self, cmd, env):
198 def __init__(self, cmd, env):
199 self.cmd = cmd
199 self.cmd = cmd
200 self.env = env
200 self.env = env
201
201
202 def run(self, args):
202 def run(self, args):
203 cmd = self.cmd + args
203 cmd = self.cmd + args
204 returncode, out, err = runcmd(cmd, self.env)
204 returncode, out, err = runcmd(cmd, self.env)
205 err = filterhgerr(err)
205 err = filterhgerr(err)
206 if err or returncode != 0:
206 if err or returncode != 0:
207 printf("stderr from '%s':" % (' '.join(cmd)), file=sys.stderr)
207 printf("stderr from '%s':" % (' '.join(cmd)), file=sys.stderr)
208 printf(err, file=sys.stderr)
208 printf(err, file=sys.stderr)
209 return ''
209 return ''
210 return out
210 return out
211
211
212 def filterhgerr(err):
212 def filterhgerr(err):
213 # If root is executing setup.py, but the repository is owned by
213 # If root is executing setup.py, but the repository is owned by
214 # another user (as in "sudo python setup.py install") we will get
214 # another user (as in "sudo python setup.py install") we will get
215 # trust warnings since the .hg/hgrc file is untrusted. That is
215 # trust warnings since the .hg/hgrc file is untrusted. That is
216 # fine, we don't want to load it anyway. Python may warn about
216 # fine, we don't want to load it anyway. Python may warn about
217 # a missing __init__.py in mercurial/locale, we also ignore that.
217 # a missing __init__.py in mercurial/locale, we also ignore that.
218 err = [e for e in err.splitlines()
218 err = [e for e in err.splitlines()
219 if (not e.startswith(b'not trusting file')
219 if (not e.startswith(b'not trusting file')
220 and not e.startswith(b'warning: Not importing')
220 and not e.startswith(b'warning: Not importing')
221 and not e.startswith(b'obsolete feature not enabled'))]
221 and not e.startswith(b'obsolete feature not enabled'))]
222 return b'\n'.join(b' ' + e for e in err)
222 return b'\n'.join(b' ' + e for e in err)
223
223
224 def findhg():
224 def findhg():
225 """Try to figure out how we should invoke hg for examining the local
225 """Try to figure out how we should invoke hg for examining the local
226 repository contents.
226 repository contents.
227
227
228 Returns an hgcommand object."""
228 Returns an hgcommand object."""
229 # By default, prefer the "hg" command in the user's path. This was
229 # By default, prefer the "hg" command in the user's path. This was
230 # presumably the hg command that the user used to create this repository.
230 # presumably the hg command that the user used to create this repository.
231 #
231 #
232 # This repository may require extensions or other settings that would not
232 # This repository may require extensions or other settings that would not
233 # be enabled by running the hg script directly from this local repository.
233 # be enabled by running the hg script directly from this local repository.
234 hgenv = os.environ.copy()
234 hgenv = os.environ.copy()
235 # Use HGPLAIN to disable hgrc settings that would change output formatting,
235 # Use HGPLAIN to disable hgrc settings that would change output formatting,
236 # and disable localization for the same reasons.
236 # and disable localization for the same reasons.
237 hgenv['HGPLAIN'] = '1'
237 hgenv['HGPLAIN'] = '1'
238 hgenv['LANGUAGE'] = 'C'
238 hgenv['LANGUAGE'] = 'C'
239 hgcmd = ['hg']
239 hgcmd = ['hg']
240 # Run a simple "hg log" command just to see if using hg from the user's
240 # Run a simple "hg log" command just to see if using hg from the user's
241 # path works and can successfully interact with this repository.
241 # path works and can successfully interact with this repository.
242 check_cmd = ['log', '-r.', '-Ttest']
242 check_cmd = ['log', '-r.', '-Ttest']
243 try:
243 try:
244 retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
244 retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
245 except EnvironmentError:
245 except EnvironmentError:
246 retcode = -1
246 retcode = -1
247 if retcode == 0 and not filterhgerr(err):
247 if retcode == 0 and not filterhgerr(err):
248 return hgcommand(hgcmd, hgenv)
248 return hgcommand(hgcmd, hgenv)
249
249
250 # Fall back to trying the local hg installation.
250 # Fall back to trying the local hg installation.
251 hgenv = localhgenv()
251 hgenv = localhgenv()
252 # Don't source any system hgrc files when using the local hg.
252 # Don't source any system hgrc files when using the local hg.
253 hgenv['HGRCPATH'] = ''
253 hgenv['HGRCPATH'] = ''
254 hgcmd = [sys.executable, 'hg']
254 hgcmd = [sys.executable, 'hg']
255 try:
255 try:
256 retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
256 retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
257 except EnvironmentError:
257 except EnvironmentError:
258 retcode = -1
258 retcode = -1
259 if retcode == 0 and not filterhgerr(err):
259 if retcode == 0 and not filterhgerr(err):
260 return hgcommand(hgcmd, hgenv)
260 return hgcommand(hgcmd, hgenv)
261
261
262 raise SystemExit('Unable to find a working hg binary to extract the '
262 raise SystemExit('Unable to find a working hg binary to extract the '
263 'version from the repository tags')
263 'version from the repository tags')
264
264
265 def localhgenv():
265 def localhgenv():
266 """Get an environment dictionary to use for invoking or importing
266 """Get an environment dictionary to use for invoking or importing
267 mercurial from the local repository."""
267 mercurial from the local repository."""
268 # Execute hg out of this directory with a custom environment which takes
268 # Execute hg out of this directory with a custom environment which takes
269 # care to not use any hgrc files and do no localization.
269 # care to not use any hgrc files and do no localization.
270 env = {'HGMODULEPOLICY': 'py',
270 env = {'HGMODULEPOLICY': 'py',
271 'HGRCPATH': '',
271 'HGRCPATH': '',
272 'LANGUAGE': 'C',
272 'LANGUAGE': 'C',
273 'PATH': ''} # make pypi modules that use os.environ['PATH'] happy
273 'PATH': ''} # make pypi modules that use os.environ['PATH'] happy
274 if 'LD_LIBRARY_PATH' in os.environ:
274 if 'LD_LIBRARY_PATH' in os.environ:
275 env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
275 env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
276 if 'SystemRoot' in os.environ:
276 if 'SystemRoot' in os.environ:
277 # SystemRoot is required by Windows to load various DLLs. See:
277 # SystemRoot is required by Windows to load various DLLs. See:
278 # https://bugs.python.org/issue13524#msg148850
278 # https://bugs.python.org/issue13524#msg148850
279 env['SystemRoot'] = os.environ['SystemRoot']
279 env['SystemRoot'] = os.environ['SystemRoot']
280 return env
280 return env
281
281
282 version = ''
282 version = ''
283
283
284 if os.path.isdir('.hg'):
284 if os.path.isdir('.hg'):
285 hg = findhg()
285 hg = findhg()
286 cmd = ['log', '-r', '.', '--template', '{tags}\n']
286 cmd = ['log', '-r', '.', '--template', '{tags}\n']
287 numerictags = [t for t in hg.run(cmd).split() if t[0:1].isdigit()]
287 numerictags = [t for t in hg.run(cmd).split() if t[0:1].isdigit()]
288 hgid = hg.run(['id', '-i']).strip()
288 hgid = hg.run(['id', '-i']).strip()
289 if not hgid:
289 if not hgid:
290 # Bail out if hg is having problems interacting with this repository,
290 # Bail out if hg is having problems interacting with this repository,
291 # rather than falling through and producing a bogus version number.
291 # rather than falling through and producing a bogus version number.
292 # Continuing with an invalid version number will break extensions
292 # Continuing with an invalid version number will break extensions
293 # that define minimumhgversion.
293 # that define minimumhgversion.
294 raise SystemExit('Unable to determine hg version from local repository')
294 raise SystemExit('Unable to determine hg version from local repository')
295 if numerictags: # tag(s) found
295 if numerictags: # tag(s) found
296 version = numerictags[-1]
296 version = numerictags[-1]
297 if hgid.endswith('+'): # propagate the dirty status to the tag
297 if hgid.endswith('+'): # propagate the dirty status to the tag
298 version += '+'
298 version += '+'
299 else: # no tag found
299 else: # no tag found
300 ltagcmd = ['parents', '--template', '{latesttag}']
300 ltagcmd = ['parents', '--template', '{latesttag}']
301 ltag = hg.run(ltagcmd)
301 ltag = hg.run(ltagcmd)
302 changessincecmd = ['log', '-T', 'x\n', '-r', "only(.,'%s')" % ltag]
302 changessincecmd = ['log', '-T', 'x\n', '-r', "only(.,'%s')" % ltag]
303 changessince = len(hg.run(changessincecmd).splitlines())
303 changessince = len(hg.run(changessincecmd).splitlines())
304 version = '%s+%s-%s' % (ltag, changessince, hgid)
304 version = '%s+%s-%s' % (ltag, changessince, hgid)
305 if version.endswith('+'):
305 if version.endswith('+'):
306 version += time.strftime('%Y%m%d')
306 version += time.strftime('%Y%m%d')
307 elif os.path.exists('.hg_archival.txt'):
307 elif os.path.exists('.hg_archival.txt'):
308 kw = dict([[t.strip() for t in l.split(':', 1)]
308 kw = dict([[t.strip() for t in l.split(':', 1)]
309 for l in open('.hg_archival.txt')])
309 for l in open('.hg_archival.txt')])
310 if 'tag' in kw:
310 if 'tag' in kw:
311 version = kw['tag']
311 version = kw['tag']
312 elif 'latesttag' in kw:
312 elif 'latesttag' in kw:
313 if 'changessincelatesttag' in kw:
313 if 'changessincelatesttag' in kw:
314 version = '%(latesttag)s+%(changessincelatesttag)s-%(node).12s' % kw
314 version = '%(latesttag)s+%(changessincelatesttag)s-%(node).12s' % kw
315 else:
315 else:
316 version = '%(latesttag)s+%(latesttagdistance)s-%(node).12s' % kw
316 version = '%(latesttag)s+%(latesttagdistance)s-%(node).12s' % kw
317 else:
317 else:
318 version = kw.get('node', '')[:12]
318 version = kw.get('node', '')[:12]
319
319
320 if version:
320 if version:
321 with open("mercurial/__version__.py", "w") as f:
321 with open("mercurial/__version__.py", "w") as f:
322 f.write('# this file is autogenerated by setup.py\n')
322 f.write('# this file is autogenerated by setup.py\n')
323 f.write('version = "%s"\n' % version)
323 f.write('version = "%s"\n' % version)
324
324
325 try:
325 try:
326 oldpolicy = os.environ.get('HGMODULEPOLICY', None)
326 oldpolicy = os.environ.get('HGMODULEPOLICY', None)
327 os.environ['HGMODULEPOLICY'] = 'py'
327 os.environ['HGMODULEPOLICY'] = 'py'
328 from mercurial import __version__
328 from mercurial import __version__
329 version = __version__.version
329 version = __version__.version
330 except ImportError:
330 except ImportError:
331 version = 'unknown'
331 version = 'unknown'
332 finally:
332 finally:
333 if oldpolicy is None:
333 if oldpolicy is None:
334 del os.environ['HGMODULEPOLICY']
334 del os.environ['HGMODULEPOLICY']
335 else:
335 else:
336 os.environ['HGMODULEPOLICY'] = oldpolicy
336 os.environ['HGMODULEPOLICY'] = oldpolicy
337
337
338 class hgbuild(build):
338 class hgbuild(build):
339 # Insert hgbuildmo first so that files in mercurial/locale/ are found
339 # Insert hgbuildmo first so that files in mercurial/locale/ are found
340 # when build_py is run next.
340 # when build_py is run next.
341 sub_commands = [('build_mo', None)] + build.sub_commands
341 sub_commands = [('build_mo', None)] + build.sub_commands
342
342
343 class hgbuildmo(build):
343 class hgbuildmo(build):
344
344
345 description = "build translations (.mo files)"
345 description = "build translations (.mo files)"
346
346
347 def run(self):
347 def run(self):
348 if not find_executable('msgfmt'):
348 if not find_executable('msgfmt'):
349 self.warn("could not find msgfmt executable, no translations "
349 self.warn("could not find msgfmt executable, no translations "
350 "will be built")
350 "will be built")
351 return
351 return
352
352
353 podir = 'i18n'
353 podir = 'i18n'
354 if not os.path.isdir(podir):
354 if not os.path.isdir(podir):
355 self.warn("could not find %s/ directory" % podir)
355 self.warn("could not find %s/ directory" % podir)
356 return
356 return
357
357
358 join = os.path.join
358 join = os.path.join
359 for po in os.listdir(podir):
359 for po in os.listdir(podir):
360 if not po.endswith('.po'):
360 if not po.endswith('.po'):
361 continue
361 continue
362 pofile = join(podir, po)
362 pofile = join(podir, po)
363 modir = join('locale', po[:-3], 'LC_MESSAGES')
363 modir = join('locale', po[:-3], 'LC_MESSAGES')
364 mofile = join(modir, 'hg.mo')
364 mofile = join(modir, 'hg.mo')
365 mobuildfile = join('mercurial', mofile)
365 mobuildfile = join('mercurial', mofile)
366 cmd = ['msgfmt', '-v', '-o', mobuildfile, pofile]
366 cmd = ['msgfmt', '-v', '-o', mobuildfile, pofile]
367 if sys.platform != 'sunos5':
367 if sys.platform != 'sunos5':
368 # msgfmt on Solaris does not know about -c
368 # msgfmt on Solaris does not know about -c
369 cmd.append('-c')
369 cmd.append('-c')
370 self.mkpath(join('mercurial', modir))
370 self.mkpath(join('mercurial', modir))
371 self.make_file([pofile], mobuildfile, spawn, (cmd,))
371 self.make_file([pofile], mobuildfile, spawn, (cmd,))
372
372
373
373
374 class hgdist(Distribution):
374 class hgdist(Distribution):
375 pure = False
375 pure = False
376 cffi = ispypy
376 cffi = ispypy
377
377
378 global_options = Distribution.global_options + \
378 global_options = Distribution.global_options + \
379 [('pure', None, "use pure (slow) Python "
379 [('pure', None, "use pure (slow) Python "
380 "code instead of C extensions"),
380 "code instead of C extensions"),
381 ]
381 ]
382
382
383 def has_ext_modules(self):
383 def has_ext_modules(self):
384 # self.ext_modules is emptied in hgbuildpy.finalize_options which is
384 # self.ext_modules is emptied in hgbuildpy.finalize_options which is
385 # too late for some cases
385 # too late for some cases
386 return not self.pure and Distribution.has_ext_modules(self)
386 return not self.pure and Distribution.has_ext_modules(self)
387
387
388 # This is ugly as a one-liner. So use a variable.
388 # This is ugly as a one-liner. So use a variable.
389 buildextnegops = dict(getattr(build_ext, 'negative_options', {}))
389 buildextnegops = dict(getattr(build_ext, 'negative_options', {}))
390 buildextnegops['no-zstd'] = 'zstd'
390 buildextnegops['no-zstd'] = 'zstd'
391
391
392 class hgbuildext(build_ext):
392 class hgbuildext(build_ext):
393 user_options = build_ext.user_options + [
393 user_options = build_ext.user_options + [
394 ('zstd', None, 'compile zstd bindings [default]'),
394 ('zstd', None, 'compile zstd bindings [default]'),
395 ('no-zstd', None, 'do not compile zstd bindings'),
395 ('no-zstd', None, 'do not compile zstd bindings'),
396 ]
396 ]
397
397
398 boolean_options = build_ext.boolean_options + ['zstd']
398 boolean_options = build_ext.boolean_options + ['zstd']
399 negative_opt = buildextnegops
399 negative_opt = buildextnegops
400
400
401 def initialize_options(self):
401 def initialize_options(self):
402 self.zstd = True
402 self.zstd = True
403 return build_ext.initialize_options(self)
403 return build_ext.initialize_options(self)
404
404
405 def build_extensions(self):
405 def build_extensions(self):
406 # Filter out zstd if disabled via argument.
406 # Filter out zstd if disabled via argument.
407 if not self.zstd:
407 if not self.zstd:
408 self.extensions = [e for e in self.extensions
408 self.extensions = [e for e in self.extensions
409 if e.name != 'mercurial.zstd']
409 if e.name != 'mercurial.zstd']
410
410
411 return build_ext.build_extensions(self)
411 return build_ext.build_extensions(self)
412
412
413 def build_extension(self, ext):
413 def build_extension(self, ext):
414 try:
414 try:
415 build_ext.build_extension(self, ext)
415 build_ext.build_extension(self, ext)
416 except CCompilerError:
416 except CCompilerError:
417 if not getattr(ext, 'optional', False):
417 if not getattr(ext, 'optional', False):
418 raise
418 raise
419 log.warn("Failed to build optional extension '%s' (skipping)",
419 log.warn("Failed to build optional extension '%s' (skipping)",
420 ext.name)
420 ext.name)
421
421
422 class hgbuildscripts(build_scripts):
422 class hgbuildscripts(build_scripts):
423 def run(self):
423 def run(self):
424 if os.name != 'nt' or self.distribution.pure:
424 if os.name != 'nt' or self.distribution.pure:
425 return build_scripts.run(self)
425 return build_scripts.run(self)
426
426
427 exebuilt = False
427 exebuilt = False
428 try:
428 try:
429 self.run_command('build_hgexe')
429 self.run_command('build_hgexe')
430 exebuilt = True
430 exebuilt = True
431 except (DistutilsError, CCompilerError):
431 except (DistutilsError, CCompilerError):
432 log.warn('failed to build optional hg.exe')
432 log.warn('failed to build optional hg.exe')
433
433
434 if exebuilt:
434 if exebuilt:
435 # Copying hg.exe to the scripts build directory ensures it is
435 # Copying hg.exe to the scripts build directory ensures it is
436 # installed by the install_scripts command.
436 # installed by the install_scripts command.
437 hgexecommand = self.get_finalized_command('build_hgexe')
437 hgexecommand = self.get_finalized_command('build_hgexe')
438 dest = os.path.join(self.build_dir, 'hg.exe')
438 dest = os.path.join(self.build_dir, 'hg.exe')
439 self.mkpath(self.build_dir)
439 self.mkpath(self.build_dir)
440 self.copy_file(hgexecommand.hgexepath, dest)
440 self.copy_file(hgexecommand.hgexepath, dest)
441
441
442 # Remove hg.bat because it is redundant with hg.exe.
442 # Remove hg.bat because it is redundant with hg.exe.
443 self.scripts.remove('contrib/win32/hg.bat')
443 self.scripts.remove('contrib/win32/hg.bat')
444
444
445 return build_scripts.run(self)
445 return build_scripts.run(self)
446
446
447 class hgbuildpy(build_py):
447 class hgbuildpy(build_py):
448 def finalize_options(self):
448 def finalize_options(self):
449 build_py.finalize_options(self)
449 build_py.finalize_options(self)
450
450
451 if self.distribution.pure:
451 if self.distribution.pure:
452 self.distribution.ext_modules = []
452 self.distribution.ext_modules = []
453 elif self.distribution.cffi:
453 elif self.distribution.cffi:
454 from mercurial.cffi import (
454 from mercurial.cffi import (
455 bdiffbuild,
455 bdiffbuild,
456 mpatchbuild,
456 mpatchbuild,
457 )
457 )
458 exts = [mpatchbuild.ffi.distutils_extension(),
458 exts = [mpatchbuild.ffi.distutils_extension(),
459 bdiffbuild.ffi.distutils_extension()]
459 bdiffbuild.ffi.distutils_extension()]
460 # cffi modules go here
460 # cffi modules go here
461 if sys.platform == 'darwin':
461 if sys.platform == 'darwin':
462 from mercurial.cffi import osutilbuild
462 from mercurial.cffi import osutilbuild
463 exts.append(osutilbuild.ffi.distutils_extension())
463 exts.append(osutilbuild.ffi.distutils_extension())
464 self.distribution.ext_modules = exts
464 self.distribution.ext_modules = exts
465 else:
465 else:
466 h = os.path.join(get_python_inc(), 'Python.h')
466 h = os.path.join(get_python_inc(), 'Python.h')
467 if not os.path.exists(h):
467 if not os.path.exists(h):
468 raise SystemExit('Python headers are required to build '
468 raise SystemExit('Python headers are required to build '
469 'Mercurial but weren\'t found in %s' % h)
469 'Mercurial but weren\'t found in %s' % h)
470
470
471 def run(self):
471 def run(self):
472 basepath = os.path.join(self.build_lib, 'mercurial')
472 basepath = os.path.join(self.build_lib, 'mercurial')
473 self.mkpath(basepath)
473 self.mkpath(basepath)
474
474
475 if self.distribution.pure:
475 if self.distribution.pure:
476 modulepolicy = 'py'
476 modulepolicy = 'py'
477 elif self.build_lib == '.':
477 elif self.build_lib == '.':
478 # in-place build should run without rebuilding C extensions
478 # in-place build should run without rebuilding C extensions
479 modulepolicy = 'allow'
479 modulepolicy = 'allow'
480 else:
480 else:
481 modulepolicy = 'c'
481 modulepolicy = 'c'
482 with open(os.path.join(basepath, '__modulepolicy__.py'), "w") as f:
482 with open(os.path.join(basepath, '__modulepolicy__.py'), "w") as f:
483 f.write('# this file is autogenerated by setup.py\n')
483 f.write('# this file is autogenerated by setup.py\n')
484 f.write('modulepolicy = b"%s"\n' % modulepolicy)
484 f.write('modulepolicy = b"%s"\n' % modulepolicy)
485
485
486 build_py.run(self)
486 build_py.run(self)
487
487
488 class buildhgextindex(Command):
488 class buildhgextindex(Command):
489 description = 'generate prebuilt index of hgext (for frozen package)'
489 description = 'generate prebuilt index of hgext (for frozen package)'
490 user_options = []
490 user_options = []
491 _indexfilename = 'hgext/__index__.py'
491 _indexfilename = 'hgext/__index__.py'
492
492
493 def initialize_options(self):
493 def initialize_options(self):
494 pass
494 pass
495
495
496 def finalize_options(self):
496 def finalize_options(self):
497 pass
497 pass
498
498
499 def run(self):
499 def run(self):
500 if os.path.exists(self._indexfilename):
500 if os.path.exists(self._indexfilename):
501 with open(self._indexfilename, 'w') as f:
501 with open(self._indexfilename, 'w') as f:
502 f.write('# empty\n')
502 f.write('# empty\n')
503
503
504 # here no extension enabled, disabled() lists up everything
504 # here no extension enabled, disabled() lists up everything
505 code = ('import pprint; from mercurial import extensions; '
505 code = ('import pprint; from mercurial import extensions; '
506 'pprint.pprint(extensions.disabled())')
506 'pprint.pprint(extensions.disabled())')
507 returncode, out, err = runcmd([sys.executable, '-c', code],
507 returncode, out, err = runcmd([sys.executable, '-c', code],
508 localhgenv())
508 localhgenv())
509 if err or returncode != 0:
509 if err or returncode != 0:
510 raise DistutilsExecError(err)
510 raise DistutilsExecError(err)
511
511
512 with open(self._indexfilename, 'w') as f:
512 with open(self._indexfilename, 'w') as f:
513 f.write('# this file is autogenerated by setup.py\n')
513 f.write('# this file is autogenerated by setup.py\n')
514 f.write('docs = ')
514 f.write('docs = ')
515 f.write(out)
515 f.write(out)
516
516
517 class buildhgexe(build_ext):
517 class buildhgexe(build_ext):
518 description = 'compile hg.exe from mercurial/exewrapper.c'
518 description = 'compile hg.exe from mercurial/exewrapper.c'
519
519
520 def build_extensions(self):
520 def build_extensions(self):
521 if os.name != 'nt':
521 if os.name != 'nt':
522 return
522 return
523 if isinstance(self.compiler, HackedMingw32CCompiler):
523 if isinstance(self.compiler, HackedMingw32CCompiler):
524 self.compiler.compiler_so = self.compiler.compiler # no -mdll
524 self.compiler.compiler_so = self.compiler.compiler # no -mdll
525 self.compiler.dll_libraries = [] # no -lmsrvc90
525 self.compiler.dll_libraries = [] # no -lmsrvc90
526
526
527 # Different Python installs can have different Python library
527 # Different Python installs can have different Python library
528 # names. e.g. the official CPython distribution uses pythonXY.dll
528 # names. e.g. the official CPython distribution uses pythonXY.dll
529 # and MinGW uses libpythonX.Y.dll.
529 # and MinGW uses libpythonX.Y.dll.
530 _kernel32 = ctypes.windll.kernel32
530 _kernel32 = ctypes.windll.kernel32
531 _kernel32.GetModuleFileNameA.argtypes = [ctypes.c_void_p,
531 _kernel32.GetModuleFileNameA.argtypes = [ctypes.c_void_p,
532 ctypes.c_void_p,
532 ctypes.c_void_p,
533 ctypes.c_ulong]
533 ctypes.c_ulong]
534 _kernel32.GetModuleFileNameA.restype = ctypes.c_ulong
534 _kernel32.GetModuleFileNameA.restype = ctypes.c_ulong
535 size = 1000
535 size = 1000
536 buf = ctypes.create_string_buffer(size + 1)
536 buf = ctypes.create_string_buffer(size + 1)
537 filelen = _kernel32.GetModuleFileNameA(sys.dllhandle, ctypes.byref(buf),
537 filelen = _kernel32.GetModuleFileNameA(sys.dllhandle, ctypes.byref(buf),
538 size)
538 size)
539
539
540 if filelen > 0 and filelen != size:
540 if filelen > 0 and filelen != size:
541 dllbasename = os.path.basename(buf.value)
541 dllbasename = os.path.basename(buf.value)
542 if not dllbasename.lower().endswith('.dll'):
542 if not dllbasename.lower().endswith('.dll'):
543 raise SystemExit('Python DLL does not end with .dll: %s' %
543 raise SystemExit('Python DLL does not end with .dll: %s' %
544 dllbasename)
544 dllbasename)
545 pythonlib = dllbasename[:-4]
545 pythonlib = dllbasename[:-4]
546 else:
546 else:
547 log.warn('could not determine Python DLL filename; '
547 log.warn('could not determine Python DLL filename; '
548 'assuming pythonXY')
548 'assuming pythonXY')
549
549
550 hv = sys.hexversion
550 hv = sys.hexversion
551 pythonlib = 'python%d%d' % (hv >> 24, (hv >> 16) & 0xff)
551 pythonlib = 'python%d%d' % (hv >> 24, (hv >> 16) & 0xff)
552
552
553 log.info('using %s as Python library name' % pythonlib)
553 log.info('using %s as Python library name' % pythonlib)
554 with open('mercurial/hgpythonlib.h', 'wb') as f:
554 with open('mercurial/hgpythonlib.h', 'wb') as f:
555 f.write('/* this file is autogenerated by setup.py */\n')
555 f.write('/* this file is autogenerated by setup.py */\n')
556 f.write('#define HGPYTHONLIB "%s"\n' % pythonlib)
556 f.write('#define HGPYTHONLIB "%s"\n' % pythonlib)
557 objects = self.compiler.compile(['mercurial/exewrapper.c'],
557 objects = self.compiler.compile(['mercurial/exewrapper.c'],
558 output_dir=self.build_temp)
558 output_dir=self.build_temp)
559 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
559 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
560 target = os.path.join(dir, 'hg')
560 target = os.path.join(dir, 'hg')
561 self.compiler.link_executable(objects, target,
561 self.compiler.link_executable(objects, target,
562 libraries=[],
562 libraries=[],
563 output_dir=self.build_temp)
563 output_dir=self.build_temp)
564
564
565 @property
565 @property
566 def hgexepath(self):
566 def hgexepath(self):
567 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
567 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
568 return os.path.join(self.build_temp, dir, 'hg.exe')
568 return os.path.join(self.build_temp, dir, 'hg.exe')
569
569
570 class hginstall(install):
570 class hginstall(install):
571
571
572 user_options = install.user_options + [
572 user_options = install.user_options + [
573 ('old-and-unmanageable', None,
573 ('old-and-unmanageable', None,
574 'noop, present for eggless setuptools compat'),
574 'noop, present for eggless setuptools compat'),
575 ('single-version-externally-managed', None,
575 ('single-version-externally-managed', None,
576 'noop, present for eggless setuptools compat'),
576 'noop, present for eggless setuptools compat'),
577 ]
577 ]
578
578
579 # Also helps setuptools not be sad while we refuse to create eggs.
579 # Also helps setuptools not be sad while we refuse to create eggs.
580 single_version_externally_managed = True
580 single_version_externally_managed = True
581
581
582 def get_sub_commands(self):
582 def get_sub_commands(self):
583 # Screen out egg related commands to prevent egg generation. But allow
583 # Screen out egg related commands to prevent egg generation. But allow
584 # mercurial.egg-info generation, since that is part of modern
584 # mercurial.egg-info generation, since that is part of modern
585 # packaging.
585 # packaging.
586 excl = set(['bdist_egg'])
586 excl = set(['bdist_egg'])
587 return filter(lambda x: x not in excl, install.get_sub_commands(self))
587 return filter(lambda x: x not in excl, install.get_sub_commands(self))
588
588
589 class hginstalllib(install_lib):
589 class hginstalllib(install_lib):
590 '''
590 '''
591 This is a specialization of install_lib that replaces the copy_file used
591 This is a specialization of install_lib that replaces the copy_file used
592 there so that it supports setting the mode of files after copying them,
592 there so that it supports setting the mode of files after copying them,
593 instead of just preserving the mode that the files originally had. If your
593 instead of just preserving the mode that the files originally had. If your
594 system has a umask of something like 027, preserving the permissions when
594 system has a umask of something like 027, preserving the permissions when
595 copying will lead to a broken install.
595 copying will lead to a broken install.
596
596
597 Note that just passing keep_permissions=False to copy_file would be
597 Note that just passing keep_permissions=False to copy_file would be
598 insufficient, as it might still be applying a umask.
598 insufficient, as it might still be applying a umask.
599 '''
599 '''
600
600
601 def run(self):
601 def run(self):
602 realcopyfile = file_util.copy_file
602 realcopyfile = file_util.copy_file
603 def copyfileandsetmode(*args, **kwargs):
603 def copyfileandsetmode(*args, **kwargs):
604 src, dst = args[0], args[1]
604 src, dst = args[0], args[1]
605 dst, copied = realcopyfile(*args, **kwargs)
605 dst, copied = realcopyfile(*args, **kwargs)
606 if copied:
606 if copied:
607 st = os.stat(src)
607 st = os.stat(src)
608 # Persist executable bit (apply it to group and other if user
608 # Persist executable bit (apply it to group and other if user
609 # has it)
609 # has it)
610 if st[stat.ST_MODE] & stat.S_IXUSR:
610 if st[stat.ST_MODE] & stat.S_IXUSR:
611 setmode = int('0755', 8)
611 setmode = int('0755', 8)
612 else:
612 else:
613 setmode = int('0644', 8)
613 setmode = int('0644', 8)
614 m = stat.S_IMODE(st[stat.ST_MODE])
614 m = stat.S_IMODE(st[stat.ST_MODE])
615 m = (m & ~int('0777', 8)) | setmode
615 m = (m & ~int('0777', 8)) | setmode
616 os.chmod(dst, m)
616 os.chmod(dst, m)
617 file_util.copy_file = copyfileandsetmode
617 file_util.copy_file = copyfileandsetmode
618 try:
618 try:
619 install_lib.run(self)
619 install_lib.run(self)
620 finally:
620 finally:
621 file_util.copy_file = realcopyfile
621 file_util.copy_file = realcopyfile
622
622
623 class hginstallscripts(install_scripts):
623 class hginstallscripts(install_scripts):
624 '''
624 '''
625 This is a specialization of install_scripts that replaces the @LIBDIR@ with
625 This is a specialization of install_scripts that replaces the @LIBDIR@ with
626 the configured directory for modules. If possible, the path is made relative
626 the configured directory for modules. If possible, the path is made relative
627 to the directory for scripts.
627 to the directory for scripts.
628 '''
628 '''
629
629
630 def initialize_options(self):
630 def initialize_options(self):
631 install_scripts.initialize_options(self)
631 install_scripts.initialize_options(self)
632
632
633 self.install_lib = None
633 self.install_lib = None
634
634
635 def finalize_options(self):
635 def finalize_options(self):
636 install_scripts.finalize_options(self)
636 install_scripts.finalize_options(self)
637 self.set_undefined_options('install',
637 self.set_undefined_options('install',
638 ('install_lib', 'install_lib'))
638 ('install_lib', 'install_lib'))
639
639
640 def run(self):
640 def run(self):
641 install_scripts.run(self)
641 install_scripts.run(self)
642
642
643 # It only makes sense to replace @LIBDIR@ with the install path if
643 # It only makes sense to replace @LIBDIR@ with the install path if
644 # the install path is known. For wheels, the logic below calculates
644 # the install path is known. For wheels, the logic below calculates
645 # the libdir to be "../..". This is because the internal layout of a
645 # the libdir to be "../..". This is because the internal layout of a
646 # wheel archive looks like:
646 # wheel archive looks like:
647 #
647 #
648 # mercurial-3.6.1.data/scripts/hg
648 # mercurial-3.6.1.data/scripts/hg
649 # mercurial/__init__.py
649 # mercurial/__init__.py
650 #
650 #
651 # When installing wheels, the subdirectories of the "<pkg>.data"
651 # When installing wheels, the subdirectories of the "<pkg>.data"
652 # directory are translated to system local paths and files therein
652 # directory are translated to system local paths and files therein
653 # are copied in place. The mercurial/* files are installed into the
653 # are copied in place. The mercurial/* files are installed into the
654 # site-packages directory. However, the site-packages directory
654 # site-packages directory. However, the site-packages directory
655 # isn't known until wheel install time. This means we have no clue
655 # isn't known until wheel install time. This means we have no clue
656 # at wheel generation time what the installed site-packages directory
656 # at wheel generation time what the installed site-packages directory
657 # will be. And, wheels don't appear to provide the ability to register
657 # will be. And, wheels don't appear to provide the ability to register
658 # custom code to run during wheel installation. This all means that
658 # custom code to run during wheel installation. This all means that
659 # we can't reliably set the libdir in wheels: the default behavior
659 # we can't reliably set the libdir in wheels: the default behavior
660 # of looking in sys.path must do.
660 # of looking in sys.path must do.
661
661
662 if (os.path.splitdrive(self.install_dir)[0] !=
662 if (os.path.splitdrive(self.install_dir)[0] !=
663 os.path.splitdrive(self.install_lib)[0]):
663 os.path.splitdrive(self.install_lib)[0]):
664 # can't make relative paths from one drive to another, so use an
664 # can't make relative paths from one drive to another, so use an
665 # absolute path instead
665 # absolute path instead
666 libdir = self.install_lib
666 libdir = self.install_lib
667 else:
667 else:
668 common = os.path.commonprefix((self.install_dir, self.install_lib))
668 common = os.path.commonprefix((self.install_dir, self.install_lib))
669 rest = self.install_dir[len(common):]
669 rest = self.install_dir[len(common):]
670 uplevel = len([n for n in os.path.split(rest) if n])
670 uplevel = len([n for n in os.path.split(rest) if n])
671
671
672 libdir = uplevel * ('..' + os.sep) + self.install_lib[len(common):]
672 libdir = uplevel * ('..' + os.sep) + self.install_lib[len(common):]
673
673
674 for outfile in self.outfiles:
674 for outfile in self.outfiles:
675 with open(outfile, 'rb') as fp:
675 with open(outfile, 'rb') as fp:
676 data = fp.read()
676 data = fp.read()
677
677
678 # skip binary files
678 # skip binary files
679 if b'\0' in data:
679 if b'\0' in data:
680 continue
680 continue
681
681
682 # During local installs, the shebang will be rewritten to the final
682 # During local installs, the shebang will be rewritten to the final
683 # install path. During wheel packaging, the shebang has a special
683 # install path. During wheel packaging, the shebang has a special
684 # value.
684 # value.
685 if data.startswith(b'#!python'):
685 if data.startswith(b'#!python'):
686 log.info('not rewriting @LIBDIR@ in %s because install path '
686 log.info('not rewriting @LIBDIR@ in %s because install path '
687 'not known' % outfile)
687 'not known' % outfile)
688 continue
688 continue
689
689
690 data = data.replace(b'@LIBDIR@', libdir.encode(libdir_escape))
690 data = data.replace(b'@LIBDIR@', libdir.encode(libdir_escape))
691 with open(outfile, 'wb') as fp:
691 with open(outfile, 'wb') as fp:
692 fp.write(data)
692 fp.write(data)
693
693
694 cmdclass = {'build': hgbuild,
694 cmdclass = {'build': hgbuild,
695 'build_mo': hgbuildmo,
695 'build_mo': hgbuildmo,
696 'build_ext': hgbuildext,
696 'build_ext': hgbuildext,
697 'build_py': hgbuildpy,
697 'build_py': hgbuildpy,
698 'build_scripts': hgbuildscripts,
698 'build_scripts': hgbuildscripts,
699 'build_hgextindex': buildhgextindex,
699 'build_hgextindex': buildhgextindex,
700 'install': hginstall,
700 'install': hginstall,
701 'install_lib': hginstalllib,
701 'install_lib': hginstalllib,
702 'install_scripts': hginstallscripts,
702 'install_scripts': hginstallscripts,
703 'build_hgexe': buildhgexe,
703 'build_hgexe': buildhgexe,
704 }
704 }
705
705
706 packages = ['mercurial',
706 packages = ['mercurial',
707 'mercurial.cext',
707 'mercurial.cext',
708 'mercurial.cffi',
708 'mercurial.cffi',
709 'mercurial.hgweb',
709 'mercurial.hgweb',
710 'mercurial.httpclient',
710 'mercurial.httpclient',
711 'mercurial.pure',
711 'mercurial.pure',
712 'hgext', 'hgext.convert', 'hgext.fsmonitor',
712 'hgext', 'hgext.convert', 'hgext.fsmonitor',
713 'hgext.fsmonitor.pywatchman', 'hgext.highlight',
713 'hgext.fsmonitor.pywatchman', 'hgext.highlight',
714 'hgext.largefiles', 'hgext.zeroconf', 'hgext3rd',
714 'hgext.largefiles', 'hgext.zeroconf', 'hgext3rd',
715 'hgdemandimport']
715 'hgdemandimport']
716
716
717 common_depends = ['mercurial/bitmanipulation.h',
717 common_depends = ['mercurial/bitmanipulation.h',
718 'mercurial/compat.h',
718 'mercurial/compat.h',
719 'mercurial/cext/util.h']
719 'mercurial/cext/util.h']
720 common_include_dirs = ['mercurial']
720 common_include_dirs = ['mercurial']
721
721
722 osutil_cflags = []
722 osutil_cflags = []
723 osutil_ldflags = []
723 osutil_ldflags = []
724
724
725 # platform specific macros
725 # platform specific macros
726 for plat, func in [('bsd', 'setproctitle')]:
726 for plat, func in [('bsd', 'setproctitle')]:
727 if re.search(plat, sys.platform) and hasfunction(new_compiler(), func):
727 if re.search(plat, sys.platform) and hasfunction(new_compiler(), func):
728 osutil_cflags.append('-DHAVE_%s' % func.upper())
728 osutil_cflags.append('-DHAVE_%s' % func.upper())
729
729
730 for plat, macro, code in [
730 for plat, macro, code in [
731 ('bsd|darwin', 'BSD_STATFS', '''
731 ('bsd|darwin', 'BSD_STATFS', '''
732 #include <sys/param.h>
732 #include <sys/param.h>
733 #include <sys/mount.h>
733 #include <sys/mount.h>
734 int main() { struct statfs s; return sizeof(s.f_fstypename); }
734 int main() { struct statfs s; return sizeof(s.f_fstypename); }
735 '''),
735 '''),
736 ('linux', 'LINUX_STATFS', '''
736 ('linux', 'LINUX_STATFS', '''
737 #include <linux/magic.h>
737 #include <linux/magic.h>
738 #include <sys/vfs.h>
738 #include <sys/vfs.h>
739 int main() { struct statfs s; return sizeof(s.f_type); }
739 int main() { struct statfs s; return sizeof(s.f_type); }
740 '''),
740 '''),
741 ]:
741 ]:
742 if re.search(plat, sys.platform) and cancompile(new_compiler(), code):
742 if re.search(plat, sys.platform) and cancompile(new_compiler(), code):
743 osutil_cflags.append('-DHAVE_%s' % macro)
743 osutil_cflags.append('-DHAVE_%s' % macro)
744
744
745 if sys.platform == 'darwin':
745 if sys.platform == 'darwin':
746 osutil_ldflags += ['-framework', 'ApplicationServices']
746 osutil_ldflags += ['-framework', 'ApplicationServices']
747
747
748 extmodules = [
748 extmodules = [
749 Extension('mercurial.cext.base85', ['mercurial/cext/base85.c'],
749 Extension('mercurial.cext.base85', ['mercurial/cext/base85.c'],
750 include_dirs=common_include_dirs,
750 include_dirs=common_include_dirs,
751 depends=common_depends),
751 depends=common_depends),
752 Extension('mercurial.cext.bdiff', ['mercurial/bdiff.c',
752 Extension('mercurial.cext.bdiff', ['mercurial/bdiff.c',
753 'mercurial/cext/bdiff.c'],
753 'mercurial/cext/bdiff.c'],
754 include_dirs=common_include_dirs,
754 include_dirs=common_include_dirs,
755 depends=common_depends + ['mercurial/bdiff.h']),
755 depends=common_depends + ['mercurial/bdiff.h']),
756 Extension('mercurial.cext.diffhelpers', ['mercurial/cext/diffhelpers.c'],
756 Extension('mercurial.cext.diffhelpers', ['mercurial/cext/diffhelpers.c'],
757 include_dirs=common_include_dirs,
757 include_dirs=common_include_dirs,
758 depends=common_depends),
758 depends=common_depends),
759 Extension('mercurial.cext.mpatch', ['mercurial/mpatch.c',
759 Extension('mercurial.cext.mpatch', ['mercurial/mpatch.c',
760 'mercurial/cext/mpatch.c'],
760 'mercurial/cext/mpatch.c'],
761 include_dirs=common_include_dirs,
761 include_dirs=common_include_dirs,
762 depends=common_depends),
762 depends=common_depends),
763 Extension('mercurial.cext.parsers', ['mercurial/cext/dirs.c',
763 Extension('mercurial.cext.parsers', ['mercurial/cext/charencode.c',
764 'mercurial/cext/dirs.c',
764 'mercurial/cext/manifest.c',
765 'mercurial/cext/manifest.c',
765 'mercurial/cext/parsers.c',
766 'mercurial/cext/parsers.c',
766 'mercurial/cext/pathencode.c',
767 'mercurial/cext/pathencode.c',
767 'mercurial/cext/revlog.c'],
768 'mercurial/cext/revlog.c'],
768 include_dirs=common_include_dirs,
769 include_dirs=common_include_dirs,
769 depends=common_depends),
770 depends=common_depends),
770 Extension('mercurial.cext.osutil', ['mercurial/cext/osutil.c'],
771 Extension('mercurial.cext.osutil', ['mercurial/cext/osutil.c'],
771 include_dirs=common_include_dirs,
772 include_dirs=common_include_dirs,
772 extra_compile_args=osutil_cflags,
773 extra_compile_args=osutil_cflags,
773 extra_link_args=osutil_ldflags,
774 extra_link_args=osutil_ldflags,
774 depends=common_depends),
775 depends=common_depends),
775 Extension('hgext.fsmonitor.pywatchman.bser',
776 Extension('hgext.fsmonitor.pywatchman.bser',
776 ['hgext/fsmonitor/pywatchman/bser.c']),
777 ['hgext/fsmonitor/pywatchman/bser.c']),
777 ]
778 ]
778
779
779 sys.path.insert(0, 'contrib/python-zstandard')
780 sys.path.insert(0, 'contrib/python-zstandard')
780 import setup_zstd
781 import setup_zstd
781 extmodules.append(setup_zstd.get_c_extension(name='mercurial.zstd'))
782 extmodules.append(setup_zstd.get_c_extension(name='mercurial.zstd'))
782
783
783 try:
784 try:
784 from distutils import cygwinccompiler
785 from distutils import cygwinccompiler
785
786
786 # the -mno-cygwin option has been deprecated for years
787 # the -mno-cygwin option has been deprecated for years
787 compiler = cygwinccompiler.Mingw32CCompiler
788 compiler = cygwinccompiler.Mingw32CCompiler
788
789
789 class HackedMingw32CCompiler(cygwinccompiler.Mingw32CCompiler):
790 class HackedMingw32CCompiler(cygwinccompiler.Mingw32CCompiler):
790 def __init__(self, *args, **kwargs):
791 def __init__(self, *args, **kwargs):
791 compiler.__init__(self, *args, **kwargs)
792 compiler.__init__(self, *args, **kwargs)
792 for i in 'compiler compiler_so linker_exe linker_so'.split():
793 for i in 'compiler compiler_so linker_exe linker_so'.split():
793 try:
794 try:
794 getattr(self, i).remove('-mno-cygwin')
795 getattr(self, i).remove('-mno-cygwin')
795 except ValueError:
796 except ValueError:
796 pass
797 pass
797
798
798 cygwinccompiler.Mingw32CCompiler = HackedMingw32CCompiler
799 cygwinccompiler.Mingw32CCompiler = HackedMingw32CCompiler
799 except ImportError:
800 except ImportError:
800 # the cygwinccompiler package is not available on some Python
801 # the cygwinccompiler package is not available on some Python
801 # distributions like the ones from the optware project for Synology
802 # distributions like the ones from the optware project for Synology
802 # DiskStation boxes
803 # DiskStation boxes
803 class HackedMingw32CCompiler(object):
804 class HackedMingw32CCompiler(object):
804 pass
805 pass
805
806
806 if os.name == 'nt':
807 if os.name == 'nt':
807 # Allow compiler/linker flags to be added to Visual Studio builds. Passing
808 # Allow compiler/linker flags to be added to Visual Studio builds. Passing
808 # extra_link_args to distutils.extensions.Extension() doesn't have any
809 # extra_link_args to distutils.extensions.Extension() doesn't have any
809 # effect.
810 # effect.
810 from distutils import msvccompiler
811 from distutils import msvccompiler
811
812
812 compiler = msvccompiler.MSVCCompiler
813 compiler = msvccompiler.MSVCCompiler
813
814
814 class HackedMSVCCompiler(msvccompiler.MSVCCompiler):
815 class HackedMSVCCompiler(msvccompiler.MSVCCompiler):
815 def initialize(self):
816 def initialize(self):
816 compiler.initialize(self)
817 compiler.initialize(self)
817 # "warning LNK4197: export 'func' specified multiple times"
818 # "warning LNK4197: export 'func' specified multiple times"
818 self.ldflags_shared.append('/ignore:4197')
819 self.ldflags_shared.append('/ignore:4197')
819 self.ldflags_shared_debug.append('/ignore:4197')
820 self.ldflags_shared_debug.append('/ignore:4197')
820
821
821 msvccompiler.MSVCCompiler = HackedMSVCCompiler
822 msvccompiler.MSVCCompiler = HackedMSVCCompiler
822
823
823 packagedata = {'mercurial': ['locale/*/LC_MESSAGES/hg.mo',
824 packagedata = {'mercurial': ['locale/*/LC_MESSAGES/hg.mo',
824 'help/*.txt',
825 'help/*.txt',
825 'help/internals/*.txt',
826 'help/internals/*.txt',
826 'default.d/*.rc',
827 'default.d/*.rc',
827 'dummycert.pem']}
828 'dummycert.pem']}
828
829
829 def ordinarypath(p):
830 def ordinarypath(p):
830 return p and p[0] != '.' and p[-1] != '~'
831 return p and p[0] != '.' and p[-1] != '~'
831
832
832 for root in ('templates',):
833 for root in ('templates',):
833 for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
834 for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
834 curdir = curdir.split(os.sep, 1)[1]
835 curdir = curdir.split(os.sep, 1)[1]
835 dirs[:] = filter(ordinarypath, dirs)
836 dirs[:] = filter(ordinarypath, dirs)
836 for f in filter(ordinarypath, files):
837 for f in filter(ordinarypath, files):
837 f = os.path.join(curdir, f)
838 f = os.path.join(curdir, f)
838 packagedata['mercurial'].append(f)
839 packagedata['mercurial'].append(f)
839
840
840 datafiles = []
841 datafiles = []
841
842
842 # distutils expects version to be str/unicode. Converting it to
843 # distutils expects version to be str/unicode. Converting it to
843 # unicode on Python 2 still works because it won't contain any
844 # unicode on Python 2 still works because it won't contain any
844 # non-ascii bytes and will be implicitly converted back to bytes
845 # non-ascii bytes and will be implicitly converted back to bytes
845 # when operated on.
846 # when operated on.
846 assert isinstance(version, bytes)
847 assert isinstance(version, bytes)
847 setupversion = version.decode('ascii')
848 setupversion = version.decode('ascii')
848
849
849 extra = {}
850 extra = {}
850
851
851 if issetuptools:
852 if issetuptools:
852 extra['python_requires'] = supportedpy
853 extra['python_requires'] = supportedpy
853 if py2exeloaded:
854 if py2exeloaded:
854 extra['console'] = [
855 extra['console'] = [
855 {'script':'hg',
856 {'script':'hg',
856 'copyright':'Copyright (C) 2005-2017 Matt Mackall and others',
857 'copyright':'Copyright (C) 2005-2017 Matt Mackall and others',
857 'product_version':version}]
858 'product_version':version}]
858 # sub command of 'build' because 'py2exe' does not handle sub_commands
859 # sub command of 'build' because 'py2exe' does not handle sub_commands
859 build.sub_commands.insert(0, ('build_hgextindex', None))
860 build.sub_commands.insert(0, ('build_hgextindex', None))
860 # put dlls in sub directory so that they won't pollute PATH
861 # put dlls in sub directory so that they won't pollute PATH
861 extra['zipfile'] = 'lib/library.zip'
862 extra['zipfile'] = 'lib/library.zip'
862
863
863 if os.name == 'nt':
864 if os.name == 'nt':
864 # Windows binary file versions for exe/dll files must have the
865 # Windows binary file versions for exe/dll files must have the
865 # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
866 # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
866 setupversion = version.split('+', 1)[0]
867 setupversion = version.split('+', 1)[0]
867
868
868 if sys.platform == 'darwin' and os.path.exists('/usr/bin/xcodebuild'):
869 if sys.platform == 'darwin' and os.path.exists('/usr/bin/xcodebuild'):
869 version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[1].splitlines()
870 version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[1].splitlines()
870 if version:
871 if version:
871 version = version[0]
872 version = version[0]
872 if sys.version_info[0] == 3:
873 if sys.version_info[0] == 3:
873 version = version.decode('utf-8')
874 version = version.decode('utf-8')
874 xcode4 = (version.startswith('Xcode') and
875 xcode4 = (version.startswith('Xcode') and
875 StrictVersion(version.split()[1]) >= StrictVersion('4.0'))
876 StrictVersion(version.split()[1]) >= StrictVersion('4.0'))
876 xcode51 = re.match(r'^Xcode\s+5\.1', version) is not None
877 xcode51 = re.match(r'^Xcode\s+5\.1', version) is not None
877 else:
878 else:
878 # xcodebuild returns empty on OS X Lion with XCode 4.3 not
879 # xcodebuild returns empty on OS X Lion with XCode 4.3 not
879 # installed, but instead with only command-line tools. Assume
880 # installed, but instead with only command-line tools. Assume
880 # that only happens on >= Lion, thus no PPC support.
881 # that only happens on >= Lion, thus no PPC support.
881 xcode4 = True
882 xcode4 = True
882 xcode51 = False
883 xcode51 = False
883
884
884 # XCode 4.0 dropped support for ppc architecture, which is hardcoded in
885 # XCode 4.0 dropped support for ppc architecture, which is hardcoded in
885 # distutils.sysconfig
886 # distutils.sysconfig
886 if xcode4:
887 if xcode4:
887 os.environ['ARCHFLAGS'] = ''
888 os.environ['ARCHFLAGS'] = ''
888
889
889 # XCode 5.1 changes clang such that it now fails to compile if the
890 # XCode 5.1 changes clang such that it now fails to compile if the
890 # -mno-fused-madd flag is passed, but the version of Python shipped with
891 # -mno-fused-madd flag is passed, but the version of Python shipped with
891 # OS X 10.9 Mavericks includes this flag. This causes problems in all
892 # OS X 10.9 Mavericks includes this flag. This causes problems in all
892 # C extension modules, and a bug has been filed upstream at
893 # C extension modules, and a bug has been filed upstream at
893 # http://bugs.python.org/issue21244. We also need to patch this here
894 # http://bugs.python.org/issue21244. We also need to patch this here
894 # so Mercurial can continue to compile in the meantime.
895 # so Mercurial can continue to compile in the meantime.
895 if xcode51:
896 if xcode51:
896 cflags = get_config_var('CFLAGS')
897 cflags = get_config_var('CFLAGS')
897 if cflags and re.search(r'-mno-fused-madd\b', cflags) is not None:
898 if cflags and re.search(r'-mno-fused-madd\b', cflags) is not None:
898 os.environ['CFLAGS'] = (
899 os.environ['CFLAGS'] = (
899 os.environ.get('CFLAGS', '') + ' -Qunused-arguments')
900 os.environ.get('CFLAGS', '') + ' -Qunused-arguments')
900
901
901 setup(name='mercurial',
902 setup(name='mercurial',
902 version=setupversion,
903 version=setupversion,
903 author='Matt Mackall and many others',
904 author='Matt Mackall and many others',
904 author_email='mercurial@mercurial-scm.org',
905 author_email='mercurial@mercurial-scm.org',
905 url='https://mercurial-scm.org/',
906 url='https://mercurial-scm.org/',
906 download_url='https://mercurial-scm.org/release/',
907 download_url='https://mercurial-scm.org/release/',
907 description=('Fast scalable distributed SCM (revision control, version '
908 description=('Fast scalable distributed SCM (revision control, version '
908 'control) system'),
909 'control) system'),
909 long_description=('Mercurial is a distributed SCM tool written in Python.'
910 long_description=('Mercurial is a distributed SCM tool written in Python.'
910 ' It is used by a number of large projects that require'
911 ' It is used by a number of large projects that require'
911 ' fast, reliable distributed revision control, such as '
912 ' fast, reliable distributed revision control, such as '
912 'Mozilla.'),
913 'Mozilla.'),
913 license='GNU GPLv2 or any later version',
914 license='GNU GPLv2 or any later version',
914 classifiers=[
915 classifiers=[
915 'Development Status :: 6 - Mature',
916 'Development Status :: 6 - Mature',
916 'Environment :: Console',
917 'Environment :: Console',
917 'Intended Audience :: Developers',
918 'Intended Audience :: Developers',
918 'Intended Audience :: System Administrators',
919 'Intended Audience :: System Administrators',
919 'License :: OSI Approved :: GNU General Public License (GPL)',
920 'License :: OSI Approved :: GNU General Public License (GPL)',
920 'Natural Language :: Danish',
921 'Natural Language :: Danish',
921 'Natural Language :: English',
922 'Natural Language :: English',
922 'Natural Language :: German',
923 'Natural Language :: German',
923 'Natural Language :: Italian',
924 'Natural Language :: Italian',
924 'Natural Language :: Japanese',
925 'Natural Language :: Japanese',
925 'Natural Language :: Portuguese (Brazilian)',
926 'Natural Language :: Portuguese (Brazilian)',
926 'Operating System :: Microsoft :: Windows',
927 'Operating System :: Microsoft :: Windows',
927 'Operating System :: OS Independent',
928 'Operating System :: OS Independent',
928 'Operating System :: POSIX',
929 'Operating System :: POSIX',
929 'Programming Language :: C',
930 'Programming Language :: C',
930 'Programming Language :: Python',
931 'Programming Language :: Python',
931 'Topic :: Software Development :: Version Control',
932 'Topic :: Software Development :: Version Control',
932 ],
933 ],
933 scripts=scripts,
934 scripts=scripts,
934 packages=packages,
935 packages=packages,
935 ext_modules=extmodules,
936 ext_modules=extmodules,
936 data_files=datafiles,
937 data_files=datafiles,
937 package_data=packagedata,
938 package_data=packagedata,
938 cmdclass=cmdclass,
939 cmdclass=cmdclass,
939 distclass=hgdist,
940 distclass=hgdist,
940 options={'py2exe': {'packages': ['hgdemandimport', 'hgext', 'email',
941 options={'py2exe': {'packages': ['hgdemandimport', 'hgext', 'email',
941 # implicitly imported per module policy
942 # implicitly imported per module policy
942 # (cffi wouldn't be used as a frozen exe)
943 # (cffi wouldn't be used as a frozen exe)
943 'mercurial.cext',
944 'mercurial.cext',
944 #'mercurial.cffi',
945 #'mercurial.cffi',
945 'mercurial.pure']},
946 'mercurial.pure']},
946 'bdist_mpkg': {'zipdist': False,
947 'bdist_mpkg': {'zipdist': False,
947 'license': 'COPYING',
948 'license': 'COPYING',
948 'readme': 'contrib/macosx/Readme.html',
949 'readme': 'contrib/macosx/Readme.html',
949 'welcome': 'contrib/macosx/Welcome.html',
950 'welcome': 'contrib/macosx/Welcome.html',
950 },
951 },
951 },
952 },
952 **extra)
953 **extra)
General Comments 0
You need to be logged in to leave comments. Login now