|
#include <Python.h> |
|
|
|
static PyObject* markup; |
|
|
|
static int |
|
init_constants(void) |
|
{ |
|
PyObject *module; |
|
|
|
|
|
module = PyImport_ImportModule("markupsafe"); |
|
if (!module) |
|
return 0; |
|
markup = PyObject_GetAttrString(module, "Markup"); |
|
Py_DECREF(module); |
|
|
|
return 1; |
|
} |
|
|
|
#define GET_DELTA(inp, inp_end, delta) \ |
|
while (inp < inp_end) { \ |
|
switch (*inp++) { \ |
|
case '"': \ |
|
case '\'': \ |
|
case '&': \ |
|
delta += 4; \ |
|
break; \ |
|
case '<': \ |
|
case '>': \ |
|
delta += 3; \ |
|
break; \ |
|
} \ |
|
} |
|
|
|
#define DO_ESCAPE(inp, inp_end, outp) \ |
|
{ \ |
|
Py_ssize_t ncopy = 0; \ |
|
while (inp < inp_end) { \ |
|
switch (*inp) { \ |
|
case '"': \ |
|
memcpy(outp, inp-ncopy, sizeof(*outp)*ncopy); \ |
|
outp += ncopy; ncopy = 0; \ |
|
*outp++ = '&'; \ |
|
*outp++ = '#'; \ |
|
*outp++ = '3'; \ |
|
*outp++ = '4'; \ |
|
*outp++ = ';'; \ |
|
break; \ |
|
case '\'': \ |
|
memcpy(outp, inp-ncopy, sizeof(*outp)*ncopy); \ |
|
outp += ncopy; ncopy = 0; \ |
|
*outp++ = '&'; \ |
|
*outp++ = '#'; \ |
|
*outp++ = '3'; \ |
|
*outp++ = '9'; \ |
|
*outp++ = ';'; \ |
|
break; \ |
|
case '&': \ |
|
memcpy(outp, inp-ncopy, sizeof(*outp)*ncopy); \ |
|
outp += ncopy; ncopy = 0; \ |
|
*outp++ = '&'; \ |
|
*outp++ = 'a'; \ |
|
*outp++ = 'm'; \ |
|
*outp++ = 'p'; \ |
|
*outp++ = ';'; \ |
|
break; \ |
|
case '<': \ |
|
memcpy(outp, inp-ncopy, sizeof(*outp)*ncopy); \ |
|
outp += ncopy; ncopy = 0; \ |
|
*outp++ = '&'; \ |
|
*outp++ = 'l'; \ |
|
*outp++ = 't'; \ |
|
*outp++ = ';'; \ |
|
break; \ |
|
case '>': \ |
|
memcpy(outp, inp-ncopy, sizeof(*outp)*ncopy); \ |
|
outp += ncopy; ncopy = 0; \ |
|
*outp++ = '&'; \ |
|
*outp++ = 'g'; \ |
|
*outp++ = 't'; \ |
|
*outp++ = ';'; \ |
|
break; \ |
|
default: \ |
|
ncopy++; \ |
|
} \ |
|
inp++; \ |
|
} \ |
|
memcpy(outp, inp-ncopy, sizeof(*outp)*ncopy); \ |
|
} |
|
|
|
static PyObject* |
|
escape_unicode_kind1(PyUnicodeObject *in) |
|
{ |
|
Py_UCS1 *inp = PyUnicode_1BYTE_DATA(in); |
|
Py_UCS1 *inp_end = inp + PyUnicode_GET_LENGTH(in); |
|
Py_UCS1 *outp; |
|
PyObject *out; |
|
Py_ssize_t delta = 0; |
|
|
|
GET_DELTA(inp, inp_end, delta); |
|
if (!delta) { |
|
Py_INCREF(in); |
|
return (PyObject*)in; |
|
} |
|
|
|
out = PyUnicode_New(PyUnicode_GET_LENGTH(in) + delta, |
|
PyUnicode_IS_ASCII(in) ? 127 : 255); |
|
if (!out) |
|
return NULL; |
|
|
|
inp = PyUnicode_1BYTE_DATA(in); |
|
outp = PyUnicode_1BYTE_DATA(out); |
|
DO_ESCAPE(inp, inp_end, outp); |
|
return out; |
|
} |
|
|
|
static PyObject* |
|
escape_unicode_kind2(PyUnicodeObject *in) |
|
{ |
|
Py_UCS2 *inp = PyUnicode_2BYTE_DATA(in); |
|
Py_UCS2 *inp_end = inp + PyUnicode_GET_LENGTH(in); |
|
Py_UCS2 *outp; |
|
PyObject *out; |
|
Py_ssize_t delta = 0; |
|
|
|
GET_DELTA(inp, inp_end, delta); |
|
if (!delta) { |
|
Py_INCREF(in); |
|
return (PyObject*)in; |
|
} |
|
|
|
out = PyUnicode_New(PyUnicode_GET_LENGTH(in) + delta, 65535); |
|
if (!out) |
|
return NULL; |
|
|
|
inp = PyUnicode_2BYTE_DATA(in); |
|
outp = PyUnicode_2BYTE_DATA(out); |
|
DO_ESCAPE(inp, inp_end, outp); |
|
return out; |
|
} |
|
|
|
|
|
static PyObject* |
|
escape_unicode_kind4(PyUnicodeObject *in) |
|
{ |
|
Py_UCS4 *inp = PyUnicode_4BYTE_DATA(in); |
|
Py_UCS4 *inp_end = inp + PyUnicode_GET_LENGTH(in); |
|
Py_UCS4 *outp; |
|
PyObject *out; |
|
Py_ssize_t delta = 0; |
|
|
|
GET_DELTA(inp, inp_end, delta); |
|
if (!delta) { |
|
Py_INCREF(in); |
|
return (PyObject*)in; |
|
} |
|
|
|
out = PyUnicode_New(PyUnicode_GET_LENGTH(in) + delta, 1114111); |
|
if (!out) |
|
return NULL; |
|
|
|
inp = PyUnicode_4BYTE_DATA(in); |
|
outp = PyUnicode_4BYTE_DATA(out); |
|
DO_ESCAPE(inp, inp_end, outp); |
|
return out; |
|
} |
|
|
|
static PyObject* |
|
escape_unicode(PyUnicodeObject *in) |
|
{ |
|
if (PyUnicode_READY(in)) |
|
return NULL; |
|
|
|
switch (PyUnicode_KIND(in)) { |
|
case PyUnicode_1BYTE_KIND: |
|
return escape_unicode_kind1(in); |
|
case PyUnicode_2BYTE_KIND: |
|
return escape_unicode_kind2(in); |
|
case PyUnicode_4BYTE_KIND: |
|
return escape_unicode_kind4(in); |
|
} |
|
assert(0); |
|
return NULL; |
|
} |
|
|
|
static PyObject* |
|
escape(PyObject *self, PyObject *text) |
|
{ |
|
static PyObject *id_html; |
|
PyObject *s = NULL, *rv = NULL, *html; |
|
|
|
if (id_html == NULL) { |
|
id_html = PyUnicode_InternFromString("__html__"); |
|
if (id_html == NULL) { |
|
return NULL; |
|
} |
|
} |
|
|
|
|
|
if (PyLong_CheckExact(text) || |
|
PyFloat_CheckExact(text) || PyBool_Check(text) || |
|
text == Py_None) |
|
return PyObject_CallFunctionObjArgs(markup, text, NULL); |
|
|
|
|
|
html = PyObject_GetAttr(text ,id_html); |
|
if (html) { |
|
s = PyObject_CallObject(html, NULL); |
|
Py_DECREF(html); |
|
if (s == NULL) { |
|
return NULL; |
|
} |
|
|
|
rv = PyObject_CallFunctionObjArgs(markup, (PyObject*)s, NULL); |
|
Py_DECREF(s); |
|
return rv; |
|
} |
|
|
|
|
|
PyErr_Clear(); |
|
if (!PyUnicode_Check(text)) { |
|
PyObject *unicode = PyObject_Str(text); |
|
if (!unicode) |
|
return NULL; |
|
s = escape_unicode((PyUnicodeObject*)unicode); |
|
Py_DECREF(unicode); |
|
} |
|
else |
|
s = escape_unicode((PyUnicodeObject*)text); |
|
|
|
|
|
rv = PyObject_CallFunctionObjArgs(markup, (PyObject*)s, NULL); |
|
Py_DECREF(s); |
|
return rv; |
|
} |
|
|
|
|
|
static PyObject* |
|
escape_silent(PyObject *self, PyObject *text) |
|
{ |
|
if (text != Py_None) |
|
return escape(self, text); |
|
return PyObject_CallFunctionObjArgs(markup, NULL); |
|
} |
|
|
|
|
|
static PyObject* |
|
soft_str(PyObject *self, PyObject *s) |
|
{ |
|
if (!PyUnicode_Check(s)) |
|
return PyObject_Str(s); |
|
Py_INCREF(s); |
|
return s; |
|
} |
|
|
|
|
|
static PyMethodDef module_methods[] = { |
|
{ |
|
"escape", |
|
(PyCFunction)escape, |
|
METH_O, |
|
"Replace the characters ``&``, ``<``, ``>``, ``'``, and ``\"`` in" |
|
" the string with HTML-safe sequences. Use this if you need to display" |
|
" text that might contain such characters in HTML.\n\n" |
|
"If the object has an ``__html__`` method, it is called and the" |
|
" return value is assumed to already be safe for HTML.\n\n" |
|
":param s: An object to be converted to a string and escaped.\n" |
|
":return: A :class:`Markup` string with the escaped text.\n" |
|
}, |
|
{ |
|
"escape_silent", |
|
(PyCFunction)escape_silent, |
|
METH_O, |
|
"Like :func:`escape` but treats ``None`` as the empty string." |
|
" Useful with optional values, as otherwise you get the string" |
|
" ``'None'`` when the value is ``None``.\n\n" |
|
">>> escape(None)\n" |
|
"Markup('None')\n" |
|
">>> escape_silent(None)\n" |
|
"Markup('')\n" |
|
}, |
|
{ |
|
"soft_str", |
|
(PyCFunction)soft_str, |
|
METH_O, |
|
"Convert an object to a string if it isn't already. This preserves" |
|
" a :class:`Markup` string rather than converting it back to a basic" |
|
" string, so it will still be marked as safe and won't be escaped" |
|
" again.\n\n" |
|
">>> value = escape(\"<User 1>\")\n" |
|
">>> value\n" |
|
"Markup('<User 1>')\n" |
|
">>> escape(str(value))\n" |
|
"Markup('&lt;User 1&gt;')\n" |
|
">>> escape(soft_str(value))\n" |
|
"Markup('<User 1>')\n" |
|
}, |
|
{NULL, NULL, 0, NULL} |
|
}; |
|
|
|
static struct PyModuleDef module_definition = { |
|
PyModuleDef_HEAD_INIT, |
|
"markupsafe._speedups", |
|
NULL, |
|
-1, |
|
module_methods, |
|
NULL, |
|
NULL, |
|
NULL, |
|
NULL |
|
}; |
|
|
|
PyMODINIT_FUNC |
|
PyInit__speedups(void) |
|
{ |
|
if (!init_constants()) |
|
return NULL; |
|
|
|
return PyModule_Create(&module_definition); |
|
} |
|
|