You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
296 lines
7.0 KiB
296 lines
7.0 KiB
/*
|
|
* The implementation of the supprt for setting API versions.
|
|
*
|
|
* Copyright (c) 2010 Riverbank Computing Limited <info@riverbankcomputing.com>
|
|
*
|
|
* This file is part of SIP.
|
|
*
|
|
* This copy of SIP is licensed for use under the terms of the SIP License
|
|
* Agreement. See the file LICENSE for more details.
|
|
*
|
|
* This copy of SIP may also used under the terms of the GNU General Public
|
|
* License v2 or v3 as published by the Free Software Foundation which can be
|
|
* found in the files LICENSE-GPL2 and LICENSE-GPL3 included in this package.
|
|
*
|
|
* SIP is supplied WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
*/
|
|
|
|
|
|
#include <Python.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include "sip.h"
|
|
#include "sipint.h"
|
|
|
|
|
|
/*
|
|
* The structure that defines the version number of an API.
|
|
*/
|
|
typedef struct _apiVersionDef {
|
|
/* The name of the API. */
|
|
const char *api_name;
|
|
|
|
/*
|
|
* The version number of the API. This will either be set explicitly via
|
|
* a call to sip.setapi() or implicitly by an imported module.
|
|
*/
|
|
int version_nr;
|
|
|
|
/* The next in the list of APIs. */
|
|
struct _apiVersionDef *next;
|
|
} apiVersionDef;
|
|
|
|
|
|
/*
|
|
* The list of API versions.
|
|
*/
|
|
static apiVersionDef *api_versions = NULL;
|
|
|
|
|
|
/*
|
|
* Forward declarations.
|
|
*/
|
|
static int add_api(const char *api, int version_nr);
|
|
static apiVersionDef *find_api(const char *api);
|
|
|
|
|
|
/*
|
|
* See if a range of versions of a particular API is enabled.
|
|
*/
|
|
int sip_api_is_api_enabled(const char *name, int from, int to)
|
|
{
|
|
const apiVersionDef *avd;
|
|
|
|
if ((avd = find_api(name)) == NULL)
|
|
return FALSE;
|
|
|
|
if (from > 0 && avd->version_nr < from)
|
|
return FALSE;
|
|
|
|
if (to > 0 && avd->version_nr >= to)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* Initialise the the API for a module and return a negative value on error.
|
|
*/
|
|
int sipInitAPI(sipExportedModuleDef *em, PyObject *mod_dict)
|
|
{
|
|
int *apis, i;
|
|
sipVersionedFunctionDef *vf;
|
|
sipTypeDef **tdp;
|
|
|
|
/* See if the module defines any APIs. */
|
|
if ((apis = em->em_versions) != NULL)
|
|
{
|
|
while (apis[0] >= 0)
|
|
{
|
|
/*
|
|
* See if it is an API definition rather than a range
|
|
* definition.
|
|
*/
|
|
if (apis[2] < 0)
|
|
{
|
|
const char *api_name;
|
|
const apiVersionDef *avd;
|
|
|
|
api_name = sipNameFromPool(em, apis[0]);
|
|
|
|
/* Use the default version if not already set explicitly. */
|
|
if ((avd = find_api(api_name)) == NULL)
|
|
if (add_api(api_name, apis[1]) < 0)
|
|
return -1;
|
|
}
|
|
|
|
apis += 3;
|
|
}
|
|
}
|
|
|
|
/* Add any versioned global functions to the module dictionary. */
|
|
if ((vf = em->em_versioned_functions) != NULL)
|
|
{
|
|
while (vf->vf_name >= 0)
|
|
{
|
|
if (sipIsRangeEnabled(em, vf->vf_api_range))
|
|
{
|
|
const char *func_name = sipNameFromPool(em, vf->vf_name);
|
|
PyMethodDef *pmd;
|
|
PyObject *py_func;
|
|
|
|
if ((pmd = sip_api_malloc(sizeof (PyMethodDef))) == NULL)
|
|
return -1;
|
|
|
|
pmd->ml_name = SIP_MLNAME_CAST(func_name);
|
|
pmd->ml_meth = vf->vf_function;
|
|
pmd->ml_flags = vf->vf_flags;
|
|
pmd->ml_doc = vf->vf_docstring;
|
|
|
|
if ((py_func = PyCFunction_New(pmd, NULL)) == NULL)
|
|
return -1;
|
|
|
|
if (PyDict_SetItemString(mod_dict, func_name, py_func) < 0)
|
|
{
|
|
Py_DECREF(py_func);
|
|
return -1;
|
|
}
|
|
|
|
Py_DECREF(py_func);
|
|
}
|
|
|
|
++vf;
|
|
}
|
|
}
|
|
|
|
/* Update the types table according to any version information. */
|
|
for (tdp = em->em_types, i = 0; i < em->em_nrtypes; ++i, ++tdp)
|
|
{
|
|
sipTypeDef *td;
|
|
|
|
if ((td = *tdp) != NULL && td->td_version >= 0)
|
|
{
|
|
do
|
|
{
|
|
if (sipIsRangeEnabled(em, td->td_version))
|
|
{
|
|
/* Update the type with the enabled version. */
|
|
*tdp = td;
|
|
break;
|
|
}
|
|
}
|
|
while ((td = td->td_next_version) != NULL);
|
|
|
|
/*
|
|
* If there is no enabled version then stub the disabled version
|
|
* so that we don't lose the name from the (sorted) types table.
|
|
*/
|
|
if (td == NULL)
|
|
sipTypeSetStub(*tdp);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Get the version number for an API.
|
|
*/
|
|
PyObject *sipGetAPI(PyObject *self, PyObject *args)
|
|
{
|
|
const char *api;
|
|
const apiVersionDef *avd;
|
|
|
|
if (!PyArg_ParseTuple(args, "s:getapi", &api))
|
|
return NULL;
|
|
|
|
if ((avd = find_api(api)) == NULL)
|
|
{
|
|
PyErr_Format(PyExc_ValueError, "unknown API '%s'", api);
|
|
return NULL;
|
|
}
|
|
|
|
#if PY_MAJOR_VERSION >= 3
|
|
return PyLong_FromLong(avd->version_nr);
|
|
#else
|
|
return PyInt_FromLong(avd->version_nr);
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
* Set the version number for an API.
|
|
*/
|
|
PyObject *sipSetAPI(PyObject *self, PyObject *args)
|
|
{
|
|
const char *api;
|
|
int version_nr;
|
|
const apiVersionDef *avd;
|
|
|
|
if (!PyArg_ParseTuple(args, "si:setapi", &api, &version_nr))
|
|
return NULL;
|
|
|
|
if (version_nr < 1)
|
|
{
|
|
PyErr_Format(PyExc_ValueError,
|
|
"API version numbers must be greater or equal to 1, not %d",
|
|
version_nr);
|
|
return NULL;
|
|
}
|
|
|
|
if ((avd = find_api(api)) == NULL)
|
|
{
|
|
char *api_copy;
|
|
|
|
/* Make a deep copy of the name. */
|
|
if ((api_copy = sip_api_malloc(strlen(api) + 1)) == NULL)
|
|
return NULL;
|
|
|
|
strcpy(api_copy, api);
|
|
|
|
if (add_api(api_copy, version_nr) < 0)
|
|
return NULL;
|
|
}
|
|
else if (avd->version_nr != version_nr)
|
|
{
|
|
PyErr_Format(PyExc_ValueError,
|
|
"API '%s' has already been set to version %d", api,
|
|
avd->version_nr);
|
|
return NULL;
|
|
}
|
|
|
|
Py_INCREF(Py_None);
|
|
return Py_None;
|
|
}
|
|
|
|
|
|
/*
|
|
* Add a new API to the global list returning a negative value on error.
|
|
*/
|
|
static int add_api(const char *api, int version_nr)
|
|
{
|
|
apiVersionDef *avd;
|
|
|
|
if ((avd = sip_api_malloc(sizeof (apiVersionDef))) == NULL)
|
|
return -1;
|
|
|
|
avd->api_name = api;
|
|
avd->version_nr = version_nr;
|
|
avd->next = api_versions;
|
|
|
|
api_versions = avd;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Return the definition for the given API, or NULL if there was none.
|
|
*/
|
|
static apiVersionDef *find_api(const char *api)
|
|
{
|
|
apiVersionDef *avd;
|
|
|
|
for (avd = api_versions; avd != NULL; avd = avd->next)
|
|
if (strcmp(avd->api_name, api) == 0)
|
|
break;
|
|
|
|
return avd;
|
|
}
|
|
|
|
|
|
/*
|
|
* Return TRUE if a range defined by a range index is enabled.
|
|
*/
|
|
int sipIsRangeEnabled(sipExportedModuleDef *em, int range_index)
|
|
{
|
|
int *range = &em->em_versions[range_index * 3];
|
|
const char *api_name = sipNameFromPool(em, range[0]);
|
|
|
|
return sip_api_is_api_enabled(api_name, range[1], range[2]);
|
|
}
|