#include static PyObject* markup; static int init_constants(void) { PyObject *module; /* import markup type so that we can mark the return value */ 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); /* shouldn't happen */ 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; } } /* we don't have to escape integers, bools or floats */ if (PyLong_CheckExact(text) || PyFloat_CheckExact(text) || PyBool_Check(text) || text == Py_None) return PyObject_CallFunctionObjArgs(markup, text, NULL); /* if the object has an __html__ method that performs the escaping */ html = PyObject_GetAttr(text ,id_html); if (html) { s = PyObject_CallObject(html, NULL); Py_DECREF(html); if (s == NULL) { return NULL; } /* Convert to Markup object */ rv = PyObject_CallFunctionObjArgs(markup, (PyObject*)s, NULL); Py_DECREF(s); return rv; } /* otherwise make the object unicode if it isn't, then escape */ 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); /* convert the unicode string into a markup object. */ 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(\"\")\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} /* Sentinel */ }; 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); }