mercurial/dirs.c
changeset 18900 02ee846b246a
child 18901 66d3aebe2d95
equal deleted inserted replaced
18899:d8ff607ef721 18900:02ee846b246a
       
     1 /*
       
     2  dirs.c - dynamic directory diddling for dirstates
       
     3 
       
     4  Copyright 2013 Facebook
       
     5 
       
     6  This software may be used and distributed according to the terms of
       
     7  the GNU General Public License, incorporated herein by reference.
       
     8 */
       
     9 
       
    10 #define PY_SSIZE_T_CLEAN
       
    11 #include <Python.h>
       
    12 #include "util.h"
       
    13 
       
    14 /*
       
    15  * This is a multiset of directory names, built from the files that
       
    16  * appear in a dirstate or manifest.
       
    17  */
       
    18 typedef struct {
       
    19 	PyObject_HEAD
       
    20 	PyObject *dict;
       
    21 } dirsObject;
       
    22 
       
    23 static inline Py_ssize_t _finddir(PyObject *path, Py_ssize_t pos)
       
    24 {
       
    25 	const char *s = PyString_AS_STRING(path);
       
    26 
       
    27 	while (pos != -1) {
       
    28 		if (s[pos] == '/')
       
    29 			break;
       
    30 		pos -= 1;
       
    31 	}
       
    32 
       
    33 	return pos;
       
    34 }
       
    35 
       
    36 static int _addpath(PyObject *dirs, PyObject *path)
       
    37 {
       
    38 	Py_ssize_t pos = PyString_GET_SIZE(path);
       
    39 	PyObject *newval = NULL, *key = NULL;
       
    40 	int ret = -1;
       
    41 
       
    42 	while ((pos = _finddir(path, pos - 1)) != -1) {
       
    43 		PyObject *val;
       
    44 		long v = 0;
       
    45 
       
    46 		key = PyString_FromStringAndSize(PyString_AS_STRING(path), pos);
       
    47 
       
    48 		if (key == NULL)
       
    49 			goto bail;
       
    50 
       
    51 		val = PyDict_GetItem(dirs, key);
       
    52 		if (val != NULL)
       
    53 			v = PyInt_AS_LONG(val);
       
    54 
       
    55 		newval = PyInt_FromLong(v + 1);
       
    56 
       
    57 		if (newval == NULL)
       
    58 			goto bail;
       
    59 
       
    60 		ret = PyDict_SetItem(dirs, key, newval);
       
    61 		if (ret == -1)
       
    62 			goto bail;
       
    63 		Py_CLEAR(key);
       
    64 		Py_CLEAR(newval);
       
    65 	}
       
    66 	ret = 0;
       
    67 
       
    68 bail:
       
    69 	Py_XDECREF(key);
       
    70 	Py_XDECREF(newval);
       
    71 
       
    72 	return ret;
       
    73 }
       
    74 
       
    75 static int _delpath(PyObject *dirs, PyObject *path)
       
    76 {
       
    77 	Py_ssize_t pos = PyString_GET_SIZE(path);
       
    78 	PyObject *newval = NULL, *key = NULL;
       
    79 	int ret = -1;
       
    80 
       
    81 	while ((pos = _finddir(path, pos - 1)) != -1) {
       
    82 		PyObject *val;
       
    83 		long v;
       
    84 
       
    85 		key = PyString_FromStringAndSize(PyString_AS_STRING(path), pos);
       
    86 
       
    87 		if (key == NULL)
       
    88 			goto bail;
       
    89 
       
    90 		val = PyDict_GetItem(dirs, key);
       
    91 		if (val == NULL) {
       
    92 			PyErr_SetString(PyExc_ValueError,
       
    93 					"expected a value, found none");
       
    94 			goto bail;
       
    95 		}
       
    96 		v = PyInt_AS_LONG(val);
       
    97 
       
    98 		if (v <= 1) {
       
    99 			if (PyDict_DelItem(dirs, key) == -1)
       
   100 				goto bail;
       
   101 			continue;
       
   102 		}
       
   103 		newval = PyInt_FromLong(v - 1);
       
   104 
       
   105 		if (newval == NULL)
       
   106 			goto bail;
       
   107 
       
   108 		ret = PyDict_SetItem(dirs, key, newval);
       
   109 		if (ret == -1)
       
   110 			goto bail;
       
   111 		Py_CLEAR(key);
       
   112 		Py_CLEAR(newval);
       
   113 	}
       
   114 	ret = 0;
       
   115 
       
   116 bail:
       
   117 	Py_XDECREF(key);
       
   118 	Py_XDECREF(newval);
       
   119 
       
   120 	return ret;
       
   121 }
       
   122 
       
   123 static int dirs_fromdict(PyObject *dirs, PyObject *source, char skipchar)
       
   124 {
       
   125 	PyObject *key, *value;
       
   126 	Py_ssize_t pos = 0;
       
   127 
       
   128 	while (PyDict_Next(source, &pos, &key, &value)) {
       
   129 		if (!PyString_Check(key)) {
       
   130 			PyErr_SetString(PyExc_TypeError, "expected string key");
       
   131 			return -1;
       
   132 		}
       
   133 		if (skipchar) {
       
   134 			PyObject *st;
       
   135 
       
   136 			if (!PyTuple_Check(value) ||
       
   137 			    PyTuple_GET_SIZE(value) == 0) {
       
   138 				PyErr_SetString(PyExc_TypeError,
       
   139 						"expected non-empty tuple");
       
   140 				return -1;
       
   141 			}
       
   142 
       
   143 			st = PyTuple_GET_ITEM(value, 0);
       
   144 
       
   145 			if (!PyString_Check(st) || PyString_GET_SIZE(st) == 0) {
       
   146 				PyErr_SetString(PyExc_TypeError,
       
   147 						"expected non-empty string "
       
   148 						"at tuple index 0");
       
   149 				return -1;
       
   150 			}
       
   151 
       
   152 			if (PyString_AS_STRING(st)[0] == skipchar)
       
   153 				continue;
       
   154 		}
       
   155 
       
   156 		if (_addpath(dirs, key) == -1)
       
   157 			return -1;
       
   158 	}
       
   159 
       
   160 	return 0;
       
   161 }
       
   162 
       
   163 static int dirs_fromiter(PyObject *dirs, PyObject *source)
       
   164 {
       
   165 	PyObject *iter, *item = NULL;
       
   166 	int ret;
       
   167 
       
   168 	iter = PyObject_GetIter(source);
       
   169 	if (iter == NULL)
       
   170 		return -1;
       
   171 
       
   172 	while ((item = PyIter_Next(iter)) != NULL) {
       
   173 		if (!PyString_Check(item)) {
       
   174 			PyErr_SetString(PyExc_TypeError, "expected string");
       
   175 			break;
       
   176 		}
       
   177 
       
   178 		if (_addpath(dirs, item) == -1)
       
   179 			break;
       
   180 		Py_CLEAR(item);
       
   181 	}
       
   182 
       
   183 	ret = PyErr_Occurred() ? -1 : 0;
       
   184 	Py_XDECREF(item);
       
   185 	return ret;
       
   186 }
       
   187 
       
   188 /*
       
   189  * Calculate a refcounted set of directory names for the files in a
       
   190  * dirstate.
       
   191  */
       
   192 static int dirs_init(dirsObject *self, PyObject *args)
       
   193 {
       
   194 	PyObject *dirs = NULL, *source = NULL;
       
   195 	char skipchar = 0;
       
   196 	int ret = -1;
       
   197 
       
   198 	self->dict = NULL;
       
   199 
       
   200 	if (!PyArg_ParseTuple(args, "|Oc:__init__", &source, &skipchar))
       
   201 		return -1;
       
   202 
       
   203 	dirs = PyDict_New();
       
   204 
       
   205 	if (dirs == NULL)
       
   206 		return -1;
       
   207 
       
   208 	if (source == NULL)
       
   209 		ret = 0;
       
   210 	else if (PyDict_Check(source))
       
   211 		ret = dirs_fromdict(dirs, source, skipchar);
       
   212 	else if (skipchar)
       
   213 		PyErr_SetString(PyExc_ValueError,
       
   214 				"skip character is only supported "
       
   215 				"with a dict source");
       
   216 	else
       
   217 		ret = dirs_fromiter(dirs, source);
       
   218 
       
   219 	if (ret == -1)
       
   220 		Py_XDECREF(dirs);
       
   221 	else
       
   222 		self->dict = dirs;
       
   223 
       
   224 	return ret;
       
   225 }
       
   226 
       
   227 PyObject *dirs_addpath(dirsObject *self, PyObject *args)
       
   228 {
       
   229 	PyObject *path;
       
   230 
       
   231 	if (!PyArg_ParseTuple(args, "O!:addpath", &PyString_Type, &path))
       
   232 		return NULL;
       
   233 
       
   234 	if (_addpath(self->dict, path) == -1)
       
   235 		return NULL;
       
   236 
       
   237 	Py_RETURN_NONE;
       
   238 }
       
   239 
       
   240 static PyObject *dirs_delpath(dirsObject *self, PyObject *args)
       
   241 {
       
   242 	PyObject *path;
       
   243 
       
   244 	if (!PyArg_ParseTuple(args, "O!:delpath", &PyString_Type, &path))
       
   245 		return NULL;
       
   246 
       
   247 	if (_delpath(self->dict, path) == -1)
       
   248 		return NULL;
       
   249 
       
   250 	Py_RETURN_NONE;
       
   251 }
       
   252 
       
   253 static int dirs_contains(dirsObject *self, PyObject *value)
       
   254 {
       
   255 	return PyString_Check(value) ? PyDict_Contains(self->dict, value) : 0;
       
   256 }
       
   257 
       
   258 static void dirs_dealloc(dirsObject *self)
       
   259 {
       
   260 	Py_XDECREF(self->dict);
       
   261 	PyObject_Del(self);
       
   262 }
       
   263 
       
   264 static PyObject *dirs_iter(dirsObject *self)
       
   265 {
       
   266 	return PyObject_GetIter(self->dict);
       
   267 }
       
   268 
       
   269 static PySequenceMethods dirs_sequence_methods;
       
   270 
       
   271 static PyMethodDef dirs_methods[] = {
       
   272 	{"addpath", (PyCFunction)dirs_addpath, METH_VARARGS, "add a path"},
       
   273 	{"delpath", (PyCFunction)dirs_delpath, METH_VARARGS, "remove a path"},
       
   274 	{NULL} /* Sentinel */
       
   275 };
       
   276 
       
   277 static PyTypeObject dirsType = { PyObject_HEAD_INIT(NULL) };
       
   278 
       
   279 void dirs_module_init(PyObject *mod)
       
   280 {
       
   281 	dirs_sequence_methods.sq_contains = (objobjproc)dirs_contains;
       
   282 	dirsType.tp_name = "parsers.dirs";
       
   283 	dirsType.tp_new = PyType_GenericNew;
       
   284 	dirsType.tp_basicsize = sizeof(dirsObject);
       
   285 	dirsType.tp_dealloc = (destructor)dirs_dealloc;
       
   286 	dirsType.tp_as_sequence = &dirs_sequence_methods;
       
   287 	dirsType.tp_flags = Py_TPFLAGS_DEFAULT;
       
   288 	dirsType.tp_doc = "dirs";
       
   289 	dirsType.tp_iter = (getiterfunc)dirs_iter;
       
   290 	dirsType.tp_methods = dirs_methods;
       
   291 	dirsType.tp_init = (initproc)dirs_init;
       
   292 
       
   293 	if (PyType_Ready(&dirsType) < 0)
       
   294 		return;
       
   295 	Py_INCREF(&dirsType);
       
   296 
       
   297 	PyModule_AddObject(mod, "dirs", (PyObject *)&dirsType);
       
   298 }