2 * Copyright (C) 2009 Jelmer Vernooij <jelmer@samba.org>
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; version 2
7 * of the License or (at your option) a later version of the License.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
24 #if (PY_VERSION_HEX < 0x02050000)
25 typedef int Py_ssize_t;
28 #if defined(__MINGW32_VERSION) || defined(__APPLE__)
29 size_t rep_strnlen(char *text, size_t maxlen);
30 size_t rep_strnlen(char *text, size_t maxlen)
32 const char *last = memchr(text, '\0', maxlen);
33 return last ? (size_t) (last - text) : maxlen;
35 #define strnlen rep_strnlen
38 #define bytehex(x) (((x)<0xa)?('0'+(x)):('a'-0xa+(x)))
40 static PyObject *tree_entry_cls;
41 static PyObject *object_format_exception_cls;
43 static PyObject *sha_to_pyhex(const unsigned char *sha)
47 for (i = 0; i < 20; i++) {
48 hexsha[i*2] = bytehex((sha[i] & 0xF0) >> 4);
49 hexsha[i*2+1] = bytehex(sha[i] & 0x0F);
52 return PyString_FromStringAndSize(hexsha, 40);
55 static PyObject *py_parse_tree(PyObject *self, PyObject *args, PyObject *kw)
57 char *text, *start, *end;
58 int len, namelen, strict;
59 PyObject *ret, *item, *name, *sha, *py_strict = NULL;
60 static char *kwlist[] = {"text", "strict", NULL};
62 if (!PyArg_ParseTupleAndKeywords(args, kw, "s#|O", kwlist,
63 &text, &len, &py_strict))
65 strict = py_strict ? PyObject_IsTrue(py_strict) : 0;
66 /* TODO: currently this returns a list; if memory usage is a concern,
67 * consider rewriting as a custom iterator object */
76 if (strict && text[0] == '0') {
77 PyErr_SetString(object_format_exception_cls,
78 "Illegal leading zero on mode");
82 mode = strtol(text, &text, 8);
84 PyErr_SetString(PyExc_ValueError, "Expected space");
89 namelen = strnlen(text, len - (text - start));
90 name = PyString_FromStringAndSize(text, namelen);
95 if (text + namelen + 20 >= end) {
96 PyErr_SetString(PyExc_ValueError, "SHA truncated");
101 sha = sha_to_pyhex((unsigned char *)text+namelen+1);
107 item = Py_BuildValue("(NlN)", name, mode, sha);
114 if (PyList_Append(ret, item) == -1) {
131 int cmp_tree_item(const void *_a, const void *_b)
133 const struct tree_item *a = _a, *b = _b;
134 const char *remain_a, *remain_b;
136 if (strlen(a->name) > strlen(b->name)) {
137 common = strlen(b->name);
138 remain_a = a->name + common;
139 remain_b = (S_ISDIR(b->mode)?"/":"");
140 } else if (strlen(b->name) > strlen(a->name)) {
141 common = strlen(a->name);
142 remain_a = (S_ISDIR(a->mode)?"/":"");
143 remain_b = b->name + common;
144 } else { /* strlen(a->name) == strlen(b->name) */
149 ret = strncmp(a->name, b->name, common);
152 return strcmp(remain_a, remain_b);
155 int cmp_tree_item_name_order(const void *_a, const void *_b) {
156 const struct tree_item *a = _a, *b = _b;
157 return strcmp(a->name, b->name);
160 static PyObject *py_sorted_tree_items(PyObject *self, PyObject *args)
162 struct tree_item *qsort_entries = NULL;
163 int name_order, num_entries, n = 0, i;
164 PyObject *entries, *py_name_order, *ret, *key, *value, *py_mode, *py_sha;
166 int (*cmp)(const void *, const void *);
168 if (!PyArg_ParseTuple(args, "OO", &entries, &py_name_order))
171 if (!PyDict_Check(entries)) {
172 PyErr_SetString(PyExc_TypeError, "Argument not a dictionary");
176 name_order = PyObject_IsTrue(py_name_order);
177 if (name_order == -1)
179 cmp = name_order ? cmp_tree_item_name_order : cmp_tree_item;
181 num_entries = PyDict_Size(entries);
182 if (PyErr_Occurred())
184 qsort_entries = PyMem_New(struct tree_item, num_entries);
185 if (!qsort_entries) {
190 while (PyDict_Next(entries, &pos, &key, &value)) {
191 if (!PyString_Check(key)) {
192 PyErr_SetString(PyExc_TypeError, "Name is not a string");
196 if (PyTuple_Size(value) != 2) {
197 PyErr_SetString(PyExc_ValueError, "Tuple has invalid size");
201 py_mode = PyTuple_GET_ITEM(value, 0);
202 if (!PyInt_Check(py_mode)) {
203 PyErr_SetString(PyExc_TypeError, "Mode is not an integral type");
207 py_sha = PyTuple_GET_ITEM(value, 1);
208 if (!PyString_Check(py_sha)) {
209 PyErr_SetString(PyExc_TypeError, "SHA is not a string");
212 qsort_entries[n].name = PyString_AS_STRING(key);
213 qsort_entries[n].mode = PyInt_AS_LONG(py_mode);
215 qsort_entries[n].tuple = PyObject_CallFunctionObjArgs(
216 tree_entry_cls, key, py_mode, py_sha, NULL);
217 if (qsort_entries[n].tuple == NULL)
222 qsort(qsort_entries, num_entries, sizeof(struct tree_item), cmp);
224 ret = PyList_New(num_entries);
230 for (i = 0; i < num_entries; i++) {
231 PyList_SET_ITEM(ret, i, qsort_entries[i].tuple);
233 PyMem_Free(qsort_entries);
237 for (i = 0; i < n; i++) {
238 Py_XDECREF(qsort_entries[i].tuple);
240 PyMem_Free(qsort_entries);
244 static PyMethodDef py_objects_methods[] = {
245 { "parse_tree", (PyCFunction)py_parse_tree, METH_VARARGS | METH_KEYWORDS,
247 { "sorted_tree_items", py_sorted_tree_items, METH_VARARGS, NULL },
248 { NULL, NULL, 0, NULL }
254 PyObject *m, *objects_mod, *errors_mod;
256 m = Py_InitModule3("_objects", py_objects_methods, NULL);
261 errors_mod = PyImport_ImportModule("dulwich.errors");
262 if (errors_mod == NULL)
265 object_format_exception_cls = PyObject_GetAttrString(
266 errors_mod, "ObjectFormatException");
267 Py_DECREF(errors_mod);
268 if (object_format_exception_cls == NULL)
271 /* This is a circular import but should be safe since this module is
272 * imported at at the very bottom of objects.py. */
273 objects_mod = PyImport_ImportModule("dulwich.objects");
274 if (objects_mod == NULL)
277 tree_entry_cls = PyObject_GetAttrString(objects_mod, "TreeEntry");
278 Py_DECREF(objects_mod);
279 if (tree_entry_cls == NULL)