/* Python bindings for the libbeep library --- configuration management. Copyright (c) 2005 Scott Grayban This file is part of PyBMP and is an internal module for BMP configuration management. PyBMP is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 dated June, 1991. PyBMP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with PyBMP; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. $Id: _bmpconfigmodule.c 3 2006-03-28 11:52:53Z sgrayban $ */ #include #include #include /* For Python pre-2.3 compatibility * * Note: this could go in a common include file for _xmmscon{trol,fig}module.c * but distutils doesn't handle dependencies, so it is duplicated in the two * modules for now... */ #ifndef PyDoc_STRVAR /* Define macros for inline documentation. */ #define PyDoc_VAR(name) static char name[] #define PyDoc_STRVAR(name,str) PyDoc_VAR(name) = PyDoc_STR(str) #ifndef WITHOUT_DOC_STRINGS #define PyDoc_STR(str) str #else #define PyDoc_STR(str) "" #endif /* WITHOUT_DOC_STRINGS */ #endif /* PyDoc_STRVAR */ static PyObject *ConfigError; static PyObject *ConfigWriteToFileError; typedef struct { PyObject_HEAD ConfigFile *config; } PyBMPConfigObject; static void Config_dealloc(PyBMPConfigObject *self) { xmms_cfg_free(self->config); self->ob_type->tp_free((PyObject *)self); } /* Important note: it is guaranteed that after Config_new as well as * after Config_init, the "config" member of a PyBMPConfigObject has * been initialized by libxmms (with xmms_cfg_new, xmms_cfg_open_file * or xmms_cfg_open_default_file) and is still perfectly usable. */ static PyObject * Config_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyBMPConfigObject *self; self = (PyBMPConfigObject *) type->tp_alloc(type, 0); if (self != NULL) self->config = xmms_cfg_new(); return (PyObject *)self; } static int Config_init(PyBMPConfigObject *self, PyObject *args, PyObject *kwds) { char *filename = NULL; static char *kwlist[] = {"fromfile", NULL}; if (! PyArg_ParseTupleAndKeywords(args, kwds, "|s:__init__", kwlist, &filename)) return -1; if (filename != NULL) { /* Alas, xmms_cfg_open_*file don't take a ConfigFile * argument but perform the allocation themselves, so we have to deallocate the memory that was allocated in __new__ to initialize self->config from a file... */ xmms_cfg_free(self->config); if (! strcmp(filename, "")) { if ( (self->config = xmms_cfg_open_default_file()) == NULL) PyErr_SetString(PyExc_IOError, "unable to read the " "default configuration file for BMP"); } else { if ( (self->config = xmms_cfg_open_file((gchar *) filename)) == NULL) PyErr_Format(PyExc_IOError, "unable to read \"%s\"", filename); } if (self->config == NULL) { /* This is critical because the rest of the module assumes * that the config member of a Config object is always an * initialized ConfigFile*. In particular, Config_dealloc * calls xmms_cfg_free on that member. */ self->config = xmms_cfg_new(); return -1; } } return 0; } PyDoc_STRVAR(Config_dump_doc, "Dump an xmms configuration.\n\ \n\ Return the configuration contained in 'self' as a list of\n\ xmms.config.ConfigSection instances."); static PyObject * Config_dump(PyBMPConfigObject *self) { ConfigFile *cfg = self->config; ConfigSection *section; ConfigLine *line; GList *section_list, *line_list; PyObject *high_level_module=NULL, *ConfigLine_class_object=NULL, *ConfigSection_class_object=NULL, *pyseclist=NULL, *pysection=NULL, *pyline=NULL, *empty_tuple=NULL; PyObject *kw; /* Prepare all we need to create ConfigLine and ConfigSection instances */ high_level_module = PyImport_ImportModule("config"); if (high_level_module == NULL) return NULL; ConfigLine_class_object = PyObject_GetAttrString(high_level_module, "ConfigLine"); if (ConfigLine_class_object == NULL) goto error; ConfigSection_class_object = PyObject_GetAttrString(high_level_module, "ConfigSection"); if (ConfigSection_class_object == NULL) goto error; /* An empty tuple that we'll use later in *args, **kwargs calls */ empty_tuple = PyTuple_New(0); if (empty_tuple == NULL) goto error; /* The list of ConfigSection instances to return */ pyseclist = PyList_New(0); if (pyseclist == NULL) goto error; section_list = cfg->sections; while (section_list != NULL) { section = (ConfigSection *) section_list->data; /* It is correct to use empty_tuple as the lines parameter because * this argument is only iterated over in ConfigSection.__init__. * Since we reuse the same object for each section we create, this * would be wrong if ConfigSection.__init__ stored a reference to * that object in the ConfigSection instance instead of iterating * over it. */ kw = Py_BuildValue("{s:s,s:O}", "name", section->name, "lines", empty_tuple); if (kw == NULL) goto error; /* Create an xmms.config.ConfigSection instance */ pysection = PyObject_Call(ConfigSection_class_object, empty_tuple, kw); Py_DECREF(kw); if (pysection == NULL) goto error; line_list = section->lines; while (line_list != NULL) { line = (ConfigLine *) line_list->data; kw = Py_BuildValue("{s:s,s:s}", "key", line->key, "value", line->value); if (kw == NULL) goto error; /* Create an xmms.config.ConfigLine instance */ pyline = PyObject_Call(ConfigLine_class_object, empty_tuple, kw); Py_DECREF(kw); if (pyline == NULL) goto error; if (PyObject_CallMethod(pysection, "append", "O", pyline) == NULL) goto error; Py_DECREF(pyline); /* pyline must be reset to NULL in case we go to "error" * before it points to a new ConfigLine instance. */ pyline = NULL; line_list = g_list_next(line_list); } if (PyList_Append(pyseclist, pysection) < 0) goto error; Py_DECREF(pysection); /* pysection must be reset to NULL in case we go to "error" before * it points to a new ConfigSection instance. */ pysection = NULL; section_list = g_list_next(section_list); } Py_DECREF(high_level_module); Py_DECREF(ConfigLine_class_object); Py_DECREF(ConfigSection_class_object); Py_DECREF(empty_tuple); return pyseclist; error: Py_XDECREF(high_level_module); Py_XDECREF(ConfigLine_class_object); Py_XDECREF(ConfigSection_class_object); Py_XDECREF(pyseclist); Py_XDECREF(pysection); Py_XDECREF(pyline); Py_XDECREF(empty_tuple); return NULL; } PyDoc_STRVAR(Config_create_section_doc, "Create an empty section in an BMP configuration object.\n\ \n\ create_section(self, secname) -> None\n\ \n\ secname -- name of the section to create"); static PyObject * Config_create_section(PyBMPConfigObject *self, PyObject *args, PyObject *kwds) { ConfigFile *cfg = self->config; ConfigSection *section; char *section_name; static char *kwlist[] = {"secname", NULL}; if (! PyArg_ParseTupleAndKeywords(args, kwds, "s:create_section", kwlist, §ion_name)) return NULL; /* This sucks because libxmms doesn't export xmms_cfg_create_section (at * least in BMP 1.2.8), so I have to duplicate its code here. */ section = g_malloc0(sizeof (ConfigSection)); section->name = g_strdup((gchar *)section_name); cfg->sections = g_list_append(cfg->sections, section); Py_INCREF(Py_None); return Py_None; } PyDoc_STRVAR(Config_write_string_doc, "Write a string value in an BMP configuration object.\n\ \n\ write_string(self, secname, key, value) -> None\n\ \n\ secname -- name of the section to modify\n\ key -- key of the line to create or modify\n\ value -- the new value to set"); static PyObject * Config_write_string(PyBMPConfigObject *self, PyObject *args, PyObject *kwds) { ConfigFile *cfg = self->config; char *section, *key, *value; static char *kwlist[] = {"secname", "key", "value", NULL}; if (! PyArg_ParseTupleAndKeywords(args, kwds, "sss:write_string", kwlist, §ion, &key, &value)) return NULL; xmms_cfg_write_string(cfg, (gchar *)section, (gchar *)key, (gchar *)value); Py_INCREF(Py_None); return Py_None; } PyDoc_STRVAR(Config_write_to_file_doc, "Write the contents of an BMP configuration object to a file.\n\ \n\ write_to_file(self, filename=\"\") -> None\n\ \n\ filename -- name of the file to write to\n\ \n\ *********************************************************************\n\ * WARNING *\n\ *********************************************************************\n\ * If \"filename\" is not specified, the configuration is written to the *\n\ * default BMP configuration file. *\n\ *********************************************************************\n\ * YOU HAVE BEEN WARNED *\n\ * *******************************************************************"); static PyObject * Config_write_to_file(PyBMPConfigObject *self, PyObject *args, PyObject *kwds) { ConfigFile *cfg = self->config; char *filename=NULL; gboolean ret; static char *kwlist[] = {"filename", NULL}; if (! PyArg_ParseTupleAndKeywords(args, kwds, "|s:write", kwlist, &filename)) return NULL; if ((filename != NULL) && strcmp(filename, "")) ret = xmms_cfg_write_file(cfg, (gchar *)filename); else ret = xmms_cfg_write_default_file(cfg); if (! ret) { PyErr_SetString(ConfigWriteToFileError, "unable to write the configuration to the file"); return NULL; } Py_INCREF(Py_None); return Py_None; } static PyMethodDef ConfigObject_methods[] = { {"dump", (PyCFunction)Config_dump, METH_NOARGS, Config_dump_doc}, {"create_section", (PyCFunction)Config_create_section, METH_VARARGS | METH_KEYWORDS, Config_create_section_doc}, {"write_string", (PyCFunction)Config_write_string, METH_VARARGS | METH_KEYWORDS, Config_write_string_doc}, {"write_to_file", (PyCFunction)Config_write_to_file, METH_VARARGS | METH_KEYWORDS, Config_write_to_file_doc}, {NULL} /* Sentinel */ }; static PyTypeObject PyBMPConfigType = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "xmms._xmmsconfig.Config", /* tp_name */ sizeof(PyBMPConfigObject), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)Config_dealloc,/* 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 | Py_TPFLAGS_BASETYPE, /* tp_flags */ "BMP Configuration objects", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ ConfigObject_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)Config_init, /* tp_init */ 0, /* tp_alloc */ Config_new, /* tp_new */ }; static PyMethodDef module_methods[] = { {NULL} /* Sentinel */ }; #ifndef PyMODINIT_FUNC #define PyMODINIT_FUNC void #endif PyDoc_STRVAR(module_doc, "Python interface to BMP --- internal module for configuration management.\n\ \n\ This module is an internal part of PyBMP and should not be used\n\ directly by \"user\" modules. It contains the direct interface to\n\ libbeep with respect to configuration management.\n\ \n\ \"User\" modules should use the xmms.config module instead."); PyMODINIT_FUNC init_xmmsconfig(void) /* Executed on the first import */ { PyObject* m; if (PyType_Ready(&PyBMPConfigType) < 0) return; /* Create Exception objects */ ConfigError = PyErr_NewException("xmms._xmmsconfig.error", NULL, NULL); if (ConfigError == NULL) return; Py_INCREF(ConfigError); /* Keep a reference to the exception */ ConfigWriteToFileError = PyErr_NewException( "xmms._xmmsconfig.WriteToFileError", ConfigError, NULL); if (ConfigWriteToFileError == NULL) return; Py_INCREF(ConfigWriteToFileError); /* Keep a reference to the exception */ /* Initialize the module */ m = Py_InitModule3("_xmmsconfig", module_methods, module_doc); if (m == NULL) return; /* Add the Exception objects to the module */ if (PyModule_AddObject(m, "error", ConfigError) < 0) return; if (PyModule_AddObject(m, "WriteToFileError", ConfigWriteToFileError) < 0) return; /* Add the Config type object to the module */ Py_INCREF(&PyBMPConfigType); if (PyModule_AddObject(m, "Config", (PyObject *)&PyBMPConfigType) < 0) return; return; }