/* 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 #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_XDECREF(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 = PyTuple_Pack(1, 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 */ }; PyMODINIT_FUNC 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); if (m == NULL) return; 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 */ ; }