/* open/DurusWorks/qpy/quoted.c */ #include "Python.h" #include "structmember.h" #if PY_VERSION_HEX < 0x02050000 typedef int Py_ssize_t; #endif #if PY_VERSION_HEX >= 0x03000000 #define PyObject_Unicode_UTF8 PyObject_Str #define PyString_Check PyBytes_Check #define PyString_Size PyBytes_Size #else PyObject * PyObject_Unicode_UTF8 (PyObject *x) { PyObject *result; #if PY_VERSION_HEX < 0x03000000 const char *encoding; encoding = PyUnicode_GetDefaultEncoding(); PyUnicode_SetDefaultEncoding("utf-8"); result = PyObject_Unicode(x); PyUnicode_SetDefaultEncoding(encoding); #else result = PyObject_Unicode(x); #endif return result; } #endif typedef PyUnicodeObject xml_object; typedef struct { PyObject_HEAD PyObject *value; } _quote_wrapper_object; static PyTypeObject xml_type; static PyTypeObject _quote_wrapper_type; #define is_xml_object(v) ((v)->ob_type == &xml_type) /* _get_empty_unicode() is here for use by join_str(). */ static PyObject * _get_empty_unicode (void) { static PyObject *empty = NULL; if (empty == NULL) { empty = PyUnicode_DecodeUTF8("", 0, NULL); } else { Py_INCREF(empty); } return empty; } /* _get_empty_xml() is here for use by join_xml(). */ PyObject * _get_empty_xml (void) { static xml_object *xml_empty; PyUnicodeObject *x; if (xml_empty == NULL) { /* Make sure we get a *new* empty PyUnicode instance. */ x = PyObject_New(PyUnicodeObject, &xml_type); if (x == NULL) return NULL; #if PY_VERSION_HEX < 0x03030000 x->str = PyMem_NEW(Py_UNICODE, 1); if (x->str == NULL) { Py_DECREF(x); return NULL; } x->str[0] = 0; x->length = 0; x->hash = -1; x->defenc = NULL; #else /* Python 3.3 has a different unicode structure. */ #define _PyUnicode_WSTR(op) \ (((PyASCIIObject*)(op))->wstr) #define _PyUnicode_WSTR_LENGTH(op) \ (((PyCompactUnicodeObject*)(op))->wstr_length) #define _PyUnicode_LENGTH(op) \ (((PyASCIIObject *)(op))->length) #define _PyUnicode_STATE(op) \ (((PyASCIIObject *)(op))->state) #define _PyUnicode_HASH(op) \ (((PyASCIIObject *)(op))->hash) #define _PyUnicode_UTF8_LENGTH(op) \ (((PyCompactUnicodeObject*)(op))->utf8_length) #define _PyUnicode_DATA_ANY(op) \ (((PyUnicodeObject*)(op))->data.any) #define _PyUnicode_UTF8(op) \ (((PyCompactUnicodeObject*)(op))->utf8) _PyUnicode_WSTR(x) = (Py_UNICODE*) PyObject_MALLOC(sizeof(Py_UNICODE)); if (!_PyUnicode_WSTR(x)) { Py_DECREF(x); PyErr_NoMemory(); return NULL; } _PyUnicode_WSTR(x)[0] = 0; _PyUnicode_WSTR_LENGTH(x) = 0; _PyUnicode_HASH(x) = -1; _PyUnicode_STATE(x).interned = 0; _PyUnicode_STATE(x).kind = 0; _PyUnicode_STATE(x).compact = 0; _PyUnicode_STATE(x).ready = 0; _PyUnicode_STATE(x).ascii = 0; _PyUnicode_DATA_ANY(x) = NULL; _PyUnicode_LENGTH(x) = 0; _PyUnicode_UTF8(x) = NULL; _PyUnicode_UTF8_LENGTH(x) = 0; assert(_PyUnicode_CheckConsistency((PyObject *)x, 0)); #endif xml_empty = (xml_object *)x; } else { Py_INCREF(xml_empty); } return (PyObject *)xml_empty; } /* _quote_str() is here to be used by _str_list() */ static PyObject * _quote_str(PyObject *x) { PyObject *result; if (x == Py_None) { result = _get_empty_unicode(); } else { result = PyObject_Unicode_UTF8(x); } return result; } /* returns the list of _quote_str()-ed values in args */ static PyObject * _str_list(PyObject* args) { PyObject *value = NULL; PyObject *qvalue = NULL; Py_ssize_t i = 0; Py_ssize_t n = 0; PyObject *quoted_args = PySequence_List(args); if (quoted_args == NULL) return NULL; n = PyList_Size(quoted_args); for (i=0; i < n; i++) { value = PyList_GET_ITEM(quoted_args, i); if (value == NULL) goto error; qvalue = _quote_str(value); if (qvalue == NULL) goto error; if (PyList_SetItem(quoted_args, i, qvalue) < 0) goto error; } return quoted_args; error: Py_DECREF(quoted_args); return NULL; } static PyObject * _xml_new_from_unicode(PyObject *x) { PyObject *args = NULL; PyObject *result = NULL; if (!PyUnicode_Check(x)) return NULL; if (PyUnicode_GetSize(x) == 0) return (PyObject *)_get_empty_xml(); else { args = PyTuple_New(1); if (args == NULL) return NULL; Py_INCREF(x); PyTuple_SET_ITEM(args, 0, x); result = PyUnicode_Type.tp_new(&xml_type, args, NULL); Py_DECREF(args); return result; } } static PyObject * _escape_unicode(PyObject *obj) { Py_UNICODE *u; PyObject *newobj; Py_ssize_t i, j, extra_space, size, new_size; assert (PyUnicode_Check(obj)); size = PyUnicode_GET_SIZE(obj); extra_space = 0; for (i=0; i < size; i++) { switch (PyUnicode_AS_UNICODE(obj)[i]) { case '&': extra_space += 4; break; case '<': case '>': extra_space += 3; break; case '"': extra_space += 5; break; case 39: // apostrophe extra_space += 4; break; } } if (extra_space == 0) { Py_INCREF(obj); return (PyObject *)obj; } new_size = size + extra_space; newobj = PyUnicode_FromUnicode(NULL, new_size); if (newobj == NULL) return NULL; u = PyUnicode_AS_UNICODE(newobj); for (i=0, j=0; i < size; i++) { switch (PyUnicode_AS_UNICODE(obj)[i]) { case '&': u[j++] = '&'; u[j++] = 'a'; u[j++] = 'm'; u[j++] = 'p'; u[j++] = ';'; break; case '<': u[j++] = '&'; u[j++] = 'l'; u[j++] = 't'; u[j++] = ';'; break; case '>': u[j++] = '&'; u[j++] = 'g'; u[j++] = 't'; u[j++] = ';'; break; case '"': u[j++] = '&'; u[j++] = 'q'; u[j++] = 'u'; u[j++] = 'o'; u[j++] = 't'; u[j++] = ';'; break; case 39: // apostrophe u[j++] = '&'; u[j++] = '#'; u[j++] = '3'; u[j++] = '9'; u[j++] = ';'; break; default: u[j++] = PyUnicode_AS_UNICODE(obj)[i]; break; } } assert (j == new_size); return (PyObject *)newobj; } static PyObject * quote_object(PyObject *obj) { PyObject *unicode_obj; PyObject *unicode_result; PyObject *result; if (is_xml_object(obj)) { Py_INCREF(obj); return obj; } if (obj == Py_None) return _get_empty_xml(); unicode_obj = PyObject_Unicode_UTF8(obj); if (unicode_obj == NULL) return NULL; unicode_result = _escape_unicode(unicode_obj); Py_DECREF(unicode_obj); if (unicode_result == NULL) return NULL; result = _xml_new_from_unicode(unicode_result); Py_DECREF(unicode_result); return result; } PyDoc_STRVAR(quote_doc, "(args) -> xml\n" "Converts as needed to xml-quoted xml instances."); static PyObject * quote(PyObject *self, PyObject *obj) { return quote_object(obj); } static PyObject * _quote_list(PyObject* args) { PyObject *value = NULL; PyObject *qvalue = NULL; Py_ssize_t i = 0; Py_ssize_t n = 0; PyObject *quoted_args = PySequence_List(args); if (quoted_args == NULL) return NULL; n = PyList_Size(quoted_args); for (i=0; i < n; i++) { value = PyList_GET_ITEM(quoted_args, i); if (value == NULL) goto error; qvalue = quote_object(value); if (qvalue == NULL) goto error; if (PyList_SetItem(quoted_args, i, qvalue) < 0) goto error; } return quoted_args; error: Py_DECREF(quoted_args); return NULL; } PyDoc_STRVAR(join_str_doc, "(args) -> str\n" "Joins the str()s of the non-None items in args"); static PyObject * join_str(PyObject *self, PyObject *args) { PyObject *quoted_args = NULL; PyObject *result = NULL; PyObject *empty = NULL; quoted_args = _str_list(args); if (quoted_args == NULL) return NULL; empty = _get_empty_unicode(); if (empty == NULL) { Py_DECREF(quoted_args); return NULL; } result = PyUnicode_Join(empty, quoted_args); Py_DECREF(empty); Py_DECREF(quoted_args); return result; } static PyObject * xml_join(PyObject *self, PyObject *args) { PyObject *quoted_args = NULL; PyObject *unicode_result = NULL; PyObject *result = NULL; PyObject *obj; if (!PyArg_ParseTuple(args, "O", &obj)) { return NULL; } quoted_args = _quote_list(obj); if (quoted_args == NULL) return NULL; unicode_result = PyUnicode_Join(self, quoted_args); Py_DECREF(quoted_args); if (unicode_result == NULL) return NULL; result = _xml_new_from_unicode(unicode_result); Py_DECREF(unicode_result); return result; } PyDoc_STRVAR(join_xml_doc, "(args) -> str\n" "Joins the quote()s of the items in args"); static PyObject * join_xml(PyObject *self, PyObject *args) { PyObject *empty; PyObject *result; empty = _get_empty_xml(); if (empty == NULL) return NULL; result = xml_join(empty, args); Py_DECREF(empty); return result; } static PyObject * xml_new(PyTypeObject *type, PyObject *args, PyObject *kw) { PyObject *x = NULL; static char *kwlist[] = {"string", "encoding", "errors", 0}; char *encoding = NULL; char *errors = NULL; PyObject *result; if (!PyArg_ParseTupleAndKeywords( args, kw, "|Oss:u8", kwlist, &x, &encoding, &errors)) return NULL; if ((x == NULL) || (x == Py_None) || (PyString_Check(x) && (PyString_Size(x) == 0))) { result = _get_empty_xml(); } else if (PyUnicode_Check(x)) { result = _xml_new_from_unicode(x); } else { result = PyUnicode_Type.tp_new(&xml_type, args, kw); } return result; } static PyObject * _quote_wrap(PyObject *value) { _quote_wrapper_object *self; #if PY_VERSION_HEX >= 0x03000000 if (PyLong_Check(value) || PyFloat_Check(value)) { #else if (PyInt_Check(value) || PyFloat_Check(value) || PyLong_Check(value)) { #endif /* no need for wrapper */ Py_INCREF(value); return value; } self = PyObject_New(_quote_wrapper_object, &_quote_wrapper_type); if (self == NULL) return NULL; Py_INCREF(value); self->value = value; return (PyObject *)self; } static void _quote_wrapper_dealloc(_quote_wrapper_object *self) { Py_DECREF(self->value); PyObject_Del(self); } static PyObject * _quote_wrapper_repr(_quote_wrapper_object *self) { PyObject *s = PyObject_Repr(self->value); PyObject *qs = NULL; if (s == NULL) return NULL; qs = quote_object(s); Py_DECREF(s); return qs; } static PyObject * _quote_wrapper_str(_quote_wrapper_object *self) { return quote_object(self->value); } static PyObject * _quote_wrapper_subscript(_quote_wrapper_object *self, PyObject *key) { PyObject *w = NULL; PyObject *v = PyObject_GetItem(self->value, key); if (v == NULL) return NULL; w = _quote_wrap(v); Py_DECREF(v); return w; } static PyObject * _quote_wrapper_getattr(_quote_wrapper_object *self, PyObject *name) { PyObject *w = NULL; PyObject *v = PyObject_GetAttr(self->value, name); if (v == NULL) return NULL; w = _quote_wrap(v); Py_DECREF(v); return w; } static PyObject * _format_arg_wrap(PyObject *arg) { PyObject *wrapped; if (PyTuple_Check(arg)) { Py_ssize_t i; Py_ssize_t n = PyTuple_GET_SIZE(arg); wrapped = PyTuple_New(n); for (i=0; i < n; i++) { PyObject *v = PyTuple_GET_ITEM(arg, i); v = _quote_wrap(v); if (v == NULL) { Py_DECREF(wrapped); return NULL; } PyTuple_SetItem(wrapped, i, v); } } else { wrapped = _quote_wrap(arg); if (wrapped == NULL) return NULL; } return wrapped; } static PyObject * xml_add(PyObject *left, PyObject *right) { PyObject *quoted_left; PyObject *quoted_right; PyObject *unicode_result; PyObject *result; if (!(PyUnicode_Check(left) || PyString_Check(left))) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } else { quoted_left = quote_object(left); if (quoted_left == NULL) return NULL; quoted_right = quote_object(right); if (quoted_right == NULL) { Py_DECREF(quoted_left); return NULL; } unicode_result = PyUnicode_Concat(quoted_left, quoted_right); Py_DECREF(quoted_left); Py_DECREF(quoted_right); if (unicode_result == NULL) { return NULL; } result = _xml_new_from_unicode(unicode_result); Py_DECREF(unicode_result); return result; } } static PyObject * xml_repeat(PyObject *self, Py_ssize_t n) { PyObject *unicode_result; PyObject *result; unicode_result = PyUnicode_Type.tp_as_sequence->sq_repeat(self, n); if (unicode_result == NULL) return NULL; result = _xml_new_from_unicode(unicode_result); Py_DECREF(unicode_result); return result; } static PyObject * xml_mod(PyObject *v, PyObject *w) { PyObject *wrapped; PyObject *unicode_result; PyObject *result; if (!is_xml_object(v)) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } wrapped = _format_arg_wrap(w); if (wrapped == NULL) return NULL; unicode_result = PyUnicode_Format(v, wrapped); Py_DECREF(wrapped); if (unicode_result == NULL) return NULL; result = _xml_new_from_unicode(unicode_result); Py_DECREF(unicode_result); return result; } static PyObject * _call_unicode_format_method_with_keywords(PyObject *self, PyObject *args, PyObject *kwargs) { PyObject *unicode_self = PyUnicode_FromObject(self); PyObject *unicode_self_format = PyObject_GetAttrString(unicode_self, "format"); if (unicode_self_format == NULL) { return NULL; } Py_DECREF(unicode_self); PyObject *unicode_result = PyObject_Call(unicode_self_format, args, kwargs); Py_DECREF(unicode_self_format); return unicode_result; } static PyObject * _wrap_kwargs(PyObject *kwargs) { if (kwargs == NULL) { return NULL; } PyObject *result = PyDict_New(); Py_ssize_t i = 0; PyObject *key; PyObject *value; PyObject *wrapped_value; while (PyDict_Next(kwargs, &i, &key, &value)) { wrapped_value = _format_arg_wrap(value); PyDict_SetItem(result, key, wrapped_value); Py_DECREF(wrapped_value); } return result; } static PyObject * xml_format(PyObject *self, PyObject *args, PyObject *kwargs) { if (!is_xml_object(self)) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } PyObject *wrapped_args = _format_arg_wrap(args); PyObject *wrapped_kwargs = _wrap_kwargs(kwargs); PyObject *unicode_result = _call_unicode_format_method_with_keywords( self, wrapped_args, wrapped_kwargs); Py_DECREF(wrapped_args); Py_XDECREF(wrapped_kwargs); if (unicode_result == NULL) return NULL; PyObject *result = _xml_new_from_unicode(unicode_result); Py_DECREF(unicode_result); return result; } static PyNumberMethods xml_as_number = { xml_add, /* nb_add */ 0, /* nb_subtract */ 0, /* nb_multiply */ #if PY_VERSION_HEX < 0x03000000 0, /* nb_divide */ #endif xml_mod, /* nb_remainder */ }; static PySequenceMethods xml_as_sequence = { 0, /* sq_length */ 0, /* sq_concat */ xml_repeat, /* sq_repeat */ }; static PyObject * xml_html(PyObject *self) { Py_INCREF(self); return self; } PyDoc_STRVAR(xml_join_doc, "(s:sequence) -> xml"); PyDoc_STRVAR(xml_quote_doc, "(s:anything) -> xml\n" "Return an xml instance, based on s. Escape xml characters as needed."); PyDoc_STRVAR(xml_html_doc, "() -> xml\n" "Returns this xml instance. Included for compatibility with\n" "other software packages that have designated this method to\n" "return html that needs no more quoting."); PyDoc_STRVAR(xml_format_doc, "xml_format_doc (s:anything) -> xml\n" "Return an xml instance, based on s. Escape xml characters as needed."); static PyMethodDef xml_methods[] = { {"join", xml_join, METH_VARARGS, xml_join_doc}, {"quote", quote, METH_STATIC | METH_O, xml_quote_doc}, {"__html__", (PyCFunction)xml_html, METH_NOARGS, xml_html_doc}, {"format", (PyCFunction)xml_format, METH_VARARGS | METH_KEYWORDS, xml_format_doc}, {NULL, NULL}, }; PyDoc_STRVAR(xml_doc, "This subclass of str is designated as needing no more xml quoting.\n" "Some operations that combine this with non-xml instances quote the\n" "non-xml instances and produce xml instances.\n\n"); static PyTypeObject xml_type = { #if PY_VERSION_HEX < 0x03000000 PyObject_HEAD_INIT(0) 0, #else PyVarObject_HEAD_INIT(0, 0) #endif "qpy.xml", sizeof(xml_object), 0, 0, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ &xml_as_number, /* tp_as_number */ &xml_as_sequence, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ #if PY_VERSION_HEX < 0x03000000 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, #else Py_TPFLAGS_DEFAULT | Py_TPFLAGS_UNICODE_SUBCLASS, /* tp_flags */ #endif xml_doc, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ xml_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ (newfunc)xml_new, /* tp_new */ }; static PyMappingMethods _quote_wrapper_as_mapping = { 0, /* mp_length */ (binaryfunc)_quote_wrapper_subscript, /* mp_subscript */ }; static PyTypeObject _quote_wrapper_type = { #if PY_VERSION_HEX < 0x03000000 PyObject_HEAD_INIT(0) 0, #else PyVarObject_HEAD_INIT(0, 0) #endif "_xml_quote_wrapper", /*tp_name*/ sizeof(_quote_wrapper_object), /*tp_basicsize*/ 0, /*tp_itemsize*/ (destructor)_quote_wrapper_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ (unaryfunc)_quote_wrapper_repr, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ &_quote_wrapper_as_mapping, /*tp_as_mapping*/ 0, /*tp_hash*/ 0, /*tp_call*/ (unaryfunc)_quote_wrapper_str, /*tp_str*/ (PyCFunction)&_quote_wrapper_getattr, /*tp_getattro */ }; static PyMethodDef quoted_functions[] = { {"join_str", join_str, METH_O, join_str_doc}, {"join_xml", join_xml, METH_VARARGS, join_xml_doc}, {"xml_quote", quote, METH_O, quote_doc}, {NULL, NULL} }; PyDoc_STRVAR(quoted_doc, "The quoted module defines the xml class."); #if PY_VERSION_HEX >= 0x03000000 static struct PyModuleDef quoted_module = { PyModuleDef_HEAD_INIT, "quoted", quoted_doc, -1, quoted_functions, NULL, NULL, NULL, NULL }; #endif PyObject * init_quoted_module(void) { PyObject *m; xml_type.tp_base = &PyUnicode_Type; if (PyType_Ready(&xml_type) < 0) return NULL; Py_INCREF(&xml_type); if (PyType_Ready(&_quote_wrapper_type) < 0) return NULL; Py_INCREF(&_quote_wrapper_type); #if PY_VERSION_HEX >= 0x03000000 m = PyModule_Create("ed_module); #else m = Py_InitModule3("quoted", quoted_functions, quoted_doc); #endif if (!m) return NULL; if (PyModule_AddObject(m, "xml", (PyObject *)&xml_type) < 0) return NULL; _get_empty_unicode(); _get_empty_xml(); return m; } #if PY_VERSION_HEX >= 0x03000000 PyMODINIT_FUNC PyInit_quoted(void) { return init_quoted_module(); } #else PyMODINIT_FUNC initquoted(void) { init_quoted_module(); } #endif