; _ _ _ ; | \ | | ___ | |_ ___ ___ ; | \| |/ _ \| __/ _ \/ __| ; | |\ | (_) | || __/\__ \ ; |_| \_|\___/ \__\___||___/ ; Scheme foreign interface doesn't handle char** at all. ; Better to pass around a c-pointer (void*) and convert to char** when needed ; _____ _ _____ ; | ___|__ _ __ ___(_) __ _ _ __ |_ _| _ _ __ ___ ___ ; | |_ / _ \| '__/ _ \ |/ _` | '_ \ | || | | | '_ \ / _ \/ __| ; | _| (_) | | | __/ | (_| | | | | | || |_| | |_) | __/\__ \ ; |_| \___/|_| \___|_|\__, |_| |_| |_| \__, | .__/ \___||___/ ; |___/ |___/|_| (define-foreign-type doc (c-pointer "xmlDoc")) (define-foreign-type node (c-pointer "xmlNode")) (define-foreign-type parser-context (c-pointer "xmlParserCtxt")) (define-foreign-type text-writer (c-pointer "xmlTextWriter")) (define-foreign-type text-reader (c-pointer "xmlTextReader")) (define-foreign-type sax-handler (c-pointer "xmlSAXHandler")) ; __ __ _ ; | \/ (_)___ ___ ; | |\/| | / __|/ __| ; | | | | \__ \ (__ _ ; |_| |_|_|___/\___(_) ; Converts a simple list into a a-list ; e.g (id name bot alice) -> ((id . name) (bot . alice)) (define (list->pair-alist in #!optional (result '())) (if (eq? in #f) #f (if (>= (length in) 2) ; Create the pair and join the result list (list->pair-alist (cddr in) `((,(car in) . ,(cadr in)) . ,result)) result))) @(heading "Miscellaneous") ; Assumes attributes is an association list (define (attributes->string attributes) @("Converts an attribute list to string" (@no-source) (@to "string") (attributes "List of attributes") (@example "Example: " (attributes->string `(("id1" . "value1") ("id2" . "value2"))))) (if (not (pair? attributes)) "" (foldl (lambda (result x) (s-append result (s-join "" `(" " ,(car x) "=\"" ,(cdr x) "\"" )))) "" attributes))) ; ____ ___ __ __ ; | _ \ / _ \| \/ | ; | | | | | | | |\/| | ; | |_| | |_| | | | | ; |____/ \___/|_| |_| @(heading "DOM Parser") @(text "DOM stands for the Document Object Model; this is an API for accessing XML or HTML structured documents.") @(noop) @(subheading "Example") @(source (define (dom-demo) (define (print-element-names node) (let loop ((n node)) (when n ; (print "to-string: " (dom:to-string doc n)) (when (dom:is-element-node? n) (print "element <" (dom:node-name n) ">" ) (print "@ => " (dom:attributes n))) (when (dom:is-text-node? n) (print "content => " (dom:node-content n))) (print-element-names (dom:node-children n)) (loop (dom:next-node n))))) (define ctx (dom:make-parser-context)) (define doc (dom:read-file-with-context ctx "foo.xml" #f 0)) (define root (dom:root-element doc)) (define valid? (dom:is-valid? ctx)) (print "XML is valid?: " valid?) (print "root: " root) (print-element-names root) (dom:free-doc doc) (dom:cleanup-parser))) @(noop) @(subheading "Node Types") (define dom:element-node @("DOM element node" (@no-source)) 1) (define dom:attribute-node @("DOM attribute node" (@no-source)) 2) (define dom:text-node @("DOM text node" (@no-source)) 3) (define dom:cdata_section_node @("DOM CData node" (@no-source)) 4) (define dom:entity-ref-node @("DOM Entity reference node" (@no-source)) 5) (define dom:entity-node @("DOM entity node" (@no-source)) 6) (define dom:pi-node @("DOM pi-node " (@no-source)) 7) (define dom:comment-node @("DOM comment node" (@no-source)) 8) (define dom:document-node @("DOM document node" (@no-source)) 9) (define dom:document-type-node @("DOM document type node" (@no-source)) 10) (define dom:document-frag-node @("DOM document frag node" (@no-source)) 11) (define dom:notation-node @("DOM notation node" (@no-source)) 12) (define dom:html-document-node @("DOM HTML document node" (@no-source)) 13) (define dom:dtd-node @("DOM DTD node" (@no-source)) 14) (define dom:element-decl @("DOM element declaration" (@no-source)) 15) (define dom:attribute-decl @("DOM attributte declaration" (@no-source)) 16) (define dom:entity-decl @("DOM entity declaration" (@no-source)) 17) (define dom:namespace-decl @("DOM namespace declaration" (@no-source)) 18) (define dom:xinclude-start @("DOM xinclude start declaration" (@no-source)) 19) (define dom:xinclude-end @("DOM xinlude end declaration" (@no-source)) 20) @(subheading "API") (define (dom:is-element-node? node) @("Checks if specified dom:node is a element node" (@no-source) (@to "boolean") (node "A dom:xml-node")) (= (dom:node-type node) dom:element-node)) (define (dom:is-text-node? node) @("Checks if specified dom:node is a text node" (@no-source) (@to "boolean") (node "A dom:xml-node")) (= (dom:node-type node) dom:text-node)) (define (dom:is-attribute-node? node) @("Checks if specified dom:node is an attribute node" (@no-source) (@to "boolean") (node "A dom:xml-node")) (= (dom:node-type node) dom:attribute-node)) ; Parser (define dom:parse-string @("Parse string using the DOM parser API" (@no-source) (@to "dom:doc") (xml-string "XML string") (xml-size "Size of the XML string") (URL "XML URL") (encoding "Encoding") (options "Options")) ; buffer, size, URL, encoding, options (foreign-lambda doc "xmlReadMemory" c-string int c-string c-string int)) (define (dom:parse-string-default str) @("Parse string using the DOM parser API with default options and encoding" (@to "dom:doc") (@no-source) (xml-string "XML string")) (dom:parse-string-default-helper str (string-length str))) (define (dom:parse-string-default-helper str size) (dom:parse-string str size "noname.xml" #f 0)) (define dom:cleanup-parser @("Free the dom:doc" (@no-source)) (foreign-lambda void "xmlCleanupParser")) (define dom:memory-dump ; This is to debug memory for regression tests (foreign-lambda void "xmlMemoryDump")) (define dom:parse-file @("Parse a file using the DOM parser API" (@no-source) (filename "XML file") (@to "dom:doc")) (foreign-lambda doc "xmlParseFile" c-string)) (define dom:free-doc @("Free the dom:doc" (@no-source) (@to "unspecified")) (foreign-lambda void "xmlFreeDoc" doc)) (define dom:make-parser-context @("Create a DOM parser context" (@no-source) (@to "dom:parser-context")) (foreign-lambda parser-context "xmlNewParserCtxt")) (define dom:read-file-with-context @("Parse a XML file using the given DOM parser context" (@no-source) (context "DOM parser context") (filename "") (encoding "") (options "") (@to "dom:doc")) (foreign-lambda doc "xmlCtxtReadFile" parser-context c-string c-string int)) (define dom:is-valid? @("Checks if the parser context is valid after parsing a file" (@no-source) (context "DOM parser context") (@to "boolean")) (foreign-safe-lambda* bool ((parser-context ctx)) "if (ctx->valid == 0) \n" " C_return(C_SCHEME_TRUE); \n" " C_return(C_SCHEME_FALSE);")) (define dom:free-parser-context @("Free the dom:parser-context" (@no-source) (@to "unspecified")) (foreign-lambda void "xmlFreeParserCtxt" parser-context)) (define dom:to-string @("Convert a dom:node to string including the children nodes" (@no-source) (@to "string")) (foreign-safe-lambda* scheme-object ((doc document) (node currentNode)) "C_word lst = C_SCHEME_END_OF_LIST, len, str, *a;\n" "xmlBufferPtr buffer = xmlBufferCreate();\n" "int size = xmlNodeDump(buffer, document, currentNode, 0, 1);\n" "a = C_alloc(C_SIZEOF_STRING(size));\n" "str = C_string(&a, size, buffer->content);\n" "xmlBufferFree(buffer);\n" "C_return(str);\n")) (define dom:copy-doc (foreign-lambda doc "xmlCopyDoc" doc int)) (define dom:root-element (foreign-lambda node "xmlDocGetRootElement" doc)) (define dom:copy-node (foreign-lambda node "xmlCopyNode" node int)) (define dom:copy-node-list (foreign-lambda node "xmlCopyNodeList" node)) (define dom:next-node @("Move to the next dom:node" (@no-source) (@to "dom:node")) (foreign-safe-lambda* node ((node currentNode)) "C_return(currentNode->next);")) (define dom:node-content @("Returns the contents (text) of the dom:node" (@no-source) (@to "string")) (foreign-safe-lambda* c-string ((node currentNode)) "C_return(currentNode->content);")) (define dom:node-children @("Returns the first child node" (@no-source) (@to "dom:node")) (foreign-safe-lambda* node ((node currentNode)) "C_return(currentNode->children);")) (define dom:node-type (foreign-safe-lambda* int ((node currentNode)) "C_return(currentNode->type);")) (define dom:node-name @("Returns the name of the dom:node" (@no-source) (@to "dom:node")) (foreign-safe-lambda* c-string ((node currentNode)) "C_return(currentNode->name);")) (define dom:is-element-name? @("Checks if the current name of the dom:node matches the specified string" (@no-source) (@to "boolean") (name "Name (string) to match") (dom:node "")) (foreign-safe-lambda* bool ((c-string name) (node currentNode)) "C_return(!xmlStrcmp(currentNode->name, (const xmlChar *) name));")) (define dom:get-attribute @("Returns the attribute from the specified key" (@no-source) (@to "string") (key "string") (dom:node "")) (foreign-safe-lambda* c-string ((c-string key) (node currentNode)) "C_return(xmlGetProp(currentNode, key));")) ;; TODO ;(define dom:li->string ; (foreign-self-lambda* scheme-object ((node n)) ; "C_word lst = C_SCHEME_END_OF_LIST, len, str, *a;\n" ; "\n" ; ; "if (!(n && n->properties))\n" ; " C_return(C_SCHEME_FALSE);\n" ; "\n" ; Reads the attributes into a simple list. ; e.g. properties))\n" " C_return(C_SCHEME_FALSE);\n" "\n" "xmlAttr* attribute = n->properties;\n" "while(attribute && attribute->name && attribute->children) {\n" " xmlChar* value = xmlNodeListGetString(n->doc, attribute->children, 1);\n" ; Add the value " len = strlen(value);\n" " a = C_alloc(C_SIZEOF_PAIR + C_SIZEOF_STRING(len));\n" " str = C_string(&a, len, value);\n" " lst = C_a_pair(&a, str, lst);\n" ; Add the key " len = strlen(attribute->name);\n" " a = C_alloc(C_SIZEOF_PAIR + C_SIZEOF_STRING(len));\n" " str = C_string(&a, len, attribute->name);\n" " lst = C_a_pair(&a, str, lst);\n" " xmlFree(value);\n" " attribute = attribute->next; \n" " }" " C_return(lst);\n")) (define (dom:attributes n) @("Returns the complete set of XML attributes for the given node" (@no-source) (@to "Association list") (dom:node "")) (list->pair-alist (dom:attributes-helper n))) ; ____ _ __ __ ; / ___| / \ \ \/ / ; \___ \ / _ \ \ / ; ___) / ___ \ / \ ; |____/_/ \_\/_/\_\ ; Externally defined call-back procedures (define sax:*on-start-element* #f) (define sax:*on-end-element* #f) (define sax:*on-characters* #f) @(heading "SAX Parser") @(text "Sometimes the DOM tree output is just too large to fit reasonably into memory. In that case (and if you don't expect to save back the XML document loaded using libxml), it's better to use the SAX interface of libxml. SAX is a callback-based interface to the parser. Before parsing, the application layer registers a customized set of callbacks which are called by the library as it progresses through the XML input.") @(noop) @(subheading "Example") @(source (define (sax-demo) (define sax (sax:make-handler (lambda (localname attribute-list) (print "<" localname ">") (print "@ => " attribute-list)) (lambda (localname) (print "<" localname "/>")) (lambda (characters) (print "[on-chars]: characters: " characters)))) (sax:parse-file sax #f "foo.xml") (sax:free-handler sax))) ; Converts the attributes (char**) to a Scheme list (define sax:attributes->list @("Converts attributes from (char**) to a Scheme list" (@no-source) (@internal)) (foreign-safe-lambda* scheme-object ((c-pointer ptr) (int nb)) "const int fields = 5;" "char** p = (char**)(ptr);" "C_word lst = C_SCHEME_END_OF_LIST, len, str, *a;" "int i;" "for (i = 0; i < nb; i++) {" " const xmlChar *localname = p[i * fields + 0];" " const xmlChar *prefix = p[i * fields + 1];" " const xmlChar *URI = p[i * fields + 2];" " const xmlChar *value_start = p[i * fields + 3];" " const xmlChar *value_end = p[i * fields + 4];" " int size = value_end - value_start;" " xmlChar* value = (xmlChar *) malloc(sizeof(xmlChar) * size + 1);" " memcpy(value, value_start, size);" " value[size] = \'\\0\';" ; Add the value " len = strlen(value);\n" " a = C_alloc(C_SIZEOF_PAIR + C_SIZEOF_STRING(len));\n" " str = C_string(&a, len, value);\n" " lst = C_a_pair(&a, str, lst);\n" ; Add the key " len = strlen(localname);\n" " a = C_alloc(C_SIZEOF_PAIR + C_SIZEOF_STRING(len));\n" " str = C_string(&a, len, localname);\n" " lst = C_a_pair(&a, str, lst);\n" " free(value);" "}" "C_return(lst);")) (define-external (onCharacters (c-pointer context) (c-string characters) (int size)) void (if sax:*on-characters* (sax:*on-characters* (s-left size characters)) (print "[" (s-left size characters) "]"))) (define-external (onStartElement (c-pointer context) (c-string localname) (c-string prefix) (c-string uri) (int nb_namespaces) (c-pointer namespaces) (int nb_attributes) (int nb_defaulted) (c-pointer attributes)) void ; Build the attribute association list (let* ((attribute-list (if (<= nb_attributes 0) #f (list->pair-alist (sax:attributes->list attributes nb_attributes))))) (if sax:*on-start-element* (sax:*on-start-element* localname attribute-list) (print "<" localname ">")))) (define-external (onEndElement (c-pointer context) (c-string localname) (c-string prefix) (c-string uri)) void (if sax:*on-end-element* (sax:*on-end-element* localname) (print "<" localname "/>"))) ;; SAX ; Callbacks can only be called from a foreign-safe-lambda so use a safe-lambda* ; here even though it's simple call to the C function and a foreign-lambda ; would normally suffice. (define sax:parse-file @("Parse a XML file using the SAX handler" (@no-source) (handler "SAX handler") (user-data "SAX parser context") (@to "number")) (foreign-safe-lambda* int ((sax-handler handler) (c-pointer userData) (c-string filename)) "xmlSAXUserParseFile(handler, userData, filename);")) (define sax:parse-string @("Parse a XML string using the SAX handler" (@no-source) (@to "number") (sax-handler "") (user-data "SAX parser context") (xml-string "") (size "The size of the XML string")) (foreign-lambda int "xmlSAXUserParseMemory" sax-handler c-pointer c-string int)) (define (sax:make-handler on-start on-end on-characters) @("Makes a SAX handler" (@no-source) (@to "sax-handler") (on-start "λ called on start of element") (on-end "λ called on end of element") (on-characters "λ called on start of reading characters")) (set! sax:*on-start-element* on-start) (set! sax:*on-end-element* on-end) (set! sax:*on-characters* on-characters) (sax:make-handler-helper)) (define sax:make-handler-helper (foreign-safe-lambda* sax-handler () "xmlSAXHandler* handler = (xmlSAXHandler*) malloc(sizeof(xmlSAXHandler));" "memset(handler, 0, sizeof(xmlSAXHandler));" "handler->initialized = XML_SAX2_MAGIC;" "handler->startElementNs = onStartElement;" "handler->endElementNs = onEndElement;" "handler->characters = onCharacters;" "C_return(handler);")) (define sax:free-handler @("Frees the SAX handler" (@no-source) (@to "unspecified") (sax-handler "")) (foreign-safe-lambda* void ((sax-handler handler)) "free(handler);")) ; _____ _ ____ _ ; |_ _|____ _| |_ | _ \ ___ __ _ __| | ___ _ __ ; | |/ _ \ \/ / __| | |_) / _ \/ _` |/ _` |/ _ \ '__| ; | | __/> <| |_ | _ < __/ (_| | (_| | __/ | ; |_|\___/_/\_\\__| |_| \_\___|\__,_|\__,_|\___|_| @(heading "Text Reader Parser") @(text "Libxml2 main API is tree based, where the parsing operation results in a document loaded completely in memory, and expose it as a tree of nodes all availble at the same time. This is very simple and quite powerful, but has the major limitation that the size of the document that can be handled is limited by the size of the memory available. Libxml2 also provide a SAX based API, but that version was designed upon one of the early expat version of SAX, SAX is also not formally defined for C. SAX basically work by registering callbacks which are called directly by the parser as it progresses through the document streams. The problem is that this programming model is relatively complex, not well standardized, cannot provide validation directly, makes entity, namespace and base processing relatively hard. The text-reader API provides a far simpler programming model. The API acts as a cursor going forward on the document stream and stopping at each node in the way. The user's code keeps control of the progress and simply calls a read-next procedure repeatedly to progress to each node in sequence in document order. There is direct support for namespaces, xml:base, entity handling and adding DTD validation on top of it was relatively simple. This API is really close to the DOM Core specification This provides a far more standard, easy to use and powerful API than the existing SAX. Moreover integrating extension features based on the tree seems relatively easy. In a nutshell the text-reader API provides a simpler, more standard and more extensible interface to handle large documents than the existing SAX version.") @(noop) @(subheading "Example") @(source (define (text-reader-demo) (define tr (text-reader:make "foo.xml")) (define (helper tr) (when (text-reader:element-node? tr) (print "<" (text-reader:name tr) ">") (print "@ => " (text-reader:all-attributes tr))) (when (text-reader:text-node? tr) (print "value =>" (text-reader:value tr))) (if (> (text-reader:read-more tr) 0) (helper tr))) (helper tr) (text-reader:free tr))) @(noop) @(subheading "Node Types") (define text-reader:none @("Text-Reader none" (@no-source)) 0) (define text-reader:element @("Text-Reader element" (@no-source)) 1) (define text-reader:attribute @("Text-Reader attribute" (@no-source)) 2) (define text-reader:text @("Text-Reader text" (@no-source)) 3) (define text-reader:cdata @("Text-Reader cdata" (@no-source)) 4) (define text-reader:entity-reference @("Text-Reader entity reference" (@no-source)) 5) (define text-reader:entity @("Text-Reader entity" (@no-source)) 6) (define text-reader:processing-instruction @("Text-Reader processing instruction" (@no-source)) 7) (define text-reader:comment @("Text-Reader comment" (@no-source)) 8) (define text-reader:document @("Text-Reader document" (@no-source)) 9) (define text-reader:document-type @("Text-Reader document type" (@no-source)) 10) (define text-reader:document-fragmenta @("Text-Reader document fragments" (@no-source)) 11) (define text-reader:notation @("Text-Reader notation" (@no-source)) 12) (define text-reader:whitespace @("Text-Reader whitespace" (@no-source)) 13) (define text-reader:significant-whitespace @("Text-Reader signficiant whitespace" (@no-source)) 14) (define text-reader:end-element @("Text-Reader element end" (@no-source)) 15) (define text-reader:end-entity @("Text-Reader entity end" (@no-source)) 16) (define text-reader:xml-declaration @("Text-Reader XML declaration" (@no-source)) 17) @(subheading "API") (define (text-reader:element-to-string r) @("Converts a text reader to string including child nodes" (@no-source) (@to "string") (text-reader "")) (when (not (text-reader:element-node? r)) (error "Node is not the start of an XML element")) (define stack (make-stack)) (define start-element (text-reader:name r)) (let loop ((depth 0)) (let ((name (text-reader:name r)) (value (text-reader:value r)) (empty? (text-reader:empty-element? r))) (cond ; Node is an element-start ((text-reader:element-node? r) (stack-push! stack (s-join "" `("<" ,name ,(attributes->string (text-reader:all-attributes r)) ,(if empty? "/>" ">")))) (when (not empty?) (set! depth (+ depth 1)))) ; Node is text ((text-reader:text-node? r) (stack-push! stack value)) ; Node is an element-end ((text-reader:end-element-node? r) (set! depth (- depth 1)) (stack-push! stack (s-join "" `(""))))) (if (and (> depth 0) (> (text-reader:read-more r) 0)) (loop depth)))) (s-join " " (reverse (stack->list stack)))) (define (text-reader:end-element-is? name reader) @("Checks if end element is specified name" (@no-source) (@to "boolean") (name "Element name (string)") (text-reader "")) (and (= (text-reader:node-type reader) text-reader:end-element) (string? (text-reader:name reader)) (string-ci=? (text-reader:name reader) name ))) (define (text-reader:start-element-is? name reader) @("Checks if start element is specified name" (@no-source) (@to "boolean") (name "Element name (string)") (text-reader "")) (and (= (text-reader:node-type reader) text-reader:element) (string? (text-reader:name reader)) (string-ci=? (text-reader:name reader) name))) (define (text-reader:end-element-node? reader) @("Checks if node is an end element" (@no-source) (@to "boolean") (reader "")) (= (text-reader:node-type reader) text-reader:end-element)) (define (text-reader:text-node? reader) @("Checks for text node" (@no-source) (@to "boolean") (reader "")) (= (text-reader:node-type reader) text-reader:text)) (define (text-reader:element-node? reader) @("Checks if node is an element" (@no-source) (@to "boolean") (reader "")) (= (text-reader:node-type reader) text-reader:element)) (define text-reader:make @("Makes a new text-reader" (@no-source) (filename "") (@to "text-reader")) (foreign-lambda text-reader "xmlNewTextReaderFilename" c-string)) (define text-reader:read-more @("Reads the next node in the text-reader" (@no-source) (@to "unspecified") (text-reader "")) (foreign-lambda int "xmlTextReaderRead" text-reader)) (define text-reader:free @("Free the specfied text-reader" (@no-source) (@to "unspecified") (text-reader "")) (foreign-lambda void "xmlFreeTextReader" text-reader)) (define text-reader:depth (foreign-lambda int "xmlTextReaderDepth" text-reader)) (define text-reader:node-type @("Returns the node type" (@no-source) (@to "Node type (number)") (text-reader "")) (foreign-lambda int "xmlTextReaderNodeType" text-reader)); (define text-reader:empty-element? @("Checks if text-reader is empty" (@no-source) (@to "boolean") (text-reader "")) (foreign-lambda bool "xmlTextReaderIsEmptyElement" text-reader)) (define text-reader:move-to-attribute @("Moves text-reader to the specified attribute" (@no-source) (@to "number") (text-reader "") (attribute-name "(string)")) (foreign-lambda int "xmlTextReaderMoveToAttribute" text-reader c-string)) (define (text-reader:all-attributes r) @("Extracts all the attributes from the element. Attributes are placed into an association list" (@no-source) (@to "list") (text-reader "")) (define (helper rc result) (if (> rc 0) (let* ((new `(,(text-reader:name r) . ,(text-reader:value r))) (attributes (cons new result))) (helper (text-reader:move-to-next-attribute r) attributes)) (and (not (null? result)) result))) (and (text-reader:element-node? r) (text-reader:move-to-first-attribute r) (helper (text-reader:move-to-first-attribute r) (list)))) (define text-reader:move-to-next-attribute @("Moves text-reader to the next attribute" (@no-source) (@to "number") (text-reader "")) (foreign-lambda int "xmlTextReaderMoveToNextAttribute" text-reader)) (define text-reader:move-to-first-attribute @("Moves text-reader to the first attribute" (@no-source) (@to "number") (text-reader "")) (foreign-lambda int "xmlTextReaderMoveToFirstAttribute" text-reader)) (define text-reader:move-to-element @("Moves text-reader to first element" (@no-source) (@to "number") (text-reader "")) (foreign-lambda int "xmlTextReaderMoveToElement" text-reader)) (define text-reader:next @("Moves text-reader to next node" (@no-source) (@to "number") (text-reader "")) (foreign-lambda int "xmlTextReaderNext" text-reader)) (define text-reader:next-sibling @("Moves text-reader to next sibling node" (@no-source) (@to "number") (text-reader "")) (foreign-lambda int "xmlTextReaderNextSibling" text-reader)) (define text-reader:name @("Returns the name of the node" (@no-source) (@to "string") (text-reader "")) (foreign-lambda c-string "xmlTextReaderName" text-reader)) (define text-reader:value @("Returns the value of the node" (@no-source) (@to "string") (text-reader "")) (foreign-lambda c-string "xmlTextReaderValue" text-reader))