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