root/livinglogic.python.xist/src/ll/xist/helpers.c @ 2533:4e2231a50a71

Revision 2533:4e2231a50a71, 6.0 KB (checked in by Walter Doerwald <walter@…>, 14 years ago)

Enhance helpers.escape() to be able to transform str and unicode objects.

Line 
1/*
2** Copyright 1999-2006 by LivingLogic AG, Bayreuth, Germany.
3** Copyright 1999-2006 by Walter Dörwald
4**
5** All Rights Reserved
6**
7** See xist/__init__.py for the license
8*/
9
10
11#include "Python.h"
12
13
14/* define unicode version of escape */
15#define STRINGLIB_NAME escape_unicode
16#define STRINGLIB_CHAR Py_UNICODE
17#define STRINGLIB_LEN  PyUnicode_GET_SIZE
18#define STRINGLIB_NEW  PyUnicode_FromUnicode
19#define STRINGLIB_STR  PyUnicode_AS_UNICODE
20
21#include "helpers.h"
22
23#undef STRINGLIB_NAME
24#undef STRINGLIB_CHAR
25#undef STRINGLIB_LEN
26#undef STRINGLIB_NEW
27#undef STRINGLIB_STR
28
29
30/* define str version of escape */
31#define STRINGLIB_NAME escape_str
32#define STRINGLIB_CHAR unsigned char
33#define STRINGLIB_LEN PyString_GET_SIZE
34#define STRINGLIB_NEW PyString_FromStringAndSize
35#define STRINGLIB_STR PyString_AS_STRING
36
37#include "helpers.h"
38
39#undef STRINGLIB_NAME
40#undef STRINGLIB_CHAR
41#undef STRINGLIB_LEN
42#undef STRINGLIB_NEW
43#undef STRINGLIB_STR
44
45
46static PyObject *escape(PyObject *str, int inattr)
47{
48    if (PyUnicode_Check(str))
49    {
50        return escape_unicode(str, inattr);
51    }
52    else if (PyString_Check(str))
53    {
54        return escape_str(str, inattr);
55    }
56    else
57    {
58        PyErr_SetString(PyExc_TypeError, "need str or unicode");
59        return NULL;
60    }
61}
62
63
64static char escapetext__doc__[] =
65"escapetext(unicode) -> unicode\n\
66\n\
67Return a copy of the string S, where every occurrence of\n\
68'<', '>', '&' and restricted characters has been replaced\n\
69with its XML character entity or character reference.";
70
71
72static PyObject *escapetext(PyObject *self, PyObject *arg)
73{
74    return escape(arg, 0);
75}
76
77
78static char escapeattr__doc__[] =
79"escapeattr(unicode) -> unicode\n\
80\n\
81Return a copy of the string S, where every occurrence of\n\
82'<', '>', '&', '\"' and restricted characters has been replaced\n\
83with their XML character entity or character reference.";
84
85
86static PyObject *escapeattr(PyObject *self, PyObject *arg)
87{
88    return escape(arg, 1);
89}
90
91
92static char cssescapereplace__doc__[] =
93"cssescapereplace(unicode, encoding) -> unicode\n\
94\n\
95Return a copy of the string S, where every unencodable character\n\
96in the specified encoding has been replaced with a \\xx hexadecimal\n\
97escape sequence.";
98
99
100static Py_UNICODE hexdigits[] = { (Py_UNICODE)'0', (Py_UNICODE)'1', (Py_UNICODE)'2', (Py_UNICODE)'3', (Py_UNICODE)'4', (Py_UNICODE)'5', (Py_UNICODE)'6', (Py_UNICODE)'7', (Py_UNICODE)'8', (Py_UNICODE)'9', (Py_UNICODE)'A', (Py_UNICODE)'B', (Py_UNICODE)'C', (Py_UNICODE)'D', (Py_UNICODE)'E', (Py_UNICODE)'F' };
101
102static PyObject *cssescapereplace(PyObject *self, PyObject *args)
103{
104    PyObject *str;
105    int oldsize;
106    const char *encoding;
107    PyObject *test;
108
109    if (!PyArg_ParseTuple(args, "O!s:cssescapereplace", &PyUnicode_Type, &str, &encoding))
110        return NULL;
111
112    oldsize = PyUnicode_GET_SIZE(str);
113    test = PyUnicode_AsEncodedString((PyObject *)str, encoding, NULL);
114
115    if (test == NULL) /* an exception has occurred */
116    {
117        if (PyErr_ExceptionMatches(PyExc_UnicodeError)) /* OK, we try it character by character */
118        {
119            int i;
120            int oldsize = PyUnicode_GET_SIZE(str);
121            int newsize = 0;
122            PyObject *result;
123            Py_UNICODE *p;
124
125            PyErr_Clear();
126
127            /* determine the space we'll need */
128            for (i = 0; i < oldsize; ++i)
129            {
130                Py_UNICODE ch = PyUnicode_AS_UNICODE(str)[i];
131                test = PyUnicode_Encode(&ch, 1, encoding, NULL);
132
133                if (test == NULL) /* exception */
134                {
135                    if (PyErr_ExceptionMatches(PyExc_UnicodeError)) /* we must create a character reference for it */
136                    {
137                        PyErr_Clear();
138
139                        #if Py_UNICODE_SIZE==4
140                        if (ch>=0x100000)
141                            newsize += 1+6;
142                        else if (ch>=0x10000)
143                            newsize += 1+5;
144                        else
145                        #endif
146                        if (ch>=0x1000)
147                            newsize += 1+4;
148                        else if (ch>=0x100)
149                            newsize += 1+3;
150                        else if (ch>=0x10)
151                            newsize += 1+2;
152                        else
153                            newsize += 1+1;
154                    }
155                    else
156                        return NULL;
157                }
158                else /* we were able to encode this character, so use it */
159                {
160                    ++newsize;
161                    Py_DECREF(test);
162                }
163            }
164
165            /* allocate what we calculated */
166            result = PyUnicode_FromUnicode(NULL, newsize);
167
168            if (result == NULL)
169                return NULL;
170
171            /* create the string */
172            p = PyUnicode_AS_UNICODE(result);
173            for (i = 0; i < oldsize; ++i)
174            {
175                Py_UNICODE ch = PyUnicode_AS_UNICODE(str)[i];
176                test = PyUnicode_Encode(&ch, 1, encoding, NULL);
177
178                if (test == NULL) /* exception */
179                {
180                    if (PyErr_ExceptionMatches(PyExc_UnicodeError)) /* we must create a character reference for it */
181                    {
182                        PyErr_Clear();
183
184                        *p++ = (Py_UNICODE)'\\';
185                        #if Py_UNICODE_SIZE==4
186                        if (ch>=0x100000)
187                        {
188                            *p++ = hexdigits[ch/0x100000];
189                            ch = ch % 0x100000;
190                            goto digit1;
191                        }
192                        if (ch>=0x10000)
193                        {
194                            digit1:
195                            *p++ = hexdigits[ch/0x10000];
196                            ch = ch % 0x10000;
197                            goto digit2;
198                        }
199                        #endif
200                        if (ch>=0x1000)
201                        {
202                            digit2:
203                            *p++ = hexdigits[ch/0x1000];
204                            ch = ch % 0x1000;
205                            goto digit3;
206                        }
207                        if (ch>=0x100)
208                        {
209                            digit3:
210                            *p++ = hexdigits[ch/0x100];
211                            ch = ch % 0x100;
212                            goto digit4;
213                        }
214                        if (ch>=0x10)
215                        {
216                            digit4:
217                            *p++ = hexdigits[ch/0x10];
218                            ch = ch % 0x10;
219                        }
220                        *p++ = hexdigits[ch];
221                    }
222                    else
223                    {
224                        Py_DECREF(result);
225                        return NULL;
226                    }
227                }
228                else /* we were able to encode this character, so use it */
229                {
230                    *p++ = ch;
231                    Py_DECREF(test);
232                }
233            }
234            return result;
235        }
236        else /* a "real exception" */
237            return NULL;
238    }
239    else /* no exception, i.e. it worked, so we can use the string as it is */
240    {
241        Py_DECREF(test); /* we no longer need the encoded version */
242        Py_INCREF(str);
243        return str;
244    }
245}
246
247
248/* ==================================================================== */
249/* python module interface */
250
251static PyMethodDef _functions[] =
252{
253    {"cssescapereplace", cssescapereplace, METH_VARARGS, cssescapereplace__doc__ },
254    {"escapetext",       escapetext,       METH_O      , escapetext__doc__},
255    {"escapeattr",       escapeattr,       METH_O      , escapeattr__doc__},
256    {NULL, NULL}
257};
258
259
260void
261#ifdef WIN32
262__declspec(dllexport)
263#endif
264inithelpers(void)
265{
266    Py_InitModule("helpers", _functions);
267}
Note: See TracBrowser for help on using the browser.