1*635a8641SAndroid Build Coastguard Worker /**
2*635a8641SAndroid Build Coastguard Worker * markupsafe._speedups
3*635a8641SAndroid Build Coastguard Worker * ~~~~~~~~~~~~~~~~~~~~
4*635a8641SAndroid Build Coastguard Worker *
5*635a8641SAndroid Build Coastguard Worker * This module implements functions for automatic escaping in C for better
6*635a8641SAndroid Build Coastguard Worker * performance.
7*635a8641SAndroid Build Coastguard Worker *
8*635a8641SAndroid Build Coastguard Worker * :copyright: (c) 2010 by Armin Ronacher.
9*635a8641SAndroid Build Coastguard Worker * :license: BSD.
10*635a8641SAndroid Build Coastguard Worker */
11*635a8641SAndroid Build Coastguard Worker
12*635a8641SAndroid Build Coastguard Worker #include <Python.h>
13*635a8641SAndroid Build Coastguard Worker
14*635a8641SAndroid Build Coastguard Worker #define ESCAPED_CHARS_TABLE_SIZE 63
15*635a8641SAndroid Build Coastguard Worker #define UNICHR(x) (PyUnicode_AS_UNICODE((PyUnicodeObject*)PyUnicode_DecodeASCII(x, strlen(x), NULL)));
16*635a8641SAndroid Build Coastguard Worker
17*635a8641SAndroid Build Coastguard Worker #if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
18*635a8641SAndroid Build Coastguard Worker typedef int Py_ssize_t;
19*635a8641SAndroid Build Coastguard Worker #define PY_SSIZE_T_MAX INT_MAX
20*635a8641SAndroid Build Coastguard Worker #define PY_SSIZE_T_MIN INT_MIN
21*635a8641SAndroid Build Coastguard Worker #endif
22*635a8641SAndroid Build Coastguard Worker
23*635a8641SAndroid Build Coastguard Worker
24*635a8641SAndroid Build Coastguard Worker static PyObject* markup;
25*635a8641SAndroid Build Coastguard Worker static Py_ssize_t escaped_chars_delta_len[ESCAPED_CHARS_TABLE_SIZE];
26*635a8641SAndroid Build Coastguard Worker static Py_UNICODE *escaped_chars_repl[ESCAPED_CHARS_TABLE_SIZE];
27*635a8641SAndroid Build Coastguard Worker
28*635a8641SAndroid Build Coastguard Worker static int
init_constants(void)29*635a8641SAndroid Build Coastguard Worker init_constants(void)
30*635a8641SAndroid Build Coastguard Worker {
31*635a8641SAndroid Build Coastguard Worker PyObject *module;
32*635a8641SAndroid Build Coastguard Worker /* happing of characters to replace */
33*635a8641SAndroid Build Coastguard Worker escaped_chars_repl['"'] = UNICHR(""");
34*635a8641SAndroid Build Coastguard Worker escaped_chars_repl['\''] = UNICHR("'");
35*635a8641SAndroid Build Coastguard Worker escaped_chars_repl['&'] = UNICHR("&");
36*635a8641SAndroid Build Coastguard Worker escaped_chars_repl['<'] = UNICHR("<");
37*635a8641SAndroid Build Coastguard Worker escaped_chars_repl['>'] = UNICHR(">");
38*635a8641SAndroid Build Coastguard Worker
39*635a8641SAndroid Build Coastguard Worker /* lengths of those characters when replaced - 1 */
40*635a8641SAndroid Build Coastguard Worker memset(escaped_chars_delta_len, 0, sizeof (escaped_chars_delta_len));
41*635a8641SAndroid Build Coastguard Worker escaped_chars_delta_len['"'] = escaped_chars_delta_len['\''] = \
42*635a8641SAndroid Build Coastguard Worker escaped_chars_delta_len['&'] = 4;
43*635a8641SAndroid Build Coastguard Worker escaped_chars_delta_len['<'] = escaped_chars_delta_len['>'] = 3;
44*635a8641SAndroid Build Coastguard Worker
45*635a8641SAndroid Build Coastguard Worker /* import markup type so that we can mark the return value */
46*635a8641SAndroid Build Coastguard Worker module = PyImport_ImportModule("markupsafe");
47*635a8641SAndroid Build Coastguard Worker if (!module)
48*635a8641SAndroid Build Coastguard Worker return 0;
49*635a8641SAndroid Build Coastguard Worker markup = PyObject_GetAttrString(module, "Markup");
50*635a8641SAndroid Build Coastguard Worker Py_DECREF(module);
51*635a8641SAndroid Build Coastguard Worker
52*635a8641SAndroid Build Coastguard Worker return 1;
53*635a8641SAndroid Build Coastguard Worker }
54*635a8641SAndroid Build Coastguard Worker
55*635a8641SAndroid Build Coastguard Worker static PyObject*
escape_unicode(PyUnicodeObject * in)56*635a8641SAndroid Build Coastguard Worker escape_unicode(PyUnicodeObject *in)
57*635a8641SAndroid Build Coastguard Worker {
58*635a8641SAndroid Build Coastguard Worker PyUnicodeObject *out;
59*635a8641SAndroid Build Coastguard Worker Py_UNICODE *inp = PyUnicode_AS_UNICODE(in);
60*635a8641SAndroid Build Coastguard Worker const Py_UNICODE *inp_end = PyUnicode_AS_UNICODE(in) + PyUnicode_GET_SIZE(in);
61*635a8641SAndroid Build Coastguard Worker Py_UNICODE *next_escp;
62*635a8641SAndroid Build Coastguard Worker Py_UNICODE *outp;
63*635a8641SAndroid Build Coastguard Worker Py_ssize_t delta=0, erepl=0, delta_len=0;
64*635a8641SAndroid Build Coastguard Worker
65*635a8641SAndroid Build Coastguard Worker /* First we need to figure out how long the escaped string will be */
66*635a8641SAndroid Build Coastguard Worker while (*(inp) || inp < inp_end) {
67*635a8641SAndroid Build Coastguard Worker if (*inp < ESCAPED_CHARS_TABLE_SIZE) {
68*635a8641SAndroid Build Coastguard Worker delta += escaped_chars_delta_len[*inp];
69*635a8641SAndroid Build Coastguard Worker erepl += !!escaped_chars_delta_len[*inp];
70*635a8641SAndroid Build Coastguard Worker }
71*635a8641SAndroid Build Coastguard Worker ++inp;
72*635a8641SAndroid Build Coastguard Worker }
73*635a8641SAndroid Build Coastguard Worker
74*635a8641SAndroid Build Coastguard Worker /* Do we need to escape anything at all? */
75*635a8641SAndroid Build Coastguard Worker if (!erepl) {
76*635a8641SAndroid Build Coastguard Worker Py_INCREF(in);
77*635a8641SAndroid Build Coastguard Worker return (PyObject*)in;
78*635a8641SAndroid Build Coastguard Worker }
79*635a8641SAndroid Build Coastguard Worker
80*635a8641SAndroid Build Coastguard Worker out = (PyUnicodeObject*)PyUnicode_FromUnicode(NULL, PyUnicode_GET_SIZE(in) + delta);
81*635a8641SAndroid Build Coastguard Worker if (!out)
82*635a8641SAndroid Build Coastguard Worker return NULL;
83*635a8641SAndroid Build Coastguard Worker
84*635a8641SAndroid Build Coastguard Worker outp = PyUnicode_AS_UNICODE(out);
85*635a8641SAndroid Build Coastguard Worker inp = PyUnicode_AS_UNICODE(in);
86*635a8641SAndroid Build Coastguard Worker while (erepl-- > 0) {
87*635a8641SAndroid Build Coastguard Worker /* look for the next substitution */
88*635a8641SAndroid Build Coastguard Worker next_escp = inp;
89*635a8641SAndroid Build Coastguard Worker while (next_escp < inp_end) {
90*635a8641SAndroid Build Coastguard Worker if (*next_escp < ESCAPED_CHARS_TABLE_SIZE &&
91*635a8641SAndroid Build Coastguard Worker (delta_len = escaped_chars_delta_len[*next_escp])) {
92*635a8641SAndroid Build Coastguard Worker ++delta_len;
93*635a8641SAndroid Build Coastguard Worker break;
94*635a8641SAndroid Build Coastguard Worker }
95*635a8641SAndroid Build Coastguard Worker ++next_escp;
96*635a8641SAndroid Build Coastguard Worker }
97*635a8641SAndroid Build Coastguard Worker
98*635a8641SAndroid Build Coastguard Worker if (next_escp > inp) {
99*635a8641SAndroid Build Coastguard Worker /* copy unescaped chars between inp and next_escp */
100*635a8641SAndroid Build Coastguard Worker Py_UNICODE_COPY(outp, inp, next_escp-inp);
101*635a8641SAndroid Build Coastguard Worker outp += next_escp - inp;
102*635a8641SAndroid Build Coastguard Worker }
103*635a8641SAndroid Build Coastguard Worker
104*635a8641SAndroid Build Coastguard Worker /* escape 'next_escp' */
105*635a8641SAndroid Build Coastguard Worker Py_UNICODE_COPY(outp, escaped_chars_repl[*next_escp], delta_len);
106*635a8641SAndroid Build Coastguard Worker outp += delta_len;
107*635a8641SAndroid Build Coastguard Worker
108*635a8641SAndroid Build Coastguard Worker inp = next_escp + 1;
109*635a8641SAndroid Build Coastguard Worker }
110*635a8641SAndroid Build Coastguard Worker if (inp < inp_end)
111*635a8641SAndroid Build Coastguard Worker Py_UNICODE_COPY(outp, inp, PyUnicode_GET_SIZE(in) - (inp - PyUnicode_AS_UNICODE(in)));
112*635a8641SAndroid Build Coastguard Worker
113*635a8641SAndroid Build Coastguard Worker return (PyObject*)out;
114*635a8641SAndroid Build Coastguard Worker }
115*635a8641SAndroid Build Coastguard Worker
116*635a8641SAndroid Build Coastguard Worker
117*635a8641SAndroid Build Coastguard Worker static PyObject*
escape(PyObject * self,PyObject * text)118*635a8641SAndroid Build Coastguard Worker escape(PyObject *self, PyObject *text)
119*635a8641SAndroid Build Coastguard Worker {
120*635a8641SAndroid Build Coastguard Worker PyObject *s = NULL, *rv = NULL, *html;
121*635a8641SAndroid Build Coastguard Worker
122*635a8641SAndroid Build Coastguard Worker /* we don't have to escape integers, bools or floats */
123*635a8641SAndroid Build Coastguard Worker if (PyLong_CheckExact(text) ||
124*635a8641SAndroid Build Coastguard Worker #if PY_MAJOR_VERSION < 3
125*635a8641SAndroid Build Coastguard Worker PyInt_CheckExact(text) ||
126*635a8641SAndroid Build Coastguard Worker #endif
127*635a8641SAndroid Build Coastguard Worker PyFloat_CheckExact(text) || PyBool_Check(text) ||
128*635a8641SAndroid Build Coastguard Worker text == Py_None)
129*635a8641SAndroid Build Coastguard Worker return PyObject_CallFunctionObjArgs(markup, text, NULL);
130*635a8641SAndroid Build Coastguard Worker
131*635a8641SAndroid Build Coastguard Worker /* if the object has an __html__ method that performs the escaping */
132*635a8641SAndroid Build Coastguard Worker html = PyObject_GetAttrString(text, "__html__");
133*635a8641SAndroid Build Coastguard Worker if (html) {
134*635a8641SAndroid Build Coastguard Worker rv = PyObject_CallObject(html, NULL);
135*635a8641SAndroid Build Coastguard Worker Py_DECREF(html);
136*635a8641SAndroid Build Coastguard Worker return rv;
137*635a8641SAndroid Build Coastguard Worker }
138*635a8641SAndroid Build Coastguard Worker
139*635a8641SAndroid Build Coastguard Worker /* otherwise make the object unicode if it isn't, then escape */
140*635a8641SAndroid Build Coastguard Worker PyErr_Clear();
141*635a8641SAndroid Build Coastguard Worker if (!PyUnicode_Check(text)) {
142*635a8641SAndroid Build Coastguard Worker #if PY_MAJOR_VERSION < 3
143*635a8641SAndroid Build Coastguard Worker PyObject *unicode = PyObject_Unicode(text);
144*635a8641SAndroid Build Coastguard Worker #else
145*635a8641SAndroid Build Coastguard Worker PyObject *unicode = PyObject_Str(text);
146*635a8641SAndroid Build Coastguard Worker #endif
147*635a8641SAndroid Build Coastguard Worker if (!unicode)
148*635a8641SAndroid Build Coastguard Worker return NULL;
149*635a8641SAndroid Build Coastguard Worker s = escape_unicode((PyUnicodeObject*)unicode);
150*635a8641SAndroid Build Coastguard Worker Py_DECREF(unicode);
151*635a8641SAndroid Build Coastguard Worker }
152*635a8641SAndroid Build Coastguard Worker else
153*635a8641SAndroid Build Coastguard Worker s = escape_unicode((PyUnicodeObject*)text);
154*635a8641SAndroid Build Coastguard Worker
155*635a8641SAndroid Build Coastguard Worker /* convert the unicode string into a markup object. */
156*635a8641SAndroid Build Coastguard Worker rv = PyObject_CallFunctionObjArgs(markup, (PyObject*)s, NULL);
157*635a8641SAndroid Build Coastguard Worker Py_DECREF(s);
158*635a8641SAndroid Build Coastguard Worker return rv;
159*635a8641SAndroid Build Coastguard Worker }
160*635a8641SAndroid Build Coastguard Worker
161*635a8641SAndroid Build Coastguard Worker
162*635a8641SAndroid Build Coastguard Worker static PyObject*
escape_silent(PyObject * self,PyObject * text)163*635a8641SAndroid Build Coastguard Worker escape_silent(PyObject *self, PyObject *text)
164*635a8641SAndroid Build Coastguard Worker {
165*635a8641SAndroid Build Coastguard Worker if (text != Py_None)
166*635a8641SAndroid Build Coastguard Worker return escape(self, text);
167*635a8641SAndroid Build Coastguard Worker return PyObject_CallFunctionObjArgs(markup, NULL);
168*635a8641SAndroid Build Coastguard Worker }
169*635a8641SAndroid Build Coastguard Worker
170*635a8641SAndroid Build Coastguard Worker
171*635a8641SAndroid Build Coastguard Worker static PyObject*
soft_unicode(PyObject * self,PyObject * s)172*635a8641SAndroid Build Coastguard Worker soft_unicode(PyObject *self, PyObject *s)
173*635a8641SAndroid Build Coastguard Worker {
174*635a8641SAndroid Build Coastguard Worker if (!PyUnicode_Check(s))
175*635a8641SAndroid Build Coastguard Worker #if PY_MAJOR_VERSION < 3
176*635a8641SAndroid Build Coastguard Worker return PyObject_Unicode(s);
177*635a8641SAndroid Build Coastguard Worker #else
178*635a8641SAndroid Build Coastguard Worker return PyObject_Str(s);
179*635a8641SAndroid Build Coastguard Worker #endif
180*635a8641SAndroid Build Coastguard Worker Py_INCREF(s);
181*635a8641SAndroid Build Coastguard Worker return s;
182*635a8641SAndroid Build Coastguard Worker }
183*635a8641SAndroid Build Coastguard Worker
184*635a8641SAndroid Build Coastguard Worker
185*635a8641SAndroid Build Coastguard Worker static PyMethodDef module_methods[] = {
186*635a8641SAndroid Build Coastguard Worker {"escape", (PyCFunction)escape, METH_O,
187*635a8641SAndroid Build Coastguard Worker "escape(s) -> markup\n\n"
188*635a8641SAndroid Build Coastguard Worker "Convert the characters &, <, >, ', and \" in string s to HTML-safe\n"
189*635a8641SAndroid Build Coastguard Worker "sequences. Use this if you need to display text that might contain\n"
190*635a8641SAndroid Build Coastguard Worker "such characters in HTML. Marks return value as markup string."},
191*635a8641SAndroid Build Coastguard Worker {"escape_silent", (PyCFunction)escape_silent, METH_O,
192*635a8641SAndroid Build Coastguard Worker "escape_silent(s) -> markup\n\n"
193*635a8641SAndroid Build Coastguard Worker "Like escape but converts None to an empty string."},
194*635a8641SAndroid Build Coastguard Worker {"soft_unicode", (PyCFunction)soft_unicode, METH_O,
195*635a8641SAndroid Build Coastguard Worker "soft_unicode(object) -> string\n\n"
196*635a8641SAndroid Build Coastguard Worker "Make a string unicode if it isn't already. That way a markup\n"
197*635a8641SAndroid Build Coastguard Worker "string is not converted back to unicode."},
198*635a8641SAndroid Build Coastguard Worker {NULL, NULL, 0, NULL} /* Sentinel */
199*635a8641SAndroid Build Coastguard Worker };
200*635a8641SAndroid Build Coastguard Worker
201*635a8641SAndroid Build Coastguard Worker
202*635a8641SAndroid Build Coastguard Worker #if PY_MAJOR_VERSION < 3
203*635a8641SAndroid Build Coastguard Worker
204*635a8641SAndroid Build Coastguard Worker #ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
205*635a8641SAndroid Build Coastguard Worker #define PyMODINIT_FUNC void
206*635a8641SAndroid Build Coastguard Worker #endif
207*635a8641SAndroid Build Coastguard Worker PyMODINIT_FUNC
init_speedups(void)208*635a8641SAndroid Build Coastguard Worker init_speedups(void)
209*635a8641SAndroid Build Coastguard Worker {
210*635a8641SAndroid Build Coastguard Worker if (!init_constants())
211*635a8641SAndroid Build Coastguard Worker return;
212*635a8641SAndroid Build Coastguard Worker
213*635a8641SAndroid Build Coastguard Worker Py_InitModule3("markupsafe._speedups", module_methods, "");
214*635a8641SAndroid Build Coastguard Worker }
215*635a8641SAndroid Build Coastguard Worker
216*635a8641SAndroid Build Coastguard Worker #else /* Python 3.x module initialization */
217*635a8641SAndroid Build Coastguard Worker
218*635a8641SAndroid Build Coastguard Worker static struct PyModuleDef module_definition = {
219*635a8641SAndroid Build Coastguard Worker PyModuleDef_HEAD_INIT,
220*635a8641SAndroid Build Coastguard Worker "markupsafe._speedups",
221*635a8641SAndroid Build Coastguard Worker NULL,
222*635a8641SAndroid Build Coastguard Worker -1,
223*635a8641SAndroid Build Coastguard Worker module_methods,
224*635a8641SAndroid Build Coastguard Worker NULL,
225*635a8641SAndroid Build Coastguard Worker NULL,
226*635a8641SAndroid Build Coastguard Worker NULL,
227*635a8641SAndroid Build Coastguard Worker NULL
228*635a8641SAndroid Build Coastguard Worker };
229*635a8641SAndroid Build Coastguard Worker
230*635a8641SAndroid Build Coastguard Worker PyMODINIT_FUNC
PyInit__speedups(void)231*635a8641SAndroid Build Coastguard Worker PyInit__speedups(void)
232*635a8641SAndroid Build Coastguard Worker {
233*635a8641SAndroid Build Coastguard Worker if (!init_constants())
234*635a8641SAndroid Build Coastguard Worker return NULL;
235*635a8641SAndroid Build Coastguard Worker
236*635a8641SAndroid Build Coastguard Worker return PyModule_Create(&module_definition);
237*635a8641SAndroid Build Coastguard Worker }
238*635a8641SAndroid Build Coastguard Worker
239*635a8641SAndroid Build Coastguard Worker #endif
240