rpm-build/python/poptmodule.c
2008-02-22 22:33:11 +03:00

660 lines
18 KiB
C

/*
*
*
*
*/
#define PY_POPT_VERSION "0.2"
static char *module_doc = "Python bindings for the popt library\n\
\n\
The popt library provides useful command-line parsing functions.\n\
The latest version of the popt library is distributed with rpm\n\
and is always available from ftp://ftp.rpm.org/pub/rpm/dist";
#include <Python.h>
#include <popt.h>
#define DEBUG 1
#if defined(DEBUG)
#define debug(x, y) { printf("%s: %s\n", x, y); }
#else
#define debug(x, y) {}
#endif
/* Functins and datatypes needed for the context object */
typedef struct poptContext_s {
PyObject_HEAD;
struct poptOption *options;
int optionsNo;
poptContext ctx;
/* The index of the option retrieved with getNextOpt()*/
int opt;
} poptContextObject;
/* The exception */
static PyObject *pypoptError;
/* Misc functions */
static PyObject * __poptOptionValue2PyObject(const struct poptOption *option)
{
if (option == NULL) {
/* This shouldn't really happen */
PyErr_BadInternalCall();
return NULL;
}
if (option->arg == NULL) {
Py_INCREF(Py_None);
return Py_None;
}
switch(option->argInfo) {
case POPT_ARG_INCLUDE_TABLE:
/* Do nothing */
Py_INCREF(Py_None);
return Py_None;
case POPT_ARG_STRING:
if (*(char **)(option->arg) == NULL) {
Py_INCREF(Py_None);
return Py_None;
}
return PyString_FromString(*(char **)(option->arg));
break;
case POPT_ARG_DOUBLE:
return PyFloat_FromDouble(*(double *)(option->arg));
break;
case POPT_ARG_LONG:
return PyInt_FromLong(*(long *)(option->arg));
break;
case POPT_ARG_NONE:
case POPT_ARG_VAL:
return PyInt_FromLong(*(int *)(option->arg));
break;
}
/* This shouldn't really happen */
PyErr_BadInternalCall();
return NULL;
}
static PyObject * ctxReset(poptContextObject *self)
{
poptResetContext(self->ctx);
self->opt = -1;
Py_INCREF(Py_None);
return Py_None;
}
static PyObject * ctxGetNextOpt(poptContextObject *self)
{
self->opt = poptGetNextOpt(self->ctx);
return PyInt_FromLong(self->opt);
}
static PyObject * ctxGetOptArg(poptContextObject *self)
{
const char *opt;
opt = poptGetOptArg(self->ctx);
if (opt == NULL) {
Py_INCREF(Py_None);
return Py_None;
}
return PyString_FromString(opt);
}
static PyObject * ctxGetArg(poptContextObject *self)
{
const char *arg;
arg = poptGetArg(self->ctx);
if (arg == NULL) {
Py_INCREF(Py_None);
return Py_None;
}
return PyString_FromString(arg);
}
static PyObject * ctxPeekArg(poptContextObject *self)
{
const char *arg;
arg = poptPeekArg(self->ctx);
if (arg == NULL) {
Py_INCREF(Py_None);
return Py_None;
}
return PyString_FromString(arg);
}
static PyObject * ctxGetArgs(poptContextObject *self)
{
const char **args;
PyObject *list;
int size, i;
args = poptGetArgs(self->ctx);
if (args == NULL) {
Py_INCREF(Py_None);
return Py_None;
}
/* Compute the list size */
for (size = 0; args[size]; size++);
/* Create the list */
list = PyList_New(size);
if (list == NULL)
return NULL;
for (i = 0; i < size; i++)
PyList_SetItem(list, i, PyString_FromString(args[i]));
return list;
}
static PyObject * ctxBadOption(poptContextObject *self, PyObject *args)
{
int flags = 0;
const char *badOption;
if (!PyArg_ParseTuple(args, "|i", &flags))
return NULL;
badOption = poptBadOption(self->ctx, flags);
if (badOption == NULL) {
Py_INCREF(Py_None);
return Py_None;
}
return PyString_FromString(badOption);
}
static PyObject * ctxReadDefaultConfig(poptContextObject *self, PyObject *args)
{
int flags = 0;
if (!PyArg_ParseTuple(args, "|i", &flags))
return NULL;
return PyInt_FromLong(poptReadDefaultConfig(self->ctx, flags));
}
static PyObject * ctxReadConfigFile(poptContextObject *self, PyObject *args)
{
const char *filename;
if (!PyArg_ParseTuple(args, "s", &filename))
return NULL;
return PyInt_FromLong(poptReadConfigFile(self->ctx, filename));
}
static PyObject * ctxSetOtherOptionHelp(poptContextObject *self, PyObject *args)
{
const char *option;
if (!PyArg_ParseTuple(args, "s", &option))
return NULL;
poptSetOtherOptionHelp(self->ctx, option);
Py_INCREF(Py_None);
return Py_None;
}
static PyObject * ctxPrintHelp(poptContextObject *self, PyObject *args)
{
FILE *f;
int flags = 0;
PyObject *file;
if (!PyArg_ParseTuple(args, "|O!i", &PyFile_Type, &file, &flags))
return NULL;
f = PyFile_AsFile(file);
if (f == NULL)
f = stderr;
poptPrintHelp(self->ctx, f, flags);
Py_INCREF(Py_None);
return Py_None;
}
static PyObject * ctxPrintUsage(poptContextObject *self, PyObject *args)
{
FILE *f;
int flags = 0;
PyObject *file;
if (!PyArg_ParseTuple(args, "|O!i", &PyFile_Type, &file, &flags))
return NULL;
f = PyFile_AsFile(file);
if (f == NULL)
f = stderr;
poptPrintUsage(self->ctx, f, flags);
Py_INCREF(Py_None);
return Py_None;
}
/* XXX addAlias */
/* XXX stuffArgs */
/* XXX callbackType */
/*******************************/
/* Added ctxGetOptValues */
/*******************************/
/* Builds a list of values corresponding to each option */
static PyObject * ctxGetOptValues(poptContextObject *self)
{
PyObject *list;
int i;
/* Create the list */
list = PyList_New(self->optionsNo);
if (list == NULL)
return NULL;
for (i = 0; i < self->optionsNo; i++) {
PyObject *item;
item = __poptOptionValue2PyObject(self->options + i);
item = __poptOptionValue2PyObject(self->options + i);
if (item == NULL)
return NULL;
PyList_SetItem(list, i, item);
}
return list;
}
static PyObject * ctxGetOptValue(poptContextObject *self)
{
int i;
if (self->opt < 0) {
/* No processing */
Py_INCREF(Py_None);
return Py_None;
}
/* Look for the option that returned this value */
for (i = 0; i < self->optionsNo; i++)
if (self->options[i].val == self->opt) {
/* Cool, this is the one */
return __poptOptionValue2PyObject(self->options + i);
}
/* Not found */
Py_INCREF(Py_None);
return Py_None;
}
static struct PyMethodDef ctxMethods[] = {
{"reset", (PyCFunction)ctxReset, METH_NOARGS},
{"getNextOpt", (PyCFunction)ctxGetNextOpt, METH_NOARGS},
{"getOptArg", (PyCFunction)ctxGetOptArg, METH_NOARGS},
{"getArg", (PyCFunction)ctxGetArg, METH_NOARGS},
{"peekArg", (PyCFunction)ctxPeekArg, METH_NOARGS},
{"getArgs", (PyCFunction)ctxGetArgs, METH_NOARGS},
{"badOption", (PyCFunction)ctxBadOption, METH_VARARGS},
{"readDefaultConfig", (PyCFunction)ctxReadDefaultConfig, METH_VARARGS},
{"readConfigFile", (PyCFunction)ctxReadConfigFile, METH_VARARGS},
{"setOtherOptionHelp", (PyCFunction)ctxSetOtherOptionHelp, METH_VARARGS},
{"printHelp", (PyCFunction)ctxPrintHelp, METH_VARARGS},
{"printUsage", (PyCFunction)ctxPrintUsage, METH_VARARGS},
/*
{"addAlias", (PyCFunction)ctxAddAlias},
{"stuffArgs", (PyCFunction)ctxStuffArgs},
{"callbackType", (PyCFunction)ctxCallbackType},
*/
{"getOptValues", (PyCFunction)ctxGetOptValues, METH_NOARGS},
{"getOptValue", (PyCFunction)ctxGetOptValue, METH_NOARGS},
{NULL, NULL}
};
static PyObject * ctxGetAttr(poptContextObject *s, char *name)
{
return Py_FindMethod(ctxMethods, (PyObject *)s, name);
}
static void ctxDealloc(poptContextObject *self, PyObject *args)
{
if (self->options != NULL) {
int i;
for (i = 0; i < self->optionsNo; i++) {
struct poptOption *o = self->options + i;
if (o->argInfo != POPT_ARG_INCLUDE_TABLE && o->arg)
free(o->arg);
}
free(self->options);
self->options = NULL;
}
poptFreeContext(self->ctx);
PyObject_Del(self);
}
static PyTypeObject poptContextType = {
PyObject_HEAD_INIT(&PyType_Type)
0, /* ob_size */
"poptContext", /* tp_name */
sizeof(poptContextObject), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)ctxDealloc, /* tp_dealloc */
(printfunc)NULL, /* tp_print */
(getattrfunc)ctxGetAttr, /* tp_getattr */
(setattrfunc)NULL, /* tp_setattr */
(cmpfunc)NULL, /* tp_compare */
(reprfunc)NULL, /* tp_repr */
NULL, /* tp_as_number */
NULL, /* tp_as_sequence */
NULL /* tp_as_mapping */
};
/* Functions and datatypes needed for the popt module */
#define AUTOHELP "autohelp"
static const struct poptOption __autohelp[] = {
POPT_AUTOHELP
POPT_TABLEEND
};
/* Misc functions */
static int __setPoptOption(PyObject *list, struct poptOption *opt)
{
int listSize;
PyObject *o;
const char *s;
int objsize;
/* Look for autohelp stuff first */
if (PyString_Check(list)) {
if (!strcmp(AUTOHELP, PyString_AsString(list))) {
/* Autohelp */
*opt = __autohelp[0];
return 1;
}
PyErr_SetString(pypoptError, "Expected list or autohelp");
return 0;
}
if (!PyList_Check(list)) {
PyErr_SetString(pypoptError, "List expected");
return 0;
}
listSize = PyList_Size(list);
if (listSize < 3) {
PyErr_SetString(pypoptError, "List is too short");
return 0;
}
/* longName */
o = PyList_GetItem(list, 0);
/* Sanity check */
if (o == Py_None)
opt->longName = NULL;
else {
if (!PyString_Check(o)) {
PyErr_SetString(pypoptError, "Long name should be a string");
return 0;
}
opt->longName = PyString_AsString(o);
}
/* shortName */
o = PyList_GetItem(list, 1);
/* Sanity check */
if (o == Py_None)
opt->shortName = '\0';
else {
if (!PyString_Check(o)) {
PyErr_SetString(pypoptError, "Short name should be a string");
return 0;
}
s = PyString_AsString(o);
/* If s is the empty string, we set the short name to '\0', which is
* the expected behaviour */
opt->shortName = s[0];
}
/* Make sure they have specified at least one of the long name or short
* name; we don't allow for table inclusions and callbacks for now, even
* if it would be nice to have them. The table inclusion is broken anyway
* unless I find a good way to pass the second table as a reference; I
* would normally have to pass it thru the arg field, but I don't pass
* that in the python table */
if (opt->longName == NULL && opt->shortName == '\0') {
PyErr_SetString(pypoptError, "At least one of the short name and long name must be specified");
return 0;
}
/* argInfo */
o = PyList_GetItem(list, 2);
/* Sanity check */
if (!PyInt_Check(o)) {
PyErr_SetString(pypoptError, "argInfo is not an int");
return 0;
}
opt->argInfo = PyInt_AsLong(o);
/* Initialize the rest of the arguments with safe defaults */
switch(opt->argInfo) {
case POPT_ARG_STRING:
objsize = sizeof(char *);
break;
case POPT_ARG_DOUBLE:
objsize = sizeof(double);
break;
case POPT_ARG_NONE:
case POPT_ARG_VAL:
objsize = sizeof(int);
break;
case POPT_ARG_LONG:
objsize = sizeof(long);
break;
default:
PyErr_SetString(pypoptError, "Wrong value for argInfo");
return 0;
}
opt->arg = (void *)malloc(objsize);
if (opt->arg == NULL) {
PyErr_NoMemory();
return 0;
}
memset(opt->arg, '\0', objsize);
opt->val = 0;
opt->descrip = NULL;
opt->argDescrip = NULL;
/* If nothing left, end the stuff here */
if (listSize == 3)
return 1;
/* val */
o = PyList_GetItem(list, 3);
/* Sanity check */
if (o == Py_None)
opt->val = 0;
else {
if (!PyInt_Check(o)) {
PyErr_SetString(pypoptError, "Val should be int or None");
return 0;
}
opt->val = PyInt_AsLong(o);
}
/* If nothing left, end the stuff here */
if (listSize == 4)
return 1;
/* descrip */
o = PyList_GetItem(list, 4);
/* Sanity check */
if (!PyString_Check(o) && o != Py_None) {
PyErr_SetString(pypoptError, "Invalid value passed for the description");
return 0;
}
if (o == Py_None)
opt->descrip = NULL;
else
opt->descrip = PyString_AsString(o);
/* If nothing left, end the stuff here */
if (listSize == 5)
return 1;
/* argDescrip */
o = PyList_GetItem(list, 5);
/* Sanity check */
if (!PyString_Check(o) && o != Py_None) {
PyErr_SetString(pypoptError, "Invalid value passed for the argument description");
return 0;
}
if (o == Py_None)
opt->argDescrip = NULL;
else
opt->argDescrip = PyString_AsString(o);
return 1;
}
static struct poptOption * __getPoptOptions(PyObject *list, int *count)
{
int listSize, item, totalmem;
struct poptOption *opts;
struct poptOption sentinel = POPT_TABLEEND;
if (!PyList_Check(list)) {
PyErr_SetString(pypoptError, "List expected");
return NULL;
}
listSize = PyList_Size(list);
/* Malloc exactly the size of the list */
totalmem = (1 + listSize) * sizeof(struct poptOption);
opts = (struct poptOption *)malloc(totalmem);
if (opts == NULL) {
PyErr_NoMemory();
return NULL;
}
memset(opts, '\0', totalmem);
for (item = 0; item < listSize; item++) {
int ret;
/* Retrieve the item */
PyObject *o = PyList_GetItem(list, item);
ret = __setPoptOption(o, opts + item);
if (ret == 0) {
/* Presumably we pass the error from the previous level */
return NULL;
}
}
/* Sentinel */
opts[listSize] = sentinel;
*count = listSize;
return opts;
}
static char ** __getArgv(PyObject *list, int *argc)
{
int listSize, item, totalmem;
char **argv;
listSize = PyList_Size(list);
/* Malloc exactly the size of the list */
totalmem = (1 + listSize) * sizeof(char *);
argv = (char **)malloc(totalmem);
if (argv == NULL) {
PyErr_NoMemory();
return NULL;
}
memset(argv, '\0', totalmem);
for (item = 0; item < listSize; item++) {
/* Retrieve the item */
PyObject *o = PyList_GetItem(list, item);
if (!PyString_Check(o)) {
PyErr_SetString(pypoptError, "Expected a string as value for the argument");
return NULL;
}
argv[item] = PyString_AsString(o);
//debug("getArgv", argv[item]);
}
/* Sentinel */
argv[listSize] = NULL;
*argc = listSize;
return argv;
}
static PyObject * getContext(PyObject *self, PyObject *args)
{
const char *name;
PyObject *a, *o;
char **argv;
int argc, count, flags = 0;
struct poptOption *opts;
poptContextObject *c;
/* We should receive name, argv and a list */
if (!PyArg_ParseTuple(args, "zO!O!|i", &name, &PyList_Type, &a,
&PyList_Type, &o, &flags))
return NULL;
/* Parse argv */
argv = __getArgv(a, &argc);
if (argv == NULL)
return NULL;
/* Parse argv */
/* Parse opts */
opts = __getPoptOptions(o, &count);
if (opts == NULL)
/* Presumably they've set the exception at a previous level */
return NULL;
/* Parse argv */
c = PyObject_New(poptContextObject, &poptContextType);
c->options = opts;
c->optionsNo = count;
c->opt = -1;
c->ctx = poptGetContext(name, argc, (const char **)argv, opts, flags);
return (PyObject *)c;
}
struct _pyIntConstant {
char *name;
const int value;
};
static PyObject * _strerror(PyObject *self, PyObject *args)
{
int error;
if (!PyArg_ParseTuple(args, "i", &error)) {
return NULL;
}
return PyString_FromString(poptStrerror(error));
}
/* Methods for the popt module */
static struct PyMethodDef poptModuleMethods[] = {
{"getContext", (PyCFunction)getContext, METH_VARARGS, NULL},
{"strerror", (PyCFunction)_strerror, METH_VARARGS, NULL},
{NULL, NULL}
};
#define ADD_INT(NAME) {#NAME, NAME}
static const struct _pyIntConstant intConstants[] = {
/* Arg info */
ADD_INT(POPT_ARG_NONE),
ADD_INT(POPT_ARG_STRING),
{"POPT_ARG_INT", POPT_ARG_LONG},
ADD_INT(POPT_ARG_VAL),
{"POPT_ARG_FLOAT", POPT_ARG_DOUBLE},
ADD_INT(POPT_ARGFLAG_OR),
ADD_INT(POPT_ARGFLAG_AND),
ADD_INT(POPT_ARGFLAG_XOR),
ADD_INT(POPT_ARGFLAG_NOT),
ADD_INT(POPT_ARGFLAG_ONEDASH),
ADD_INT(POPT_ARGFLAG_DOC_HIDDEN),
ADD_INT(POPT_ARGFLAG_OPTIONAL),
/* Context flags*/
ADD_INT(POPT_CONTEXT_NO_EXEC),
ADD_INT(POPT_CONTEXT_KEEP_FIRST),
ADD_INT(POPT_CONTEXT_POSIXMEHARDER),
/* Errors */
ADD_INT(POPT_ERROR_NOARG),
ADD_INT(POPT_ERROR_BADOPT),
ADD_INT(POPT_ERROR_OPTSTOODEEP),
ADD_INT(POPT_ERROR_BADQUOTE),
ADD_INT(POPT_ERROR_BADNUMBER),
ADD_INT(POPT_ERROR_OVERFLOW),
ADD_INT(POPT_ERROR_ERRNO),
/* Misc */
ADD_INT(POPT_BADOPTION_NOALIAS),
{NULL}
};
void initpopt()
{
PyObject *dict, *module;
const struct _pyIntConstant *c;
module = Py_InitModule3("popt", poptModuleMethods, module_doc);
/* Init the constants */
dict = PyModule_GetDict(module);
PyDict_SetItemString(dict, "__version__",
PyString_FromString(PY_POPT_VERSION));
for (c = intConstants; c->name; c++) {
PyObject *val = PyInt_FromLong(c->value);
PyDict_SetItemString(dict, c->name, val);
Py_DECREF(val);
}
/* Add the autohelp stuff */
{
PyObject *val = PyString_FromString(AUTOHELP);
PyDict_SetItemString(dict, "POPT_AUTOHELP", val);
Py_DECREF(val);
}
/* The exception */
pypoptError = PyErr_NewException("popt.error", NULL, NULL);
PyDict_SetItemString(dict, "error", pypoptError);
}