• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * debugXML.c : This is a set of routines used for debugging the tree
3  *              produced by the XML parser.
4  *
5  * See Copyright for the status of this software.
6  *
7  * Daniel Veillard <daniel@veillard.com>
8  */
9 
10 #define IN_LIBXML
11 #include "libxml.h"
12 #ifdef LIBXML_DEBUG_ENABLED
13 
14 #include <string.h>
15 #include <stdlib.h>
16 
17 #include <libxml/xmlmemory.h>
18 #include <libxml/tree.h>
19 #include <libxml/parser.h>
20 #include <libxml/parserInternals.h>
21 #include <libxml/debugXML.h>
22 #include <libxml/HTMLtree.h>
23 #include <libxml/HTMLparser.h>
24 #include <libxml/xmlerror.h>
25 #include <libxml/xpathInternals.h>
26 #include <libxml/uri.h>
27 #ifdef LIBXML_SCHEMAS_ENABLED
28 #include <libxml/relaxng.h>
29 #endif
30 
31 #include "private/error.h"
32 
33 #define DUMP_TEXT_TYPE 1
34 
35 typedef struct _xmlDebugCtxt xmlDebugCtxt;
36 typedef xmlDebugCtxt *xmlDebugCtxtPtr;
37 struct _xmlDebugCtxt {
38     FILE *output;               /* the output file */
39     char shift[101];            /* used for indenting */
40     int depth;                  /* current depth */
41     xmlDocPtr doc;              /* current document */
42     xmlNodePtr node;		/* current node */
43     xmlDictPtr dict;		/* the doc dictionary */
44     int check;                  /* do just checkings */
45     int errors;                 /* number of errors found */
46     int nodict;			/* if the document has no dictionary */
47     int options;		/* options */
48 };
49 
50 static void xmlCtxtDumpNodeList(xmlDebugCtxtPtr ctxt, xmlNodePtr node);
51 
52 static void
xmlCtxtDumpInitCtxt(xmlDebugCtxtPtr ctxt)53 xmlCtxtDumpInitCtxt(xmlDebugCtxtPtr ctxt)
54 {
55     int i;
56 
57     ctxt->depth = 0;
58     ctxt->check = 0;
59     ctxt->errors = 0;
60     ctxt->output = stdout;
61     ctxt->doc = NULL;
62     ctxt->node = NULL;
63     ctxt->dict = NULL;
64     ctxt->nodict = 0;
65     ctxt->options = 0;
66     for (i = 0; i < 100; i++)
67         ctxt->shift[i] = ' ';
68     ctxt->shift[100] = 0;
69 }
70 
71 static void
xmlCtxtDumpCleanCtxt(xmlDebugCtxtPtr ctxt ATTRIBUTE_UNUSED)72 xmlCtxtDumpCleanCtxt(xmlDebugCtxtPtr ctxt ATTRIBUTE_UNUSED)
73 {
74  /* remove the ATTRIBUTE_UNUSED when this is added */
75 }
76 
77 /**
78  * xmlNsCheckScope:
79  * @node: the node
80  * @ns: the namespace node
81  *
82  * Check that a given namespace is in scope on a node.
83  *
84  * Returns 1 if in scope, -1 in case of argument error,
85  *         -2 if the namespace is not in scope, and -3 if not on
86  *         an ancestor node.
87  */
88 static int
xmlNsCheckScope(xmlNodePtr node,xmlNsPtr ns)89 xmlNsCheckScope(xmlNodePtr node, xmlNsPtr ns)
90 {
91     xmlNsPtr cur;
92 
93     if ((node == NULL) || (ns == NULL))
94         return(-1);
95 
96     if ((node->type != XML_ELEMENT_NODE) &&
97 	(node->type != XML_ATTRIBUTE_NODE) &&
98 	(node->type != XML_DOCUMENT_NODE) &&
99 	(node->type != XML_TEXT_NODE) &&
100 	(node->type != XML_HTML_DOCUMENT_NODE) &&
101 	(node->type != XML_XINCLUDE_START))
102 	return(-2);
103 
104     while ((node != NULL) &&
105            ((node->type == XML_ELEMENT_NODE) ||
106             (node->type == XML_ATTRIBUTE_NODE) ||
107             (node->type == XML_TEXT_NODE) ||
108 	    (node->type == XML_XINCLUDE_START))) {
109 	if ((node->type == XML_ELEMENT_NODE) ||
110 	    (node->type == XML_XINCLUDE_START)) {
111 	    cur = node->nsDef;
112 	    while (cur != NULL) {
113 	        if (cur == ns)
114 		    return(1);
115 		if (xmlStrEqual(cur->prefix, ns->prefix))
116 		    return(-2);
117 		cur = cur->next;
118 	    }
119 	}
120 	node = node->parent;
121     }
122     /* the xml namespace may be declared on the document node */
123     if ((node != NULL) &&
124         ((node->type == XML_DOCUMENT_NODE) ||
125 	 (node->type == XML_HTML_DOCUMENT_NODE))) {
126 	 xmlNsPtr oldNs = ((xmlDocPtr) node)->oldNs;
127 	 if (oldNs == ns)
128 	     return(1);
129     }
130     return(-3);
131 }
132 
133 static void
xmlCtxtDumpSpaces(xmlDebugCtxtPtr ctxt)134 xmlCtxtDumpSpaces(xmlDebugCtxtPtr ctxt)
135 {
136     if (ctxt->check)
137         return;
138     if ((ctxt->output != NULL) && (ctxt->depth > 0)) {
139         if (ctxt->depth < 50)
140             fprintf(ctxt->output, "%s", &ctxt->shift[100 - 2 * ctxt->depth]);
141         else
142             fprintf(ctxt->output, "%s", ctxt->shift);
143     }
144 }
145 
146 /**
147  * xmlDebugErr:
148  * @ctxt:  a debug context
149  * @error:  the error code
150  *
151  * Handle a debug error.
152  */
153 static void
xmlDebugErr(xmlDebugCtxtPtr ctxt,int error,const char * msg)154 xmlDebugErr(xmlDebugCtxtPtr ctxt, int error, const char *msg)
155 {
156     ctxt->errors++;
157     fprintf(ctxt->output, "ERROR %d: %s", error, msg);
158 }
159 static void LIBXML_ATTR_FORMAT(3,0)
xmlDebugErr2(xmlDebugCtxtPtr ctxt,int error,const char * msg,int extra)160 xmlDebugErr2(xmlDebugCtxtPtr ctxt, int error, const char *msg, int extra)
161 {
162     ctxt->errors++;
163     fprintf(ctxt->output, "ERROR %d: ", error);
164     fprintf(ctxt->output, msg, extra);
165 }
166 static void LIBXML_ATTR_FORMAT(3,0)
xmlDebugErr3(xmlDebugCtxtPtr ctxt,int error,const char * msg,const char * extra)167 xmlDebugErr3(xmlDebugCtxtPtr ctxt, int error, const char *msg, const char *extra)
168 {
169     ctxt->errors++;
170     fprintf(ctxt->output, "ERROR %d: ", error);
171     fprintf(ctxt->output, msg, extra);
172 }
173 
174 /**
175  * xmlCtxtNsCheckScope:
176  * @ctxt: the debugging context
177  * @node: the node
178  * @ns: the namespace node
179  *
180  * Report if a given namespace is is not in scope.
181  */
182 static void
xmlCtxtNsCheckScope(xmlDebugCtxtPtr ctxt,xmlNodePtr node,xmlNsPtr ns)183 xmlCtxtNsCheckScope(xmlDebugCtxtPtr ctxt, xmlNodePtr node, xmlNsPtr ns)
184 {
185     int ret;
186 
187     ret = xmlNsCheckScope(node, ns);
188     if (ret == -2) {
189         if (ns->prefix == NULL)
190 	    xmlDebugErr(ctxt, XML_CHECK_NS_SCOPE,
191 			"Reference to default namespace not in scope\n");
192 	else
193 	    xmlDebugErr3(ctxt, XML_CHECK_NS_SCOPE,
194 			 "Reference to namespace '%s' not in scope\n",
195 			 (char *) ns->prefix);
196     }
197     if (ret == -3) {
198         if (ns->prefix == NULL)
199 	    xmlDebugErr(ctxt, XML_CHECK_NS_ANCESTOR,
200 			"Reference to default namespace not on ancestor\n");
201 	else
202 	    xmlDebugErr3(ctxt, XML_CHECK_NS_ANCESTOR,
203 			 "Reference to namespace '%s' not on ancestor\n",
204 			 (char *) ns->prefix);
205     }
206 }
207 
208 /**
209  * xmlCtxtCheckString:
210  * @ctxt: the debug context
211  * @str: the string
212  *
213  * Do debugging on the string, currently it just checks the UTF-8 content
214  */
215 static void
xmlCtxtCheckString(xmlDebugCtxtPtr ctxt,const xmlChar * str)216 xmlCtxtCheckString(xmlDebugCtxtPtr ctxt, const xmlChar * str)
217 {
218     if (str == NULL) return;
219     if (ctxt->check) {
220         if (!xmlCheckUTF8(str)) {
221 	    xmlDebugErr3(ctxt, XML_CHECK_NOT_UTF8,
222 			 "String is not UTF-8 %s", (const char *) str);
223 	}
224     }
225 }
226 
227 /**
228  * xmlCtxtCheckName:
229  * @ctxt: the debug context
230  * @name: the name
231  *
232  * Do debugging on the name, for example the dictionary status and
233  * conformance to the Name production.
234  */
235 static void
xmlCtxtCheckName(xmlDebugCtxtPtr ctxt,const xmlChar * name)236 xmlCtxtCheckName(xmlDebugCtxtPtr ctxt, const xmlChar * name)
237 {
238     if (ctxt->check) {
239 	if (name == NULL) {
240 	    xmlDebugErr(ctxt, XML_CHECK_NO_NAME, "Name is NULL");
241 	    return;
242 	}
243 #if defined(LIBXML_TREE_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED)
244         if (xmlValidateName(name, 0)) {
245 	    xmlDebugErr3(ctxt, XML_CHECK_NOT_NCNAME,
246 			 "Name is not an NCName '%s'", (const char *) name);
247 	}
248 #endif
249 	if ((ctxt->dict != NULL) &&
250 	    (!xmlDictOwns(ctxt->dict, name)) &&
251             ((ctxt->doc == NULL) ||
252              ((ctxt->doc->parseFlags & (XML_PARSE_SAX1 | XML_PARSE_NODICT)) == 0))) {
253 	    xmlDebugErr3(ctxt, XML_CHECK_OUTSIDE_DICT,
254 			 "Name is not from the document dictionary '%s'",
255 			 (const char *) name);
256 	}
257     }
258 }
259 
260 static void
xmlCtxtGenericNodeCheck(xmlDebugCtxtPtr ctxt,xmlNodePtr node)261 xmlCtxtGenericNodeCheck(xmlDebugCtxtPtr ctxt, xmlNodePtr node) {
262     xmlDocPtr doc;
263     xmlDictPtr dict;
264 
265     doc = node->doc;
266 
267     if (node->parent == NULL)
268         xmlDebugErr(ctxt, XML_CHECK_NO_PARENT,
269 	            "Node has no parent\n");
270     if (node->doc == NULL) {
271         xmlDebugErr(ctxt, XML_CHECK_NO_DOC,
272 	            "Node has no doc\n");
273         dict = NULL;
274     } else {
275 	dict = doc->dict;
276 	if ((dict == NULL) && (ctxt->nodict == 0)) {
277 #if 0
278             /* deactivated right now as it raises too many errors */
279 	    if (doc->type == XML_DOCUMENT_NODE)
280 		xmlDebugErr(ctxt, XML_CHECK_NO_DICT,
281 			    "Document has no dictionary\n");
282 #endif
283 	    ctxt->nodict = 1;
284 	}
285 	if (ctxt->doc == NULL)
286 	    ctxt->doc = doc;
287 
288 	if (ctxt->dict == NULL) {
289 	    ctxt->dict = dict;
290 	}
291     }
292     if ((node->parent != NULL) && (node->doc != node->parent->doc) &&
293         (!xmlStrEqual(node->name, BAD_CAST "pseudoroot")))
294         xmlDebugErr(ctxt, XML_CHECK_WRONG_DOC,
295 	            "Node doc differs from parent's one\n");
296     if (node->prev == NULL) {
297         if (node->type == XML_ATTRIBUTE_NODE) {
298 	    if ((node->parent != NULL) &&
299 	        (node != (xmlNodePtr) node->parent->properties))
300 		xmlDebugErr(ctxt, XML_CHECK_NO_PREV,
301                     "Attr has no prev and not first of attr list\n");
302 
303         } else if ((node->parent != NULL) && (node->parent->children != node))
304 	    xmlDebugErr(ctxt, XML_CHECK_NO_PREV,
305                     "Node has no prev and not first of parent list\n");
306     } else {
307         if (node->prev->next != node)
308 	    xmlDebugErr(ctxt, XML_CHECK_WRONG_PREV,
309                         "Node prev->next : back link wrong\n");
310     }
311     if (node->next == NULL) {
312 	if ((node->parent != NULL) && (node->type != XML_ATTRIBUTE_NODE) &&
313 	    (node->parent->last != node) &&
314 	    (node->parent->type == XML_ELEMENT_NODE))
315 	    xmlDebugErr(ctxt, XML_CHECK_NO_NEXT,
316                     "Node has no next and not last of parent list\n");
317     } else {
318         if (node->next->prev != node)
319 	    xmlDebugErr(ctxt, XML_CHECK_WRONG_NEXT,
320                     "Node next->prev : forward link wrong\n");
321         if (node->next->parent != node->parent)
322 	    xmlDebugErr(ctxt, XML_CHECK_WRONG_PARENT,
323                     "Node next->prev : forward link wrong\n");
324     }
325     if (node->type == XML_ELEMENT_NODE) {
326         xmlNsPtr ns;
327 
328 	ns = node->nsDef;
329 	while (ns != NULL) {
330 	    xmlCtxtNsCheckScope(ctxt, node, ns);
331 	    ns = ns->next;
332 	}
333 	if (node->ns != NULL)
334 	    xmlCtxtNsCheckScope(ctxt, node, node->ns);
335     } else if (node->type == XML_ATTRIBUTE_NODE) {
336 	if (node->ns != NULL)
337 	    xmlCtxtNsCheckScope(ctxt, node, node->ns);
338     }
339 
340     if ((node->type != XML_ELEMENT_NODE) &&
341 	(node->type != XML_ATTRIBUTE_NODE) &&
342 	(node->type != XML_ELEMENT_DECL) &&
343 	(node->type != XML_ATTRIBUTE_DECL) &&
344 	(node->type != XML_DTD_NODE) &&
345 	(node->type != XML_HTML_DOCUMENT_NODE) &&
346 	(node->type != XML_DOCUMENT_NODE)) {
347 	if (node->content != NULL)
348 	    xmlCtxtCheckString(ctxt, (const xmlChar *) node->content);
349     }
350     switch (node->type) {
351         case XML_ELEMENT_NODE:
352         case XML_ATTRIBUTE_NODE:
353 	    xmlCtxtCheckName(ctxt, node->name);
354 	    break;
355         case XML_TEXT_NODE:
356 	    if ((node->name == xmlStringText) ||
357 	        (node->name == xmlStringTextNoenc))
358 		break;
359 	    /* some case of entity substitution can lead to this */
360 	    if ((ctxt->dict != NULL) &&
361 	        (node->name == xmlDictLookup(ctxt->dict, BAD_CAST "nbktext",
362 		                             7)))
363 		break;
364 
365 	    xmlDebugErr3(ctxt, XML_CHECK_WRONG_NAME,
366 			 "Text node has wrong name '%s'",
367 			 (const char *) node->name);
368 	    break;
369         case XML_COMMENT_NODE:
370 	    if (node->name == xmlStringComment)
371 		break;
372 	    xmlDebugErr3(ctxt, XML_CHECK_WRONG_NAME,
373 			 "Comment node has wrong name '%s'",
374 			 (const char *) node->name);
375 	    break;
376         case XML_PI_NODE:
377 	    xmlCtxtCheckName(ctxt, node->name);
378 	    break;
379         case XML_CDATA_SECTION_NODE:
380 	    if (node->name == NULL)
381 		break;
382 	    xmlDebugErr3(ctxt, XML_CHECK_NAME_NOT_NULL,
383 			 "CData section has non NULL name '%s'",
384 			 (const char *) node->name);
385 	    break;
386         case XML_ENTITY_REF_NODE:
387         case XML_ENTITY_NODE:
388         case XML_DOCUMENT_TYPE_NODE:
389         case XML_DOCUMENT_FRAG_NODE:
390         case XML_NOTATION_NODE:
391         case XML_DTD_NODE:
392         case XML_ELEMENT_DECL:
393         case XML_ATTRIBUTE_DECL:
394         case XML_ENTITY_DECL:
395         case XML_NAMESPACE_DECL:
396         case XML_XINCLUDE_START:
397         case XML_XINCLUDE_END:
398         case XML_DOCUMENT_NODE:
399         case XML_HTML_DOCUMENT_NODE:
400 	    break;
401     }
402 }
403 
404 static void
xmlCtxtDumpString(xmlDebugCtxtPtr ctxt,const xmlChar * str)405 xmlCtxtDumpString(xmlDebugCtxtPtr ctxt, const xmlChar * str)
406 {
407     int i;
408 
409     if (ctxt->check) {
410         return;
411     }
412     /* TODO: check UTF8 content of the string */
413     if (str == NULL) {
414         fprintf(ctxt->output, "(NULL)");
415         return;
416     }
417     for (i = 0; i < 40; i++)
418         if (str[i] == 0)
419             return;
420         else if (IS_BLANK_CH(str[i]))
421             fputc(' ', ctxt->output);
422         else if (str[i] >= 0x80)
423             fprintf(ctxt->output, "#%X", str[i]);
424         else
425             fputc(str[i], ctxt->output);
426     fprintf(ctxt->output, "...");
427 }
428 
429 static void
xmlCtxtDumpDtdNode(xmlDebugCtxtPtr ctxt,xmlDtdPtr dtd)430 xmlCtxtDumpDtdNode(xmlDebugCtxtPtr ctxt, xmlDtdPtr dtd)
431 {
432     xmlCtxtDumpSpaces(ctxt);
433 
434     if (dtd == NULL) {
435         if (!ctxt->check)
436             fprintf(ctxt->output, "DTD node is NULL\n");
437         return;
438     }
439 
440     if (dtd->type != XML_DTD_NODE) {
441 	xmlDebugErr(ctxt, XML_CHECK_NOT_DTD,
442 	            "Node is not a DTD");
443         return;
444     }
445     if (!ctxt->check) {
446         if (dtd->name != NULL)
447             fprintf(ctxt->output, "DTD(%s)", (char *) dtd->name);
448         else
449             fprintf(ctxt->output, "DTD");
450         if (dtd->ExternalID != NULL)
451             fprintf(ctxt->output, ", PUBLIC %s", (char *) dtd->ExternalID);
452         if (dtd->SystemID != NULL)
453             fprintf(ctxt->output, ", SYSTEM %s", (char *) dtd->SystemID);
454         fprintf(ctxt->output, "\n");
455     }
456     /*
457      * Do a bit of checking
458      */
459     xmlCtxtGenericNodeCheck(ctxt, (xmlNodePtr) dtd);
460 }
461 
462 static void
xmlCtxtDumpAttrDecl(xmlDebugCtxtPtr ctxt,xmlAttributePtr attr)463 xmlCtxtDumpAttrDecl(xmlDebugCtxtPtr ctxt, xmlAttributePtr attr)
464 {
465     xmlCtxtDumpSpaces(ctxt);
466 
467     if (attr == NULL) {
468         if (!ctxt->check)
469             fprintf(ctxt->output, "Attribute declaration is NULL\n");
470         return;
471     }
472     if (attr->type != XML_ATTRIBUTE_DECL) {
473 	xmlDebugErr(ctxt, XML_CHECK_NOT_ATTR_DECL,
474 	            "Node is not an attribute declaration");
475         return;
476     }
477     if (attr->name != NULL) {
478         if (!ctxt->check)
479             fprintf(ctxt->output, "ATTRDECL(%s)", (char *) attr->name);
480     } else
481 	xmlDebugErr(ctxt, XML_CHECK_NO_NAME,
482 	            "Node attribute declaration has no name");
483     if (attr->elem != NULL) {
484         if (!ctxt->check)
485             fprintf(ctxt->output, " for %s", (char *) attr->elem);
486     } else
487 	xmlDebugErr(ctxt, XML_CHECK_NO_ELEM,
488 	            "Node attribute declaration has no element name");
489     if (!ctxt->check) {
490         switch (attr->atype) {
491             case XML_ATTRIBUTE_CDATA:
492                 fprintf(ctxt->output, " CDATA");
493                 break;
494             case XML_ATTRIBUTE_ID:
495                 fprintf(ctxt->output, " ID");
496                 break;
497             case XML_ATTRIBUTE_IDREF:
498                 fprintf(ctxt->output, " IDREF");
499                 break;
500             case XML_ATTRIBUTE_IDREFS:
501                 fprintf(ctxt->output, " IDREFS");
502                 break;
503             case XML_ATTRIBUTE_ENTITY:
504                 fprintf(ctxt->output, " ENTITY");
505                 break;
506             case XML_ATTRIBUTE_ENTITIES:
507                 fprintf(ctxt->output, " ENTITIES");
508                 break;
509             case XML_ATTRIBUTE_NMTOKEN:
510                 fprintf(ctxt->output, " NMTOKEN");
511                 break;
512             case XML_ATTRIBUTE_NMTOKENS:
513                 fprintf(ctxt->output, " NMTOKENS");
514                 break;
515             case XML_ATTRIBUTE_ENUMERATION:
516                 fprintf(ctxt->output, " ENUMERATION");
517                 break;
518             case XML_ATTRIBUTE_NOTATION:
519                 fprintf(ctxt->output, " NOTATION ");
520                 break;
521         }
522         if (attr->tree != NULL) {
523             int indx;
524             xmlEnumerationPtr cur = attr->tree;
525 
526             for (indx = 0; indx < 5; indx++) {
527                 if (indx != 0)
528                     fprintf(ctxt->output, "|%s", (char *) cur->name);
529                 else
530                     fprintf(ctxt->output, " (%s", (char *) cur->name);
531                 cur = cur->next;
532                 if (cur == NULL)
533                     break;
534             }
535             if (cur == NULL)
536                 fprintf(ctxt->output, ")");
537             else
538                 fprintf(ctxt->output, "...)");
539         }
540         switch (attr->def) {
541             case XML_ATTRIBUTE_NONE:
542                 break;
543             case XML_ATTRIBUTE_REQUIRED:
544                 fprintf(ctxt->output, " REQUIRED");
545                 break;
546             case XML_ATTRIBUTE_IMPLIED:
547                 fprintf(ctxt->output, " IMPLIED");
548                 break;
549             case XML_ATTRIBUTE_FIXED:
550                 fprintf(ctxt->output, " FIXED");
551                 break;
552         }
553         if (attr->defaultValue != NULL) {
554             fprintf(ctxt->output, "\"");
555             xmlCtxtDumpString(ctxt, attr->defaultValue);
556             fprintf(ctxt->output, "\"");
557         }
558         fprintf(ctxt->output, "\n");
559     }
560 
561     /*
562      * Do a bit of checking
563      */
564     xmlCtxtGenericNodeCheck(ctxt, (xmlNodePtr) attr);
565 }
566 
567 static void
xmlCtxtDumpElemDecl(xmlDebugCtxtPtr ctxt,xmlElementPtr elem)568 xmlCtxtDumpElemDecl(xmlDebugCtxtPtr ctxt, xmlElementPtr elem)
569 {
570     xmlCtxtDumpSpaces(ctxt);
571 
572     if (elem == NULL) {
573         if (!ctxt->check)
574             fprintf(ctxt->output, "Element declaration is NULL\n");
575         return;
576     }
577     if (elem->type != XML_ELEMENT_DECL) {
578 	xmlDebugErr(ctxt, XML_CHECK_NOT_ELEM_DECL,
579 	            "Node is not an element declaration");
580         return;
581     }
582     if (elem->name != NULL) {
583         if (!ctxt->check) {
584             fprintf(ctxt->output, "ELEMDECL(");
585             xmlCtxtDumpString(ctxt, elem->name);
586             fprintf(ctxt->output, ")");
587         }
588     } else
589 	xmlDebugErr(ctxt, XML_CHECK_NO_NAME,
590 	            "Element declaration has no name");
591     if (!ctxt->check) {
592         switch (elem->etype) {
593             case XML_ELEMENT_TYPE_UNDEFINED:
594                 fprintf(ctxt->output, ", UNDEFINED");
595                 break;
596             case XML_ELEMENT_TYPE_EMPTY:
597                 fprintf(ctxt->output, ", EMPTY");
598                 break;
599             case XML_ELEMENT_TYPE_ANY:
600                 fprintf(ctxt->output, ", ANY");
601                 break;
602             case XML_ELEMENT_TYPE_MIXED:
603                 fprintf(ctxt->output, ", MIXED ");
604                 break;
605             case XML_ELEMENT_TYPE_ELEMENT:
606                 fprintf(ctxt->output, ", MIXED ");
607                 break;
608         }
609         if ((elem->type != XML_ELEMENT_NODE) && (elem->content != NULL)) {
610             char buf[5001];
611 
612             buf[0] = 0;
613             xmlSnprintfElementContent(buf, 5000, elem->content, 1);
614             buf[5000] = 0;
615             fprintf(ctxt->output, "%s", buf);
616         }
617         fprintf(ctxt->output, "\n");
618     }
619 
620     /*
621      * Do a bit of checking
622      */
623     xmlCtxtGenericNodeCheck(ctxt, (xmlNodePtr) elem);
624 }
625 
626 static void
xmlCtxtDumpEntityDecl(xmlDebugCtxtPtr ctxt,xmlEntityPtr ent)627 xmlCtxtDumpEntityDecl(xmlDebugCtxtPtr ctxt, xmlEntityPtr ent)
628 {
629     xmlCtxtDumpSpaces(ctxt);
630 
631     if (ent == NULL) {
632         if (!ctxt->check)
633             fprintf(ctxt->output, "Entity declaration is NULL\n");
634         return;
635     }
636     if (ent->type != XML_ENTITY_DECL) {
637 	xmlDebugErr(ctxt, XML_CHECK_NOT_ENTITY_DECL,
638 	            "Node is not an entity declaration");
639         return;
640     }
641     if (ent->name != NULL) {
642         if (!ctxt->check) {
643             fprintf(ctxt->output, "ENTITYDECL(");
644             xmlCtxtDumpString(ctxt, ent->name);
645             fprintf(ctxt->output, ")");
646         }
647     } else
648 	xmlDebugErr(ctxt, XML_CHECK_NO_NAME,
649 	            "Entity declaration has no name");
650     if (!ctxt->check) {
651         switch (ent->etype) {
652             case XML_INTERNAL_GENERAL_ENTITY:
653                 fprintf(ctxt->output, ", internal\n");
654                 break;
655             case XML_EXTERNAL_GENERAL_PARSED_ENTITY:
656                 fprintf(ctxt->output, ", external parsed\n");
657                 break;
658             case XML_EXTERNAL_GENERAL_UNPARSED_ENTITY:
659                 fprintf(ctxt->output, ", unparsed\n");
660                 break;
661             case XML_INTERNAL_PARAMETER_ENTITY:
662                 fprintf(ctxt->output, ", parameter\n");
663                 break;
664             case XML_EXTERNAL_PARAMETER_ENTITY:
665                 fprintf(ctxt->output, ", external parameter\n");
666                 break;
667             case XML_INTERNAL_PREDEFINED_ENTITY:
668                 fprintf(ctxt->output, ", predefined\n");
669                 break;
670         }
671         if (ent->ExternalID) {
672             xmlCtxtDumpSpaces(ctxt);
673             fprintf(ctxt->output, " ExternalID=%s\n",
674                     (char *) ent->ExternalID);
675         }
676         if (ent->SystemID) {
677             xmlCtxtDumpSpaces(ctxt);
678             fprintf(ctxt->output, " SystemID=%s\n",
679                     (char *) ent->SystemID);
680         }
681         if (ent->URI != NULL) {
682             xmlCtxtDumpSpaces(ctxt);
683             fprintf(ctxt->output, " URI=%s\n", (char *) ent->URI);
684         }
685         if (ent->content) {
686             xmlCtxtDumpSpaces(ctxt);
687             fprintf(ctxt->output, " content=");
688             xmlCtxtDumpString(ctxt, ent->content);
689             fprintf(ctxt->output, "\n");
690         }
691     }
692 
693     /*
694      * Do a bit of checking
695      */
696     xmlCtxtGenericNodeCheck(ctxt, (xmlNodePtr) ent);
697 }
698 
699 static void
xmlCtxtDumpNamespace(xmlDebugCtxtPtr ctxt,xmlNsPtr ns)700 xmlCtxtDumpNamespace(xmlDebugCtxtPtr ctxt, xmlNsPtr ns)
701 {
702     xmlCtxtDumpSpaces(ctxt);
703 
704     if (ns == NULL) {
705         if (!ctxt->check)
706             fprintf(ctxt->output, "namespace node is NULL\n");
707         return;
708     }
709     if (ns->type != XML_NAMESPACE_DECL) {
710 	xmlDebugErr(ctxt, XML_CHECK_NOT_NS_DECL,
711 	            "Node is not a namespace declaration");
712         return;
713     }
714     if (ns->href == NULL) {
715         if (ns->prefix != NULL)
716 	    xmlDebugErr3(ctxt, XML_CHECK_NO_HREF,
717                     "Incomplete namespace %s href=NULL\n",
718                     (char *) ns->prefix);
719         else
720 	    xmlDebugErr(ctxt, XML_CHECK_NO_HREF,
721                     "Incomplete default namespace href=NULL\n");
722     } else {
723         if (!ctxt->check) {
724             if (ns->prefix != NULL)
725                 fprintf(ctxt->output, "namespace %s href=",
726                         (char *) ns->prefix);
727             else
728                 fprintf(ctxt->output, "default namespace href=");
729 
730             xmlCtxtDumpString(ctxt, ns->href);
731             fprintf(ctxt->output, "\n");
732         }
733     }
734 }
735 
736 static void
xmlCtxtDumpNamespaceList(xmlDebugCtxtPtr ctxt,xmlNsPtr ns)737 xmlCtxtDumpNamespaceList(xmlDebugCtxtPtr ctxt, xmlNsPtr ns)
738 {
739     while (ns != NULL) {
740         xmlCtxtDumpNamespace(ctxt, ns);
741         ns = ns->next;
742     }
743 }
744 
745 static void
xmlCtxtDumpEntity(xmlDebugCtxtPtr ctxt,xmlEntityPtr ent)746 xmlCtxtDumpEntity(xmlDebugCtxtPtr ctxt, xmlEntityPtr ent)
747 {
748     xmlCtxtDumpSpaces(ctxt);
749 
750     if (ent == NULL) {
751         if (!ctxt->check)
752             fprintf(ctxt->output, "Entity is NULL\n");
753         return;
754     }
755     if (!ctxt->check) {
756         switch (ent->etype) {
757             case XML_INTERNAL_GENERAL_ENTITY:
758                 fprintf(ctxt->output, "INTERNAL_GENERAL_ENTITY ");
759                 break;
760             case XML_EXTERNAL_GENERAL_PARSED_ENTITY:
761                 fprintf(ctxt->output, "EXTERNAL_GENERAL_PARSED_ENTITY ");
762                 break;
763             case XML_EXTERNAL_GENERAL_UNPARSED_ENTITY:
764                 fprintf(ctxt->output, "EXTERNAL_GENERAL_UNPARSED_ENTITY ");
765                 break;
766             case XML_INTERNAL_PARAMETER_ENTITY:
767                 fprintf(ctxt->output, "INTERNAL_PARAMETER_ENTITY ");
768                 break;
769             case XML_EXTERNAL_PARAMETER_ENTITY:
770                 fprintf(ctxt->output, "EXTERNAL_PARAMETER_ENTITY ");
771                 break;
772             default:
773                 fprintf(ctxt->output, "ENTITY_%d ! ", (int) ent->etype);
774         }
775         fprintf(ctxt->output, "%s\n", ent->name);
776         if (ent->ExternalID) {
777             xmlCtxtDumpSpaces(ctxt);
778             fprintf(ctxt->output, "ExternalID=%s\n",
779                     (char *) ent->ExternalID);
780         }
781         if (ent->SystemID) {
782             xmlCtxtDumpSpaces(ctxt);
783             fprintf(ctxt->output, "SystemID=%s\n", (char *) ent->SystemID);
784         }
785         if (ent->URI) {
786             xmlCtxtDumpSpaces(ctxt);
787             fprintf(ctxt->output, "URI=%s\n", (char *) ent->URI);
788         }
789         if (ent->content) {
790             xmlCtxtDumpSpaces(ctxt);
791             fprintf(ctxt->output, "content=");
792             xmlCtxtDumpString(ctxt, ent->content);
793             fprintf(ctxt->output, "\n");
794         }
795     }
796 }
797 
798 /**
799  * xmlCtxtDumpAttr:
800  * @output:  the FILE * for the output
801  * @attr:  the attribute
802  * @depth:  the indentation level.
803  *
804  * Dumps debug information for the attribute
805  */
806 static void
xmlCtxtDumpAttr(xmlDebugCtxtPtr ctxt,xmlAttrPtr attr)807 xmlCtxtDumpAttr(xmlDebugCtxtPtr ctxt, xmlAttrPtr attr)
808 {
809     xmlCtxtDumpSpaces(ctxt);
810 
811     if (attr == NULL) {
812         if (!ctxt->check)
813             fprintf(ctxt->output, "Attr is NULL");
814         return;
815     }
816     if (!ctxt->check) {
817         fprintf(ctxt->output, "ATTRIBUTE ");
818 	xmlCtxtDumpString(ctxt, attr->name);
819         fprintf(ctxt->output, "\n");
820         if (attr->children != NULL) {
821             ctxt->depth++;
822             xmlCtxtDumpNodeList(ctxt, attr->children);
823             ctxt->depth--;
824         }
825     }
826     if (attr->name == NULL)
827 	xmlDebugErr(ctxt, XML_CHECK_NO_NAME,
828 	            "Attribute has no name");
829 
830     /*
831      * Do a bit of checking
832      */
833     xmlCtxtGenericNodeCheck(ctxt, (xmlNodePtr) attr);
834 }
835 
836 /**
837  * xmlCtxtDumpAttrList:
838  * @output:  the FILE * for the output
839  * @attr:  the attribute list
840  * @depth:  the indentation level.
841  *
842  * Dumps debug information for the attribute list
843  */
844 static void
xmlCtxtDumpAttrList(xmlDebugCtxtPtr ctxt,xmlAttrPtr attr)845 xmlCtxtDumpAttrList(xmlDebugCtxtPtr ctxt, xmlAttrPtr attr)
846 {
847     while (attr != NULL) {
848         xmlCtxtDumpAttr(ctxt, attr);
849         attr = attr->next;
850     }
851 }
852 
853 /**
854  * xmlCtxtDumpOneNode:
855  * @output:  the FILE * for the output
856  * @node:  the node
857  * @depth:  the indentation level.
858  *
859  * Dumps debug information for the element node, it is not recursive
860  */
861 static void
xmlCtxtDumpOneNode(xmlDebugCtxtPtr ctxt,xmlNodePtr node)862 xmlCtxtDumpOneNode(xmlDebugCtxtPtr ctxt, xmlNodePtr node)
863 {
864     if (node == NULL) {
865         if (!ctxt->check) {
866             xmlCtxtDumpSpaces(ctxt);
867             fprintf(ctxt->output, "node is NULL\n");
868         }
869         return;
870     }
871     ctxt->node = node;
872 
873     switch (node->type) {
874         case XML_ELEMENT_NODE:
875             if (!ctxt->check) {
876                 xmlCtxtDumpSpaces(ctxt);
877                 fprintf(ctxt->output, "ELEMENT ");
878                 if ((node->ns != NULL) && (node->ns->prefix != NULL)) {
879                     xmlCtxtDumpString(ctxt, node->ns->prefix);
880                     fprintf(ctxt->output, ":");
881                 }
882                 xmlCtxtDumpString(ctxt, node->name);
883                 fprintf(ctxt->output, "\n");
884             }
885             break;
886         case XML_ATTRIBUTE_NODE:
887             if (!ctxt->check)
888                 xmlCtxtDumpSpaces(ctxt);
889             fprintf(ctxt->output, "Error, ATTRIBUTE found here\n");
890             xmlCtxtGenericNodeCheck(ctxt, node);
891             return;
892         case XML_TEXT_NODE:
893             if (!ctxt->check) {
894                 xmlCtxtDumpSpaces(ctxt);
895                 if (node->name == (const xmlChar *) xmlStringTextNoenc)
896                     fprintf(ctxt->output, "TEXT no enc");
897                 else
898                     fprintf(ctxt->output, "TEXT");
899 		if (ctxt->options & DUMP_TEXT_TYPE) {
900 		    if (node->content == (xmlChar *) &(node->properties))
901 			fprintf(ctxt->output, " compact\n");
902 		    else if (xmlDictOwns(ctxt->dict, node->content) == 1)
903 			fprintf(ctxt->output, " interned\n");
904 		    else
905 			fprintf(ctxt->output, "\n");
906 		} else
907 		    fprintf(ctxt->output, "\n");
908             }
909             break;
910         case XML_CDATA_SECTION_NODE:
911             if (!ctxt->check) {
912                 xmlCtxtDumpSpaces(ctxt);
913                 fprintf(ctxt->output, "CDATA_SECTION\n");
914             }
915             break;
916         case XML_ENTITY_REF_NODE:
917             if (!ctxt->check) {
918                 xmlCtxtDumpSpaces(ctxt);
919                 fprintf(ctxt->output, "ENTITY_REF(%s)\n",
920                         (char *) node->name);
921             }
922             break;
923         case XML_ENTITY_NODE:
924             if (!ctxt->check) {
925                 xmlCtxtDumpSpaces(ctxt);
926                 fprintf(ctxt->output, "ENTITY\n");
927             }
928             break;
929         case XML_PI_NODE:
930             if (!ctxt->check) {
931                 xmlCtxtDumpSpaces(ctxt);
932                 fprintf(ctxt->output, "PI %s\n", (char *) node->name);
933             }
934             break;
935         case XML_COMMENT_NODE:
936             if (!ctxt->check) {
937                 xmlCtxtDumpSpaces(ctxt);
938                 fprintf(ctxt->output, "COMMENT\n");
939             }
940             break;
941         case XML_DOCUMENT_NODE:
942         case XML_HTML_DOCUMENT_NODE:
943             if (!ctxt->check) {
944                 xmlCtxtDumpSpaces(ctxt);
945             }
946             fprintf(ctxt->output, "Error, DOCUMENT found here\n");
947             xmlCtxtGenericNodeCheck(ctxt, node);
948             return;
949         case XML_DOCUMENT_TYPE_NODE:
950             if (!ctxt->check) {
951                 xmlCtxtDumpSpaces(ctxt);
952                 fprintf(ctxt->output, "DOCUMENT_TYPE\n");
953             }
954             break;
955         case XML_DOCUMENT_FRAG_NODE:
956             if (!ctxt->check) {
957                 xmlCtxtDumpSpaces(ctxt);
958                 fprintf(ctxt->output, "DOCUMENT_FRAG\n");
959             }
960             break;
961         case XML_NOTATION_NODE:
962             if (!ctxt->check) {
963                 xmlCtxtDumpSpaces(ctxt);
964                 fprintf(ctxt->output, "NOTATION\n");
965             }
966             break;
967         case XML_DTD_NODE:
968             xmlCtxtDumpDtdNode(ctxt, (xmlDtdPtr) node);
969             return;
970         case XML_ELEMENT_DECL:
971             xmlCtxtDumpElemDecl(ctxt, (xmlElementPtr) node);
972             return;
973         case XML_ATTRIBUTE_DECL:
974             xmlCtxtDumpAttrDecl(ctxt, (xmlAttributePtr) node);
975             return;
976         case XML_ENTITY_DECL:
977             xmlCtxtDumpEntityDecl(ctxt, (xmlEntityPtr) node);
978             return;
979         case XML_NAMESPACE_DECL:
980             xmlCtxtDumpNamespace(ctxt, (xmlNsPtr) node);
981             return;
982         case XML_XINCLUDE_START:
983             if (!ctxt->check) {
984                 xmlCtxtDumpSpaces(ctxt);
985                 fprintf(ctxt->output, "INCLUDE START\n");
986             }
987             return;
988         case XML_XINCLUDE_END:
989             if (!ctxt->check) {
990                 xmlCtxtDumpSpaces(ctxt);
991                 fprintf(ctxt->output, "INCLUDE END\n");
992             }
993             return;
994         default:
995             if (!ctxt->check)
996                 xmlCtxtDumpSpaces(ctxt);
997 	    xmlDebugErr2(ctxt, XML_CHECK_UNKNOWN_NODE,
998 	                "Unknown node type %d\n", node->type);
999             return;
1000     }
1001     if (node->doc == NULL) {
1002         if (!ctxt->check) {
1003             xmlCtxtDumpSpaces(ctxt);
1004         }
1005         fprintf(ctxt->output, "PBM: doc == NULL !!!\n");
1006     }
1007     ctxt->depth++;
1008     if ((node->type == XML_ELEMENT_NODE) && (node->nsDef != NULL))
1009         xmlCtxtDumpNamespaceList(ctxt, node->nsDef);
1010     if ((node->type == XML_ELEMENT_NODE) && (node->properties != NULL))
1011         xmlCtxtDumpAttrList(ctxt, node->properties);
1012     if (node->type != XML_ENTITY_REF_NODE) {
1013         if ((node->type != XML_ELEMENT_NODE) && (node->content != NULL)) {
1014             if (!ctxt->check) {
1015                 xmlCtxtDumpSpaces(ctxt);
1016                 fprintf(ctxt->output, "content=");
1017                 xmlCtxtDumpString(ctxt, node->content);
1018                 fprintf(ctxt->output, "\n");
1019             }
1020         }
1021     } else {
1022         xmlEntityPtr ent;
1023 
1024         ent = xmlGetDocEntity(node->doc, node->name);
1025         if (ent != NULL)
1026             xmlCtxtDumpEntity(ctxt, ent);
1027     }
1028     ctxt->depth--;
1029 
1030     /*
1031      * Do a bit of checking
1032      */
1033     xmlCtxtGenericNodeCheck(ctxt, node);
1034 }
1035 
1036 /**
1037  * xmlCtxtDumpNode:
1038  * @output:  the FILE * for the output
1039  * @node:  the node
1040  * @depth:  the indentation level.
1041  *
1042  * Dumps debug information for the element node, it is recursive
1043  */
1044 static void
xmlCtxtDumpNode(xmlDebugCtxtPtr ctxt,xmlNodePtr node)1045 xmlCtxtDumpNode(xmlDebugCtxtPtr ctxt, xmlNodePtr node)
1046 {
1047     if (node == NULL) {
1048         if (!ctxt->check) {
1049             xmlCtxtDumpSpaces(ctxt);
1050             fprintf(ctxt->output, "node is NULL\n");
1051         }
1052         return;
1053     }
1054     xmlCtxtDumpOneNode(ctxt, node);
1055     if ((node->type != XML_NAMESPACE_DECL) &&
1056         (node->children != NULL) && (node->type != XML_ENTITY_REF_NODE)) {
1057         ctxt->depth++;
1058         xmlCtxtDumpNodeList(ctxt, node->children);
1059         ctxt->depth--;
1060     }
1061 }
1062 
1063 /**
1064  * xmlCtxtDumpNodeList:
1065  * @output:  the FILE * for the output
1066  * @node:  the node list
1067  * @depth:  the indentation level.
1068  *
1069  * Dumps debug information for the list of element node, it is recursive
1070  */
1071 static void
xmlCtxtDumpNodeList(xmlDebugCtxtPtr ctxt,xmlNodePtr node)1072 xmlCtxtDumpNodeList(xmlDebugCtxtPtr ctxt, xmlNodePtr node)
1073 {
1074     while (node != NULL) {
1075         xmlCtxtDumpNode(ctxt, node);
1076         node = node->next;
1077     }
1078 }
1079 
1080 static void
xmlCtxtDumpDocHead(xmlDebugCtxtPtr ctxt,xmlDocPtr doc)1081 xmlCtxtDumpDocHead(xmlDebugCtxtPtr ctxt, xmlDocPtr doc)
1082 {
1083     if (doc == NULL) {
1084         if (!ctxt->check)
1085             fprintf(ctxt->output, "DOCUMENT == NULL !\n");
1086         return;
1087     }
1088     ctxt->node = (xmlNodePtr) doc;
1089 
1090     switch (doc->type) {
1091         case XML_ELEMENT_NODE:
1092 	    xmlDebugErr(ctxt, XML_CHECK_FOUND_ELEMENT,
1093 	                "Misplaced ELEMENT node\n");
1094             break;
1095         case XML_ATTRIBUTE_NODE:
1096 	    xmlDebugErr(ctxt, XML_CHECK_FOUND_ATTRIBUTE,
1097 	                "Misplaced ATTRIBUTE node\n");
1098             break;
1099         case XML_TEXT_NODE:
1100 	    xmlDebugErr(ctxt, XML_CHECK_FOUND_TEXT,
1101 	                "Misplaced TEXT node\n");
1102             break;
1103         case XML_CDATA_SECTION_NODE:
1104 	    xmlDebugErr(ctxt, XML_CHECK_FOUND_CDATA,
1105 	                "Misplaced CDATA node\n");
1106             break;
1107         case XML_ENTITY_REF_NODE:
1108 	    xmlDebugErr(ctxt, XML_CHECK_FOUND_ENTITYREF,
1109 	                "Misplaced ENTITYREF node\n");
1110             break;
1111         case XML_ENTITY_NODE:
1112 	    xmlDebugErr(ctxt, XML_CHECK_FOUND_ENTITY,
1113 	                "Misplaced ENTITY node\n");
1114             break;
1115         case XML_PI_NODE:
1116 	    xmlDebugErr(ctxt, XML_CHECK_FOUND_PI,
1117 	                "Misplaced PI node\n");
1118             break;
1119         case XML_COMMENT_NODE:
1120 	    xmlDebugErr(ctxt, XML_CHECK_FOUND_COMMENT,
1121 	                "Misplaced COMMENT node\n");
1122             break;
1123         case XML_DOCUMENT_NODE:
1124 	    if (!ctxt->check)
1125 		fprintf(ctxt->output, "DOCUMENT\n");
1126             break;
1127         case XML_HTML_DOCUMENT_NODE:
1128 	    if (!ctxt->check)
1129 		fprintf(ctxt->output, "HTML DOCUMENT\n");
1130             break;
1131         case XML_DOCUMENT_TYPE_NODE:
1132 	    xmlDebugErr(ctxt, XML_CHECK_FOUND_DOCTYPE,
1133 	                "Misplaced DOCTYPE node\n");
1134             break;
1135         case XML_DOCUMENT_FRAG_NODE:
1136 	    xmlDebugErr(ctxt, XML_CHECK_FOUND_FRAGMENT,
1137 	                "Misplaced FRAGMENT node\n");
1138             break;
1139         case XML_NOTATION_NODE:
1140 	    xmlDebugErr(ctxt, XML_CHECK_FOUND_NOTATION,
1141 	                "Misplaced NOTATION node\n");
1142             break;
1143         default:
1144 	    xmlDebugErr2(ctxt, XML_CHECK_UNKNOWN_NODE,
1145 	                "Unknown node type %d\n", doc->type);
1146     }
1147 }
1148 
1149 /**
1150  * xmlCtxtDumpDocumentHead:
1151  * @output:  the FILE * for the output
1152  * @doc:  the document
1153  *
1154  * Dumps debug information concerning the document, not recursive
1155  */
1156 static void
xmlCtxtDumpDocumentHead(xmlDebugCtxtPtr ctxt,xmlDocPtr doc)1157 xmlCtxtDumpDocumentHead(xmlDebugCtxtPtr ctxt, xmlDocPtr doc)
1158 {
1159     if (doc == NULL) return;
1160     xmlCtxtDumpDocHead(ctxt, doc);
1161     if (!ctxt->check) {
1162         if (doc->name != NULL) {
1163             fprintf(ctxt->output, "name=");
1164             xmlCtxtDumpString(ctxt, BAD_CAST doc->name);
1165             fprintf(ctxt->output, "\n");
1166         }
1167         if (doc->version != NULL) {
1168             fprintf(ctxt->output, "version=");
1169             xmlCtxtDumpString(ctxt, doc->version);
1170             fprintf(ctxt->output, "\n");
1171         }
1172         if (doc->encoding != NULL) {
1173             fprintf(ctxt->output, "encoding=");
1174             xmlCtxtDumpString(ctxt, doc->encoding);
1175             fprintf(ctxt->output, "\n");
1176         }
1177         if (doc->URL != NULL) {
1178             fprintf(ctxt->output, "URL=");
1179             xmlCtxtDumpString(ctxt, doc->URL);
1180             fprintf(ctxt->output, "\n");
1181         }
1182         if (doc->standalone)
1183             fprintf(ctxt->output, "standalone=true\n");
1184     }
1185     if (doc->oldNs != NULL)
1186         xmlCtxtDumpNamespaceList(ctxt, doc->oldNs);
1187 }
1188 
1189 /**
1190  * xmlCtxtDumpDocument:
1191  * @output:  the FILE * for the output
1192  * @doc:  the document
1193  *
1194  * Dumps debug information for the document, it's recursive
1195  */
1196 static void
xmlCtxtDumpDocument(xmlDebugCtxtPtr ctxt,xmlDocPtr doc)1197 xmlCtxtDumpDocument(xmlDebugCtxtPtr ctxt, xmlDocPtr doc)
1198 {
1199     if (doc == NULL) {
1200         if (!ctxt->check)
1201             fprintf(ctxt->output, "DOCUMENT == NULL !\n");
1202         return;
1203     }
1204     xmlCtxtDumpDocumentHead(ctxt, doc);
1205     if (((doc->type == XML_DOCUMENT_NODE) ||
1206          (doc->type == XML_HTML_DOCUMENT_NODE))
1207         && (doc->children != NULL)) {
1208         ctxt->depth++;
1209         xmlCtxtDumpNodeList(ctxt, doc->children);
1210         ctxt->depth--;
1211     }
1212 }
1213 
1214 static void
xmlCtxtDumpEntityCallback(void * payload,void * data,const xmlChar * name ATTRIBUTE_UNUSED)1215 xmlCtxtDumpEntityCallback(void *payload, void *data,
1216                           const xmlChar *name ATTRIBUTE_UNUSED)
1217 {
1218     xmlEntityPtr cur = (xmlEntityPtr) payload;
1219     xmlDebugCtxtPtr ctxt = (xmlDebugCtxtPtr) data;
1220     if (cur == NULL) {
1221         if (!ctxt->check)
1222             fprintf(ctxt->output, "Entity is NULL");
1223         return;
1224     }
1225     if (!ctxt->check) {
1226         fprintf(ctxt->output, "%s : ", (char *) cur->name);
1227         switch (cur->etype) {
1228             case XML_INTERNAL_GENERAL_ENTITY:
1229                 fprintf(ctxt->output, "INTERNAL GENERAL, ");
1230                 break;
1231             case XML_EXTERNAL_GENERAL_PARSED_ENTITY:
1232                 fprintf(ctxt->output, "EXTERNAL PARSED, ");
1233                 break;
1234             case XML_EXTERNAL_GENERAL_UNPARSED_ENTITY:
1235                 fprintf(ctxt->output, "EXTERNAL UNPARSED, ");
1236                 break;
1237             case XML_INTERNAL_PARAMETER_ENTITY:
1238                 fprintf(ctxt->output, "INTERNAL PARAMETER, ");
1239                 break;
1240             case XML_EXTERNAL_PARAMETER_ENTITY:
1241                 fprintf(ctxt->output, "EXTERNAL PARAMETER, ");
1242                 break;
1243             default:
1244 		xmlDebugErr2(ctxt, XML_CHECK_ENTITY_TYPE,
1245 			     "Unknown entity type %d\n", cur->etype);
1246         }
1247         if (cur->ExternalID != NULL)
1248             fprintf(ctxt->output, "ID \"%s\"", (char *) cur->ExternalID);
1249         if (cur->SystemID != NULL)
1250             fprintf(ctxt->output, "SYSTEM \"%s\"", (char *) cur->SystemID);
1251         if (cur->orig != NULL)
1252             fprintf(ctxt->output, "\n orig \"%s\"", (char *) cur->orig);
1253         if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL))
1254             fprintf(ctxt->output, "\n content \"%s\"",
1255                     (char *) cur->content);
1256         fprintf(ctxt->output, "\n");
1257     }
1258 }
1259 
1260 /**
1261  * xmlCtxtDumpEntities:
1262  * @output:  the FILE * for the output
1263  * @doc:  the document
1264  *
1265  * Dumps debug information for all the entities in use by the document
1266  */
1267 static void
xmlCtxtDumpEntities(xmlDebugCtxtPtr ctxt,xmlDocPtr doc)1268 xmlCtxtDumpEntities(xmlDebugCtxtPtr ctxt, xmlDocPtr doc)
1269 {
1270     if (doc == NULL) return;
1271     xmlCtxtDumpDocHead(ctxt, doc);
1272     if ((doc->intSubset != NULL) && (doc->intSubset->entities != NULL)) {
1273         xmlEntitiesTablePtr table = (xmlEntitiesTablePtr)
1274             doc->intSubset->entities;
1275 
1276         if (!ctxt->check)
1277             fprintf(ctxt->output, "Entities in internal subset\n");
1278         xmlHashScan(table, xmlCtxtDumpEntityCallback, ctxt);
1279     } else
1280         fprintf(ctxt->output, "No entities in internal subset\n");
1281     if ((doc->extSubset != NULL) && (doc->extSubset->entities != NULL)) {
1282         xmlEntitiesTablePtr table = (xmlEntitiesTablePtr)
1283             doc->extSubset->entities;
1284 
1285         if (!ctxt->check)
1286             fprintf(ctxt->output, "Entities in external subset\n");
1287         xmlHashScan(table, xmlCtxtDumpEntityCallback, ctxt);
1288     } else if (!ctxt->check)
1289         fprintf(ctxt->output, "No entities in external subset\n");
1290 }
1291 
1292 /**
1293  * xmlCtxtDumpDTD:
1294  * @output:  the FILE * for the output
1295  * @dtd:  the DTD
1296  *
1297  * Dumps debug information for the DTD
1298  */
1299 static void
xmlCtxtDumpDTD(xmlDebugCtxtPtr ctxt,xmlDtdPtr dtd)1300 xmlCtxtDumpDTD(xmlDebugCtxtPtr ctxt, xmlDtdPtr dtd)
1301 {
1302     if (dtd == NULL) {
1303         if (!ctxt->check)
1304             fprintf(ctxt->output, "DTD is NULL\n");
1305         return;
1306     }
1307     xmlCtxtDumpDtdNode(ctxt, dtd);
1308     if (dtd->children == NULL)
1309         fprintf(ctxt->output, "    DTD is empty\n");
1310     else {
1311         ctxt->depth++;
1312         xmlCtxtDumpNodeList(ctxt, dtd->children);
1313         ctxt->depth--;
1314     }
1315 }
1316 
1317 /************************************************************************
1318  *									*
1319  *			Public entry points for dump			*
1320  *									*
1321  ************************************************************************/
1322 
1323 /**
1324  * xmlDebugDumpString:
1325  * @output:  the FILE * for the output
1326  * @str:  the string
1327  *
1328  * Dumps information about the string, shorten it if necessary
1329  */
1330 void
xmlDebugDumpString(FILE * output,const xmlChar * str)1331 xmlDebugDumpString(FILE * output, const xmlChar * str)
1332 {
1333     int i;
1334 
1335     if (output == NULL)
1336 	output = stdout;
1337     if (str == NULL) {
1338         fprintf(output, "(NULL)");
1339         return;
1340     }
1341     for (i = 0; i < 40; i++)
1342         if (str[i] == 0)
1343             return;
1344         else if (IS_BLANK_CH(str[i]))
1345             fputc(' ', output);
1346         else if (str[i] >= 0x80)
1347             fprintf(output, "#%X", str[i]);
1348         else
1349             fputc(str[i], output);
1350     fprintf(output, "...");
1351 }
1352 
1353 /**
1354  * xmlDebugDumpAttr:
1355  * @output:  the FILE * for the output
1356  * @attr:  the attribute
1357  * @depth:  the indentation level.
1358  *
1359  * Dumps debug information for the attribute
1360  */
1361 void
xmlDebugDumpAttr(FILE * output,xmlAttrPtr attr,int depth)1362 xmlDebugDumpAttr(FILE *output, xmlAttrPtr attr, int depth) {
1363     xmlDebugCtxt ctxt;
1364 
1365     if (output == NULL) return;
1366     xmlCtxtDumpInitCtxt(&ctxt);
1367     ctxt.output = output;
1368     ctxt.depth = depth;
1369     xmlCtxtDumpAttr(&ctxt, attr);
1370     xmlCtxtDumpCleanCtxt(&ctxt);
1371 }
1372 
1373 
1374 /**
1375  * xmlDebugDumpEntities:
1376  * @output:  the FILE * for the output
1377  * @doc:  the document
1378  *
1379  * Dumps debug information for all the entities in use by the document
1380  */
1381 void
xmlDebugDumpEntities(FILE * output,xmlDocPtr doc)1382 xmlDebugDumpEntities(FILE * output, xmlDocPtr doc)
1383 {
1384     xmlDebugCtxt ctxt;
1385 
1386     if (output == NULL) return;
1387     xmlCtxtDumpInitCtxt(&ctxt);
1388     ctxt.output = output;
1389     xmlCtxtDumpEntities(&ctxt, doc);
1390     xmlCtxtDumpCleanCtxt(&ctxt);
1391 }
1392 
1393 /**
1394  * xmlDebugDumpAttrList:
1395  * @output:  the FILE * for the output
1396  * @attr:  the attribute list
1397  * @depth:  the indentation level.
1398  *
1399  * Dumps debug information for the attribute list
1400  */
1401 void
xmlDebugDumpAttrList(FILE * output,xmlAttrPtr attr,int depth)1402 xmlDebugDumpAttrList(FILE * output, xmlAttrPtr attr, int depth)
1403 {
1404     xmlDebugCtxt ctxt;
1405 
1406     if (output == NULL) return;
1407     xmlCtxtDumpInitCtxt(&ctxt);
1408     ctxt.output = output;
1409     ctxt.depth = depth;
1410     xmlCtxtDumpAttrList(&ctxt, attr);
1411     xmlCtxtDumpCleanCtxt(&ctxt);
1412 }
1413 
1414 /**
1415  * xmlDebugDumpOneNode:
1416  * @output:  the FILE * for the output
1417  * @node:  the node
1418  * @depth:  the indentation level.
1419  *
1420  * Dumps debug information for the element node, it is not recursive
1421  */
1422 void
xmlDebugDumpOneNode(FILE * output,xmlNodePtr node,int depth)1423 xmlDebugDumpOneNode(FILE * output, xmlNodePtr node, int depth)
1424 {
1425     xmlDebugCtxt ctxt;
1426 
1427     if (output == NULL) return;
1428     xmlCtxtDumpInitCtxt(&ctxt);
1429     ctxt.output = output;
1430     ctxt.depth = depth;
1431     xmlCtxtDumpOneNode(&ctxt, node);
1432     xmlCtxtDumpCleanCtxt(&ctxt);
1433 }
1434 
1435 /**
1436  * xmlDebugDumpNode:
1437  * @output:  the FILE * for the output
1438  * @node:  the node
1439  * @depth:  the indentation level.
1440  *
1441  * Dumps debug information for the element node, it is recursive
1442  */
1443 void
xmlDebugDumpNode(FILE * output,xmlNodePtr node,int depth)1444 xmlDebugDumpNode(FILE * output, xmlNodePtr node, int depth)
1445 {
1446     xmlDebugCtxt ctxt;
1447 
1448     if (output == NULL)
1449 	output = stdout;
1450     xmlCtxtDumpInitCtxt(&ctxt);
1451     ctxt.output = output;
1452     ctxt.depth = depth;
1453     xmlCtxtDumpNode(&ctxt, node);
1454     xmlCtxtDumpCleanCtxt(&ctxt);
1455 }
1456 
1457 /**
1458  * xmlDebugDumpNodeList:
1459  * @output:  the FILE * for the output
1460  * @node:  the node list
1461  * @depth:  the indentation level.
1462  *
1463  * Dumps debug information for the list of element node, it is recursive
1464  */
1465 void
xmlDebugDumpNodeList(FILE * output,xmlNodePtr node,int depth)1466 xmlDebugDumpNodeList(FILE * output, xmlNodePtr node, int depth)
1467 {
1468     xmlDebugCtxt ctxt;
1469 
1470     if (output == NULL)
1471 	output = stdout;
1472     xmlCtxtDumpInitCtxt(&ctxt);
1473     ctxt.output = output;
1474     ctxt.depth = depth;
1475     xmlCtxtDumpNodeList(&ctxt, node);
1476     xmlCtxtDumpCleanCtxt(&ctxt);
1477 }
1478 
1479 /**
1480  * xmlDebugDumpDocumentHead:
1481  * @output:  the FILE * for the output
1482  * @doc:  the document
1483  *
1484  * Dumps debug information concerning the document, not recursive
1485  */
1486 void
xmlDebugDumpDocumentHead(FILE * output,xmlDocPtr doc)1487 xmlDebugDumpDocumentHead(FILE * output, xmlDocPtr doc)
1488 {
1489     xmlDebugCtxt ctxt;
1490 
1491     if (output == NULL)
1492 	output = stdout;
1493     xmlCtxtDumpInitCtxt(&ctxt);
1494     ctxt.options |= DUMP_TEXT_TYPE;
1495     ctxt.output = output;
1496     xmlCtxtDumpDocumentHead(&ctxt, doc);
1497     xmlCtxtDumpCleanCtxt(&ctxt);
1498 }
1499 
1500 /**
1501  * xmlDebugDumpDocument:
1502  * @output:  the FILE * for the output
1503  * @doc:  the document
1504  *
1505  * Dumps debug information for the document, it's recursive
1506  */
1507 void
xmlDebugDumpDocument(FILE * output,xmlDocPtr doc)1508 xmlDebugDumpDocument(FILE * output, xmlDocPtr doc)
1509 {
1510     xmlDebugCtxt ctxt;
1511 
1512     if (output == NULL)
1513 	output = stdout;
1514     xmlCtxtDumpInitCtxt(&ctxt);
1515     ctxt.options |= DUMP_TEXT_TYPE;
1516     ctxt.output = output;
1517     xmlCtxtDumpDocument(&ctxt, doc);
1518     xmlCtxtDumpCleanCtxt(&ctxt);
1519 }
1520 
1521 /**
1522  * xmlDebugDumpDTD:
1523  * @output:  the FILE * for the output
1524  * @dtd:  the DTD
1525  *
1526  * Dumps debug information for the DTD
1527  */
1528 void
xmlDebugDumpDTD(FILE * output,xmlDtdPtr dtd)1529 xmlDebugDumpDTD(FILE * output, xmlDtdPtr dtd)
1530 {
1531     xmlDebugCtxt ctxt;
1532 
1533     if (output == NULL)
1534 	output = stdout;
1535     xmlCtxtDumpInitCtxt(&ctxt);
1536     ctxt.options |= DUMP_TEXT_TYPE;
1537     ctxt.output = output;
1538     xmlCtxtDumpDTD(&ctxt, dtd);
1539     xmlCtxtDumpCleanCtxt(&ctxt);
1540 }
1541 
1542 /************************************************************************
1543  *									*
1544  *			Public entry points for checkings		*
1545  *									*
1546  ************************************************************************/
1547 
1548 /**
1549  * xmlDebugCheckDocument:
1550  * @output:  the FILE * for the output
1551  * @doc:  the document
1552  *
1553  * Check the document for potential content problems, and output
1554  * the errors to @output
1555  *
1556  * Returns the number of errors found
1557  */
1558 int
xmlDebugCheckDocument(FILE * output,xmlDocPtr doc)1559 xmlDebugCheckDocument(FILE * output, xmlDocPtr doc)
1560 {
1561     xmlDebugCtxt ctxt;
1562 
1563     if (output == NULL)
1564 	output = stdout;
1565     xmlCtxtDumpInitCtxt(&ctxt);
1566     ctxt.output = output;
1567     ctxt.check = 1;
1568     xmlCtxtDumpDocument(&ctxt, doc);
1569     xmlCtxtDumpCleanCtxt(&ctxt);
1570     return(ctxt.errors);
1571 }
1572 
1573 /************************************************************************
1574  *									*
1575  *			Helpers for Shell				*
1576  *									*
1577  ************************************************************************/
1578 
1579 /**
1580  * xmlLsCountNode:
1581  * @node:  the node to count
1582  *
1583  * Count the children of @node.
1584  *
1585  * Returns the number of children of @node.
1586  */
1587 int
xmlLsCountNode(xmlNodePtr node)1588 xmlLsCountNode(xmlNodePtr node) {
1589     int ret = 0;
1590     xmlNodePtr list = NULL;
1591 
1592     if (node == NULL)
1593 	return(0);
1594 
1595     switch (node->type) {
1596 	case XML_ELEMENT_NODE:
1597 	    list = node->children;
1598 	    break;
1599 	case XML_DOCUMENT_NODE:
1600 	case XML_HTML_DOCUMENT_NODE:
1601 	    list = ((xmlDocPtr) node)->children;
1602 	    break;
1603 	case XML_ATTRIBUTE_NODE:
1604 	    list = ((xmlAttrPtr) node)->children;
1605 	    break;
1606 	case XML_TEXT_NODE:
1607 	case XML_CDATA_SECTION_NODE:
1608 	case XML_PI_NODE:
1609 	case XML_COMMENT_NODE:
1610 	    if (node->content != NULL) {
1611 		ret = xmlStrlen(node->content);
1612             }
1613 	    break;
1614 	case XML_ENTITY_REF_NODE:
1615 	case XML_DOCUMENT_TYPE_NODE:
1616 	case XML_ENTITY_NODE:
1617 	case XML_DOCUMENT_FRAG_NODE:
1618 	case XML_NOTATION_NODE:
1619 	case XML_DTD_NODE:
1620         case XML_ELEMENT_DECL:
1621         case XML_ATTRIBUTE_DECL:
1622         case XML_ENTITY_DECL:
1623 	case XML_NAMESPACE_DECL:
1624 	case XML_XINCLUDE_START:
1625 	case XML_XINCLUDE_END:
1626 	    ret = 1;
1627 	    break;
1628     }
1629     for (;list != NULL;ret++)
1630         list = list->next;
1631     return(ret);
1632 }
1633 
1634 /**
1635  * xmlLsOneNode:
1636  * @output:  the FILE * for the output
1637  * @node:  the node to dump
1638  *
1639  * Dump to @output the type and name of @node.
1640  */
1641 void
xmlLsOneNode(FILE * output,xmlNodePtr node)1642 xmlLsOneNode(FILE *output, xmlNodePtr node) {
1643     if (output == NULL) return;
1644     if (node == NULL) {
1645 	fprintf(output, "NULL\n");
1646 	return;
1647     }
1648     switch (node->type) {
1649 	case XML_ELEMENT_NODE:
1650 	    fprintf(output, "-");
1651 	    break;
1652 	case XML_ATTRIBUTE_NODE:
1653 	    fprintf(output, "a");
1654 	    break;
1655 	case XML_TEXT_NODE:
1656 	    fprintf(output, "t");
1657 	    break;
1658 	case XML_CDATA_SECTION_NODE:
1659 	    fprintf(output, "C");
1660 	    break;
1661 	case XML_ENTITY_REF_NODE:
1662 	    fprintf(output, "e");
1663 	    break;
1664 	case XML_ENTITY_NODE:
1665 	    fprintf(output, "E");
1666 	    break;
1667 	case XML_PI_NODE:
1668 	    fprintf(output, "p");
1669 	    break;
1670 	case XML_COMMENT_NODE:
1671 	    fprintf(output, "c");
1672 	    break;
1673 	case XML_DOCUMENT_NODE:
1674 	    fprintf(output, "d");
1675 	    break;
1676 	case XML_HTML_DOCUMENT_NODE:
1677 	    fprintf(output, "h");
1678 	    break;
1679 	case XML_DOCUMENT_TYPE_NODE:
1680 	    fprintf(output, "T");
1681 	    break;
1682 	case XML_DOCUMENT_FRAG_NODE:
1683 	    fprintf(output, "F");
1684 	    break;
1685 	case XML_NOTATION_NODE:
1686 	    fprintf(output, "N");
1687 	    break;
1688 	case XML_NAMESPACE_DECL:
1689 	    fprintf(output, "n");
1690 	    break;
1691 	default:
1692 	    fprintf(output, "?");
1693     }
1694     if (node->type != XML_NAMESPACE_DECL) {
1695 	if (node->properties != NULL)
1696 	    fprintf(output, "a");
1697 	else
1698 	    fprintf(output, "-");
1699 	if (node->nsDef != NULL)
1700 	    fprintf(output, "n");
1701 	else
1702 	    fprintf(output, "-");
1703     }
1704 
1705     fprintf(output, " %8d ", xmlLsCountNode(node));
1706 
1707     switch (node->type) {
1708 	case XML_ELEMENT_NODE:
1709 	    if (node->name != NULL) {
1710                 if ((node->ns != NULL) && (node->ns->prefix != NULL))
1711                     fprintf(output, "%s:", node->ns->prefix);
1712 		fprintf(output, "%s", (const char *) node->name);
1713             }
1714 	    break;
1715 	case XML_ATTRIBUTE_NODE:
1716 	    if (node->name != NULL)
1717 		fprintf(output, "%s", (const char *) node->name);
1718 	    break;
1719 	case XML_TEXT_NODE:
1720 	    if (node->content != NULL) {
1721 		xmlDebugDumpString(output, node->content);
1722             }
1723 	    break;
1724 	case XML_CDATA_SECTION_NODE:
1725 	    break;
1726 	case XML_ENTITY_REF_NODE:
1727 	    if (node->name != NULL)
1728 		fprintf(output, "%s", (const char *) node->name);
1729 	    break;
1730 	case XML_ENTITY_NODE:
1731 	    if (node->name != NULL)
1732 		fprintf(output, "%s", (const char *) node->name);
1733 	    break;
1734 	case XML_PI_NODE:
1735 	    if (node->name != NULL)
1736 		fprintf(output, "%s", (const char *) node->name);
1737 	    break;
1738 	case XML_COMMENT_NODE:
1739 	    break;
1740 	case XML_DOCUMENT_NODE:
1741 	    break;
1742 	case XML_HTML_DOCUMENT_NODE:
1743 	    break;
1744 	case XML_DOCUMENT_TYPE_NODE:
1745 	    break;
1746 	case XML_DOCUMENT_FRAG_NODE:
1747 	    break;
1748 	case XML_NOTATION_NODE:
1749 	    break;
1750 	case XML_NAMESPACE_DECL: {
1751 	    xmlNsPtr ns = (xmlNsPtr) node;
1752 
1753 	    if (ns->prefix == NULL)
1754 		fprintf(output, "default -> %s", (char *)ns->href);
1755 	    else
1756 		fprintf(output, "%s -> %s", (char *)ns->prefix,
1757 			(char *)ns->href);
1758 	    break;
1759 	}
1760 	default:
1761 	    if (node->name != NULL)
1762 		fprintf(output, "%s", (const char *) node->name);
1763     }
1764     fprintf(output, "\n");
1765 }
1766 
1767 /**
1768  * xmlBoolToText:
1769  * @boolval: a bool to turn into text
1770  *
1771  * Convenient way to turn bool into text
1772  *
1773  * Returns a pointer to either "True" or "False"
1774  */
1775 const char *
xmlBoolToText(int boolval)1776 xmlBoolToText(int boolval)
1777 {
1778     if (boolval)
1779         return("True");
1780     else
1781         return("False");
1782 }
1783 
1784 #ifdef LIBXML_XPATH_ENABLED
1785 /****************************************************************
1786  *								*
1787  *		The XML shell related functions			*
1788  *								*
1789  ****************************************************************/
1790 
1791 
1792 
1793 /*
1794  * TODO: Improvement/cleanups for the XML shell
1795  *     - allow to shell out an editor on a subpart
1796  *     - cleanup function registrations (with help) and calling
1797  *     - provide registration routines
1798  */
1799 
1800 /**
1801  * xmlShellPrintXPathError:
1802  * @errorType: valid xpath error id
1803  * @arg: the argument that cause xpath to fail
1804  *
1805  * Print the xpath error to libxml default error channel
1806  */
1807 void
xmlShellPrintXPathError(int errorType,const char * arg)1808 xmlShellPrintXPathError(int errorType, const char *arg)
1809 {
1810     const char *default_arg = "Result";
1811 
1812     if (!arg)
1813         arg = default_arg;
1814 
1815     switch (errorType) {
1816         case XPATH_UNDEFINED:
1817             fprintf(stderr,
1818                             "%s: no such node\n", arg);
1819             break;
1820 
1821         case XPATH_BOOLEAN:
1822             fprintf(stderr,
1823                             "%s is a Boolean\n", arg);
1824             break;
1825         case XPATH_NUMBER:
1826             fprintf(stderr,
1827                             "%s is a number\n", arg);
1828             break;
1829         case XPATH_STRING:
1830             fprintf(stderr,
1831                             "%s is a string\n", arg);
1832             break;
1833         case XPATH_USERS:
1834             fprintf(stderr,
1835                             "%s is user-defined\n", arg);
1836             break;
1837         case XPATH_XSLT_TREE:
1838             fprintf(stderr,
1839                             "%s is an XSLT value tree\n", arg);
1840             break;
1841     }
1842 #if 0
1843     fprintf(stderr,
1844                     "Try casting the result string function (xpath builtin)\n",
1845                     arg);
1846 #endif
1847 }
1848 
1849 
1850 #ifdef LIBXML_OUTPUT_ENABLED
1851 /**
1852  * xmlShellPrintNodeCtxt:
1853  * @ctxt : a non-null shell context
1854  * @node : a non-null node to print to the output FILE
1855  *
1856  * Print node to the output FILE
1857  */
1858 static void
xmlShellPrintNodeCtxt(xmlShellCtxtPtr ctxt,xmlNodePtr node)1859 xmlShellPrintNodeCtxt(xmlShellCtxtPtr ctxt,xmlNodePtr node)
1860 {
1861     FILE *fp;
1862 
1863     if (!node)
1864         return;
1865     if (ctxt == NULL)
1866 	fp = stdout;
1867     else
1868 	fp = ctxt->output;
1869 
1870     if (node->type == XML_DOCUMENT_NODE)
1871         xmlDocDump(fp, (xmlDocPtr) node);
1872     else if (node->type == XML_ATTRIBUTE_NODE)
1873         xmlDebugDumpAttrList(fp, (xmlAttrPtr) node, 0);
1874     else
1875         xmlElemDump(fp, node->doc, node);
1876 
1877     fprintf(fp, "\n");
1878 }
1879 
1880 /**
1881  * xmlShellPrintNode:
1882  * @node : a non-null node to print to the output FILE
1883  *
1884  * Print node to the output FILE
1885  */
1886 void
xmlShellPrintNode(xmlNodePtr node)1887 xmlShellPrintNode(xmlNodePtr node)
1888 {
1889     xmlShellPrintNodeCtxt(NULL, node);
1890 }
1891 #endif /* LIBXML_OUTPUT_ENABLED */
1892 
1893 /**
1894  * xmlShellPrintXPathResultCtxt:
1895  * @ctxt: a valid shell context
1896  * @list: a valid result generated by an xpath evaluation
1897  *
1898  * Prints result to the output FILE
1899  */
1900 static void
xmlShellPrintXPathResultCtxt(xmlShellCtxtPtr ctxt,xmlXPathObjectPtr list)1901 xmlShellPrintXPathResultCtxt(xmlShellCtxtPtr ctxt,xmlXPathObjectPtr list)
1902 {
1903     if (!ctxt)
1904        return;
1905 
1906     if (list != NULL) {
1907         switch (list->type) {
1908             case XPATH_NODESET:{
1909 #ifdef LIBXML_OUTPUT_ENABLED
1910                     int indx;
1911 
1912                     if (list->nodesetval) {
1913                         for (indx = 0; indx < list->nodesetval->nodeNr;
1914                              indx++) {
1915                             xmlShellPrintNodeCtxt(ctxt,
1916 				    list->nodesetval->nodeTab[indx]);
1917                         }
1918                     } else {
1919                         fprintf(ctxt->output,
1920                                         "Empty node set\n");
1921                     }
1922                     break;
1923 #else
1924 		    fprintf(ctxt->output,
1925 				    "Node set\n");
1926 #endif /* LIBXML_OUTPUT_ENABLED */
1927                 }
1928             case XPATH_BOOLEAN:
1929                 fprintf(ctxt->output,
1930                                 "Is a Boolean:%s\n",
1931                                 xmlBoolToText(list->boolval));
1932                 break;
1933             case XPATH_NUMBER:
1934                 fprintf(ctxt->output,
1935                                 "Is a number:%0g\n", list->floatval);
1936                 break;
1937             case XPATH_STRING:
1938                 fprintf(ctxt->output,
1939                                 "Is a string:%s\n", list->stringval);
1940                 break;
1941 
1942             default:
1943                 xmlShellPrintXPathError(list->type, NULL);
1944         }
1945     }
1946 }
1947 
1948 /**
1949  * xmlShellPrintXPathResult:
1950  * @list: a valid result generated by an xpath evaluation
1951  *
1952  * Prints result to the output FILE
1953  */
1954 void
xmlShellPrintXPathResult(xmlXPathObjectPtr list)1955 xmlShellPrintXPathResult(xmlXPathObjectPtr list)
1956 {
1957     xmlShellPrintXPathResultCtxt(NULL, list);
1958 }
1959 
1960 /**
1961  * xmlShellList:
1962  * @ctxt:  the shell context
1963  * @arg:  unused
1964  * @node:  a node
1965  * @node2:  unused
1966  *
1967  * Implements the XML shell function "ls"
1968  * Does an Unix like listing of the given node (like a directory)
1969  *
1970  * Returns 0
1971  */
1972 int
xmlShellList(xmlShellCtxtPtr ctxt,char * arg ATTRIBUTE_UNUSED,xmlNodePtr node,xmlNodePtr node2 ATTRIBUTE_UNUSED)1973 xmlShellList(xmlShellCtxtPtr ctxt,
1974              char *arg ATTRIBUTE_UNUSED, xmlNodePtr node,
1975              xmlNodePtr node2 ATTRIBUTE_UNUSED)
1976 {
1977     xmlNodePtr cur;
1978     if (!ctxt)
1979         return (0);
1980     if (node == NULL) {
1981 	fprintf(ctxt->output, "NULL\n");
1982 	return (0);
1983     }
1984     if ((node->type == XML_DOCUMENT_NODE) ||
1985         (node->type == XML_HTML_DOCUMENT_NODE)) {
1986         cur = ((xmlDocPtr) node)->children;
1987     } else if (node->type == XML_NAMESPACE_DECL) {
1988         xmlLsOneNode(ctxt->output, node);
1989         return (0);
1990     } else if (node->children != NULL) {
1991         cur = node->children;
1992     } else {
1993         xmlLsOneNode(ctxt->output, node);
1994         return (0);
1995     }
1996     while (cur != NULL) {
1997         xmlLsOneNode(ctxt->output, cur);
1998         cur = cur->next;
1999     }
2000     return (0);
2001 }
2002 
2003 /**
2004  * xmlShellBase:
2005  * @ctxt:  the shell context
2006  * @arg:  unused
2007  * @node:  a node
2008  * @node2:  unused
2009  *
2010  * Implements the XML shell function "base"
2011  * dumps the current XML base of the node
2012  *
2013  * Returns 0
2014  */
2015 int
xmlShellBase(xmlShellCtxtPtr ctxt,char * arg ATTRIBUTE_UNUSED,xmlNodePtr node,xmlNodePtr node2 ATTRIBUTE_UNUSED)2016 xmlShellBase(xmlShellCtxtPtr ctxt,
2017              char *arg ATTRIBUTE_UNUSED, xmlNodePtr node,
2018              xmlNodePtr node2 ATTRIBUTE_UNUSED)
2019 {
2020     xmlChar *base;
2021     if (!ctxt)
2022         return 0;
2023     if (node == NULL) {
2024 	fprintf(ctxt->output, "NULL\n");
2025 	return (0);
2026     }
2027 
2028     base = xmlNodeGetBase(node->doc, node);
2029 
2030     if (base == NULL) {
2031         fprintf(ctxt->output, " No base found !!!\n");
2032     } else {
2033         fprintf(ctxt->output, "%s\n", base);
2034         xmlFree(base);
2035     }
2036     return (0);
2037 }
2038 
2039 #ifdef LIBXML_TREE_ENABLED
2040 /**
2041  * xmlShellSetBase:
2042  * @ctxt:  the shell context
2043  * @arg:  the new base
2044  * @node:  a node
2045  * @node2:  unused
2046  *
2047  * Implements the XML shell function "setbase"
2048  * change the current XML base of the node
2049  *
2050  * Returns 0
2051  */
2052 static int
xmlShellSetBase(xmlShellCtxtPtr ctxt ATTRIBUTE_UNUSED,char * arg ATTRIBUTE_UNUSED,xmlNodePtr node,xmlNodePtr node2 ATTRIBUTE_UNUSED)2053 xmlShellSetBase(xmlShellCtxtPtr ctxt ATTRIBUTE_UNUSED,
2054              char *arg ATTRIBUTE_UNUSED, xmlNodePtr node,
2055              xmlNodePtr node2 ATTRIBUTE_UNUSED)
2056 {
2057     xmlNodeSetBase(node, (xmlChar*) arg);
2058     return (0);
2059 }
2060 #endif
2061 
2062 #ifdef LIBXML_XPATH_ENABLED
2063 /**
2064  * xmlShellRegisterNamespace:
2065  * @ctxt:  the shell context
2066  * @arg:  a string in prefix=nsuri format
2067  * @node:  unused
2068  * @node2:  unused
2069  *
2070  * Implements the XML shell function "setns"
2071  * register/unregister a prefix=namespace pair
2072  * on the XPath context
2073  *
2074  * Returns 0 on success and a negative value otherwise.
2075  */
2076 static int
xmlShellRegisterNamespace(xmlShellCtxtPtr ctxt,char * arg,xmlNodePtr node ATTRIBUTE_UNUSED,xmlNodePtr node2 ATTRIBUTE_UNUSED)2077 xmlShellRegisterNamespace(xmlShellCtxtPtr ctxt, char *arg,
2078       xmlNodePtr node ATTRIBUTE_UNUSED, xmlNodePtr node2 ATTRIBUTE_UNUSED)
2079 {
2080     xmlChar* nsListDup;
2081     xmlChar* prefix;
2082     xmlChar* href;
2083     xmlChar* next;
2084 
2085     nsListDup = xmlStrdup((xmlChar *) arg);
2086     next = nsListDup;
2087     while(next != NULL) {
2088 	/* skip spaces */
2089 	/*while((*next) == ' ') next++;*/
2090 	if((*next) == '\0') break;
2091 
2092 	/* find prefix */
2093 	prefix = next;
2094 	next = (xmlChar*)xmlStrchr(next, '=');
2095 	if(next == NULL) {
2096 	    fprintf(ctxt->output, "setns: prefix=[nsuri] required\n");
2097 	    xmlFree(nsListDup);
2098 	    return(-1);
2099 	}
2100 	*(next++) = '\0';
2101 
2102 	/* find href */
2103 	href = next;
2104 	next = (xmlChar*)xmlStrchr(next, ' ');
2105 	if(next != NULL) {
2106 	    *(next++) = '\0';
2107 	}
2108 
2109 	/* do register namespace */
2110 	if(xmlXPathRegisterNs(ctxt->pctxt, prefix, href) != 0) {
2111 	    fprintf(ctxt->output,"Error: unable to register NS with prefix=\"%s\" and href=\"%s\"\n", prefix, href);
2112 	    xmlFree(nsListDup);
2113 	    return(-1);
2114 	}
2115     }
2116 
2117     xmlFree(nsListDup);
2118     return(0);
2119 }
2120 /**
2121  * xmlShellRegisterRootNamespaces:
2122  * @ctxt:  the shell context
2123  * @arg:  unused
2124  * @node:  the root element
2125  * @node2:  unused
2126  *
2127  * Implements the XML shell function "setrootns"
2128  * which registers all namespaces declarations found on the root element.
2129  *
2130  * Returns 0 on success and a negative value otherwise.
2131  */
2132 static int
xmlShellRegisterRootNamespaces(xmlShellCtxtPtr ctxt,char * arg ATTRIBUTE_UNUSED,xmlNodePtr root,xmlNodePtr node2 ATTRIBUTE_UNUSED)2133 xmlShellRegisterRootNamespaces(xmlShellCtxtPtr ctxt, char *arg ATTRIBUTE_UNUSED,
2134       xmlNodePtr root, xmlNodePtr node2 ATTRIBUTE_UNUSED)
2135 {
2136     xmlNsPtr ns;
2137 
2138     if ((root == NULL) || (root->type != XML_ELEMENT_NODE) ||
2139         (root->nsDef == NULL) || (ctxt == NULL) || (ctxt->pctxt == NULL))
2140 	return(-1);
2141     ns = root->nsDef;
2142     while (ns != NULL) {
2143         if (ns->prefix == NULL)
2144 	    xmlXPathRegisterNs(ctxt->pctxt, BAD_CAST "defaultns", ns->href);
2145 	else
2146 	    xmlXPathRegisterNs(ctxt->pctxt, ns->prefix, ns->href);
2147         ns = ns->next;
2148     }
2149     return(0);
2150 }
2151 #endif
2152 
2153 /**
2154  * xmlShellGrep:
2155  * @ctxt:  the shell context
2156  * @arg:  the string or regular expression to find
2157  * @node:  a node
2158  * @node2:  unused
2159  *
2160  * Implements the XML shell function "grep"
2161  * dumps information about the node (namespace, attributes, content).
2162  *
2163  * Returns 0
2164  */
2165 static int
xmlShellGrep(xmlShellCtxtPtr ctxt ATTRIBUTE_UNUSED,char * arg,xmlNodePtr node,xmlNodePtr node2 ATTRIBUTE_UNUSED)2166 xmlShellGrep(xmlShellCtxtPtr ctxt ATTRIBUTE_UNUSED,
2167             char *arg, xmlNodePtr node, xmlNodePtr node2 ATTRIBUTE_UNUSED)
2168 {
2169     if (!ctxt)
2170         return (0);
2171     if (node == NULL)
2172 	return (0);
2173     if (arg == NULL)
2174 	return (0);
2175 #ifdef LIBXML_REGEXP_ENABLED
2176     if ((xmlStrchr((xmlChar *) arg, '?')) ||
2177 	(xmlStrchr((xmlChar *) arg, '*')) ||
2178 	(xmlStrchr((xmlChar *) arg, '.')) ||
2179 	(xmlStrchr((xmlChar *) arg, '['))) {
2180     }
2181 #endif
2182     while (node != NULL) {
2183         if (node->type == XML_COMMENT_NODE) {
2184 	    if (xmlStrstr(node->content, (xmlChar *) arg)) {
2185 
2186 		fprintf(ctxt->output, "%s : ", xmlGetNodePath(node));
2187                 xmlShellList(ctxt, NULL, node, NULL);
2188 	    }
2189         } else if (node->type == XML_TEXT_NODE) {
2190 	    if (xmlStrstr(node->content, (xmlChar *) arg)) {
2191 
2192 		fprintf(ctxt->output, "%s : ", xmlGetNodePath(node->parent));
2193                 xmlShellList(ctxt, NULL, node->parent, NULL);
2194 	    }
2195         }
2196 
2197         /*
2198          * Browse the full subtree, deep first
2199          */
2200 
2201         if ((node->type == XML_DOCUMENT_NODE) ||
2202             (node->type == XML_HTML_DOCUMENT_NODE)) {
2203             node = ((xmlDocPtr) node)->children;
2204         } else if ((node->children != NULL)
2205                    && (node->type != XML_ENTITY_REF_NODE)) {
2206             /* deep first */
2207             node = node->children;
2208         } else if (node->next != NULL) {
2209             /* then siblings */
2210             node = node->next;
2211         } else {
2212             /* go up to parents->next if needed */
2213             while (node != NULL) {
2214                 if (node->parent != NULL) {
2215                     node = node->parent;
2216                 }
2217                 if (node->next != NULL) {
2218                     node = node->next;
2219                     break;
2220                 }
2221                 if (node->parent == NULL) {
2222                     node = NULL;
2223                     break;
2224                 }
2225             }
2226 	}
2227     }
2228     return (0);
2229 }
2230 
2231 /**
2232  * xmlShellDir:
2233  * @ctxt:  the shell context
2234  * @arg:  unused
2235  * @node:  a node
2236  * @node2:  unused
2237  *
2238  * Implements the XML shell function "dir"
2239  * dumps information about the node (namespace, attributes, content).
2240  *
2241  * Returns 0
2242  */
2243 int
xmlShellDir(xmlShellCtxtPtr ctxt ATTRIBUTE_UNUSED,char * arg ATTRIBUTE_UNUSED,xmlNodePtr node,xmlNodePtr node2 ATTRIBUTE_UNUSED)2244 xmlShellDir(xmlShellCtxtPtr ctxt ATTRIBUTE_UNUSED,
2245             char *arg ATTRIBUTE_UNUSED, xmlNodePtr node,
2246             xmlNodePtr node2 ATTRIBUTE_UNUSED)
2247 {
2248     if (!ctxt)
2249         return (0);
2250     if (node == NULL) {
2251 	fprintf(ctxt->output, "NULL\n");
2252 	return (0);
2253     }
2254     if ((node->type == XML_DOCUMENT_NODE) ||
2255         (node->type == XML_HTML_DOCUMENT_NODE)) {
2256         xmlDebugDumpDocumentHead(ctxt->output, (xmlDocPtr) node);
2257     } else if (node->type == XML_ATTRIBUTE_NODE) {
2258         xmlDebugDumpAttr(ctxt->output, (xmlAttrPtr) node, 0);
2259     } else {
2260         xmlDebugDumpOneNode(ctxt->output, node, 0);
2261     }
2262     return (0);
2263 }
2264 
2265 /**
2266  * xmlShellSetContent:
2267  * @ctxt:  the shell context
2268  * @value:  the content as a string
2269  * @node:  a node
2270  * @node2:  unused
2271  *
2272  * Implements the XML shell function "dir"
2273  * dumps information about the node (namespace, attributes, content).
2274  *
2275  * Returns 0
2276  */
2277 static int
xmlShellSetContent(xmlShellCtxtPtr ctxt ATTRIBUTE_UNUSED,char * value,xmlNodePtr node,xmlNodePtr node2 ATTRIBUTE_UNUSED)2278 xmlShellSetContent(xmlShellCtxtPtr ctxt ATTRIBUTE_UNUSED,
2279             char *value, xmlNodePtr node,
2280             xmlNodePtr node2 ATTRIBUTE_UNUSED)
2281 {
2282     xmlNodePtr results;
2283     xmlParserErrors ret;
2284 
2285     if (!ctxt)
2286         return (0);
2287     if (node == NULL) {
2288 	fprintf(ctxt->output, "NULL\n");
2289 	return (0);
2290     }
2291     if (value == NULL) {
2292         fprintf(ctxt->output, "NULL\n");
2293 	return (0);
2294     }
2295 
2296     ret = xmlParseInNodeContext(node, value, strlen(value), 0, &results);
2297     if (ret == XML_ERR_OK) {
2298 	if (node->children != NULL) {
2299 	    xmlFreeNodeList(node->children);
2300 	    node->children = NULL;
2301 	    node->last = NULL;
2302 	}
2303 	xmlAddChildList(node, results);
2304     } else {
2305         fprintf(ctxt->output, "failed to parse content\n");
2306     }
2307     return (0);
2308 }
2309 
2310 static void
xmlShellPrintf(void * ctx,const char * msg,...)2311 xmlShellPrintf(void *ctx, const char *msg, ...) {
2312     xmlShellCtxtPtr sctxt = ctx;
2313     va_list ap;
2314 
2315     va_start(ap, msg);
2316     vfprintf(sctxt->output, msg, ap);
2317     va_end(ap);
2318 }
2319 
2320 #ifdef LIBXML_SCHEMAS_ENABLED
2321 /**
2322  * xmlShellRNGValidate:
2323  * @ctxt:  the shell context
2324  * @schemas:  the path to the Relax-NG schemas
2325  * @node:  a node
2326  * @node2:  unused
2327  *
2328  * Implements the XML shell function "relaxng"
2329  * validating the instance against a Relax-NG schemas
2330  *
2331  * Returns 0
2332  */
2333 static int
xmlShellRNGValidate(xmlShellCtxtPtr sctxt,char * schemas,xmlNodePtr node ATTRIBUTE_UNUSED,xmlNodePtr node2 ATTRIBUTE_UNUSED)2334 xmlShellRNGValidate(xmlShellCtxtPtr sctxt, char *schemas,
2335             xmlNodePtr node ATTRIBUTE_UNUSED,
2336 	    xmlNodePtr node2 ATTRIBUTE_UNUSED)
2337 {
2338     xmlRelaxNGPtr relaxngschemas;
2339     xmlRelaxNGParserCtxtPtr ctxt;
2340     xmlRelaxNGValidCtxtPtr vctxt;
2341     int ret;
2342 
2343     ctxt = xmlRelaxNGNewParserCtxt(schemas);
2344     xmlRelaxNGSetParserErrors(ctxt, xmlShellPrintf, xmlShellPrintf, sctxt);
2345     relaxngschemas = xmlRelaxNGParse(ctxt);
2346     xmlRelaxNGFreeParserCtxt(ctxt);
2347     if (relaxngschemas == NULL) {
2348 	fprintf(sctxt->output,
2349 		"Relax-NG schema %s failed to compile\n", schemas);
2350 	return(-1);
2351     }
2352     vctxt = xmlRelaxNGNewValidCtxt(relaxngschemas);
2353     xmlRelaxNGSetValidErrors(vctxt, xmlShellPrintf, xmlShellPrintf, sctxt);
2354     ret = xmlRelaxNGValidateDoc(vctxt, sctxt->doc);
2355     if (ret == 0) {
2356 	fprintf(sctxt->output, "%s validates\n", sctxt->filename);
2357     } else if (ret > 0) {
2358 	fprintf(sctxt->output, "%s fails to validate\n", sctxt->filename);
2359     } else {
2360 	fprintf(sctxt->output, "%s validation generated an internal error\n",
2361 	       sctxt->filename);
2362     }
2363     xmlRelaxNGFreeValidCtxt(vctxt);
2364     if (relaxngschemas != NULL)
2365 	xmlRelaxNGFree(relaxngschemas);
2366     return(0);
2367 }
2368 #endif
2369 
2370 #ifdef LIBXML_OUTPUT_ENABLED
2371 /**
2372  * xmlShellCat:
2373  * @ctxt:  the shell context
2374  * @arg:  unused
2375  * @node:  a node
2376  * @node2:  unused
2377  *
2378  * Implements the XML shell function "cat"
2379  * dumps the serialization node content (XML or HTML).
2380  *
2381  * Returns 0
2382  */
2383 int
xmlShellCat(xmlShellCtxtPtr ctxt,char * arg ATTRIBUTE_UNUSED,xmlNodePtr node,xmlNodePtr node2 ATTRIBUTE_UNUSED)2384 xmlShellCat(xmlShellCtxtPtr ctxt, char *arg ATTRIBUTE_UNUSED,
2385             xmlNodePtr node, xmlNodePtr node2 ATTRIBUTE_UNUSED)
2386 {
2387     if (!ctxt)
2388         return (0);
2389     if (node == NULL) {
2390 	fprintf(ctxt->output, "NULL\n");
2391 	return (0);
2392     }
2393     if (ctxt->doc->type == XML_HTML_DOCUMENT_NODE) {
2394 #ifdef LIBXML_HTML_ENABLED
2395         if (node->type == XML_HTML_DOCUMENT_NODE)
2396             htmlDocDump(ctxt->output, (htmlDocPtr) node);
2397         else
2398             htmlNodeDumpFile(ctxt->output, ctxt->doc, node);
2399 #else
2400         if (node->type == XML_DOCUMENT_NODE)
2401             xmlDocDump(ctxt->output, (xmlDocPtr) node);
2402         else
2403             xmlElemDump(ctxt->output, ctxt->doc, node);
2404 #endif /* LIBXML_HTML_ENABLED */
2405     } else {
2406         if (node->type == XML_DOCUMENT_NODE)
2407             xmlDocDump(ctxt->output, (xmlDocPtr) node);
2408         else
2409             xmlElemDump(ctxt->output, ctxt->doc, node);
2410     }
2411     fprintf(ctxt->output, "\n");
2412     return (0);
2413 }
2414 #endif /* LIBXML_OUTPUT_ENABLED */
2415 
2416 /**
2417  * xmlShellLoad:
2418  * @ctxt:  the shell context
2419  * @filename:  the file name
2420  * @node:  unused
2421  * @node2:  unused
2422  *
2423  * Implements the XML shell function "load"
2424  * loads a new document specified by the filename
2425  *
2426  * Returns 0 or -1 if loading failed
2427  */
2428 int
xmlShellLoad(xmlShellCtxtPtr ctxt,char * filename,xmlNodePtr node ATTRIBUTE_UNUSED,xmlNodePtr node2 ATTRIBUTE_UNUSED)2429 xmlShellLoad(xmlShellCtxtPtr ctxt, char *filename,
2430              xmlNodePtr node ATTRIBUTE_UNUSED,
2431              xmlNodePtr node2 ATTRIBUTE_UNUSED)
2432 {
2433     xmlDocPtr doc;
2434     int html = 0;
2435 
2436     if ((ctxt == NULL) || (filename == NULL)) return(-1);
2437     if (ctxt->doc != NULL)
2438         html = (ctxt->doc->type == XML_HTML_DOCUMENT_NODE);
2439 
2440     if (html) {
2441 #ifdef LIBXML_HTML_ENABLED
2442         doc = htmlParseFile(filename, NULL);
2443 #else
2444         fprintf(ctxt->output, "HTML support not compiled in\n");
2445         doc = NULL;
2446 #endif /* LIBXML_HTML_ENABLED */
2447     } else {
2448         doc = xmlReadFile(filename,NULL,0);
2449     }
2450     if (doc != NULL) {
2451         if (ctxt->loaded == 1) {
2452             xmlFreeDoc(ctxt->doc);
2453         }
2454         ctxt->loaded = 1;
2455 #ifdef LIBXML_XPATH_ENABLED
2456         xmlXPathFreeContext(ctxt->pctxt);
2457 #endif /* LIBXML_XPATH_ENABLED */
2458         xmlFree(ctxt->filename);
2459         ctxt->doc = doc;
2460         ctxt->node = (xmlNodePtr) doc;
2461 #ifdef LIBXML_XPATH_ENABLED
2462         ctxt->pctxt = xmlXPathNewContext(doc);
2463 #endif /* LIBXML_XPATH_ENABLED */
2464         ctxt->filename = (char *) xmlCanonicPath((xmlChar *) filename);
2465     } else
2466         return (-1);
2467     return (0);
2468 }
2469 
2470 #ifdef LIBXML_OUTPUT_ENABLED
2471 /**
2472  * xmlShellWrite:
2473  * @ctxt:  the shell context
2474  * @filename:  the file name
2475  * @node:  a node in the tree
2476  * @node2:  unused
2477  *
2478  * Implements the XML shell function "write"
2479  * Write the current node to the filename, it saves the serialization
2480  * of the subtree under the @node specified
2481  *
2482  * Returns 0 or -1 in case of error
2483  */
2484 int
xmlShellWrite(xmlShellCtxtPtr ctxt,char * filename,xmlNodePtr node,xmlNodePtr node2 ATTRIBUTE_UNUSED)2485 xmlShellWrite(xmlShellCtxtPtr ctxt, char *filename, xmlNodePtr node,
2486               xmlNodePtr node2 ATTRIBUTE_UNUSED)
2487 {
2488     if (node == NULL)
2489         return (-1);
2490     if ((filename == NULL) || (filename[0] == 0)) {
2491         return (-1);
2492     }
2493 #ifdef W_OK
2494     if (access((char *) filename, W_OK)) {
2495         fprintf(ctxt->output,
2496                         "Cannot write to %s\n", filename);
2497         return (-1);
2498     }
2499 #endif
2500     switch (node->type) {
2501         case XML_DOCUMENT_NODE:
2502             if (xmlSaveFile((char *) filename, ctxt->doc) < -1) {
2503                 fprintf(ctxt->output,
2504                                 "Failed to write to %s\n", filename);
2505                 return (-1);
2506             }
2507             break;
2508         case XML_HTML_DOCUMENT_NODE:
2509 #ifdef LIBXML_HTML_ENABLED
2510             if (htmlSaveFile((char *) filename, ctxt->doc) < 0) {
2511                 fprintf(ctxt->output,
2512                                 "Failed to write to %s\n", filename);
2513                 return (-1);
2514             }
2515 #else
2516             if (xmlSaveFile((char *) filename, ctxt->doc) < -1) {
2517                 fprintf(ctxt->output,
2518                                 "Failed to write to %s\n", filename);
2519                 return (-1);
2520             }
2521 #endif /* LIBXML_HTML_ENABLED */
2522             break;
2523         default:{
2524                 FILE *f;
2525 
2526                 f = fopen((char *) filename, "w");
2527                 if (f == NULL) {
2528                     fprintf(ctxt->output,
2529                                     "Failed to write to %s\n", filename);
2530                     return (-1);
2531                 }
2532                 xmlElemDump(f, ctxt->doc, node);
2533                 fclose(f);
2534             }
2535     }
2536     return (0);
2537 }
2538 
2539 /**
2540  * xmlShellSave:
2541  * @ctxt:  the shell context
2542  * @filename:  the file name (optional)
2543  * @node:  unused
2544  * @node2:  unused
2545  *
2546  * Implements the XML shell function "save"
2547  * Write the current document to the filename, or it's original name
2548  *
2549  * Returns 0 or -1 in case of error
2550  */
2551 int
xmlShellSave(xmlShellCtxtPtr ctxt,char * filename,xmlNodePtr node ATTRIBUTE_UNUSED,xmlNodePtr node2 ATTRIBUTE_UNUSED)2552 xmlShellSave(xmlShellCtxtPtr ctxt, char *filename,
2553              xmlNodePtr node ATTRIBUTE_UNUSED,
2554              xmlNodePtr node2 ATTRIBUTE_UNUSED)
2555 {
2556     if ((ctxt == NULL) || (ctxt->doc == NULL))
2557         return (-1);
2558     if ((filename == NULL) || (filename[0] == 0))
2559         filename = ctxt->filename;
2560     if (filename == NULL)
2561         return (-1);
2562 #ifdef W_OK
2563     if (access((char *) filename, W_OK)) {
2564         fprintf(ctxt->output,
2565                         "Cannot save to %s\n", filename);
2566         return (-1);
2567     }
2568 #endif
2569     switch (ctxt->doc->type) {
2570         case XML_DOCUMENT_NODE:
2571             if (xmlSaveFile((char *) filename, ctxt->doc) < 0) {
2572                 fprintf(ctxt->output,
2573                                 "Failed to save to %s\n", filename);
2574             }
2575             break;
2576         case XML_HTML_DOCUMENT_NODE:
2577 #ifdef LIBXML_HTML_ENABLED
2578             if (htmlSaveFile((char *) filename, ctxt->doc) < 0) {
2579                 fprintf(ctxt->output,
2580                                 "Failed to save to %s\n", filename);
2581             }
2582 #else
2583             if (xmlSaveFile((char *) filename, ctxt->doc) < 0) {
2584                 fprintf(ctxt->output,
2585                                 "Failed to save to %s\n", filename);
2586             }
2587 #endif /* LIBXML_HTML_ENABLED */
2588             break;
2589         default:
2590             fprintf(ctxt->output,
2591 	    "To save to subparts of a document use the 'write' command\n");
2592             return (-1);
2593 
2594     }
2595     return (0);
2596 }
2597 #endif /* LIBXML_OUTPUT_ENABLED */
2598 
2599 #ifdef LIBXML_VALID_ENABLED
2600 /**
2601  * xmlShellValidate:
2602  * @ctxt:  the shell context
2603  * @dtd:  the DTD URI (optional)
2604  * @node:  unused
2605  * @node2:  unused
2606  *
2607  * Implements the XML shell function "validate"
2608  * Validate the document, if a DTD path is provided, then the validation
2609  * is done against the given DTD.
2610  *
2611  * Returns 0 or -1 in case of error
2612  */
2613 int
xmlShellValidate(xmlShellCtxtPtr ctxt,char * dtd,xmlNodePtr node ATTRIBUTE_UNUSED,xmlNodePtr node2 ATTRIBUTE_UNUSED)2614 xmlShellValidate(xmlShellCtxtPtr ctxt, char *dtd,
2615                  xmlNodePtr node ATTRIBUTE_UNUSED,
2616                  xmlNodePtr node2 ATTRIBUTE_UNUSED)
2617 {
2618     xmlValidCtxt vctxt;
2619     int res = -1;
2620 
2621     if ((ctxt == NULL) || (ctxt->doc == NULL)) return(-1);
2622     memset(&vctxt, 0, sizeof(vctxt));
2623     vctxt.error = xmlShellPrintf;
2624     vctxt.warning = xmlShellPrintf;
2625     vctxt.userData = ctxt;
2626 
2627     if ((dtd == NULL) || (dtd[0] == 0)) {
2628         res = xmlValidateDocument(&vctxt, ctxt->doc);
2629     } else {
2630         xmlDtdPtr subset;
2631 
2632         subset = xmlParseDTD(NULL, (xmlChar *) dtd);
2633         if (subset != NULL) {
2634             res = xmlValidateDtd(&vctxt, ctxt->doc, subset);
2635 
2636             xmlFreeDtd(subset);
2637         }
2638     }
2639     return (res);
2640 }
2641 #endif /* LIBXML_VALID_ENABLED */
2642 
2643 /**
2644  * xmlShellDu:
2645  * @ctxt:  the shell context
2646  * @arg:  unused
2647  * @tree:  a node defining a subtree
2648  * @node2:  unused
2649  *
2650  * Implements the XML shell function "du"
2651  * show the structure of the subtree under node @tree
2652  * If @tree is null, the command works on the current node.
2653  *
2654  * Returns 0 or -1 in case of error
2655  */
2656 int
xmlShellDu(xmlShellCtxtPtr ctxt,char * arg ATTRIBUTE_UNUSED,xmlNodePtr tree,xmlNodePtr node2 ATTRIBUTE_UNUSED)2657 xmlShellDu(xmlShellCtxtPtr ctxt,
2658            char *arg ATTRIBUTE_UNUSED, xmlNodePtr tree,
2659            xmlNodePtr node2 ATTRIBUTE_UNUSED)
2660 {
2661     xmlNodePtr node;
2662     int indent = 0, i;
2663 
2664     if (!ctxt)
2665 	return (-1);
2666 
2667     if (tree == NULL)
2668         return (-1);
2669     node = tree;
2670     while (node != NULL) {
2671         if ((node->type == XML_DOCUMENT_NODE) ||
2672             (node->type == XML_HTML_DOCUMENT_NODE)) {
2673             fprintf(ctxt->output, "/\n");
2674         } else if (node->type == XML_ELEMENT_NODE) {
2675             for (i = 0; i < indent; i++)
2676                 fprintf(ctxt->output, "  ");
2677             if ((node->ns) && (node->ns->prefix))
2678                 fprintf(ctxt->output, "%s:", node->ns->prefix);
2679             fprintf(ctxt->output, "%s\n", node->name);
2680         } else {
2681         }
2682 
2683         /*
2684          * Browse the full subtree, deep first
2685          */
2686 
2687         if ((node->type == XML_DOCUMENT_NODE) ||
2688             (node->type == XML_HTML_DOCUMENT_NODE)) {
2689             node = ((xmlDocPtr) node)->children;
2690         } else if ((node->children != NULL)
2691                    && (node->type != XML_ENTITY_REF_NODE)) {
2692             /* deep first */
2693             node = node->children;
2694             indent++;
2695         } else if ((node != tree) && (node->next != NULL)) {
2696             /* then siblings */
2697             node = node->next;
2698         } else if (node != tree) {
2699             /* go up to parents->next if needed */
2700             while (node != tree) {
2701                 if (node->parent != NULL) {
2702                     node = node->parent;
2703                     indent--;
2704                 }
2705                 if ((node != tree) && (node->next != NULL)) {
2706                     node = node->next;
2707                     break;
2708                 }
2709                 if (node->parent == NULL) {
2710                     node = NULL;
2711                     break;
2712                 }
2713                 if (node == tree) {
2714                     node = NULL;
2715                     break;
2716                 }
2717             }
2718             /* exit condition */
2719             if (node == tree)
2720                 node = NULL;
2721         } else
2722             node = NULL;
2723     }
2724     return (0);
2725 }
2726 
2727 /**
2728  * xmlShellPwd:
2729  * @ctxt:  the shell context
2730  * @buffer:  the output buffer
2731  * @node:  a node
2732  * @node2:  unused
2733  *
2734  * Implements the XML shell function "pwd"
2735  * Show the full path from the root to the node, if needed building
2736  * thumblers when similar elements exists at a given ancestor level.
2737  * The output is compatible with XPath commands.
2738  *
2739  * Returns 0 or -1 in case of error
2740  */
2741 int
xmlShellPwd(xmlShellCtxtPtr ctxt ATTRIBUTE_UNUSED,char * buffer,xmlNodePtr node,xmlNodePtr node2 ATTRIBUTE_UNUSED)2742 xmlShellPwd(xmlShellCtxtPtr ctxt ATTRIBUTE_UNUSED, char *buffer,
2743             xmlNodePtr node, xmlNodePtr node2 ATTRIBUTE_UNUSED)
2744 {
2745     xmlChar *path;
2746 
2747     if ((node == NULL) || (buffer == NULL))
2748         return (-1);
2749 
2750     path = xmlGetNodePath(node);
2751     if (path == NULL)
2752 	return (-1);
2753 
2754     /*
2755      * This test prevents buffer overflow, because this routine
2756      * is only called by xmlShell, in which the second argument is
2757      * 500 chars long.
2758      * It is a dirty hack before a cleaner solution is found.
2759      * Documentation should mention that the second argument must
2760      * be at least 500 chars long, and could be stripped if too long.
2761      */
2762     snprintf(buffer, 499, "%s", path);
2763     buffer[499] = '0';
2764     xmlFree(path);
2765 
2766     return (0);
2767 }
2768 
2769 /**
2770  * xmlShell:
2771  * @doc:  the initial document
2772  * @filename:  the output buffer
2773  * @input:  the line reading function
2774  * @output:  the output FILE*, defaults to stdout if NULL
2775  *
2776  * Implements the XML shell
2777  * This allow to load, validate, view, modify and save a document
2778  * using a environment similar to a UNIX commandline.
2779  */
2780 void
xmlShell(xmlDocPtr doc,const char * filename,xmlShellReadlineFunc input,FILE * output)2781 xmlShell(xmlDocPtr doc, const char *filename, xmlShellReadlineFunc input,
2782          FILE * output)
2783 {
2784     char prompt[500] = "/ > ";
2785     char *cmdline = NULL, *cur;
2786     char command[100];
2787     char arg[400];
2788     int i;
2789     xmlShellCtxtPtr ctxt;
2790     xmlXPathObjectPtr list;
2791 
2792     if (doc == NULL)
2793         return;
2794     if (filename == NULL)
2795         return;
2796     if (input == NULL)
2797         return;
2798     if (output == NULL)
2799         output = stdout;
2800     ctxt = (xmlShellCtxtPtr) xmlMalloc(sizeof(xmlShellCtxt));
2801     if (ctxt == NULL)
2802         return;
2803     ctxt->loaded = 0;
2804     ctxt->doc = doc;
2805     ctxt->input = input;
2806     ctxt->output = output;
2807     ctxt->filename = (char *) xmlStrdup((xmlChar *) filename);
2808     ctxt->node = (xmlNodePtr) ctxt->doc;
2809 
2810 #ifdef LIBXML_XPATH_ENABLED
2811     ctxt->pctxt = xmlXPathNewContext(ctxt->doc);
2812     if (ctxt->pctxt == NULL) {
2813         xmlFree(ctxt);
2814         return;
2815     }
2816 #endif /* LIBXML_XPATH_ENABLED */
2817     while (1) {
2818         if (ctxt->node == (xmlNodePtr) ctxt->doc)
2819             snprintf(prompt, sizeof(prompt), "%s > ", "/");
2820         else if ((ctxt->node != NULL) && (ctxt->node->name) &&
2821                  (ctxt->node->ns) && (ctxt->node->ns->prefix))
2822             snprintf(prompt, sizeof(prompt), "%s:%s > ",
2823                      (ctxt->node->ns->prefix), ctxt->node->name);
2824         else if ((ctxt->node != NULL) && (ctxt->node->name))
2825             snprintf(prompt, sizeof(prompt), "%s > ", ctxt->node->name);
2826         else
2827             snprintf(prompt, sizeof(prompt), "? > ");
2828         prompt[sizeof(prompt) - 1] = 0;
2829 
2830         /*
2831          * Get a new command line
2832          */
2833         cmdline = ctxt->input(prompt);
2834         if (cmdline == NULL)
2835             break;
2836 
2837         /*
2838          * Parse the command itself
2839          */
2840         cur = cmdline;
2841         while ((*cur == ' ') || (*cur == '\t'))
2842             cur++;
2843         i = 0;
2844         while ((*cur != ' ') && (*cur != '\t') &&
2845                (*cur != '\n') && (*cur != '\r')) {
2846             if (*cur == 0)
2847                 break;
2848             command[i++] = *cur++;
2849         }
2850         command[i] = 0;
2851         if (i == 0)
2852             continue;
2853 
2854         /*
2855          * Parse the argument
2856          */
2857         while ((*cur == ' ') || (*cur == '\t'))
2858             cur++;
2859         i = 0;
2860         while ((*cur != '\n') && (*cur != '\r') && (*cur != 0)) {
2861             if (*cur == 0)
2862                 break;
2863             arg[i++] = *cur++;
2864         }
2865         arg[i] = 0;
2866 
2867         /*
2868          * start interpreting the command
2869          */
2870         if (!strcmp(command, "exit"))
2871             break;
2872         if (!strcmp(command, "quit"))
2873             break;
2874         if (!strcmp(command, "bye"))
2875             break;
2876 		if (!strcmp(command, "help")) {
2877 		  fprintf(ctxt->output, "\tbase         display XML base of the node\n");
2878 		  fprintf(ctxt->output, "\tsetbase URI  change the XML base of the node\n");
2879 		  fprintf(ctxt->output, "\tbye          leave shell\n");
2880 		  fprintf(ctxt->output, "\tcat [node]   display node or current node\n");
2881 		  fprintf(ctxt->output, "\tcd [path]    change directory to path or to root\n");
2882 		  fprintf(ctxt->output, "\tdir [path]   dumps information about the node (namespace, attributes, content)\n");
2883 		  fprintf(ctxt->output, "\tdu [path]    show the structure of the subtree under path or the current node\n");
2884 		  fprintf(ctxt->output, "\texit         leave shell\n");
2885 		  fprintf(ctxt->output, "\thelp         display this help\n");
2886 		  fprintf(ctxt->output, "\tfree         display memory usage\n");
2887 		  fprintf(ctxt->output, "\tload [name]  load a new document with name\n");
2888 		  fprintf(ctxt->output, "\tls [path]    list contents of path or the current directory\n");
2889 		  fprintf(ctxt->output, "\tset xml_fragment replace the current node content with the fragment parsed in context\n");
2890 #ifdef LIBXML_XPATH_ENABLED
2891 		  fprintf(ctxt->output, "\txpath expr   evaluate the XPath expression in that context and print the result\n");
2892 		  fprintf(ctxt->output, "\tsetns nsreg  register a namespace to a prefix in the XPath evaluation context\n");
2893 		  fprintf(ctxt->output, "\t             format for nsreg is: prefix=[nsuri] (i.e. prefix= unsets a prefix)\n");
2894 		  fprintf(ctxt->output, "\tsetrootns    register all namespace found on the root element\n");
2895 		  fprintf(ctxt->output, "\t             the default namespace if any uses 'defaultns' prefix\n");
2896 #endif /* LIBXML_XPATH_ENABLED */
2897 		  fprintf(ctxt->output, "\tpwd          display current working directory\n");
2898 		  fprintf(ctxt->output, "\twhereis      display absolute path of [path] or current working directory\n");
2899 		  fprintf(ctxt->output, "\tquit         leave shell\n");
2900 #ifdef LIBXML_OUTPUT_ENABLED
2901 		  fprintf(ctxt->output, "\tsave [name]  save this document to name or the original name\n");
2902 		  fprintf(ctxt->output, "\twrite [name] write the current node to the filename\n");
2903 #endif /* LIBXML_OUTPUT_ENABLED */
2904 #ifdef LIBXML_VALID_ENABLED
2905 		  fprintf(ctxt->output, "\tvalidate     check the document for errors\n");
2906 #endif /* LIBXML_VALID_ENABLED */
2907 #ifdef LIBXML_SCHEMAS_ENABLED
2908 		  fprintf(ctxt->output, "\trelaxng rng  validate the document against the Relax-NG schemas\n");
2909 #endif
2910 		  fprintf(ctxt->output, "\tgrep string  search for a string in the subtree\n");
2911 #ifdef LIBXML_VALID_ENABLED
2912         } else if (!strcmp(command, "validate")) {
2913             xmlShellValidate(ctxt, arg, NULL, NULL);
2914 #endif /* LIBXML_VALID_ENABLED */
2915         } else if (!strcmp(command, "load")) {
2916             xmlShellLoad(ctxt, arg, NULL, NULL);
2917 #ifdef LIBXML_SCHEMAS_ENABLED
2918         } else if (!strcmp(command, "relaxng")) {
2919             xmlShellRNGValidate(ctxt, arg, NULL, NULL);
2920 #endif
2921 #ifdef LIBXML_OUTPUT_ENABLED
2922         } else if (!strcmp(command, "save")) {
2923             xmlShellSave(ctxt, arg, NULL, NULL);
2924         } else if (!strcmp(command, "write")) {
2925 	    if (arg[0] == 0)
2926 		fprintf(ctxt->output,
2927                         "Write command requires a filename argument\n");
2928 	    else
2929 		xmlShellWrite(ctxt, arg, ctxt->node, NULL);
2930 #endif /* LIBXML_OUTPUT_ENABLED */
2931         } else if (!strcmp(command, "grep")) {
2932             xmlShellGrep(ctxt, arg, ctxt->node, NULL);
2933         } else if (!strcmp(command, "pwd")) {
2934             char dir[500];
2935 
2936             if (!xmlShellPwd(ctxt, dir, ctxt->node, NULL))
2937                 fprintf(ctxt->output, "%s\n", dir);
2938         } else if (!strcmp(command, "du")) {
2939             if (arg[0] == 0) {
2940                 xmlShellDu(ctxt, NULL, ctxt->node, NULL);
2941             } else {
2942                 ctxt->pctxt->node = ctxt->node;
2943 #ifdef LIBXML_XPATH_ENABLED
2944                 ctxt->pctxt->node = ctxt->node;
2945                 list = xmlXPathEval((xmlChar *) arg, ctxt->pctxt);
2946 #else
2947                 list = NULL;
2948 #endif /* LIBXML_XPATH_ENABLED */
2949                 if (list != NULL) {
2950                     switch (list->type) {
2951                         case XPATH_UNDEFINED:
2952                             fprintf(ctxt->output,
2953                                             "%s: no such node\n", arg);
2954                             break;
2955                         case XPATH_NODESET:{
2956                             int indx;
2957 
2958                             if (list->nodesetval == NULL)
2959                                 break;
2960 
2961                             for (indx = 0;
2962                                  indx < list->nodesetval->nodeNr;
2963                                  indx++)
2964                                 xmlShellDu(ctxt, NULL,
2965                                            list->nodesetval->
2966                                            nodeTab[indx], NULL);
2967                             break;
2968                         }
2969                         case XPATH_BOOLEAN:
2970                             fprintf(ctxt->output,
2971                                             "%s is a Boolean\n", arg);
2972                             break;
2973                         case XPATH_NUMBER:
2974                             fprintf(ctxt->output,
2975                                             "%s is a number\n", arg);
2976                             break;
2977                         case XPATH_STRING:
2978                             fprintf(ctxt->output,
2979                                             "%s is a string\n", arg);
2980                             break;
2981                         case XPATH_USERS:
2982                             fprintf(ctxt->output,
2983                                             "%s is user-defined\n", arg);
2984                             break;
2985                         case XPATH_XSLT_TREE:
2986                             fprintf(ctxt->output,
2987                                             "%s is an XSLT value tree\n",
2988                                             arg);
2989                             break;
2990                     }
2991 #ifdef LIBXML_XPATH_ENABLED
2992                     xmlXPathFreeObject(list);
2993 #endif
2994                 } else {
2995                     fprintf(ctxt->output,
2996                                     "%s: no such node\n", arg);
2997                 }
2998                 ctxt->pctxt->node = NULL;
2999             }
3000         } else if (!strcmp(command, "base")) {
3001             xmlShellBase(ctxt, NULL, ctxt->node, NULL);
3002         } else if (!strcmp(command, "set")) {
3003 	    xmlShellSetContent(ctxt, arg, ctxt->node, NULL);
3004 #ifdef LIBXML_XPATH_ENABLED
3005         } else if (!strcmp(command, "setns")) {
3006             if (arg[0] == 0) {
3007 		fprintf(ctxt->output,
3008 				"setns: prefix=[nsuri] required\n");
3009             } else {
3010                 xmlShellRegisterNamespace(ctxt, arg, NULL, NULL);
3011             }
3012         } else if (!strcmp(command, "setrootns")) {
3013 	    xmlNodePtr root;
3014 
3015 	    root = xmlDocGetRootElement(ctxt->doc);
3016 	    xmlShellRegisterRootNamespaces(ctxt, NULL, root, NULL);
3017         } else if (!strcmp(command, "xpath")) {
3018             if (arg[0] == 0) {
3019 		fprintf(ctxt->output,
3020 				"xpath: expression required\n");
3021 	    } else {
3022                 ctxt->pctxt->node = ctxt->node;
3023                 list = xmlXPathEval((xmlChar *) arg, ctxt->pctxt);
3024 		xmlXPathDebugDumpObject(ctxt->output, list, 0);
3025 		xmlXPathFreeObject(list);
3026 	    }
3027 #endif /* LIBXML_XPATH_ENABLED */
3028 #ifdef LIBXML_TREE_ENABLED
3029         } else if (!strcmp(command, "setbase")) {
3030             xmlShellSetBase(ctxt, arg, ctxt->node, NULL);
3031 #endif
3032         } else if ((!strcmp(command, "ls")) || (!strcmp(command, "dir"))) {
3033             int dir = (!strcmp(command, "dir"));
3034 
3035             if (arg[0] == 0) {
3036                 if (dir)
3037                     xmlShellDir(ctxt, NULL, ctxt->node, NULL);
3038                 else
3039                     xmlShellList(ctxt, NULL, ctxt->node, NULL);
3040             } else {
3041                 ctxt->pctxt->node = ctxt->node;
3042 #ifdef LIBXML_XPATH_ENABLED
3043                 ctxt->pctxt->node = ctxt->node;
3044                 list = xmlXPathEval((xmlChar *) arg, ctxt->pctxt);
3045 #else
3046                 list = NULL;
3047 #endif /* LIBXML_XPATH_ENABLED */
3048                 if (list != NULL) {
3049                     switch (list->type) {
3050                         case XPATH_UNDEFINED:
3051                             fprintf(ctxt->output,
3052                                             "%s: no such node\n", arg);
3053                             break;
3054                         case XPATH_NODESET:{
3055                                 int indx;
3056 
3057 				if (list->nodesetval == NULL)
3058 				    break;
3059 
3060                                 for (indx = 0;
3061                                      indx < list->nodesetval->nodeNr;
3062                                      indx++) {
3063                                     if (dir)
3064                                         xmlShellDir(ctxt, NULL,
3065                                                     list->nodesetval->
3066                                                     nodeTab[indx], NULL);
3067                                     else
3068                                         xmlShellList(ctxt, NULL,
3069                                                      list->nodesetval->
3070                                                      nodeTab[indx], NULL);
3071                                 }
3072                                 break;
3073                             }
3074                         case XPATH_BOOLEAN:
3075                             fprintf(ctxt->output,
3076                                             "%s is a Boolean\n", arg);
3077                             break;
3078                         case XPATH_NUMBER:
3079                             fprintf(ctxt->output,
3080                                             "%s is a number\n", arg);
3081                             break;
3082                         case XPATH_STRING:
3083                             fprintf(ctxt->output,
3084                                             "%s is a string\n", arg);
3085                             break;
3086                         case XPATH_USERS:
3087                             fprintf(ctxt->output,
3088                                             "%s is user-defined\n", arg);
3089                             break;
3090                         case XPATH_XSLT_TREE:
3091                             fprintf(ctxt->output,
3092                                             "%s is an XSLT value tree\n",
3093                                             arg);
3094                             break;
3095                     }
3096 #ifdef LIBXML_XPATH_ENABLED
3097                     xmlXPathFreeObject(list);
3098 #endif
3099                 } else {
3100                     fprintf(ctxt->output,
3101                                     "%s: no such node\n", arg);
3102                 }
3103                 ctxt->pctxt->node = NULL;
3104             }
3105         } else if (!strcmp(command, "whereis")) {
3106             char dir[500];
3107 
3108             if (arg[0] == 0) {
3109                 if (!xmlShellPwd(ctxt, dir, ctxt->node, NULL))
3110                     fprintf(ctxt->output, "%s\n", dir);
3111             } else {
3112                 ctxt->pctxt->node = ctxt->node;
3113 #ifdef LIBXML_XPATH_ENABLED
3114                 list = xmlXPathEval((xmlChar *) arg, ctxt->pctxt);
3115 #else
3116                 list = NULL;
3117 #endif /* LIBXML_XPATH_ENABLED */
3118                 if (list != NULL) {
3119                     switch (list->type) {
3120                         case XPATH_UNDEFINED:
3121                             fprintf(ctxt->output,
3122                                             "%s: no such node\n", arg);
3123                             break;
3124                         case XPATH_NODESET:{
3125                                 int indx;
3126 
3127 				if (list->nodesetval == NULL)
3128 				    break;
3129 
3130                                 for (indx = 0;
3131                                      indx < list->nodesetval->nodeNr;
3132                                      indx++) {
3133                                     if (!xmlShellPwd(ctxt, dir, list->nodesetval->
3134                                                      nodeTab[indx], NULL))
3135                                         fprintf(ctxt->output, "%s\n", dir);
3136                                 }
3137                                 break;
3138                             }
3139                         case XPATH_BOOLEAN:
3140                             fprintf(ctxt->output,
3141                                             "%s is a Boolean\n", arg);
3142                             break;
3143                         case XPATH_NUMBER:
3144                             fprintf(ctxt->output,
3145                                             "%s is a number\n", arg);
3146                             break;
3147                         case XPATH_STRING:
3148                             fprintf(ctxt->output,
3149                                             "%s is a string\n", arg);
3150                             break;
3151                         case XPATH_USERS:
3152                             fprintf(ctxt->output,
3153                                             "%s is user-defined\n", arg);
3154                             break;
3155                         case XPATH_XSLT_TREE:
3156                             fprintf(ctxt->output,
3157                                             "%s is an XSLT value tree\n",
3158                                             arg);
3159                             break;
3160                     }
3161 #ifdef LIBXML_XPATH_ENABLED
3162                     xmlXPathFreeObject(list);
3163 #endif
3164                 } else {
3165                     fprintf(ctxt->output,
3166                                     "%s: no such node\n", arg);
3167                 }
3168                 ctxt->pctxt->node = NULL;
3169             }
3170         } else if (!strcmp(command, "cd")) {
3171             if (arg[0] == 0) {
3172                 ctxt->node = (xmlNodePtr) ctxt->doc;
3173             } else {
3174 #ifdef LIBXML_XPATH_ENABLED
3175                 int l;
3176 
3177                 ctxt->pctxt->node = ctxt->node;
3178 		l = strlen(arg);
3179 		if ((l >= 2) && (arg[l - 1] == '/'))
3180 		    arg[l - 1] = 0;
3181                 list = xmlXPathEval((xmlChar *) arg, ctxt->pctxt);
3182 #else
3183                 list = NULL;
3184 #endif /* LIBXML_XPATH_ENABLED */
3185                 if (list != NULL) {
3186                     switch (list->type) {
3187                         case XPATH_UNDEFINED:
3188                             fprintf(ctxt->output,
3189                                             "%s: no such node\n", arg);
3190                             break;
3191                         case XPATH_NODESET:
3192                             if (list->nodesetval != NULL) {
3193 				if (list->nodesetval->nodeNr == 1) {
3194 				    ctxt->node = list->nodesetval->nodeTab[0];
3195 				    if ((ctxt->node != NULL) &&
3196 				        (ctxt->node->type ==
3197 					 XML_NAMESPACE_DECL)) {
3198 					fprintf(ctxt->output,
3199 						    "cannot cd to namespace\n");
3200 					ctxt->node = NULL;
3201 				    }
3202 				} else
3203 				    fprintf(ctxt->output,
3204 						    "%s is a %d Node Set\n",
3205 						    arg,
3206 						    list->nodesetval->nodeNr);
3207                             } else
3208                                 fprintf(ctxt->output,
3209                                                 "%s is an empty Node Set\n",
3210                                                 arg);
3211                             break;
3212                         case XPATH_BOOLEAN:
3213                             fprintf(ctxt->output,
3214                                             "%s is a Boolean\n", arg);
3215                             break;
3216                         case XPATH_NUMBER:
3217                             fprintf(ctxt->output,
3218                                             "%s is a number\n", arg);
3219                             break;
3220                         case XPATH_STRING:
3221                             fprintf(ctxt->output,
3222                                             "%s is a string\n", arg);
3223                             break;
3224                         case XPATH_USERS:
3225                             fprintf(ctxt->output,
3226                                             "%s is user-defined\n", arg);
3227                             break;
3228                         case XPATH_XSLT_TREE:
3229                             fprintf(ctxt->output,
3230                                             "%s is an XSLT value tree\n",
3231                                             arg);
3232                             break;
3233                     }
3234 #ifdef LIBXML_XPATH_ENABLED
3235                     xmlXPathFreeObject(list);
3236 #endif
3237                 } else {
3238                     fprintf(ctxt->output,
3239                                     "%s: no such node\n", arg);
3240                 }
3241                 ctxt->pctxt->node = NULL;
3242             }
3243 #ifdef LIBXML_OUTPUT_ENABLED
3244         } else if (!strcmp(command, "cat")) {
3245             if (arg[0] == 0) {
3246                 xmlShellCat(ctxt, NULL, ctxt->node, NULL);
3247             } else {
3248                 ctxt->pctxt->node = ctxt->node;
3249 #ifdef LIBXML_XPATH_ENABLED
3250                 ctxt->pctxt->node = ctxt->node;
3251                 list = xmlXPathEval((xmlChar *) arg, ctxt->pctxt);
3252 #else
3253                 list = NULL;
3254 #endif /* LIBXML_XPATH_ENABLED */
3255                 if (list != NULL) {
3256                     switch (list->type) {
3257                         case XPATH_UNDEFINED:
3258                             fprintf(ctxt->output,
3259                                             "%s: no such node\n", arg);
3260                             break;
3261                         case XPATH_NODESET:{
3262                                 int indx;
3263 
3264 				if (list->nodesetval == NULL)
3265 				    break;
3266 
3267                                 for (indx = 0;
3268                                      indx < list->nodesetval->nodeNr;
3269                                      indx++) {
3270                                     if (i > 0)
3271                                         fprintf(ctxt->output, " -------\n");
3272                                     xmlShellCat(ctxt, NULL,
3273                                                 list->nodesetval->
3274                                                 nodeTab[indx], NULL);
3275                                 }
3276                                 break;
3277                             }
3278                         case XPATH_BOOLEAN:
3279                             fprintf(ctxt->output,
3280                                             "%s is a Boolean\n", arg);
3281                             break;
3282                         case XPATH_NUMBER:
3283                             fprintf(ctxt->output,
3284                                             "%s is a number\n", arg);
3285                             break;
3286                         case XPATH_STRING:
3287                             fprintf(ctxt->output,
3288                                             "%s is a string\n", arg);
3289                             break;
3290                         case XPATH_USERS:
3291                             fprintf(ctxt->output,
3292                                             "%s is user-defined\n", arg);
3293                             break;
3294                         case XPATH_XSLT_TREE:
3295                             fprintf(ctxt->output,
3296                                             "%s is an XSLT value tree\n",
3297                                             arg);
3298                             break;
3299                     }
3300 #ifdef LIBXML_XPATH_ENABLED
3301                     xmlXPathFreeObject(list);
3302 #endif
3303                 } else {
3304                     fprintf(ctxt->output,
3305                                     "%s: no such node\n", arg);
3306                 }
3307                 ctxt->pctxt->node = NULL;
3308             }
3309 #endif /* LIBXML_OUTPUT_ENABLED */
3310         } else {
3311             fprintf(ctxt->output,
3312                             "Unknown command %s\n", command);
3313         }
3314         free(cmdline);          /* not xmlFree here ! */
3315 	cmdline = NULL;
3316     }
3317 #ifdef LIBXML_XPATH_ENABLED
3318     xmlXPathFreeContext(ctxt->pctxt);
3319 #endif /* LIBXML_XPATH_ENABLED */
3320     if (ctxt->loaded) {
3321         xmlFreeDoc(ctxt->doc);
3322     }
3323     if (ctxt->filename != NULL)
3324         xmlFree(ctxt->filename);
3325     xmlFree(ctxt);
3326     if (cmdline != NULL)
3327         free(cmdline);          /* not xmlFree here ! */
3328 }
3329 
3330 #endif /* LIBXML_XPATH_ENABLED */
3331 
3332 #endif /* LIBXML_DEBUG_ENABLED */
3333