#include <Python.h>
#include <ftw.h>

static PyObject* callback = NULL;

static int
each_item(const char* filename, const struct stat* stat, int flags)
{
	PyObject* callback_return = \
		PyObject_CallFunction(callback, "ss#i",
		                      filename,
		                      (const char*)stat, sizeof(struct stat),
		                      flags);
	if (callback_return == NULL)
		return 1;
	Py_DECREF(callback_return);
	return 0;
}

static PyObject*
really_call_ftw(PyObject* self, PyObject* args)
{
	const char* dirname = NULL;
	if (PyArg_ParseTuple(args, "sO", &dirname, &callback) == 0)
		return NULL;

	int retval = ftw(dirname, each_item, OPEN_MAX);
	if (retval > 0)
		return NULL;
	else if (retval < 0)
		return PyErr_SetFromErrno(PyExc_OSError);

	Py_RETURN_NONE;
}

typedef struct
{
	PyObject_HEAD
	const struct stat stat;
} StatResult;

static PyTypeObject StatResultType = {
	PyObject_HEAD_INIT(NULL)
	0,                         /*ob_size*/
	"ftw.StatResult",          /*tp_name*/
	sizeof(StatResult),        /*tp_basicsize*/
	0,                         /*tp_itemsize*/
	0,                         /*tp_dealloc*/
	0,                         /*tp_print*/
	0,                         /*tp_getattr*/
	0,                         /*tp_setattr*/
	0,                         /*tp_compare*/
	0,                         /*tp_repr*/
	0,                         /*tp_as_number*/
	0,                         /*tp_as_sequence*/
	0,                         /*tp_as_mapping*/
	0,                         /*tp_hash */
	0,                         /*tp_call*/
	0,                         /*tp_str*/
	0,                         /*tp_getattro*/
	0,                         /*tp_setattro*/
	0,                         /*tp_as_buffer*/
	Py_TPFLAGS_DEFAULT,        /*tp_flags*/
	"stat structure for file entries generated by ftw.ftw()", /*tp_doc*/
};

static PyMethodDef FtwMethods[] = {
	{"ftw", really_call_ftw, METH_VARARGS, "Traverse a directory structure."},
	{NULL, NULL, 0, NULL}
};

PyMODINIT_FUNC
initftw8()
{
	/* we need to define tp_new for StatResultType, but after the generic
	 * new initializer is instantiated
	 */
	StatResultType.tp_new = PyType_GenericNew;
	if (PyType_Ready(&StatResultType) < 0)
		return;

	PyObject* thismodule = Py_InitModule("ftw8", FtwMethods);
	Py_INCREF(&StatResultType);
	PyModule_AddObject(thismodule, "StatResult", (PyObject*) &StatResultType);
}
