/** * markupsafe._speedups * ~~~~~~~~~~~~~~~~~~~~ * * This module implements functions for automatic escaping in C for better * performance. * * :copyright: (c) 2010 by Armin Ronacher. * :license: BSD. */ #include #define ESCAPED_CHARS_TABLE_SIZE 63 #define UNICHR(x) (PyUnicode_AS_UNICODE((PyUnicodeObject*)PyUnicode_DecodeASCII(x, strlen(x), NULL))); #if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN) typedef int Py_ssize_t; #define PY_SSIZE_T_MAX INT_MAX #define PY_SSIZE_T_MIN INT_MIN #endif static PyObject* markup; static Py_ssize_t escaped_chars_delta_len[ESCAPED_CHARS_TABLE_SIZE]; static Py_UNICODE *escaped_chars_repl[ESCAPED_CHARS_TABLE_SIZE]; static int init_constants(void) { PyObject *module; /* happing of characters to replace */ escaped_chars_repl['"'] = UNICHR("""); escaped_chars_repl['\''] = UNICHR("'"); escaped_chars_repl['&'] = UNICHR("&"); escaped_chars_repl['<'] = UNICHR("<"); escaped_chars_repl['>'] = UNICHR(">"); /* lengths of those characters when replaced - 1 */ memset(escaped_chars_delta_len, 0, sizeof (escaped_chars_delta_len)); escaped_chars_delta_len['"'] = escaped_chars_delta_len['\''] = \ escaped_chars_delta_len['&'] = 4; escaped_chars_delta_len['<'] = escaped_chars_delta_len['>'] = 3; /* 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; } static PyObject* escape_unicode(PyUnicodeObject *in) { PyUnicodeObject *out; Py_UNICODE *inp = PyUnicode_AS_UNICODE(in); const Py_UNICODE *inp_end = PyUnicode_AS_UNICODE(in) + PyUnicode_GET_SIZE(in); Py_UNICODE *next_escp; Py_UNICODE *outp; Py_ssize_t delta=0, erepl=0, delta_len=0; /* First we need to figure out how long the escaped string will be */ while (*(inp) || inp < inp_end) { if (*inp < ESCAPED_CHARS_TABLE_SIZE) { delta += escaped_chars_delta_len[*inp]; erepl += !!escaped_chars_delta_len[*inp]; } ++inp; } /* Do we need to escape anything at all? */ if (!erepl) { Py_INCREF(in); return (PyObject*)in; } out = (PyUnicodeObject*)PyUnicode_FromUnicode(NULL, PyUnicode_GET_SIZE(in) + delta); if (!out) return NULL; outp = PyUnicode_AS_UNICODE(out); inp = PyUnicode_AS_UNICODE(in); while (erepl-- > 0) { /* look for the next substitution */ next_escp = inp; while (next_escp < inp_end) { if (*next_escp < ESCAPED_CHARS_TABLE_SIZE && (delta_len = escaped_chars_delta_len[*next_escp])) { ++delta_len; break; } ++next_escp; } if (next_escp > inp) { /* copy unescaped chars between inp and next_escp */ Py_UNICODE_COPY(outp, inp, next_escp-inp); outp += next_escp - inp; } /* escape 'next_escp' */ Py_UNICODE_COPY(outp, escaped_chars_repl[*next_escp], delta_len); outp += delta_len; inp = next_escp + 1; } if (inp < inp_end) Py_UNICODE_COPY(outp, inp, PyUnicode_GET_SIZE(in) - (inp - PyUnicode_AS_UNICODE(in))); return (PyObject*)out; } static PyObject* escape(PyObject *self, PyObject *text) { PyObject *s = NULL, *rv = NULL, *html; /* we don't have to escape integers, bools or floats */ if (PyLong_CheckExact(text) || #if PY_MAJOR_VERSION < 3 PyInt_CheckExact(text) || #endif 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_GetAttrString(text, "__html__"); if (html) { rv = PyObject_CallObject(html, NULL); Py_DECREF(html); return rv; } /* otherwise make the object unicode if it isn't, then escape */ PyErr_Clear(); if (!PyUnicode_Check(text)) { #if PY_MAJOR_VERSION < 3 PyObject *unicode = PyObject_Unicode(text); #else PyObject *unicode = PyObject_Str(text); #endif 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_unicode(PyObject *self, PyObject *s) { if (!PyUnicode_Check(s)) #if PY_MAJOR_VERSION < 3 return PyObject_Unicode(s); #else return PyObject_Str(s); #endif Py_INCREF(s); return s; } static PyMethodDef module_methods[] = { {"escape", (PyCFunction)escape, METH_O, "escape(s) -> markup\n\n" "Convert the characters &, <, >, ', and \" in string s to HTML-safe\n" "sequences. Use this if you need to display text that might contain\n" "such characters in HTML. Marks return value as markup string."}, {"escape_silent", (PyCFunction)escape_silent, METH_O, "escape_silent(s) -> markup\n\n" "Like escape but converts None to an empty string."}, {"soft_unicode", (PyCFunction)soft_unicode, METH_O, "soft_unicode(object) -> string\n\n" "Make a string unicode if it isn't already. That way a markup\n" "string is not converted back to unicode."}, {NULL, NULL, 0, NULL} /* Sentinel */ }; #if PY_MAJOR_VERSION < 3 #ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ #define PyMODINIT_FUNC void #endif PyMODINIT_FUNC init_speedups(void) { if (!init_constants()) return; Py_InitModule3("markupsafe._speedups", module_methods, ""); } #else /* Python 3.x module initialization */ 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); } #endif