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, *py_strict = NULL;
60 static char *kwlist[] = {"text", "strict", NULL};
62 if (!PyArg_ParseTupleAndKeywords(args, kw, "s#|O", kwlist,
63 &text, &len, &py_strict))
67 strict = py_strict ? PyObject_IsTrue(py_strict) : 0;
69 /* TODO: currently this returns a list; if memory usage is a concern,
70 * consider rewriting as a custom iterator object */
82 if (strict && text[0] == '0') {
83 PyErr_SetString(object_format_exception_cls,
84 "Illegal leading zero on mode");
89 mode = strtol(text, &text, 8);
92 PyErr_SetString(PyExc_ValueError, "Expected space");
99 namelen = strnlen(text, len - (text - start));
101 name = PyString_FromStringAndSize(text, namelen);
107 if (text + namelen + 20 >= end) {
108 PyErr_SetString(PyExc_ValueError, "SHA truncated");
114 item = Py_BuildValue("(NlN)", name, mode,
115 sha_to_pyhex((unsigned char *)text+namelen+1));
121 if (PyList_Append(ret, item) == -1) {
140 int cmp_tree_item(const void *_a, const void *_b)
142 const struct tree_item *a = _a, *b = _b;
143 const char *remain_a, *remain_b;
145 if (strlen(a->name) > strlen(b->name)) {
146 common = strlen(b->name);
147 remain_a = a->name + common;
148 remain_b = (S_ISDIR(b->mode)?"/":"");
149 } else if (strlen(b->name) > strlen(a->name)) {
150 common = strlen(a->name);
151 remain_a = (S_ISDIR(a->mode)?"/":"");
152 remain_b = b->name + common;
153 } else { /* strlen(a->name) == strlen(b->name) */
158 ret = strncmp(a->name, b->name, common);
161 return strcmp(remain_a, remain_b);
164 int cmp_tree_item_name_order(const void *_a, const void *_b) {
165 const struct tree_item *a = _a, *b = _b;
166 return strcmp(a->name, b->name);
169 static PyObject *py_sorted_tree_items(PyObject *self, PyObject *args)
171 struct tree_item *qsort_entries = NULL;
172 int name_order, num_entries, n = 0, i;
173 PyObject *entries, *py_name_order, *ret, *key, *value, *py_mode, *py_sha;
175 int (*cmp)(const void *, const void *);
177 if (!PyArg_ParseTuple(args, "OO", &entries, &py_name_order))
180 if (!PyDict_Check(entries)) {
181 PyErr_SetString(PyExc_TypeError, "Argument not a dictionary");
185 name_order = PyObject_IsTrue(py_name_order);
186 if (name_order == -1)
188 cmp = name_order ? cmp_tree_item_name_order : cmp_tree_item;
190 num_entries = PyDict_Size(entries);
191 if (PyErr_Occurred())
193 qsort_entries = PyMem_New(struct tree_item, num_entries);
194 if (!qsort_entries) {
199 while (PyDict_Next(entries, &pos, &key, &value)) {
200 if (!PyString_Check(key)) {
201 PyErr_SetString(PyExc_TypeError, "Name is not a string");
205 if (PyTuple_Size(value) != 2) {
206 PyErr_SetString(PyExc_ValueError, "Tuple has invalid size");
210 py_mode = PyTuple_GET_ITEM(value, 0);
211 if (!PyInt_Check(py_mode)) {
212 PyErr_SetString(PyExc_TypeError, "Mode is not an integral type");
216 py_sha = PyTuple_GET_ITEM(value, 1);
217 if (!PyString_Check(py_sha)) {
218 PyErr_SetString(PyExc_TypeError, "SHA is not a string");
221 qsort_entries[n].name = PyString_AS_STRING(key);
222 qsort_entries[n].mode = PyInt_AS_LONG(py_mode);
224 qsort_entries[n].tuple = PyObject_CallFunctionObjArgs(
225 tree_entry_cls, key, py_mode, py_sha, NULL);
226 if (qsort_entries[n].tuple == NULL)
231 qsort(qsort_entries, num_entries, sizeof(struct tree_item), cmp);
233 ret = PyList_New(num_entries);
239 for (i = 0; i < num_entries; i++) {
240 PyList_SET_ITEM(ret, i, qsort_entries[i].tuple);
242 PyMem_Free(qsort_entries);
246 for (i = 0; i < n; i++) {
247 Py_XDECREF(qsort_entries[i].tuple);
249 PyMem_Free(qsort_entries);
253 static PyMethodDef py_objects_methods[] = {
254 { "parse_tree", (PyCFunction)py_parse_tree, METH_VARARGS | METH_KEYWORDS,
256 { "sorted_tree_items", py_sorted_tree_items, METH_VARARGS, NULL },
257 { NULL, NULL, 0, NULL }
263 PyObject *m, *objects_mod, *errors_mod;
265 m = Py_InitModule3("_objects", py_objects_methods, NULL);
270 errors_mod = PyImport_ImportModule("dulwich.errors");
271 if (errors_mod == NULL)
274 object_format_exception_cls = PyObject_GetAttrString(
275 errors_mod, "ObjectFormatException");
276 Py_DECREF(errors_mod);
277 if (object_format_exception_cls == NULL)
280 /* This is a circular import but should be safe since this module is
281 * imported at at the very bottom of objects.py. */
282 objects_mod = PyImport_ImportModule("dulwich.objects");
283 if (objects_mod == NULL)
286 tree_entry_cls = PyObject_GetAttrString(objects_mod, "TreeEntry");
287 Py_DECREF(objects_mod);
288 if (tree_entry_cls == NULL)