690 lines
15 KiB
C
690 lines
15 KiB
C
/*
|
||
XXX support range parameter on search
|
||
XXX support mstop parameter on search
|
||
*/
|
||
|
||
|
||
/* Regular expression objects */
|
||
/* This uses Tatu Ylonen's copyleft-free reimplementation of
|
||
GNU regular expressions */
|
||
|
||
#include "Python.h"
|
||
|
||
#include <ctype.h>
|
||
|
||
#include "regexpr.h"
|
||
|
||
static PyObject *RegexError; /* Exception */
|
||
|
||
typedef struct {
|
||
PyObject_HEAD
|
||
struct re_pattern_buffer re_patbuf; /* The compiled expression */
|
||
struct re_registers re_regs; /* The registers from the last match */
|
||
char re_fastmap[256]; /* Storage for fastmap */
|
||
PyObject *re_translate; /* String object for translate table */
|
||
PyObject *re_lastok; /* String object last matched/searched */
|
||
PyObject *re_groupindex; /* Group name to index dictionary */
|
||
PyObject *re_givenpat; /* Pattern with symbolic groups */
|
||
PyObject *re_realpat; /* Pattern without symbolic groups */
|
||
} regexobject;
|
||
|
||
/* Regex object methods */
|
||
|
||
static void
|
||
reg_dealloc(regexobject *re)
|
||
{
|
||
if (re->re_patbuf.buffer)
|
||
free(re->re_patbuf.buffer);
|
||
Py_XDECREF(re->re_translate);
|
||
Py_XDECREF(re->re_lastok);
|
||
Py_XDECREF(re->re_groupindex);
|
||
Py_XDECREF(re->re_givenpat);
|
||
Py_XDECREF(re->re_realpat);
|
||
PyObject_Del(re);
|
||
}
|
||
|
||
static PyObject *
|
||
makeresult(struct re_registers *regs)
|
||
{
|
||
PyObject *v;
|
||
int i;
|
||
static PyObject *filler = NULL;
|
||
|
||
if (filler == NULL) {
|
||
filler = Py_BuildValue("(ii)", -1, -1);
|
||
if (filler == NULL)
|
||
return NULL;
|
||
}
|
||
v = PyTuple_New(RE_NREGS);
|
||
if (v == NULL)
|
||
return NULL;
|
||
|
||
for (i = 0; i < RE_NREGS; i++) {
|
||
int lo = regs->start[i];
|
||
int hi = regs->end[i];
|
||
PyObject *w;
|
||
if (lo == -1 && hi == -1) {
|
||
w = filler;
|
||
Py_INCREF(w);
|
||
}
|
||
else
|
||
w = Py_BuildValue("(ii)", lo, hi);
|
||
if (w == NULL || PyTuple_SetItem(v, i, w) < 0) {
|
||
Py_DECREF(v);
|
||
return NULL;
|
||
}
|
||
}
|
||
return v;
|
||
}
|
||
|
||
static PyObject *
|
||
regobj_match(regexobject *re, PyObject *args)
|
||
{
|
||
PyObject *argstring;
|
||
char *buffer;
|
||
int size;
|
||
int offset = 0;
|
||
int result;
|
||
|
||
if (!PyArg_ParseTuple(args, "O|i:match", &argstring, &offset))
|
||
return NULL;
|
||
if (!PyArg_Parse(argstring, "t#", &buffer, &size))
|
||
return NULL;
|
||
|
||
if (offset < 0 || offset > size) {
|
||
PyErr_SetString(RegexError, "match offset out of range");
|
||
return NULL;
|
||
}
|
||
Py_XDECREF(re->re_lastok);
|
||
re->re_lastok = NULL;
|
||
result = _Py_re_match(&re->re_patbuf, (unsigned char *)buffer, size, offset,
|
||
&re->re_regs);
|
||
if (result < -1) {
|
||
/* Serious failure of some sort; if re_match didn't
|
||
set an exception, raise a generic error */
|
||
if (!PyErr_Occurred())
|
||
PyErr_SetString(RegexError, "match failure");
|
||
return NULL;
|
||
}
|
||
if (result >= 0) {
|
||
Py_INCREF(argstring);
|
||
re->re_lastok = argstring;
|
||
}
|
||
return PyInt_FromLong((long)result); /* Length of the match or -1 */
|
||
}
|
||
|
||
static PyObject *
|
||
regobj_search(regexobject *re, PyObject *args)
|
||
{
|
||
PyObject *argstring;
|
||
char *buffer;
|
||
int size;
|
||
int offset = 0;
|
||
int range;
|
||
int result;
|
||
|
||
if (!PyArg_ParseTuple(args, "O|i:search", &argstring, &offset))
|
||
return NULL;
|
||
if (!PyArg_Parse(argstring, "t#:search", &buffer, &size))
|
||
return NULL;
|
||
|
||
if (offset < 0 || offset > size) {
|
||
PyErr_SetString(RegexError, "search offset out of range");
|
||
return NULL;
|
||
}
|
||
/* NB: In Emacs 18.57, the documentation for re_search[_2] and
|
||
the implementation don't match: the documentation states that
|
||
|range| positions are tried, while the code tries |range|+1
|
||
positions. It seems more productive to believe the code! */
|
||
range = size - offset;
|
||
Py_XDECREF(re->re_lastok);
|
||
re->re_lastok = NULL;
|
||
result = _Py_re_search(&re->re_patbuf, (unsigned char *)buffer, size, offset, range,
|
||
&re->re_regs);
|
||
if (result < -1) {
|
||
/* Serious failure of some sort; if re_match didn't
|
||
set an exception, raise a generic error */
|
||
if (!PyErr_Occurred())
|
||
PyErr_SetString(RegexError, "match failure");
|
||
return NULL;
|
||
}
|
||
if (result >= 0) {
|
||
Py_INCREF(argstring);
|
||
re->re_lastok = argstring;
|
||
}
|
||
return PyInt_FromLong((long)result); /* Position of the match or -1 */
|
||
}
|
||
|
||
/* get the group from the regex where index can be a string (group name) or
|
||
an integer index [0 .. 99]
|
||
*/
|
||
static PyObject*
|
||
group_from_index(regexobject *re, PyObject *index)
|
||
{
|
||
int i, a, b;
|
||
char *v;
|
||
|
||
if (PyString_Check(index))
|
||
if (re->re_groupindex == NULL ||
|
||
!(index = PyDict_GetItem(re->re_groupindex, index)))
|
||
{
|
||
PyErr_SetString(RegexError,
|
||
"group() group name doesn't exist");
|
||
return NULL;
|
||
}
|
||
|
||
i = PyInt_AsLong(index);
|
||
if (i == -1 && PyErr_Occurred())
|
||
return NULL;
|
||
|
||
if (i < 0 || i >= RE_NREGS) {
|
||
PyErr_SetString(RegexError, "group() index out of range");
|
||
return NULL;
|
||
}
|
||
if (re->re_lastok == NULL) {
|
||
PyErr_SetString(RegexError,
|
||
"group() only valid after successful match/search");
|
||
return NULL;
|
||
}
|
||
a = re->re_regs.start[i];
|
||
b = re->re_regs.end[i];
|
||
if (a < 0 || b < 0) {
|
||
Py_INCREF(Py_None);
|
||
return Py_None;
|
||
}
|
||
|
||
if (!(v = PyString_AsString(re->re_lastok)))
|
||
return NULL;
|
||
|
||
return PyString_FromStringAndSize(v+a, b-a);
|
||
}
|
||
|
||
|
||
static PyObject *
|
||
regobj_group(regexobject *re, PyObject *args)
|
||
{
|
||
int n = PyTuple_Size(args);
|
||
int i;
|
||
PyObject *res = NULL;
|
||
|
||
if (n < 0)
|
||
return NULL;
|
||
if (n == 0) {
|
||
PyErr_SetString(PyExc_TypeError, "not enough arguments");
|
||
return NULL;
|
||
}
|
||
if (n == 1) {
|
||
/* return value is a single string */
|
||
PyObject *index = PyTuple_GetItem(args, 0);
|
||
if (!index)
|
||
return NULL;
|
||
|
||
return group_from_index(re, index);
|
||
}
|
||
|
||
/* return value is a tuple */
|
||
if (!(res = PyTuple_New(n)))
|
||
return NULL;
|
||
|
||
for (i = 0; i < n; i++) {
|
||
PyObject *index = PyTuple_GetItem(args, i);
|
||
PyObject *group = NULL;
|
||
|
||
if (!index)
|
||
goto finally;
|
||
if (!(group = group_from_index(re, index)))
|
||
goto finally;
|
||
if (PyTuple_SetItem(res, i, group) < 0)
|
||
goto finally;
|
||
}
|
||
return res;
|
||
|
||
finally:
|
||
Py_DECREF(res);
|
||
return NULL;
|
||
}
|
||
|
||
|
||
static struct PyMethodDef reg_methods[] = {
|
||
{"match", (PyCFunction)regobj_match, METH_VARARGS},
|
||
{"search", (PyCFunction)regobj_search, METH_VARARGS},
|
||
{"group", (PyCFunction)regobj_group, METH_VARARGS},
|
||
{NULL, NULL} /* sentinel */
|
||
};
|
||
|
||
|
||
|
||
static char* members[] = {
|
||
"last", "regs", "translate",
|
||
"groupindex", "realpat", "givenpat",
|
||
NULL
|
||
};
|
||
|
||
|
||
static PyObject *
|
||
regobj_getattr(regexobject *re, char *name)
|
||
{
|
||
if (strcmp(name, "regs") == 0) {
|
||
if (re->re_lastok == NULL) {
|
||
Py_INCREF(Py_None);
|
||
return Py_None;
|
||
}
|
||
return makeresult(&re->re_regs);
|
||
}
|
||
if (strcmp(name, "last") == 0) {
|
||
if (re->re_lastok == NULL) {
|
||
Py_INCREF(Py_None);
|
||
return Py_None;
|
||
}
|
||
Py_INCREF(re->re_lastok);
|
||
return re->re_lastok;
|
||
}
|
||
if (strcmp(name, "translate") == 0) {
|
||
if (re->re_translate == NULL) {
|
||
Py_INCREF(Py_None);
|
||
return Py_None;
|
||
}
|
||
Py_INCREF(re->re_translate);
|
||
return re->re_translate;
|
||
}
|
||
if (strcmp(name, "groupindex") == 0) {
|
||
if (re->re_groupindex == NULL) {
|
||
Py_INCREF(Py_None);
|
||
return Py_None;
|
||
}
|
||
Py_INCREF(re->re_groupindex);
|
||
return re->re_groupindex;
|
||
}
|
||
if (strcmp(name, "realpat") == 0) {
|
||
if (re->re_realpat == NULL) {
|
||
Py_INCREF(Py_None);
|
||
return Py_None;
|
||
}
|
||
Py_INCREF(re->re_realpat);
|
||
return re->re_realpat;
|
||
}
|
||
if (strcmp(name, "givenpat") == 0) {
|
||
if (re->re_givenpat == NULL) {
|
||
Py_INCREF(Py_None);
|
||
return Py_None;
|
||
}
|
||
Py_INCREF(re->re_givenpat);
|
||
return re->re_givenpat;
|
||
}
|
||
if (strcmp(name, "__members__") == 0) {
|
||
int i = 0;
|
||
PyObject *list = NULL;
|
||
|
||
/* okay, so it's unlikely this list will change that often.
|
||
still, it's easier to change it in just one place.
|
||
*/
|
||
while (members[i])
|
||
i++;
|
||
if (!(list = PyList_New(i)))
|
||
return NULL;
|
||
|
||
i = 0;
|
||
while (members[i]) {
|
||
PyObject* v = PyString_FromString(members[i]);
|
||
if (!v || PyList_SetItem(list, i, v) < 0) {
|
||
Py_DECREF(list);
|
||
return NULL;
|
||
}
|
||
i++;
|
||
}
|
||
return list;
|
||
}
|
||
return Py_FindMethod(reg_methods, (PyObject *)re, name);
|
||
}
|
||
|
||
static PyTypeObject Regextype = {
|
||
PyObject_HEAD_INIT(NULL)
|
||
0, /*ob_size*/
|
||
"regex.regex", /*tp_name*/
|
||
sizeof(regexobject), /*tp_size*/
|
||
0, /*tp_itemsize*/
|
||
/* methods */
|
||
(destructor)reg_dealloc, /*tp_dealloc*/
|
||
0, /*tp_print*/
|
||
(getattrfunc)regobj_getattr, /*tp_getattr*/
|
||
0, /*tp_setattr*/
|
||
0, /*tp_compare*/
|
||
0, /*tp_repr*/
|
||
};
|
||
|
||
/* reference counting invariants:
|
||
pattern: borrowed
|
||
translate: borrowed
|
||
givenpat: borrowed
|
||
groupindex: transferred
|
||
*/
|
||
static PyObject *
|
||
newregexobject(PyObject *pattern, PyObject *translate, PyObject *givenpat, PyObject *groupindex)
|
||
{
|
||
regexobject *re;
|
||
char *pat;
|
||
int size;
|
||
|
||
if (!PyArg_Parse(pattern, "t#", &pat, &size))
|
||
return NULL;
|
||
|
||
if (translate != NULL && PyString_Size(translate) != 256) {
|
||
PyErr_SetString(RegexError,
|
||
"translation table must be 256 bytes");
|
||
return NULL;
|
||
}
|
||
re = PyObject_New(regexobject, &Regextype);
|
||
if (re != NULL) {
|
||
char *error;
|
||
re->re_patbuf.buffer = NULL;
|
||
re->re_patbuf.allocated = 0;
|
||
re->re_patbuf.fastmap = (unsigned char *)re->re_fastmap;
|
||
if (translate) {
|
||
re->re_patbuf.translate = (unsigned char *)PyString_AsString(translate);
|
||
if (!re->re_patbuf.translate)
|
||
goto finally;
|
||
Py_INCREF(translate);
|
||
}
|
||
else
|
||
re->re_patbuf.translate = NULL;
|
||
re->re_translate = translate;
|
||
re->re_lastok = NULL;
|
||
re->re_groupindex = groupindex;
|
||
Py_INCREF(pattern);
|
||
re->re_realpat = pattern;
|
||
Py_INCREF(givenpat);
|
||
re->re_givenpat = givenpat;
|
||
error = _Py_re_compile_pattern((unsigned char *)pat, size, &re->re_patbuf);
|
||
if (error != NULL) {
|
||
PyErr_SetString(RegexError, error);
|
||
goto finally;
|
||
}
|
||
}
|
||
return (PyObject *)re;
|
||
finally:
|
||
Py_DECREF(re);
|
||
return NULL;
|
||
}
|
||
|
||
static PyObject *
|
||
regex_compile(PyObject *self, PyObject *args)
|
||
{
|
||
PyObject *pat = NULL;
|
||
PyObject *tran = NULL;
|
||
|
||
if (!PyArg_ParseTuple(args, "S|S:compile", &pat, &tran))
|
||
return NULL;
|
||
return newregexobject(pat, tran, pat, NULL);
|
||
}
|
||
|
||
static PyObject *
|
||
symcomp(PyObject *pattern, PyObject *gdict)
|
||
{
|
||
char *opat, *oend, *o, *n, *g, *v;
|
||
int group_count = 0;
|
||
int sz;
|
||
int escaped = 0;
|
||
char name_buf[128];
|
||
PyObject *npattern;
|
||
int require_escape = re_syntax & RE_NO_BK_PARENS ? 0 : 1;
|
||
|
||
if (!(opat = PyString_AsString(pattern)))
|
||
return NULL;
|
||
|
||
if ((sz = PyString_Size(pattern)) < 0)
|
||
return NULL;
|
||
|
||
oend = opat + sz;
|
||
o = opat;
|
||
|
||
if (oend == opat) {
|
||
Py_INCREF(pattern);
|
||
return pattern;
|
||
}
|
||
|
||
if (!(npattern = PyString_FromStringAndSize((char*)NULL, sz)) ||
|
||
!(n = PyString_AsString(npattern)))
|
||
return NULL;
|
||
|
||
while (o < oend) {
|
||
if (*o == '(' && escaped == require_escape) {
|
||
char *backtrack;
|
||
escaped = 0;
|
||
++group_count;
|
||
*n++ = *o;
|
||
if (++o >= oend || *o != '<')
|
||
continue;
|
||
/* *o == '<' */
|
||
if (o+1 < oend && *(o+1) == '>')
|
||
continue;
|
||
backtrack = o;
|
||
g = name_buf;
|
||
for (++o; o < oend;) {
|
||
if (*o == '>') {
|
||
PyObject *group_name = NULL;
|
||
PyObject *group_index = NULL;
|
||
*g++ = '\0';
|
||
group_name = PyString_FromString(name_buf);
|
||
group_index = PyInt_FromLong(group_count);
|
||
if (group_name == NULL ||
|
||
group_index == NULL ||
|
||
PyDict_SetItem(gdict, group_name,
|
||
group_index) != 0)
|
||
{
|
||
Py_XDECREF(group_name);
|
||
Py_XDECREF(group_index);
|
||
Py_XDECREF(npattern);
|
||
return NULL;
|
||
}
|
||
Py_DECREF(group_name);
|
||
Py_DECREF(group_index);
|
||
++o; /* eat the '>' */
|
||
break;
|
||
}
|
||
if (!isalnum(Py_CHARMASK(*o)) && *o != '_') {
|
||
o = backtrack;
|
||
break;
|
||
}
|
||
*g++ = *o++;
|
||
}
|
||
}
|
||
else if (*o == '[' && !escaped) {
|
||
*n++ = *o;
|
||
++o; /* eat the char following '[' */
|
||
*n++ = *o;
|
||
while (o < oend && *o != ']') {
|
||
++o;
|
||
*n++ = *o;
|
||
}
|
||
if (o < oend)
|
||
++o;
|
||
}
|
||
else if (*o == '\\') {
|
||
escaped = 1;
|
||
*n++ = *o;
|
||
++o;
|
||
}
|
||
else {
|
||
escaped = 0;
|
||
*n++ = *o;
|
||
++o;
|
||
}
|
||
}
|
||
|
||
if (!(v = PyString_AsString(npattern))) {
|
||
Py_DECREF(npattern);
|
||
return NULL;
|
||
}
|
||
/* _PyString_Resize() decrements npattern on failure */
|
||
_PyString_Resize(&npattern, n - v);
|
||
return npattern;
|
||
|
||
}
|
||
|
||
static PyObject *
|
||
regex_symcomp(PyObject *self, PyObject *args)
|
||
{
|
||
PyObject *pattern;
|
||
PyObject *tran = NULL;
|
||
PyObject *gdict = NULL;
|
||
PyObject *npattern;
|
||
PyObject *retval = NULL;
|
||
|
||
if (!PyArg_ParseTuple(args, "S|S:symcomp", &pattern, &tran))
|
||
return NULL;
|
||
|
||
gdict = PyDict_New();
|
||
if (gdict == NULL || (npattern = symcomp(pattern, gdict)) == NULL) {
|
||
Py_DECREF(gdict);
|
||
Py_DECREF(pattern);
|
||
return NULL;
|
||
}
|
||
retval = newregexobject(npattern, tran, pattern, gdict);
|
||
Py_DECREF(npattern);
|
||
return retval;
|
||
}
|
||
|
||
|
||
static PyObject *cache_pat;
|
||
static PyObject *cache_prog;
|
||
|
||
static int
|
||
update_cache(PyObject *pat)
|
||
{
|
||
PyObject *tuple = Py_BuildValue("(O)", pat);
|
||
int status = 0;
|
||
|
||
if (!tuple)
|
||
return -1;
|
||
|
||
if (pat != cache_pat) {
|
||
Py_XDECREF(cache_pat);
|
||
cache_pat = NULL;
|
||
Py_XDECREF(cache_prog);
|
||
cache_prog = regex_compile((PyObject *)NULL, tuple);
|
||
if (cache_prog == NULL) {
|
||
status = -1;
|
||
goto finally;
|
||
}
|
||
cache_pat = pat;
|
||
Py_INCREF(cache_pat);
|
||
}
|
||
finally:
|
||
Py_DECREF(tuple);
|
||
return status;
|
||
}
|
||
|
||
static PyObject *
|
||
regex_match(PyObject *self, PyObject *args)
|
||
{
|
||
PyObject *pat, *string;
|
||
PyObject *tuple, *v;
|
||
|
||
if (!PyArg_ParseTuple(args, "SS:match", &pat, &string))
|
||
return NULL;
|
||
if (update_cache(pat) < 0)
|
||
return NULL;
|
||
|
||
if (!(tuple = Py_BuildValue("(S)", string)))
|
||
return NULL;
|
||
v = regobj_match((regexobject *)cache_prog, tuple);
|
||
Py_DECREF(tuple);
|
||
return v;
|
||
}
|
||
|
||
static PyObject *
|
||
regex_search(PyObject *self, PyObject *args)
|
||
{
|
||
PyObject *pat, *string;
|
||
PyObject *tuple, *v;
|
||
|
||
if (!PyArg_ParseTuple(args, "SS:search", &pat, &string))
|
||
return NULL;
|
||
if (update_cache(pat) < 0)
|
||
return NULL;
|
||
|
||
if (!(tuple = Py_BuildValue("(S)", string)))
|
||
return NULL;
|
||
v = regobj_search((regexobject *)cache_prog, tuple);
|
||
Py_DECREF(tuple);
|
||
return v;
|
||
}
|
||
|
||
static PyObject *
|
||
regex_set_syntax(PyObject *self, PyObject *args)
|
||
{
|
||
int syntax;
|
||
if (!PyArg_ParseTuple(args, "i:set_syntax", &syntax))
|
||
return NULL;
|
||
syntax = re_set_syntax(syntax);
|
||
/* wipe the global pattern cache */
|
||
Py_XDECREF(cache_pat);
|
||
cache_pat = NULL;
|
||
Py_XDECREF(cache_prog);
|
||
cache_prog = NULL;
|
||
return PyInt_FromLong((long)syntax);
|
||
}
|
||
|
||
static PyObject *
|
||
regex_get_syntax(PyObject *self)
|
||
{
|
||
return PyInt_FromLong((long)re_syntax);
|
||
}
|
||
|
||
|
||
static struct PyMethodDef regex_global_methods[] = {
|
||
{"compile", regex_compile, METH_VARARGS},
|
||
{"symcomp", regex_symcomp, METH_VARARGS},
|
||
{"match", regex_match, METH_VARARGS},
|
||
{"search", regex_search, METH_VARARGS},
|
||
{"set_syntax", regex_set_syntax, METH_VARARGS},
|
||
{"get_syntax", (PyCFunction)regex_get_syntax, METH_NOARGS},
|
||
{NULL, NULL} /* sentinel */
|
||
};
|
||
|
||
DL_EXPORT(void)
|
||
initregex(void)
|
||
{
|
||
PyObject *m, *d, *v;
|
||
int i;
|
||
char *s;
|
||
|
||
/* Initialize object type */
|
||
Regextype.ob_type = &PyType_Type;
|
||
|
||
m = Py_InitModule("regex", regex_global_methods);
|
||
d = PyModule_GetDict(m);
|
||
|
||
if (PyErr_Warn(PyExc_DeprecationWarning,
|
||
"the regex module is deprecated; "
|
||
"please use the re module") < 0)
|
||
return;
|
||
|
||
/* Initialize regex.error exception */
|
||
v = RegexError = PyErr_NewException("regex.error", NULL, NULL);
|
||
if (v == NULL || PyDict_SetItemString(d, "error", v) != 0)
|
||
goto finally;
|
||
|
||
/* Initialize regex.casefold constant */
|
||
if (!(v = PyString_FromStringAndSize((char *)NULL, 256)))
|
||
goto finally;
|
||
|
||
if (!(s = PyString_AsString(v)))
|
||
goto finally;
|
||
|
||
for (i = 0; i < 256; i++) {
|
||
if (isupper(i))
|
||
s[i] = tolower(i);
|
||
else
|
||
s[i] = i;
|
||
}
|
||
if (PyDict_SetItemString(d, "casefold", v) < 0)
|
||
goto finally;
|
||
Py_DECREF(v);
|
||
|
||
if (!PyErr_Occurred())
|
||
return;
|
||
finally:
|
||
/* Nothing */ ;
|
||
}
|