5 * This module implements functions for automatic escaping in C for better
6 * performance. Additionally it defines a `tb_set_next` function to patch the
7 * debug traceback. If the speedups module is not compiled a ctypes
8 * implementation of `tb_set_next` and Python implementations of the other
11 * :copyright: 2008 by Armin Ronacher, Mickaël Guérin.
17 #define ESCAPED_CHARS_TABLE_SIZE 63
18 #define UNICHR(x) (((PyUnicodeObject*)PyUnicode_DecodeASCII(x, strlen(x), NULL))->str);
20 #if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
21 typedef int Py_ssize_t;
22 #define PY_SSIZE_T_MAX INT_MAX
23 #define PY_SSIZE_T_MIN INT_MIN
27 static PyObject* markup;
28 static Py_ssize_t escaped_chars_delta_len[ESCAPED_CHARS_TABLE_SIZE];
29 static Py_UNICODE *escaped_chars_repl[ESCAPED_CHARS_TABLE_SIZE];
31 static int init_constants(void)
34 /* happing of characters to replace */
35 escaped_chars_repl['"'] = UNICHR(""");
36 escaped_chars_repl['\''] = UNICHR("'");
37 escaped_chars_repl['&'] = UNICHR("&");
38 escaped_chars_repl['<'] = UNICHR("<");
39 escaped_chars_repl['>'] = UNICHR(">");
41 /* lengths of those characters when replaced - 1 */
42 memset(escaped_chars_delta_len, 0, sizeof (escaped_chars_delta_len));
43 escaped_chars_delta_len['"'] = escaped_chars_delta_len['\''] = \
44 escaped_chars_delta_len['&'] = 4;
45 escaped_chars_delta_len['<'] = escaped_chars_delta_len['>'] = 3;
47 /* import markup type so that we can mark the return value */
48 module = PyImport_ImportModule("jinja2.utils");
51 markup = PyObject_GetAttrString(module, "Markup");
57 static PyObject *escape_unicode(PyUnicodeObject *in)
60 Py_UNICODE *inp = in->str;
61 const Py_UNICODE *inp_end = in->str + in->length;
62 Py_UNICODE *next_escp;
64 Py_ssize_t delta=0, erepl=0, delta_len=0;
66 /* First we need to figure out how long the escaped string will be */
67 while (*(inp) || inp < inp_end) {
68 if (*inp < ESCAPED_CHARS_TABLE_SIZE && escaped_chars_delta_len[*inp]) {
69 delta += escaped_chars_delta_len[*inp];
75 /* Do we need to escape anything at all? */
81 out = (PyUnicodeObject*)PyUnicode_FromUnicode(NULL, in->length + delta);
88 /* look for the next substitution */
90 while (next_escp < inp_end) {
91 if (*next_escp < ESCAPED_CHARS_TABLE_SIZE &&
92 (delta_len = escaped_chars_delta_len[*next_escp])) {
99 if (next_escp > inp) {
100 /* copy unescaped chars between inp and next_escp */
101 Py_UNICODE_COPY(outp, inp, next_escp-inp);
102 outp += next_escp - inp;
105 /* escape 'next_escp' */
106 Py_UNICODE_COPY(outp, escaped_chars_repl[*next_escp], delta_len);
112 Py_UNICODE_COPY(outp, inp, in->length - (inp - in->str));
114 return (PyObject*)out;
118 static PyObject *escape(PyObject *self, PyObject *text)
120 PyObject *s = NULL, *rv = NULL, *html;
122 /* we don't have to escape integers, bools or floats */
123 if (PyInt_CheckExact(text) || PyLong_CheckExact(text) ||
124 PyFloat_CheckExact(text) || PyBool_Check(text) ||
126 return PyObject_CallFunctionObjArgs(markup, text, NULL);
128 /* if the object has an __html__ method that performs the escaping */
129 html = PyObject_GetAttrString(text, "__html__");
131 rv = PyObject_CallObject(html, NULL);
136 /* otherwise make the object unicode if it isn't, then escape */
138 if (!PyUnicode_Check(text)) {
139 PyObject *unicode = PyObject_Unicode(text);
142 s = escape_unicode((PyUnicodeObject*)unicode);
146 s = escape_unicode((PyUnicodeObject*)text);
148 /* convert the unicode string into a markup object. */
149 rv = PyObject_CallFunctionObjArgs(markup, (PyObject*)s, NULL);
155 static PyObject *soft_unicode(PyObject *self, PyObject *s)
157 if (!PyUnicode_Check(s))
158 return PyObject_Unicode(s);
164 static PyObject *tb_set_next(PyObject *self, PyObject *args)
166 PyTracebackObject *tb, *old;
169 if (!PyArg_ParseTuple(args, "O!O:tb_set_next", &PyTraceBack_Type, &tb, &next))
173 else if (!PyTraceBack_Check(next)) {
174 PyErr_SetString(PyExc_TypeError,
175 "tb_set_next arg 2 must be traceback or None");
182 tb->tb_next = (PyTracebackObject*)next;
190 static PyMethodDef module_methods[] = {
191 {"escape", (PyCFunction)escape, METH_O,
192 "escape(s) -> markup\n\n"
193 "Convert the characters &, <, >, ', and \" in string s to HTML-safe\n"
194 "sequences. Use this if you need to display text that might contain\n"
195 "such characters in HTML. Marks return value as markup string."},
196 {"soft_unicode", (PyCFunction)soft_unicode, METH_O,
197 "soft_unicode(object) -> string\n\n"
198 "Make a string unicode if it isn't already. That way a markup\n"
199 "string is not converted back to unicode."},
200 {"tb_set_next", (PyCFunction)tb_set_next, METH_VARARGS,
201 "Set the tb_next member of a traceback object."},
202 {NULL, NULL, 0, NULL} /* Sentinel */
206 #ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
207 #define PyMODINIT_FUNC void
212 if (!init_constants())
215 Py_InitModule3("jinja2._speedups", module_methods, "");