xref: /aosp_15_r20/external/libxml2/shell.c (revision 7c5688314b92172186c154356a6374bf7684c3ca)
1 /*
2  * shell.c: The xmllint shell
3  *
4  * See Copyright for the status of this software.
5  *
6  * Daniel Veillard <[email protected]>
7  */
8 
9 #include "libxml.h"
10 
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 
15 #ifdef _WIN32
16   #include <io.h>
17 #else
18   #include <unistd.h>
19 #endif
20 
21 #ifdef HAVE_LIBREADLINE
22 #include <readline/readline.h>
23 #ifdef HAVE_LIBHISTORY
24 #include <readline/history.h>
25 #endif
26 #endif
27 
28 #include <libxml/debugXML.h>
29 #include <libxml/HTMLtree.h>
30 #include <libxml/parser.h>
31 #include <libxml/uri.h>
32 #include <libxml/xpath.h>
33 #include <libxml/xpathInternals.h>
34 #ifdef LIBXML_SCHEMAS_ENABLED
35 #include <libxml/relaxng.h>
36 #endif
37 
38 #include "private/shell.h"
39 
40 #ifndef STDIN_FILENO
41   #define STDIN_FILENO 0
42 #endif
43 
44 /*
45  * TODO: Improvement/cleanups for the XML shell
46  *     - allow to shell out an editor on a subpart
47  *     - cleanup function registrations (with help) and calling
48  *     - provide registration routines
49  */
50 
51 typedef struct _xmllintShellCtxt xmllintShellCtxt;
52 typedef xmllintShellCtxt *xmllintShellCtxtPtr;
53 struct _xmllintShellCtxt {
54     char *filename;
55     xmlDocPtr doc;
56     xmlNodePtr node;
57 #ifdef LIBXML_XPATH_ENABLED
58     xmlXPathContextPtr pctxt;
59 #endif
60     int loaded;
61     FILE *output;
62 };
63 
64 /**
65  * xmllintLsCountNode:
66  * @node:  the node to count
67  *
68  * Count the children of @node.
69  *
70  * Returns the number of children of @node.
71  */
72 static int
xmllintLsCountNode(xmlNodePtr node)73 xmllintLsCountNode(xmlNodePtr node) {
74     int ret = 0;
75     xmlNodePtr list = NULL;
76 
77     if (node == NULL)
78 	return(0);
79 
80     switch (node->type) {
81 	case XML_ELEMENT_NODE:
82 	    list = node->children;
83 	    break;
84 	case XML_DOCUMENT_NODE:
85 	case XML_HTML_DOCUMENT_NODE:
86 	    list = ((xmlDocPtr) node)->children;
87 	    break;
88 	case XML_ATTRIBUTE_NODE:
89 	    list = ((xmlAttrPtr) node)->children;
90 	    break;
91 	case XML_TEXT_NODE:
92 	case XML_CDATA_SECTION_NODE:
93 	case XML_PI_NODE:
94 	case XML_COMMENT_NODE:
95 	    if (node->content != NULL) {
96 		ret = xmlStrlen(node->content);
97             }
98 	    break;
99 	case XML_ENTITY_REF_NODE:
100 	case XML_DOCUMENT_TYPE_NODE:
101 	case XML_ENTITY_NODE:
102 	case XML_DOCUMENT_FRAG_NODE:
103 	case XML_NOTATION_NODE:
104 	case XML_DTD_NODE:
105         case XML_ELEMENT_DECL:
106         case XML_ATTRIBUTE_DECL:
107         case XML_ENTITY_DECL:
108 	case XML_NAMESPACE_DECL:
109 	case XML_XINCLUDE_START:
110 	case XML_XINCLUDE_END:
111 	    ret = 1;
112 	    break;
113     }
114     for (;list != NULL;ret++)
115         list = list->next;
116     return(ret);
117 }
118 
119 /**
120  * xmllintLsOneNode:
121  * @output:  the FILE * for the output
122  * @node:  the node to dump
123  *
124  * Dump to @output the type and name of @node.
125  */
126 static void
xmllintLsOneNode(FILE * output,xmlNodePtr node)127 xmllintLsOneNode(FILE *output, xmlNodePtr node) {
128     if (output == NULL) return;
129     if (node == NULL) {
130 	fprintf(output, "NULL\n");
131 	return;
132     }
133     switch (node->type) {
134 	case XML_ELEMENT_NODE:
135 	    fprintf(output, "-");
136 	    break;
137 	case XML_ATTRIBUTE_NODE:
138 	    fprintf(output, "a");
139 	    break;
140 	case XML_TEXT_NODE:
141 	    fprintf(output, "t");
142 	    break;
143 	case XML_CDATA_SECTION_NODE:
144 	    fprintf(output, "C");
145 	    break;
146 	case XML_ENTITY_REF_NODE:
147 	    fprintf(output, "e");
148 	    break;
149 	case XML_ENTITY_NODE:
150 	    fprintf(output, "E");
151 	    break;
152 	case XML_PI_NODE:
153 	    fprintf(output, "p");
154 	    break;
155 	case XML_COMMENT_NODE:
156 	    fprintf(output, "c");
157 	    break;
158 	case XML_DOCUMENT_NODE:
159 	    fprintf(output, "d");
160 	    break;
161 	case XML_HTML_DOCUMENT_NODE:
162 	    fprintf(output, "h");
163 	    break;
164 	case XML_DOCUMENT_TYPE_NODE:
165 	    fprintf(output, "T");
166 	    break;
167 	case XML_DOCUMENT_FRAG_NODE:
168 	    fprintf(output, "F");
169 	    break;
170 	case XML_NOTATION_NODE:
171 	    fprintf(output, "N");
172 	    break;
173 	case XML_NAMESPACE_DECL:
174 	    fprintf(output, "n");
175 	    break;
176 	default:
177 	    fprintf(output, "?");
178     }
179     if (node->type != XML_NAMESPACE_DECL) {
180 	if (node->properties != NULL)
181 	    fprintf(output, "a");
182 	else
183 	    fprintf(output, "-");
184 	if (node->nsDef != NULL)
185 	    fprintf(output, "n");
186 	else
187 	    fprintf(output, "-");
188     }
189 
190     fprintf(output, " %8d ", xmllintLsCountNode(node));
191 
192     switch (node->type) {
193 	case XML_ELEMENT_NODE:
194 	    if (node->name != NULL) {
195                 if ((node->ns != NULL) && (node->ns->prefix != NULL))
196                     fprintf(output, "%s:", node->ns->prefix);
197 		fprintf(output, "%s", (const char *) node->name);
198             }
199 	    break;
200 	case XML_ATTRIBUTE_NODE:
201 	    if (node->name != NULL)
202 		fprintf(output, "%s", (const char *) node->name);
203 	    break;
204 	case XML_TEXT_NODE:
205 #ifdef LIBXML_DEBUG_ENABLED
206 	    if (node->content != NULL) {
207 		xmlDebugDumpString(output, node->content);
208             }
209 #endif
210 	    break;
211 	case XML_CDATA_SECTION_NODE:
212 	    break;
213 	case XML_ENTITY_REF_NODE:
214 	    if (node->name != NULL)
215 		fprintf(output, "%s", (const char *) node->name);
216 	    break;
217 	case XML_ENTITY_NODE:
218 	    if (node->name != NULL)
219 		fprintf(output, "%s", (const char *) node->name);
220 	    break;
221 	case XML_PI_NODE:
222 	    if (node->name != NULL)
223 		fprintf(output, "%s", (const char *) node->name);
224 	    break;
225 	case XML_COMMENT_NODE:
226 	    break;
227 	case XML_DOCUMENT_NODE:
228 	    break;
229 	case XML_HTML_DOCUMENT_NODE:
230 	    break;
231 	case XML_DOCUMENT_TYPE_NODE:
232 	    break;
233 	case XML_DOCUMENT_FRAG_NODE:
234 	    break;
235 	case XML_NOTATION_NODE:
236 	    break;
237 	case XML_NAMESPACE_DECL: {
238 	    xmlNsPtr ns = (xmlNsPtr) node;
239 
240 	    if (ns->prefix == NULL)
241 		fprintf(output, "default -> %s", (char *)ns->href);
242 	    else
243 		fprintf(output, "%s -> %s", (char *)ns->prefix,
244 			(char *)ns->href);
245 	    break;
246 	}
247 	default:
248 	    if (node->name != NULL)
249 		fprintf(output, "%s", (const char *) node->name);
250     }
251     fprintf(output, "\n");
252 }
253 
254 /**
255  * xmllintShellList:
256  * @ctxt:  the shell context
257  * @arg:  unused
258  * @node:  a node
259  * @node2:  unused
260  *
261  * Implements the XML shell function "ls"
262  * Does an Unix like listing of the given node (like a directory)
263  *
264  * Returns 0
265  */
266 static int
xmllintShellList(xmllintShellCtxtPtr ctxt,char * arg ATTRIBUTE_UNUSED,xmlNodePtr node,xmlNodePtr node2 ATTRIBUTE_UNUSED)267 xmllintShellList(xmllintShellCtxtPtr ctxt,
268              char *arg ATTRIBUTE_UNUSED, xmlNodePtr node,
269              xmlNodePtr node2 ATTRIBUTE_UNUSED)
270 {
271     xmlNodePtr cur;
272     if (!ctxt)
273         return (0);
274     if (node == NULL) {
275 	fprintf(ctxt->output, "NULL\n");
276 	return (0);
277     }
278     if ((node->type == XML_DOCUMENT_NODE) ||
279         (node->type == XML_HTML_DOCUMENT_NODE)) {
280         cur = ((xmlDocPtr) node)->children;
281     } else if (node->type == XML_NAMESPACE_DECL) {
282         xmllintLsOneNode(ctxt->output, node);
283         return (0);
284     } else if (node->children != NULL) {
285         cur = node->children;
286     } else {
287         xmllintLsOneNode(ctxt->output, node);
288         return (0);
289     }
290     while (cur != NULL) {
291         xmllintLsOneNode(ctxt->output, cur);
292         cur = cur->next;
293     }
294     return (0);
295 }
296 
297 /**
298  * xmllintShellBase:
299  * @ctxt:  the shell context
300  * @arg:  unused
301  * @node:  a node
302  * @node2:  unused
303  *
304  * Implements the XML shell function "base"
305  * dumps the current XML base of the node
306  *
307  * Returns 0
308  */
309 static int
xmllintShellBase(xmllintShellCtxtPtr ctxt,char * arg ATTRIBUTE_UNUSED,xmlNodePtr node,xmlNodePtr node2 ATTRIBUTE_UNUSED)310 xmllintShellBase(xmllintShellCtxtPtr ctxt,
311              char *arg ATTRIBUTE_UNUSED, xmlNodePtr node,
312              xmlNodePtr node2 ATTRIBUTE_UNUSED)
313 {
314     xmlChar *base;
315     if (!ctxt)
316         return 0;
317     if (node == NULL) {
318 	fprintf(ctxt->output, "NULL\n");
319 	return (0);
320     }
321 
322     base = xmlNodeGetBase(node->doc, node);
323 
324     if (base == NULL) {
325         fprintf(ctxt->output, " No base found !!!\n");
326     } else {
327         fprintf(ctxt->output, "%s\n", base);
328         xmlFree(base);
329     }
330     return (0);
331 }
332 
333 /**
334  * xmllintShellSetBase:
335  * @ctxt:  the shell context
336  * @arg:  the new base
337  * @node:  a node
338  * @node2:  unused
339  *
340  * Implements the XML shell function "setbase"
341  * change the current XML base of the node
342  *
343  * Returns 0
344  */
345 static int
xmllintShellSetBase(xmllintShellCtxtPtr ctxt ATTRIBUTE_UNUSED,char * arg ATTRIBUTE_UNUSED,xmlNodePtr node,xmlNodePtr node2 ATTRIBUTE_UNUSED)346 xmllintShellSetBase(xmllintShellCtxtPtr ctxt ATTRIBUTE_UNUSED,
347              char *arg ATTRIBUTE_UNUSED, xmlNodePtr node,
348              xmlNodePtr node2 ATTRIBUTE_UNUSED)
349 {
350     xmlNodeSetBase(node, (xmlChar*) arg);
351     return (0);
352 }
353 
354 #ifdef LIBXML_XPATH_ENABLED
355 /**
356  * xmllintShellRegisterNamespace:
357  * @ctxt:  the shell context
358  * @arg:  a string in prefix=nsuri format
359  * @node:  unused
360  * @node2:  unused
361  *
362  * Implements the XML shell function "setns"
363  * register/unregister a prefix=namespace pair
364  * on the XPath context
365  *
366  * Returns 0 on success and a negative value otherwise.
367  */
368 static int
xmllintShellRegisterNamespace(xmllintShellCtxtPtr ctxt,char * arg,xmlNodePtr node ATTRIBUTE_UNUSED,xmlNodePtr node2 ATTRIBUTE_UNUSED)369 xmllintShellRegisterNamespace(xmllintShellCtxtPtr ctxt, char *arg,
370       xmlNodePtr node ATTRIBUTE_UNUSED, xmlNodePtr node2 ATTRIBUTE_UNUSED)
371 {
372     xmlChar* nsListDup;
373     xmlChar* prefix;
374     xmlChar* href;
375     xmlChar* next;
376 
377     nsListDup = xmlStrdup((xmlChar *) arg);
378     next = nsListDup;
379     while(next != NULL) {
380 	/* skip spaces */
381 	/*while((*next) == ' ') next++;*/
382 	if((*next) == '\0') break;
383 
384 	/* find prefix */
385 	prefix = next;
386 	next = (xmlChar*)xmlStrchr(next, '=');
387 	if(next == NULL) {
388 	    fprintf(ctxt->output, "setns: prefix=[nsuri] required\n");
389 	    xmlFree(nsListDup);
390 	    return(-1);
391 	}
392 	*(next++) = '\0';
393 
394 	/* find href */
395 	href = next;
396 	next = (xmlChar*)xmlStrchr(next, ' ');
397 	if(next != NULL) {
398 	    *(next++) = '\0';
399 	}
400 
401 	/* do register namespace */
402 	if(xmlXPathRegisterNs(ctxt->pctxt, prefix, href) != 0) {
403 	    fprintf(ctxt->output,"Error: unable to register NS with prefix=\"%s\" and href=\"%s\"\n", prefix, href);
404 	    xmlFree(nsListDup);
405 	    return(-1);
406 	}
407     }
408 
409     xmlFree(nsListDup);
410     return(0);
411 }
412 /**
413  * xmllintShellRegisterRootNamespaces:
414  * @ctxt:  the shell context
415  * @arg:  unused
416  * @node:  the root element
417  * @node2:  unused
418  *
419  * Implements the XML shell function "setrootns"
420  * which registers all namespaces declarations found on the root element.
421  *
422  * Returns 0 on success and a negative value otherwise.
423  */
424 static int
xmllintShellRegisterRootNamespaces(xmllintShellCtxtPtr ctxt,char * arg ATTRIBUTE_UNUSED,xmlNodePtr root,xmlNodePtr node2 ATTRIBUTE_UNUSED)425 xmllintShellRegisterRootNamespaces(xmllintShellCtxtPtr ctxt, char *arg ATTRIBUTE_UNUSED,
426       xmlNodePtr root, xmlNodePtr node2 ATTRIBUTE_UNUSED)
427 {
428     xmlNsPtr ns;
429 
430     if ((root == NULL) || (root->type != XML_ELEMENT_NODE) ||
431         (root->nsDef == NULL) || (ctxt == NULL) || (ctxt->pctxt == NULL))
432 	return(-1);
433     ns = root->nsDef;
434     while (ns != NULL) {
435         if (ns->prefix == NULL)
436 	    xmlXPathRegisterNs(ctxt->pctxt, BAD_CAST "defaultns", ns->href);
437 	else
438 	    xmlXPathRegisterNs(ctxt->pctxt, ns->prefix, ns->href);
439         ns = ns->next;
440     }
441     return(0);
442 }
443 #endif
444 
445 /**
446  * xmllintShellGrep:
447  * @ctxt:  the shell context
448  * @arg:  the string or regular expression to find
449  * @node:  a node
450  * @node2:  unused
451  *
452  * Implements the XML shell function "grep"
453  * dumps information about the node (namespace, attributes, content).
454  *
455  * Returns 0
456  */
457 static int
xmllintShellGrep(xmllintShellCtxtPtr ctxt ATTRIBUTE_UNUSED,char * arg,xmlNodePtr node,xmlNodePtr node2 ATTRIBUTE_UNUSED)458 xmllintShellGrep(xmllintShellCtxtPtr ctxt ATTRIBUTE_UNUSED,
459             char *arg, xmlNodePtr node, xmlNodePtr node2 ATTRIBUTE_UNUSED)
460 {
461     if (!ctxt)
462         return (0);
463     if (node == NULL)
464 	return (0);
465     if (arg == NULL)
466 	return (0);
467 #ifdef LIBXML_REGEXP_ENABLED
468     if ((xmlStrchr((xmlChar *) arg, '?')) ||
469 	(xmlStrchr((xmlChar *) arg, '*')) ||
470 	(xmlStrchr((xmlChar *) arg, '.')) ||
471 	(xmlStrchr((xmlChar *) arg, '['))) {
472     }
473 #endif
474     while (node != NULL) {
475         if (node->type == XML_COMMENT_NODE) {
476 	    if (xmlStrstr(node->content, (xmlChar *) arg)) {
477 		fprintf(ctxt->output, "%s : ", xmlGetNodePath(node));
478                 xmllintShellList(ctxt, NULL, node, NULL);
479 	    }
480         } else if (node->type == XML_TEXT_NODE) {
481 	    if (xmlStrstr(node->content, (xmlChar *) arg)) {
482 		fprintf(ctxt->output, "%s : ", xmlGetNodePath(node->parent));
483                 xmllintShellList(ctxt, NULL, node->parent, NULL);
484 	    }
485         }
486 
487         /*
488          * Browse the full subtree, deep first
489          */
490 
491         if ((node->type == XML_DOCUMENT_NODE) ||
492             (node->type == XML_HTML_DOCUMENT_NODE)) {
493             node = ((xmlDocPtr) node)->children;
494         } else if ((node->children != NULL)
495                    && (node->type != XML_ENTITY_REF_NODE)) {
496             /* deep first */
497             node = node->children;
498         } else if (node->next != NULL) {
499             /* then siblings */
500             node = node->next;
501         } else {
502             /* go up to parents->next if needed */
503             while (node != NULL) {
504                 if (node->parent != NULL) {
505                     node = node->parent;
506                 }
507                 if (node->next != NULL) {
508                     node = node->next;
509                     break;
510                 }
511                 if (node->parent == NULL) {
512                     node = NULL;
513                     break;
514                 }
515             }
516 	}
517     }
518     return (0);
519 }
520 
521 /**
522  * xmllintShellDir:
523  * @ctxt:  the shell context
524  * @arg:  unused
525  * @node:  a node
526  * @node2:  unused
527  *
528  * Implements the XML shell function "dir"
529  * dumps information about the node (namespace, attributes, content).
530  *
531  * Returns 0
532  */
533 static int
xmllintShellDir(xmllintShellCtxtPtr ctxt ATTRIBUTE_UNUSED,char * arg ATTRIBUTE_UNUSED,xmlNodePtr node,xmlNodePtr node2 ATTRIBUTE_UNUSED)534 xmllintShellDir(xmllintShellCtxtPtr ctxt ATTRIBUTE_UNUSED,
535             char *arg ATTRIBUTE_UNUSED, xmlNodePtr node,
536             xmlNodePtr node2 ATTRIBUTE_UNUSED)
537 {
538     if (!ctxt)
539         return (0);
540     if (node == NULL) {
541 	fprintf(ctxt->output, "NULL\n");
542 	return (0);
543     }
544 #ifdef LIBXML_DEBUG_ENABLED
545     if ((node->type == XML_DOCUMENT_NODE) ||
546         (node->type == XML_HTML_DOCUMENT_NODE)) {
547         xmlDebugDumpDocumentHead(ctxt->output, (xmlDocPtr) node);
548     } else if (node->type == XML_ATTRIBUTE_NODE) {
549         xmlDebugDumpAttr(ctxt->output, (xmlAttrPtr) node, 0);
550     } else {
551         xmlDebugDumpOneNode(ctxt->output, node, 0);
552     }
553 #endif
554     return (0);
555 }
556 
557 /**
558  * xmllintShellSetContent:
559  * @ctxt:  the shell context
560  * @value:  the content as a string
561  * @node:  a node
562  * @node2:  unused
563  *
564  * Implements the XML shell function "dir"
565  * dumps information about the node (namespace, attributes, content).
566  *
567  * Returns 0
568  */
569 static int
xmllintShellSetContent(xmllintShellCtxtPtr ctxt ATTRIBUTE_UNUSED,char * value,xmlNodePtr node,xmlNodePtr node2 ATTRIBUTE_UNUSED)570 xmllintShellSetContent(xmllintShellCtxtPtr ctxt ATTRIBUTE_UNUSED,
571             char *value, xmlNodePtr node,
572             xmlNodePtr node2 ATTRIBUTE_UNUSED)
573 {
574     xmlNodePtr results;
575     xmlParserErrors ret;
576 
577     if (!ctxt)
578         return (0);
579     if (node == NULL) {
580 	fprintf(ctxt->output, "NULL\n");
581 	return (0);
582     }
583     if (value == NULL) {
584         fprintf(ctxt->output, "NULL\n");
585 	return (0);
586     }
587 
588     ret = xmlParseInNodeContext(node, value, strlen(value), 0, &results);
589     if (ret == XML_ERR_OK) {
590 	if (node->children != NULL) {
591 	    xmlFreeNodeList(node->children);
592 	    node->children = NULL;
593 	    node->last = NULL;
594 	}
595 	xmlAddChildList(node, results);
596     } else {
597         fprintf(ctxt->output, "failed to parse content\n");
598     }
599     return (0);
600 }
601 
602 #if defined(LIBXML_VALID_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED)
603 static void
xmllintShellPrintf(void * ctx,const char * msg,...)604 xmllintShellPrintf(void *ctx, const char *msg, ...) {
605     xmllintShellCtxtPtr sctxt = ctx;
606     va_list ap;
607 
608     va_start(ap, msg);
609     vfprintf(sctxt->output, msg, ap);
610     va_end(ap);
611 }
612 #endif /* defined(LIBXML_VALID_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED) */
613 
614 #ifdef LIBXML_SCHEMAS_ENABLED
615 /**
616  * xmllintShellRNGValidate:
617  * @ctxt:  the shell context
618  * @schemas:  the path to the Relax-NG schemas
619  * @node:  a node
620  * @node2:  unused
621  *
622  * Implements the XML shell function "relaxng"
623  * validating the instance against a Relax-NG schemas
624  *
625  * Returns 0
626  */
627 static int
xmllintShellRNGValidate(xmllintShellCtxtPtr sctxt,char * schemas,xmlNodePtr node ATTRIBUTE_UNUSED,xmlNodePtr node2 ATTRIBUTE_UNUSED)628 xmllintShellRNGValidate(xmllintShellCtxtPtr sctxt, char *schemas,
629             xmlNodePtr node ATTRIBUTE_UNUSED,
630 	    xmlNodePtr node2 ATTRIBUTE_UNUSED)
631 {
632     xmlRelaxNGPtr relaxngschemas;
633     xmlRelaxNGParserCtxtPtr ctxt;
634     xmlRelaxNGValidCtxtPtr vctxt;
635     int ret;
636 
637     ctxt = xmlRelaxNGNewParserCtxt(schemas);
638     xmlRelaxNGSetParserErrors(ctxt, xmllintShellPrintf, xmllintShellPrintf, sctxt);
639     relaxngschemas = xmlRelaxNGParse(ctxt);
640     xmlRelaxNGFreeParserCtxt(ctxt);
641     if (relaxngschemas == NULL) {
642 	fprintf(sctxt->output,
643 		"Relax-NG schema %s failed to compile\n", schemas);
644 	return(-1);
645     }
646     vctxt = xmlRelaxNGNewValidCtxt(relaxngschemas);
647     xmlRelaxNGSetValidErrors(vctxt, xmllintShellPrintf, xmllintShellPrintf, sctxt);
648     ret = xmlRelaxNGValidateDoc(vctxt, sctxt->doc);
649     if (ret == 0) {
650 	fprintf(sctxt->output, "%s validates\n", sctxt->filename);
651     } else if (ret > 0) {
652 	fprintf(sctxt->output, "%s fails to validate\n", sctxt->filename);
653     } else {
654 	fprintf(sctxt->output, "%s validation generated an internal error\n",
655 	       sctxt->filename);
656     }
657     xmlRelaxNGFreeValidCtxt(vctxt);
658     if (relaxngschemas != NULL)
659 	xmlRelaxNGFree(relaxngschemas);
660     return(0);
661 }
662 #endif
663 
664 #ifdef LIBXML_OUTPUT_ENABLED
665 /**
666  * xmllintShellCat:
667  * @ctxt:  the shell context
668  * @arg:  unused
669  * @node:  a node
670  * @node2:  unused
671  *
672  * Implements the XML shell function "cat"
673  * dumps the serialization node content (XML or HTML).
674  *
675  * Returns 0
676  */
677 static int
xmllintShellCat(xmllintShellCtxtPtr ctxt,char * arg ATTRIBUTE_UNUSED,xmlNodePtr node,xmlNodePtr node2 ATTRIBUTE_UNUSED)678 xmllintShellCat(xmllintShellCtxtPtr ctxt, char *arg ATTRIBUTE_UNUSED,
679             xmlNodePtr node, xmlNodePtr node2 ATTRIBUTE_UNUSED)
680 {
681     if (!ctxt)
682         return (0);
683     if (node == NULL) {
684 	fprintf(ctxt->output, "NULL\n");
685 	return (0);
686     }
687     if (ctxt->doc->type == XML_HTML_DOCUMENT_NODE) {
688 #ifdef LIBXML_HTML_ENABLED
689         if (node->type == XML_HTML_DOCUMENT_NODE)
690             htmlDocDump(ctxt->output, (htmlDocPtr) node);
691         else
692             htmlNodeDumpFile(ctxt->output, ctxt->doc, node);
693 #else
694         if (node->type == XML_DOCUMENT_NODE)
695             xmlDocDump(ctxt->output, (xmlDocPtr) node);
696         else
697             xmlElemDump(ctxt->output, ctxt->doc, node);
698 #endif /* LIBXML_HTML_ENABLED */
699     } else {
700         if (node->type == XML_DOCUMENT_NODE)
701             xmlDocDump(ctxt->output, (xmlDocPtr) node);
702         else
703             xmlElemDump(ctxt->output, ctxt->doc, node);
704     }
705     fprintf(ctxt->output, "\n");
706     return (0);
707 }
708 #endif /* LIBXML_OUTPUT_ENABLED */
709 
710 /**
711  * xmllintShellLoad:
712  * @ctxt:  the shell context
713  * @filename:  the file name
714  * @node:  unused
715  * @node2:  unused
716  *
717  * Implements the XML shell function "load"
718  * loads a new document specified by the filename
719  *
720  * Returns 0 or -1 if loading failed
721  */
722 static int
xmllintShellLoad(xmllintShellCtxtPtr ctxt,char * filename,xmlNodePtr node ATTRIBUTE_UNUSED,xmlNodePtr node2 ATTRIBUTE_UNUSED)723 xmllintShellLoad(xmllintShellCtxtPtr ctxt, char *filename,
724              xmlNodePtr node ATTRIBUTE_UNUSED,
725              xmlNodePtr node2 ATTRIBUTE_UNUSED)
726 {
727     xmlDocPtr doc;
728     int html = 0;
729 
730     if ((ctxt == NULL) || (filename == NULL)) return(-1);
731     if (ctxt->doc != NULL)
732         html = (ctxt->doc->type == XML_HTML_DOCUMENT_NODE);
733 
734     if (html) {
735 #ifdef LIBXML_HTML_ENABLED
736         doc = htmlParseFile(filename, NULL);
737 #else
738         fprintf(ctxt->output, "HTML support not compiled in\n");
739         doc = NULL;
740 #endif /* LIBXML_HTML_ENABLED */
741     } else {
742         doc = xmlReadFile(filename,NULL,0);
743     }
744     if (doc != NULL) {
745         if (ctxt->loaded == 1) {
746             xmlFreeDoc(ctxt->doc);
747         }
748         ctxt->loaded = 1;
749 #ifdef LIBXML_XPATH_ENABLED
750         xmlXPathFreeContext(ctxt->pctxt);
751 #endif /* LIBXML_XPATH_ENABLED */
752         xmlFree(ctxt->filename);
753         ctxt->doc = doc;
754         ctxt->node = (xmlNodePtr) doc;
755 #ifdef LIBXML_XPATH_ENABLED
756         ctxt->pctxt = xmlXPathNewContext(doc);
757 #endif /* LIBXML_XPATH_ENABLED */
758         ctxt->filename = (char *) xmlCanonicPath((xmlChar *) filename);
759     } else
760         return (-1);
761     return (0);
762 }
763 
764 #ifdef LIBXML_OUTPUT_ENABLED
765 /**
766  * xmllintShellWrite:
767  * @ctxt:  the shell context
768  * @filename:  the file name
769  * @node:  a node in the tree
770  * @node2:  unused
771  *
772  * Implements the XML shell function "write"
773  * Write the current node to the filename, it saves the serialization
774  * of the subtree under the @node specified
775  *
776  * Returns 0 or -1 in case of error
777  */
778 static int
xmllintShellWrite(xmllintShellCtxtPtr ctxt,char * filename,xmlNodePtr node,xmlNodePtr node2 ATTRIBUTE_UNUSED)779 xmllintShellWrite(xmllintShellCtxtPtr ctxt, char *filename, xmlNodePtr node,
780               xmlNodePtr node2 ATTRIBUTE_UNUSED)
781 {
782     if (node == NULL)
783         return (-1);
784     if ((filename == NULL) || (filename[0] == 0)) {
785         return (-1);
786     }
787     switch (node->type) {
788         case XML_DOCUMENT_NODE:
789             if (xmlSaveFile((char *) filename, ctxt->doc) < -1) {
790                 fprintf(ctxt->output,
791                                 "Failed to write to %s\n", filename);
792                 return (-1);
793             }
794             break;
795         case XML_HTML_DOCUMENT_NODE:
796 #ifdef LIBXML_HTML_ENABLED
797             if (htmlSaveFile((char *) filename, ctxt->doc) < 0) {
798                 fprintf(ctxt->output,
799                                 "Failed to write to %s\n", filename);
800                 return (-1);
801             }
802 #else
803             if (xmlSaveFile((char *) filename, ctxt->doc) < -1) {
804                 fprintf(ctxt->output,
805                                 "Failed to write to %s\n", filename);
806                 return (-1);
807             }
808 #endif /* LIBXML_HTML_ENABLED */
809             break;
810         default:{
811                 FILE *f;
812 
813                 f = fopen((char *) filename, "wb");
814                 if (f == NULL) {
815                     fprintf(ctxt->output,
816                                     "Failed to write to %s\n", filename);
817                     return (-1);
818                 }
819                 xmlElemDump(f, ctxt->doc, node);
820                 fclose(f);
821             }
822     }
823     return (0);
824 }
825 
826 /**
827  * xmllintShellSave:
828  * @ctxt:  the shell context
829  * @filename:  the file name (optional)
830  * @node:  unused
831  * @node2:  unused
832  *
833  * Implements the XML shell function "save"
834  * Write the current document to the filename, or it's original name
835  *
836  * Returns 0 or -1 in case of error
837  */
838 static int
xmllintShellSave(xmllintShellCtxtPtr ctxt,char * filename,xmlNodePtr node ATTRIBUTE_UNUSED,xmlNodePtr node2 ATTRIBUTE_UNUSED)839 xmllintShellSave(xmllintShellCtxtPtr ctxt, char *filename,
840              xmlNodePtr node ATTRIBUTE_UNUSED,
841              xmlNodePtr node2 ATTRIBUTE_UNUSED)
842 {
843     if ((ctxt == NULL) || (ctxt->doc == NULL))
844         return (-1);
845     if ((filename == NULL) || (filename[0] == 0))
846         filename = ctxt->filename;
847     if (filename == NULL)
848         return (-1);
849     switch (ctxt->doc->type) {
850         case XML_DOCUMENT_NODE:
851             if (xmlSaveFile((char *) filename, ctxt->doc) < 0) {
852                 fprintf(ctxt->output,
853                                 "Failed to save to %s\n", filename);
854             }
855             break;
856         case XML_HTML_DOCUMENT_NODE:
857 #ifdef LIBXML_HTML_ENABLED
858             if (htmlSaveFile((char *) filename, ctxt->doc) < 0) {
859                 fprintf(ctxt->output,
860                                 "Failed to save to %s\n", filename);
861             }
862 #else
863             if (xmlSaveFile((char *) filename, ctxt->doc) < 0) {
864                 fprintf(ctxt->output,
865                                 "Failed to save to %s\n", filename);
866             }
867 #endif /* LIBXML_HTML_ENABLED */
868             break;
869         default:
870             fprintf(ctxt->output,
871 	    "To save to subparts of a document use the 'write' command\n");
872             return (-1);
873 
874     }
875     return (0);
876 }
877 #endif /* LIBXML_OUTPUT_ENABLED */
878 
879 #ifdef LIBXML_VALID_ENABLED
880 /**
881  * xmllintShellValidate:
882  * @ctxt:  the shell context
883  * @dtd:  the DTD URI (optional)
884  * @node:  unused
885  * @node2:  unused
886  *
887  * Implements the XML shell function "validate"
888  * Validate the document, if a DTD path is provided, then the validation
889  * is done against the given DTD.
890  *
891  * Returns 0 or -1 in case of error
892  */
893 static int
xmllintShellValidate(xmllintShellCtxtPtr ctxt,char * dtd,xmlNodePtr node ATTRIBUTE_UNUSED,xmlNodePtr node2 ATTRIBUTE_UNUSED)894 xmllintShellValidate(xmllintShellCtxtPtr ctxt, char *dtd,
895                  xmlNodePtr node ATTRIBUTE_UNUSED,
896                  xmlNodePtr node2 ATTRIBUTE_UNUSED)
897 {
898     xmlValidCtxt vctxt;
899     int res = -1;
900 
901     if ((ctxt == NULL) || (ctxt->doc == NULL)) return(-1);
902     memset(&vctxt, 0, sizeof(vctxt));
903     vctxt.error = xmllintShellPrintf;
904     vctxt.warning = xmllintShellPrintf;
905     vctxt.userData = ctxt;
906 
907     if ((dtd == NULL) || (dtd[0] == 0)) {
908         res = xmlValidateDocument(&vctxt, ctxt->doc);
909     } else {
910         xmlDtdPtr subset;
911 
912         subset = xmlParseDTD(NULL, (xmlChar *) dtd);
913         if (subset != NULL) {
914             res = xmlValidateDtd(&vctxt, ctxt->doc, subset);
915 
916             xmlFreeDtd(subset);
917         }
918     }
919     return (res);
920 }
921 #endif /* LIBXML_VALID_ENABLED */
922 
923 /**
924  * xmllintShellDu:
925  * @ctxt:  the shell context
926  * @arg:  unused
927  * @tree:  a node defining a subtree
928  * @node2:  unused
929  *
930  * Implements the XML shell function "du"
931  * show the structure of the subtree under node @tree
932  * If @tree is null, the command works on the current node.
933  *
934  * Returns 0 or -1 in case of error
935  */
936 static int
xmllintShellDu(xmllintShellCtxtPtr ctxt,char * arg ATTRIBUTE_UNUSED,xmlNodePtr tree,xmlNodePtr node2 ATTRIBUTE_UNUSED)937 xmllintShellDu(xmllintShellCtxtPtr ctxt,
938            char *arg ATTRIBUTE_UNUSED, xmlNodePtr tree,
939            xmlNodePtr node2 ATTRIBUTE_UNUSED)
940 {
941     xmlNodePtr node;
942     int indent = 0, i;
943 
944     if (!ctxt)
945 	return (-1);
946 
947     if (tree == NULL)
948         return (-1);
949     node = tree;
950     while (node != NULL) {
951         if ((node->type == XML_DOCUMENT_NODE) ||
952             (node->type == XML_HTML_DOCUMENT_NODE)) {
953             fprintf(ctxt->output, "/\n");
954         } else if (node->type == XML_ELEMENT_NODE) {
955             for (i = 0; i < indent; i++)
956                 fprintf(ctxt->output, "  ");
957             if ((node->ns) && (node->ns->prefix))
958                 fprintf(ctxt->output, "%s:", node->ns->prefix);
959             fprintf(ctxt->output, "%s\n", node->name);
960         } else {
961         }
962 
963         /*
964          * Browse the full subtree, deep first
965          */
966 
967         if ((node->type == XML_DOCUMENT_NODE) ||
968             (node->type == XML_HTML_DOCUMENT_NODE)) {
969             node = ((xmlDocPtr) node)->children;
970         } else if ((node->children != NULL)
971                    && (node->type != XML_ENTITY_REF_NODE)) {
972             /* deep first */
973             node = node->children;
974             indent++;
975         } else if ((node != tree) && (node->next != NULL)) {
976             /* then siblings */
977             node = node->next;
978         } else if (node != tree) {
979             /* go up to parents->next if needed */
980             while (node != tree) {
981                 if (node->parent != NULL) {
982                     node = node->parent;
983                     indent--;
984                 }
985                 if ((node != tree) && (node->next != NULL)) {
986                     node = node->next;
987                     break;
988                 }
989                 if (node->parent == NULL) {
990                     node = NULL;
991                     break;
992                 }
993                 if (node == tree) {
994                     node = NULL;
995                     break;
996                 }
997             }
998             /* exit condition */
999             if (node == tree)
1000                 node = NULL;
1001         } else
1002             node = NULL;
1003     }
1004     return (0);
1005 }
1006 
1007 /**
1008  * xmllintShellPwd:
1009  * @ctxt:  the shell context
1010  * @buffer:  the output buffer
1011  * @node:  a node
1012  * @node2:  unused
1013  *
1014  * Implements the XML shell function "pwd"
1015  * Show the full path from the root to the node, if needed building
1016  * thumblers when similar elements exists at a given ancestor level.
1017  * The output is compatible with XPath commands.
1018  *
1019  * Returns 0 or -1 in case of error
1020  */
1021 static int
xmllintShellPwd(xmllintShellCtxtPtr ctxt ATTRIBUTE_UNUSED,char * buffer,xmlNodePtr node,xmlNodePtr node2 ATTRIBUTE_UNUSED)1022 xmllintShellPwd(xmllintShellCtxtPtr ctxt ATTRIBUTE_UNUSED, char *buffer,
1023             xmlNodePtr node, xmlNodePtr node2 ATTRIBUTE_UNUSED)
1024 {
1025     xmlChar *path;
1026 
1027     if ((node == NULL) || (buffer == NULL))
1028         return (-1);
1029 
1030     path = xmlGetNodePath(node);
1031     if (path == NULL)
1032 	return (-1);
1033 
1034     /*
1035      * This test prevents buffer overflow, because this routine
1036      * is only called by xmllintShell, in which the second argument is
1037      * 500 chars long.
1038      * It is a dirty hack before a cleaner solution is found.
1039      * Documentation should mention that the second argument must
1040      * be at least 500 chars long, and could be stripped if too long.
1041      */
1042     snprintf(buffer, 499, "%s", path);
1043     buffer[499] = '0';
1044     xmlFree(path);
1045 
1046     return (0);
1047 }
1048 
1049 /**
1050  * xmllintShellReadline:
1051  * @prompt:  the prompt value
1052  *
1053  * Read a string
1054  *
1055  * Returns a pointer to it or NULL on EOF the caller is expected to
1056  *     free the returned string.
1057  */
1058 static char *
xmllintShellReadline(char * prompt)1059 xmllintShellReadline(char *prompt) {
1060     char buf[501];
1061     char *ret;
1062     int len;
1063 
1064 #ifdef HAVE_LIBREADLINE
1065     if (isatty(STDIN_FILENO)) {
1066         char *line_read;
1067 
1068         /* Get a line from the user. */
1069         line_read = readline (prompt);
1070 
1071 #ifdef HAVE_LIBHISTORY
1072         /* If the line has any text in it, save it on the history. */
1073         if (line_read && *line_read)
1074            add_history (line_read);
1075 #endif
1076 
1077         return (line_read);
1078     }
1079 #endif
1080 
1081     if (prompt != NULL)
1082        fprintf(stdout, "%s", prompt);
1083     fflush(stdout);
1084     if (!fgets(buf, 500, stdin))
1085         return(NULL);
1086     buf[500] = 0;
1087     len = strlen(buf);
1088     ret = (char *) malloc(len + 1);
1089     if (ret != NULL) {
1090        memcpy (ret, buf, len + 1);
1091     }
1092     return(ret);
1093 }
1094 
1095 /**
1096  * xmllintShell:
1097  * @doc:  the initial document
1098  * @filename:  the output buffer
1099  * @input:  the line reading function
1100  * @output:  the output FILE*, defaults to stdout if NULL
1101  *
1102  * Implements the XML shell
1103  * This allow to load, validate, view, modify and save a document
1104  * using a environment similar to a UNIX commandline.
1105  */
1106 void
xmllintShell(xmlDocPtr doc,const char * filename,FILE * output)1107 xmllintShell(xmlDocPtr doc, const char *filename, FILE * output)
1108 {
1109     char prompt[500] = "/ > ";
1110     char *cmdline = NULL, *cur;
1111     char command[100];
1112     char arg[400];
1113     int i;
1114     xmllintShellCtxtPtr ctxt;
1115 #ifdef LIBXML_XPATH_ENABLED
1116     xmlXPathObjectPtr list;
1117 #endif
1118 
1119     if (doc == NULL)
1120         return;
1121     if (filename == NULL)
1122         return;
1123     if (output == NULL)
1124         output = stdout;
1125     ctxt = (xmllintShellCtxtPtr) xmlMalloc(sizeof(xmllintShellCtxt));
1126     if (ctxt == NULL)
1127         return;
1128     ctxt->loaded = 0;
1129     ctxt->doc = doc;
1130     ctxt->output = output;
1131     ctxt->filename = (char *) xmlStrdup((xmlChar *) filename);
1132     ctxt->node = (xmlNodePtr) ctxt->doc;
1133 
1134 #ifdef LIBXML_XPATH_ENABLED
1135     ctxt->pctxt = xmlXPathNewContext(ctxt->doc);
1136     if (ctxt->pctxt == NULL) {
1137         xmlFree(ctxt);
1138         return;
1139     }
1140 #endif /* LIBXML_XPATH_ENABLED */
1141     while (1) {
1142         if (ctxt->node == (xmlNodePtr) ctxt->doc)
1143             snprintf(prompt, sizeof(prompt), "%s > ", "/");
1144         else if ((ctxt->node != NULL) && (ctxt->node->name) &&
1145                  (ctxt->node->ns) && (ctxt->node->ns->prefix))
1146             snprintf(prompt, sizeof(prompt), "%s:%s > ",
1147                      (ctxt->node->ns->prefix), ctxt->node->name);
1148         else if ((ctxt->node != NULL) && (ctxt->node->name))
1149             snprintf(prompt, sizeof(prompt), "%s > ", ctxt->node->name);
1150         else
1151             snprintf(prompt, sizeof(prompt), "? > ");
1152         prompt[sizeof(prompt) - 1] = 0;
1153 
1154         /*
1155          * Get a new command line
1156          */
1157         cmdline = xmllintShellReadline(prompt);
1158         if (cmdline == NULL)
1159             break;
1160 
1161         /*
1162          * Parse the command itself
1163          */
1164         cur = cmdline;
1165         while ((*cur == ' ') || (*cur == '\t'))
1166             cur++;
1167         i = 0;
1168         while ((*cur != ' ') && (*cur != '\t') &&
1169                (*cur != '\n') && (*cur != '\r')) {
1170             if (*cur == 0)
1171                 break;
1172             command[i++] = *cur++;
1173         }
1174         command[i] = 0;
1175         if (i == 0)
1176             continue;
1177 
1178         /*
1179          * Parse the argument
1180          */
1181         while ((*cur == ' ') || (*cur == '\t'))
1182             cur++;
1183         i = 0;
1184         while ((*cur != '\n') && (*cur != '\r') && (*cur != 0)) {
1185             if (*cur == 0)
1186                 break;
1187             arg[i++] = *cur++;
1188         }
1189         arg[i] = 0;
1190 
1191         /*
1192          * start interpreting the command
1193          */
1194         if (!strcmp(command, "exit"))
1195             break;
1196         if (!strcmp(command, "quit"))
1197             break;
1198         if (!strcmp(command, "bye"))
1199             break;
1200 		if (!strcmp(command, "help")) {
1201 		  fprintf(ctxt->output, "\tbase         display XML base of the node\n");
1202 		  fprintf(ctxt->output, "\tsetbase URI  change the XML base of the node\n");
1203 		  fprintf(ctxt->output, "\tbye          leave shell\n");
1204 		  fprintf(ctxt->output, "\tcat [node]   display node or current node\n");
1205 		  fprintf(ctxt->output, "\tcd [path]    change directory to path or to root\n");
1206 		  fprintf(ctxt->output, "\tdir [path]   dumps information about the node (namespace, attributes, content)\n");
1207 		  fprintf(ctxt->output, "\tdu [path]    show the structure of the subtree under path or the current node\n");
1208 		  fprintf(ctxt->output, "\texit         leave shell\n");
1209 		  fprintf(ctxt->output, "\thelp         display this help\n");
1210 		  fprintf(ctxt->output, "\tfree         display memory usage\n");
1211 		  fprintf(ctxt->output, "\tload [name]  load a new document with name\n");
1212 		  fprintf(ctxt->output, "\tls [path]    list contents of path or the current directory\n");
1213 		  fprintf(ctxt->output, "\tset xml_fragment replace the current node content with the fragment parsed in context\n");
1214 #ifdef LIBXML_XPATH_ENABLED
1215 		  fprintf(ctxt->output, "\txpath expr   evaluate the XPath expression in that context and print the result\n");
1216 		  fprintf(ctxt->output, "\tsetns nsreg  register a namespace to a prefix in the XPath evaluation context\n");
1217 		  fprintf(ctxt->output, "\t             format for nsreg is: prefix=[nsuri] (i.e. prefix= unsets a prefix)\n");
1218 		  fprintf(ctxt->output, "\tsetrootns    register all namespace found on the root element\n");
1219 		  fprintf(ctxt->output, "\t             the default namespace if any uses 'defaultns' prefix\n");
1220 #endif /* LIBXML_XPATH_ENABLED */
1221 		  fprintf(ctxt->output, "\tpwd          display current working directory\n");
1222 		  fprintf(ctxt->output, "\twhereis      display absolute path of [path] or current working directory\n");
1223 		  fprintf(ctxt->output, "\tquit         leave shell\n");
1224 #ifdef LIBXML_OUTPUT_ENABLED
1225 		  fprintf(ctxt->output, "\tsave [name]  save this document to name or the original name\n");
1226 		  fprintf(ctxt->output, "\twrite [name] write the current node to the filename\n");
1227 #endif /* LIBXML_OUTPUT_ENABLED */
1228 #ifdef LIBXML_VALID_ENABLED
1229 		  fprintf(ctxt->output, "\tvalidate     check the document for errors\n");
1230 #endif /* LIBXML_VALID_ENABLED */
1231 #ifdef LIBXML_SCHEMAS_ENABLED
1232 		  fprintf(ctxt->output, "\trelaxng rng  validate the document against the Relax-NG schemas\n");
1233 #endif
1234 		  fprintf(ctxt->output, "\tgrep string  search for a string in the subtree\n");
1235 #ifdef LIBXML_VALID_ENABLED
1236         } else if (!strcmp(command, "validate")) {
1237             xmllintShellValidate(ctxt, arg, NULL, NULL);
1238 #endif /* LIBXML_VALID_ENABLED */
1239         } else if (!strcmp(command, "load")) {
1240             xmllintShellLoad(ctxt, arg, NULL, NULL);
1241 #ifdef LIBXML_SCHEMAS_ENABLED
1242         } else if (!strcmp(command, "relaxng")) {
1243             xmllintShellRNGValidate(ctxt, arg, NULL, NULL);
1244 #endif
1245 #ifdef LIBXML_OUTPUT_ENABLED
1246         } else if (!strcmp(command, "save")) {
1247             xmllintShellSave(ctxt, arg, NULL, NULL);
1248         } else if (!strcmp(command, "write")) {
1249 	    if (arg[0] == 0)
1250 		fprintf(ctxt->output,
1251                         "Write command requires a filename argument\n");
1252 	    else
1253 		xmllintShellWrite(ctxt, arg, ctxt->node, NULL);
1254 #endif /* LIBXML_OUTPUT_ENABLED */
1255         } else if (!strcmp(command, "grep")) {
1256             xmllintShellGrep(ctxt, arg, ctxt->node, NULL);
1257         } else if (!strcmp(command, "pwd")) {
1258             char dir[500];
1259 
1260             if (!xmllintShellPwd(ctxt, dir, ctxt->node, NULL))
1261                 fprintf(ctxt->output, "%s\n", dir);
1262         } else if (!strcmp(command, "du")) {
1263             if (arg[0] == 0) {
1264                 xmllintShellDu(ctxt, NULL, ctxt->node, NULL);
1265             } else {
1266 #ifdef LIBXML_XPATH_ENABLED
1267                 ctxt->pctxt->node = ctxt->node;
1268                 list = xmlXPathEval((xmlChar *) arg, ctxt->pctxt);
1269                 if (list != NULL) {
1270                     switch (list->type) {
1271                         case XPATH_UNDEFINED:
1272                             fprintf(ctxt->output,
1273                                             "%s: no such node\n", arg);
1274                             break;
1275                         case XPATH_NODESET:{
1276                             int indx;
1277 
1278                             if (list->nodesetval == NULL)
1279                                 break;
1280 
1281                             for (indx = 0;
1282                                  indx < list->nodesetval->nodeNr;
1283                                  indx++)
1284                                 xmllintShellDu(ctxt, NULL,
1285                                            list->nodesetval->
1286                                            nodeTab[indx], NULL);
1287                             break;
1288                         }
1289                         case XPATH_BOOLEAN:
1290                             fprintf(ctxt->output,
1291                                             "%s is a Boolean\n", arg);
1292                             break;
1293                         case XPATH_NUMBER:
1294                             fprintf(ctxt->output,
1295                                             "%s is a number\n", arg);
1296                             break;
1297                         case XPATH_STRING:
1298                             fprintf(ctxt->output,
1299                                             "%s is a string\n", arg);
1300                             break;
1301                         case XPATH_USERS:
1302                             fprintf(ctxt->output,
1303                                             "%s is user-defined\n", arg);
1304                             break;
1305                         case XPATH_XSLT_TREE:
1306                             fprintf(ctxt->output,
1307                                             "%s is an XSLT value tree\n",
1308                                             arg);
1309                             break;
1310                     }
1311                     xmlXPathFreeObject(list);
1312                 } else {
1313                     fprintf(ctxt->output,
1314                                     "%s: no such node\n", arg);
1315                 }
1316                 ctxt->pctxt->node = NULL;
1317 #endif /* LIBXML_XPATH_ENABLED */
1318             }
1319         } else if (!strcmp(command, "base")) {
1320             xmllintShellBase(ctxt, NULL, ctxt->node, NULL);
1321         } else if (!strcmp(command, "set")) {
1322 	    xmllintShellSetContent(ctxt, arg, ctxt->node, NULL);
1323 #ifdef LIBXML_XPATH_ENABLED
1324         } else if (!strcmp(command, "setns")) {
1325             if (arg[0] == 0) {
1326 		fprintf(ctxt->output,
1327 				"setns: prefix=[nsuri] required\n");
1328             } else {
1329                 xmllintShellRegisterNamespace(ctxt, arg, NULL, NULL);
1330             }
1331         } else if (!strcmp(command, "setrootns")) {
1332 	    xmlNodePtr root;
1333 
1334 	    root = xmlDocGetRootElement(ctxt->doc);
1335 	    xmllintShellRegisterRootNamespaces(ctxt, NULL, root, NULL);
1336 #ifdef LIBXML_DEBUG_ENABLED
1337         } else if (!strcmp(command, "xpath")) {
1338             if (arg[0] == 0) {
1339 		fprintf(ctxt->output,
1340 				"xpath: expression required\n");
1341 	    } else {
1342                 ctxt->pctxt->node = ctxt->node;
1343                 list = xmlXPathEval((xmlChar *) arg, ctxt->pctxt);
1344 		xmlXPathDebugDumpObject(ctxt->output, list, 0);
1345 		xmlXPathFreeObject(list);
1346 	    }
1347 #endif /* LIBXML_DEBUG_ENABLED */
1348 #endif /* LIBXML_XPATH_ENABLED */
1349         } else if (!strcmp(command, "setbase")) {
1350             xmllintShellSetBase(ctxt, arg, ctxt->node, NULL);
1351         } else if ((!strcmp(command, "ls")) || (!strcmp(command, "dir"))) {
1352             int dir = (!strcmp(command, "dir"));
1353 
1354             if (arg[0] == 0) {
1355                 if (dir)
1356                     xmllintShellDir(ctxt, NULL, ctxt->node, NULL);
1357                 else
1358                     xmllintShellList(ctxt, NULL, ctxt->node, NULL);
1359             } else {
1360 #ifdef LIBXML_XPATH_ENABLED
1361                 ctxt->pctxt->node = ctxt->node;
1362                 list = xmlXPathEval((xmlChar *) arg, ctxt->pctxt);
1363                 if (list != NULL) {
1364                     switch (list->type) {
1365                         case XPATH_UNDEFINED:
1366                             fprintf(ctxt->output,
1367                                             "%s: no such node\n", arg);
1368                             break;
1369                         case XPATH_NODESET:{
1370                                 int indx;
1371 
1372 				if (list->nodesetval == NULL)
1373 				    break;
1374 
1375                                 for (indx = 0;
1376                                      indx < list->nodesetval->nodeNr;
1377                                      indx++) {
1378                                     if (dir)
1379                                         xmllintShellDir(ctxt, NULL,
1380                                                     list->nodesetval->
1381                                                     nodeTab[indx], NULL);
1382                                     else
1383                                         xmllintShellList(ctxt, NULL,
1384                                                      list->nodesetval->
1385                                                      nodeTab[indx], NULL);
1386                                 }
1387                                 break;
1388                             }
1389                         case XPATH_BOOLEAN:
1390                             fprintf(ctxt->output,
1391                                             "%s is a Boolean\n", arg);
1392                             break;
1393                         case XPATH_NUMBER:
1394                             fprintf(ctxt->output,
1395                                             "%s is a number\n", arg);
1396                             break;
1397                         case XPATH_STRING:
1398                             fprintf(ctxt->output,
1399                                             "%s is a string\n", arg);
1400                             break;
1401                         case XPATH_USERS:
1402                             fprintf(ctxt->output,
1403                                             "%s is user-defined\n", arg);
1404                             break;
1405                         case XPATH_XSLT_TREE:
1406                             fprintf(ctxt->output,
1407                                             "%s is an XSLT value tree\n",
1408                                             arg);
1409                             break;
1410                     }
1411                     xmlXPathFreeObject(list);
1412                 } else {
1413                     fprintf(ctxt->output,
1414                                     "%s: no such node\n", arg);
1415                 }
1416                 ctxt->pctxt->node = NULL;
1417 #endif /* LIBXML_XPATH_ENABLED */
1418             }
1419         } else if (!strcmp(command, "whereis")) {
1420             char dir[500];
1421 
1422             if (arg[0] == 0) {
1423                 if (!xmllintShellPwd(ctxt, dir, ctxt->node, NULL))
1424                     fprintf(ctxt->output, "%s\n", dir);
1425             } else {
1426 #ifdef LIBXML_XPATH_ENABLED
1427                 ctxt->pctxt->node = ctxt->node;
1428                 list = xmlXPathEval((xmlChar *) arg, ctxt->pctxt);
1429                 if (list != NULL) {
1430                     switch (list->type) {
1431                         case XPATH_UNDEFINED:
1432                             fprintf(ctxt->output,
1433                                             "%s: no such node\n", arg);
1434                             break;
1435                         case XPATH_NODESET:{
1436                                 int indx;
1437 
1438 				if (list->nodesetval == NULL)
1439 				    break;
1440 
1441                                 for (indx = 0;
1442                                      indx < list->nodesetval->nodeNr;
1443                                      indx++) {
1444                                     if (!xmllintShellPwd(ctxt, dir, list->nodesetval->
1445                                                      nodeTab[indx], NULL))
1446                                         fprintf(ctxt->output, "%s\n", dir);
1447                                 }
1448                                 break;
1449                             }
1450                         case XPATH_BOOLEAN:
1451                             fprintf(ctxt->output,
1452                                             "%s is a Boolean\n", arg);
1453                             break;
1454                         case XPATH_NUMBER:
1455                             fprintf(ctxt->output,
1456                                             "%s is a number\n", arg);
1457                             break;
1458                         case XPATH_STRING:
1459                             fprintf(ctxt->output,
1460                                             "%s is a string\n", arg);
1461                             break;
1462                         case XPATH_USERS:
1463                             fprintf(ctxt->output,
1464                                             "%s is user-defined\n", arg);
1465                             break;
1466                         case XPATH_XSLT_TREE:
1467                             fprintf(ctxt->output,
1468                                             "%s is an XSLT value tree\n",
1469                                             arg);
1470                             break;
1471                     }
1472                     xmlXPathFreeObject(list);
1473                 } else {
1474                     fprintf(ctxt->output,
1475                                     "%s: no such node\n", arg);
1476                 }
1477                 ctxt->pctxt->node = NULL;
1478 #endif /* LIBXML_XPATH_ENABLED */
1479             }
1480         } else if (!strcmp(command, "cd")) {
1481             if (arg[0] == 0) {
1482                 ctxt->node = (xmlNodePtr) ctxt->doc;
1483             } else {
1484 #ifdef LIBXML_XPATH_ENABLED
1485                 int l;
1486 
1487                 ctxt->pctxt->node = ctxt->node;
1488 		l = strlen(arg);
1489 		if ((l >= 2) && (arg[l - 1] == '/'))
1490 		    arg[l - 1] = 0;
1491                 list = xmlXPathEval((xmlChar *) arg, ctxt->pctxt);
1492                 if (list != NULL) {
1493                     switch (list->type) {
1494                         case XPATH_UNDEFINED:
1495                             fprintf(ctxt->output,
1496                                             "%s: no such node\n", arg);
1497                             break;
1498                         case XPATH_NODESET:
1499                             if (list->nodesetval != NULL) {
1500 				if (list->nodesetval->nodeNr == 1) {
1501 				    ctxt->node = list->nodesetval->nodeTab[0];
1502 				    if ((ctxt->node != NULL) &&
1503 				        (ctxt->node->type ==
1504 					 XML_NAMESPACE_DECL)) {
1505 					fprintf(ctxt->output,
1506 						    "cannot cd to namespace\n");
1507 					ctxt->node = NULL;
1508 				    }
1509 				} else
1510 				    fprintf(ctxt->output,
1511 						    "%s is a %d Node Set\n",
1512 						    arg,
1513 						    list->nodesetval->nodeNr);
1514                             } else
1515                                 fprintf(ctxt->output,
1516                                                 "%s is an empty Node Set\n",
1517                                                 arg);
1518                             break;
1519                         case XPATH_BOOLEAN:
1520                             fprintf(ctxt->output,
1521                                             "%s is a Boolean\n", arg);
1522                             break;
1523                         case XPATH_NUMBER:
1524                             fprintf(ctxt->output,
1525                                             "%s is a number\n", arg);
1526                             break;
1527                         case XPATH_STRING:
1528                             fprintf(ctxt->output,
1529                                             "%s is a string\n", arg);
1530                             break;
1531                         case XPATH_USERS:
1532                             fprintf(ctxt->output,
1533                                             "%s is user-defined\n", arg);
1534                             break;
1535                         case XPATH_XSLT_TREE:
1536                             fprintf(ctxt->output,
1537                                             "%s is an XSLT value tree\n",
1538                                             arg);
1539                             break;
1540                     }
1541                     xmlXPathFreeObject(list);
1542                 } else {
1543                     fprintf(ctxt->output,
1544                                     "%s: no such node\n", arg);
1545                 }
1546                 ctxt->pctxt->node = NULL;
1547 #endif /* LIBXML_XPATH_ENABLED */
1548             }
1549 #ifdef LIBXML_OUTPUT_ENABLED
1550         } else if (!strcmp(command, "cat")) {
1551             if (arg[0] == 0) {
1552                 xmllintShellCat(ctxt, NULL, ctxt->node, NULL);
1553             } else {
1554 #ifdef LIBXML_XPATH_ENABLED
1555                 ctxt->pctxt->node = ctxt->node;
1556                 list = xmlXPathEval((xmlChar *) arg, ctxt->pctxt);
1557                 if (list != NULL) {
1558                     switch (list->type) {
1559                         case XPATH_UNDEFINED:
1560                             fprintf(ctxt->output,
1561                                             "%s: no such node\n", arg);
1562                             break;
1563                         case XPATH_NODESET:{
1564                                 int indx;
1565 
1566 				if (list->nodesetval == NULL)
1567 				    break;
1568 
1569                                 for (indx = 0;
1570                                      indx < list->nodesetval->nodeNr;
1571                                      indx++) {
1572                                     if (i > 0)
1573                                         fprintf(ctxt->output, " -------\n");
1574                                     xmllintShellCat(ctxt, NULL,
1575                                                 list->nodesetval->
1576                                                 nodeTab[indx], NULL);
1577                                 }
1578                                 break;
1579                             }
1580                         case XPATH_BOOLEAN:
1581                             fprintf(ctxt->output,
1582                                             "%s is a Boolean\n", arg);
1583                             break;
1584                         case XPATH_NUMBER:
1585                             fprintf(ctxt->output,
1586                                             "%s is a number\n", arg);
1587                             break;
1588                         case XPATH_STRING:
1589                             fprintf(ctxt->output,
1590                                             "%s is a string\n", arg);
1591                             break;
1592                         case XPATH_USERS:
1593                             fprintf(ctxt->output,
1594                                             "%s is user-defined\n", arg);
1595                             break;
1596                         case XPATH_XSLT_TREE:
1597                             fprintf(ctxt->output,
1598                                             "%s is an XSLT value tree\n",
1599                                             arg);
1600                             break;
1601                     }
1602                     xmlXPathFreeObject(list);
1603                 } else {
1604                     fprintf(ctxt->output,
1605                                     "%s: no such node\n", arg);
1606                 }
1607                 ctxt->pctxt->node = NULL;
1608 #endif /* LIBXML_XPATH_ENABLED */
1609             }
1610 #endif /* LIBXML_OUTPUT_ENABLED */
1611         } else {
1612             fprintf(ctxt->output,
1613                             "Unknown command %s\n", command);
1614         }
1615         free(cmdline);          /* not xmlFree here ! */
1616 	cmdline = NULL;
1617     }
1618 #ifdef LIBXML_XPATH_ENABLED
1619     xmlXPathFreeContext(ctxt->pctxt);
1620 #endif /* LIBXML_XPATH_ENABLED */
1621     if (ctxt->loaded) {
1622         xmlFreeDoc(ctxt->doc);
1623     }
1624     if (ctxt->filename != NULL)
1625         xmlFree(ctxt->filename);
1626     xmlFree(ctxt);
1627     if (cmdline != NULL)
1628         free(cmdline);          /* not xmlFree here ! */
1629 }
1630