debugXML.c revision 7a985a18c2fee0aa9b490792dd990b75506e3740
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#ifdef HAVE_STDLIB_H
16#include <stdlib.h>
17#endif
18#ifdef HAVE_STRING_H
19#include <string.h>
20#endif
21#include <libxml/xmlmemory.h>
22#include <libxml/tree.h>
23#include <libxml/parser.h>
24#include <libxml/parserInternals.h>
25#include <libxml/valid.h>
26#include <libxml/debugXML.h>
27#include <libxml/HTMLtree.h>
28#include <libxml/HTMLparser.h>
29#include <libxml/xmlerror.h>
30#include <libxml/globals.h>
31#include <libxml/xpathInternals.h>
32#include <libxml/uri.h>
33
34/**
35 * xmlDebugDumpString:
36 * @output:  the FILE * for the output
37 * @str:  the string
38 *
39 * Dumps informations about the string, shorten it if necessary
40 */
41void
42xmlDebugDumpString(FILE * output, const xmlChar * str)
43{
44    int i;
45
46    if (output == NULL)
47	output = stdout;
48    if (str == NULL) {
49        fprintf(output, "(NULL)");
50        return;
51    }
52    for (i = 0; i < 40; i++)
53        if (str[i] == 0)
54            return;
55        else if (IS_BLANK(str[i]))
56            fputc(' ', output);
57        else if (str[i] >= 0x80)
58            fprintf(output, "#%X", str[i]);
59        else
60            fputc(str[i], output);
61    fprintf(output, "...");
62}
63
64static void
65xmlDebugDumpDtdNode(FILE *output, xmlDtdPtr dtd, int depth) {
66    int i;
67    char shift[100];
68
69    for (i = 0;((i < depth) && (i < 25));i++)
70        shift[2 * i] = shift[2 * i + 1] = ' ';
71    shift[2 * i] = shift[2 * i + 1] = 0;
72
73    fprintf(output, shift);
74
75    if (dtd == NULL) {
76	fprintf(output, "DTD node is NULL\n");
77	return;
78    }
79
80    if (dtd->type != XML_DTD_NODE) {
81	fprintf(output, "PBM: not a DTD\n");
82	return;
83    }
84    if (dtd->name != NULL)
85	fprintf(output, "DTD(%s)", dtd->name);
86    else
87	fprintf(output, "DTD");
88    if (dtd->ExternalID != NULL)
89	fprintf(output, ", PUBLIC %s", dtd->ExternalID);
90    if (dtd->SystemID != NULL)
91	fprintf(output, ", SYSTEM %s", dtd->SystemID);
92    fprintf(output, "\n");
93    /*
94     * Do a bit of checking
95     */
96    if (dtd->parent == NULL)
97	fprintf(output, "PBM: DTD has no parent\n");
98    if (dtd->doc == NULL)
99	fprintf(output, "PBM: DTD has no doc\n");
100    if ((dtd->parent != NULL) && (dtd->doc != dtd->parent->doc))
101	fprintf(output, "PBM: DTD doc differs from parent's one\n");
102    if (dtd->prev == NULL) {
103	if ((dtd->parent != NULL) && (dtd->parent->children != (xmlNodePtr)dtd))
104	    fprintf(output, "PBM: DTD has no prev and not first of list\n");
105    } else {
106	if (dtd->prev->next != (xmlNodePtr) dtd)
107	    fprintf(output, "PBM: DTD prev->next : back link wrong\n");
108    }
109    if (dtd->next == NULL) {
110	if ((dtd->parent != NULL) && (dtd->parent->last != (xmlNodePtr) dtd))
111	    fprintf(output, "PBM: DTD has no next and not last of list\n");
112    } else {
113	if (dtd->next->prev != (xmlNodePtr) dtd)
114	    fprintf(output, "PBM: DTD next->prev : forward link wrong\n");
115    }
116}
117
118static void
119xmlDebugDumpAttrDecl(FILE *output, xmlAttributePtr attr, int depth) {
120    int i;
121    char shift[100];
122
123    for (i = 0;((i < depth) && (i < 25));i++)
124        shift[2 * i] = shift[2 * i + 1] = ' ';
125    shift[2 * i] = shift[2 * i + 1] = 0;
126
127    fprintf(output, shift);
128
129    if (attr == NULL) {
130	fprintf(output, "Attribute declaration is NULL\n");
131	return;
132    }
133    if (attr->type != XML_ATTRIBUTE_DECL) {
134	fprintf(output, "PBM: not a Attr\n");
135	return;
136    }
137    if (attr->name != NULL)
138	fprintf(output, "ATTRDECL(%s)", attr->name);
139    else
140	fprintf(output, "PBM ATTRDECL noname!!!");
141    if (attr->elem != NULL)
142	fprintf(output, " for %s", attr->elem);
143    else
144	fprintf(output, " PBM noelem!!!");
145    switch (attr->atype) {
146        case XML_ATTRIBUTE_CDATA:
147	    fprintf(output, " CDATA");
148	    break;
149        case XML_ATTRIBUTE_ID:
150	    fprintf(output, " ID");
151	    break;
152        case XML_ATTRIBUTE_IDREF:
153	    fprintf(output, " IDREF");
154	    break;
155        case XML_ATTRIBUTE_IDREFS:
156	    fprintf(output, " IDREFS");
157	    break;
158        case XML_ATTRIBUTE_ENTITY:
159	    fprintf(output, " ENTITY");
160	    break;
161        case XML_ATTRIBUTE_ENTITIES:
162	    fprintf(output, " ENTITIES");
163	    break;
164        case XML_ATTRIBUTE_NMTOKEN:
165	    fprintf(output, " NMTOKEN");
166	    break;
167        case XML_ATTRIBUTE_NMTOKENS:
168	    fprintf(output, " NMTOKENS");
169	    break;
170        case XML_ATTRIBUTE_ENUMERATION:
171	    fprintf(output, " ENUMERATION");
172	    break;
173        case XML_ATTRIBUTE_NOTATION:
174	    fprintf(output, " NOTATION ");
175	    break;
176    }
177    if (attr->tree != NULL) {
178	int indx;
179	xmlEnumerationPtr cur = attr->tree;
180
181	for (indx = 0;indx < 5; indx++) {
182	    if (indx != 0)
183		fprintf(output, "|%s", cur->name);
184	    else
185		fprintf(output, " (%s", cur->name);
186	    cur = cur->next;
187	    if (cur == NULL) break;
188	}
189	if (cur == NULL)
190	    fprintf(output, ")");
191	else
192	    fprintf(output, "...)");
193    }
194    switch (attr->def) {
195        case XML_ATTRIBUTE_NONE:
196	    break;
197        case XML_ATTRIBUTE_REQUIRED:
198	    fprintf(output, " REQUIRED");
199	    break;
200        case XML_ATTRIBUTE_IMPLIED:
201	    fprintf(output, " IMPLIED");
202	    break;
203        case XML_ATTRIBUTE_FIXED:
204	    fprintf(output, " FIXED");
205	    break;
206    }
207    if (attr->defaultValue != NULL) {
208	fprintf(output, "\"");
209	xmlDebugDumpString(output, attr->defaultValue);
210	fprintf(output, "\"");
211    }
212    fprintf(output, "\n");
213
214    /*
215     * Do a bit of checking
216     */
217    if (attr->parent == NULL)
218	fprintf(output, "PBM: Attr has no parent\n");
219    if (attr->doc == NULL)
220	fprintf(output, "PBM: Attr has no doc\n");
221    if ((attr->parent != NULL) && (attr->doc != attr->parent->doc))
222	fprintf(output, "PBM: Attr doc differs from parent's one\n");
223    if (attr->prev == NULL) {
224	if ((attr->parent != NULL) && (attr->parent->children != (xmlNodePtr)attr))
225	    fprintf(output, "PBM: Attr has no prev and not first of list\n");
226    } else {
227	if (attr->prev->next != (xmlNodePtr) attr)
228	    fprintf(output, "PBM: Attr prev->next : back link wrong\n");
229    }
230    if (attr->next == NULL) {
231	if ((attr->parent != NULL) && (attr->parent->last != (xmlNodePtr) attr))
232	    fprintf(output, "PBM: Attr has no next and not last of list\n");
233    } else {
234	if (attr->next->prev != (xmlNodePtr) attr)
235	    fprintf(output, "PBM: Attr next->prev : forward link wrong\n");
236    }
237}
238
239static void
240xmlDebugDumpElemDecl(FILE *output, xmlElementPtr elem, int depth) {
241    int i;
242    char shift[100];
243
244    for (i = 0;((i < depth) && (i < 25));i++)
245        shift[2 * i] = shift[2 * i + 1] = ' ';
246    shift[2 * i] = shift[2 * i + 1] = 0;
247
248    fprintf(output, shift);
249
250    if (elem == NULL) {
251	fprintf(output, "Element declaration is NULL\n");
252	return;
253    }
254    if (elem->type != XML_ELEMENT_DECL) {
255	fprintf(output, "PBM: not a Elem\n");
256	return;
257    }
258    if (elem->name != NULL) {
259	fprintf(output, "ELEMDECL(");
260	xmlDebugDumpString(output, elem->name);
261	fprintf(output, ")");
262    } else
263	fprintf(output, "PBM ELEMDECL noname!!!");
264    switch (elem->etype) {
265	case XML_ELEMENT_TYPE_UNDEFINED:
266	    fprintf(output, ", UNDEFINED");
267	    break;
268	case XML_ELEMENT_TYPE_EMPTY:
269	    fprintf(output, ", EMPTY");
270	    break;
271	case XML_ELEMENT_TYPE_ANY:
272	    fprintf(output, ", ANY");
273	    break;
274	case XML_ELEMENT_TYPE_MIXED:
275	    fprintf(output, ", MIXED ");
276	    break;
277	case XML_ELEMENT_TYPE_ELEMENT:
278	    fprintf(output, ", MIXED ");
279	    break;
280    }
281    if ((elem->type != XML_ELEMENT_NODE) &&
282	(elem->content != NULL)) {
283	char buf[5001];
284
285	buf[0] = 0;
286	xmlSnprintfElementContent(buf, 5000, elem->content, 1);
287	buf[5000] = 0;
288	fprintf(output, "%s", buf);
289    }
290    fprintf(output, "\n");
291
292    /*
293     * Do a bit of checking
294     */
295    if (elem->parent == NULL)
296	fprintf(output, "PBM: Elem has no parent\n");
297    if (elem->doc == NULL)
298	fprintf(output, "PBM: Elem has no doc\n");
299    if ((elem->parent != NULL) && (elem->doc != elem->parent->doc))
300	fprintf(output, "PBM: Elem doc differs from parent's one\n");
301    if (elem->prev == NULL) {
302	if ((elem->parent != NULL) && (elem->parent->children != (xmlNodePtr)elem))
303	    fprintf(output, "PBM: Elem has no prev and not first of list\n");
304    } else {
305	if (elem->prev->next != (xmlNodePtr) elem)
306	    fprintf(output, "PBM: Elem prev->next : back link wrong\n");
307    }
308    if (elem->next == NULL) {
309	if ((elem->parent != NULL) && (elem->parent->last != (xmlNodePtr) elem))
310	    fprintf(output, "PBM: Elem has no next and not last of list\n");
311    } else {
312	if (elem->next->prev != (xmlNodePtr) elem)
313	    fprintf(output, "PBM: Elem next->prev : forward link wrong\n");
314    }
315}
316
317static void
318xmlDebugDumpEntityDecl(FILE *output, xmlEntityPtr ent, int depth) {
319    int i;
320    char shift[100];
321
322    for (i = 0;((i < depth) && (i < 25));i++)
323        shift[2 * i] = shift[2 * i + 1] = ' ';
324    shift[2 * i] = shift[2 * i + 1] = 0;
325
326    fprintf(output, shift);
327
328    if (ent == NULL) {
329	fprintf(output, "Entity declaration is NULL\n");
330	return;
331    }
332    if (ent->type != XML_ENTITY_DECL) {
333	fprintf(output, "PBM: not a Entity decl\n");
334	return;
335    }
336    if (ent->name != NULL) {
337	fprintf(output, "ENTITYDECL(");
338	xmlDebugDumpString(output, ent->name);
339	fprintf(output, ")");
340    } else
341	fprintf(output, "PBM ENTITYDECL noname!!!");
342    switch (ent->etype) {
343	case XML_INTERNAL_GENERAL_ENTITY:
344	    fprintf(output, ", internal\n");
345	    break;
346	case XML_EXTERNAL_GENERAL_PARSED_ENTITY:
347	    fprintf(output, ", external parsed\n");
348	    break;
349	case XML_EXTERNAL_GENERAL_UNPARSED_ENTITY:
350	    fprintf(output, ", unparsed\n");
351	    break;
352	case XML_INTERNAL_PARAMETER_ENTITY:
353	    fprintf(output, ", parameter\n");
354	    break;
355	case XML_EXTERNAL_PARAMETER_ENTITY:
356	    fprintf(output, ", external parameter\n");
357	    break;
358	case XML_INTERNAL_PREDEFINED_ENTITY:
359	    fprintf(output, ", predefined\n");
360	    break;
361    }
362    if (ent->ExternalID) {
363        fprintf(output, shift);
364        fprintf(output, " ExternalID=%s\n", ent->ExternalID);
365    }
366    if (ent->SystemID) {
367        fprintf(output, shift);
368        fprintf(output, " SystemID=%s\n", ent->SystemID);
369    }
370    if (ent->URI != NULL) {
371        fprintf(output, shift);
372        fprintf(output, " URI=%s\n", ent->URI);
373    }
374    if (ent->content) {
375        fprintf(output, shift);
376	fprintf(output, " content=");
377	xmlDebugDumpString(output, ent->content);
378	fprintf(output, "\n");
379    }
380
381    /*
382     * Do a bit of checking
383     */
384    if (ent->parent == NULL)
385	fprintf(output, "PBM: Ent has no parent\n");
386    if (ent->doc == NULL)
387	fprintf(output, "PBM: Ent has no doc\n");
388    if ((ent->parent != NULL) && (ent->doc != ent->parent->doc))
389	fprintf(output, "PBM: Ent doc differs from parent's one\n");
390    if (ent->prev == NULL) {
391	if ((ent->parent != NULL) && (ent->parent->children != (xmlNodePtr)ent))
392	    fprintf(output, "PBM: Ent has no prev and not first of list\n");
393    } else {
394	if (ent->prev->next != (xmlNodePtr) ent)
395	    fprintf(output, "PBM: Ent prev->next : back link wrong\n");
396    }
397    if (ent->next == NULL) {
398	if ((ent->parent != NULL) && (ent->parent->last != (xmlNodePtr) ent))
399	    fprintf(output, "PBM: Ent has no next and not last of list\n");
400    } else {
401	if (ent->next->prev != (xmlNodePtr) ent)
402	    fprintf(output, "PBM: Ent next->prev : forward link wrong\n");
403    }
404}
405
406static void
407xmlDebugDumpNamespace(FILE *output, xmlNsPtr ns, int depth) {
408    int i;
409    char shift[100];
410
411    for (i = 0;((i < depth) && (i < 25));i++)
412        shift[2 * i] = shift[2 * i + 1] = ' ';
413    shift[2 * i] = shift[2 * i + 1] = 0;
414
415    fprintf(output, shift);
416
417    if (ns == NULL) {
418	fprintf(output, "namespace node is NULL\n");
419	return;
420    }
421    if (ns->type != XML_NAMESPACE_DECL) {
422        fprintf(output, "invalid namespace node %d\n", ns->type);
423	return;
424    }
425    if (ns->href == NULL) {
426	if (ns->prefix != NULL)
427	    fprintf(output, "incomplete namespace %s href=NULL\n", ns->prefix);
428	else
429	    fprintf(output, "incomplete default namespace href=NULL\n");
430    } else {
431	if (ns->prefix != NULL)
432	    fprintf(output, "namespace %s href=", ns->prefix);
433	else
434	    fprintf(output, "default namespace href=");
435
436	xmlDebugDumpString(output, ns->href);
437	fprintf(output, "\n");
438    }
439}
440
441static void
442xmlDebugDumpNamespaceList(FILE *output, xmlNsPtr ns, int depth) {
443    while (ns != NULL) {
444        xmlDebugDumpNamespace(output, ns, depth);
445	ns = ns->next;
446    }
447}
448
449static void
450xmlDebugDumpEntity(FILE *output, xmlEntityPtr ent, int depth) {
451    int i;
452    char shift[100];
453
454    for (i = 0;((i < depth) && (i < 25));i++)
455        shift[2 * i] = shift[2 * i + 1] = ' ';
456    shift[2 * i] = shift[2 * i + 1] = 0;
457
458    fprintf(output, shift);
459
460    if (ent == NULL) {
461	fprintf(output, "Entity is NULL\n");
462	return;
463    }
464    switch (ent->etype) {
465        case XML_INTERNAL_GENERAL_ENTITY:
466	    fprintf(output, "INTERNAL_GENERAL_ENTITY ");
467	    break;
468        case XML_EXTERNAL_GENERAL_PARSED_ENTITY:
469	    fprintf(output, "EXTERNAL_GENERAL_PARSED_ENTITY ");
470	    break;
471        case XML_EXTERNAL_GENERAL_UNPARSED_ENTITY:
472	    fprintf(output, "EXTERNAL_GENERAL_UNPARSED_ENTITY ");
473	    break;
474        case XML_INTERNAL_PARAMETER_ENTITY:
475	    fprintf(output, "INTERNAL_PARAMETER_ENTITY ");
476	    break;
477        case XML_EXTERNAL_PARAMETER_ENTITY:
478	    fprintf(output, "EXTERNAL_PARAMETER_ENTITY ");
479	    break;
480	default:
481	    fprintf(output, "ENTITY_%d ! ", ent->etype);
482    }
483    fprintf(output, "%s\n", ent->name);
484    if (ent->ExternalID) {
485        fprintf(output, shift);
486        fprintf(output, "ExternalID=%s\n", ent->ExternalID);
487    }
488    if (ent->SystemID) {
489        fprintf(output, shift);
490        fprintf(output, "SystemID=%s\n", ent->SystemID);
491    }
492    if (ent->URI) {
493        fprintf(output, shift);
494        fprintf(output, "URI=%s\n", ent->URI);
495    }
496    if (ent->content) {
497        fprintf(output, shift);
498	fprintf(output, "content=");
499	xmlDebugDumpString(output, ent->content);
500	fprintf(output, "\n");
501    }
502}
503
504/**
505 * xmlDebugDumpAttr:
506 * @output:  the FILE * for the output
507 * @attr:  the attribute
508 * @depth:  the indentation level.
509 *
510 * Dumps debug information for the attribute
511 */
512void
513xmlDebugDumpAttr(FILE *output, xmlAttrPtr attr, int depth) {
514    int i;
515    char shift[100];
516
517    for (i = 0;((i < depth) && (i < 25));i++)
518        shift[2 * i] = shift[2 * i + 1] = ' ';
519    shift[2 * i] = shift[2 * i + 1] = 0;
520
521    fprintf(output, shift);
522
523    if (attr == NULL) {
524	fprintf(output, "Attr is NULL");
525	return;
526    }
527    fprintf(output, "ATTRIBUTE ");
528    xmlDebugDumpString(output, attr->name);
529    fprintf(output, "\n");
530    if (attr->children != NULL)
531        xmlDebugDumpNodeList(output, attr->children, depth + 1);
532
533    /*
534     * Do a bit of checking
535     */
536    if (attr->parent == NULL)
537	fprintf(output, "PBM: Attr has no parent\n");
538    if (attr->doc == NULL)
539	fprintf(output, "PBM: Attr has no doc\n");
540    if ((attr->parent != NULL) && (attr->doc != attr->parent->doc))
541	fprintf(output, "PBM: Attr doc differs from parent's one\n");
542    if (attr->prev == NULL) {
543	if ((attr->parent != NULL) && (attr->parent->properties != attr))
544	    fprintf(output, "PBM: Attr has no prev and not first of list\n");
545    } else {
546	if (attr->prev->next != attr)
547	    fprintf(output, "PBM: Attr prev->next : back link wrong\n");
548    }
549    if (attr->next != NULL) {
550	if (attr->next->prev != attr)
551	    fprintf(output, "PBM: Attr next->prev : forward link wrong\n");
552    }
553}
554
555/**
556 * xmlDebugDumpAttrList:
557 * @output:  the FILE * for the output
558 * @attr:  the attribute list
559 * @depth:  the indentation level.
560 *
561 * Dumps debug information for the attribute list
562 */
563void
564xmlDebugDumpAttrList(FILE * output, xmlAttrPtr attr, int depth)
565{
566    if (output == NULL)
567	output = stdout;
568    while (attr != NULL) {
569        xmlDebugDumpAttr(output, attr, depth);
570        attr = attr->next;
571    }
572}
573
574/**
575 * xmlDebugDumpOneNode:
576 * @output:  the FILE * for the output
577 * @node:  the node
578 * @depth:  the indentation level.
579 *
580 * Dumps debug information for the element node, it is not recursive
581 */
582void
583xmlDebugDumpOneNode(FILE * output, xmlNodePtr node, int depth)
584{
585    int i;
586    char shift[100];
587
588    if (output == NULL)
589	output = stdout;
590    for (i = 0; ((i < depth) && (i < 25)); i++)
591        shift[2 * i] = shift[2 * i + 1] = ' ';
592    shift[2 * i] = shift[2 * i + 1] = 0;
593
594    if (node == NULL) {
595	fprintf(output, shift);
596	fprintf(output, "node is NULL\n");
597	return;
598    }
599    switch (node->type) {
600        case XML_ELEMENT_NODE:
601            fprintf(output, shift);
602            fprintf(output, "ELEMENT ");
603            if ((node->ns != NULL) && (node->ns->prefix != NULL)) {
604                xmlDebugDumpString(output, node->ns->prefix);
605                fprintf(output, ":");
606            }
607            xmlDebugDumpString(output, node->name);
608            fprintf(output, "\n");
609            break;
610        case XML_ATTRIBUTE_NODE:
611            fprintf(output, shift);
612            fprintf(output, "Error, ATTRIBUTE found here\n");
613            break;
614        case XML_TEXT_NODE:
615            fprintf(output, shift);
616	    if (node->name == (const xmlChar *) xmlStringTextNoenc)
617		fprintf(output, "TEXT no enc\n");
618	    else
619		fprintf(output, "TEXT\n");
620            break;
621        case XML_CDATA_SECTION_NODE:
622            fprintf(output, shift);
623            fprintf(output, "CDATA_SECTION\n");
624            break;
625        case XML_ENTITY_REF_NODE:
626            fprintf(output, shift);
627            fprintf(output, "ENTITY_REF(%s)\n", node->name);
628            break;
629        case XML_ENTITY_NODE:
630            fprintf(output, shift);
631            fprintf(output, "ENTITY\n");
632            break;
633        case XML_PI_NODE:
634            fprintf(output, shift);
635            fprintf(output, "PI %s\n", node->name);
636            break;
637        case XML_COMMENT_NODE:
638            fprintf(output, shift);
639            fprintf(output, "COMMENT\n");
640            break;
641        case XML_DOCUMENT_NODE:
642        case XML_HTML_DOCUMENT_NODE:
643            fprintf(output, shift);
644            fprintf(output, "Error, DOCUMENT found here\n");
645            break;
646        case XML_DOCUMENT_TYPE_NODE:
647            fprintf(output, shift);
648            fprintf(output, "DOCUMENT_TYPE\n");
649            break;
650        case XML_DOCUMENT_FRAG_NODE:
651            fprintf(output, shift);
652            fprintf(output, "DOCUMENT_FRAG\n");
653            break;
654        case XML_NOTATION_NODE:
655            fprintf(output, shift);
656            fprintf(output, "NOTATION\n");
657            break;
658        case XML_DTD_NODE:
659            xmlDebugDumpDtdNode(output, (xmlDtdPtr) node, depth);
660            return;
661        case XML_ELEMENT_DECL:
662            xmlDebugDumpElemDecl(output, (xmlElementPtr) node, depth);
663            return;
664        case XML_ATTRIBUTE_DECL:
665            xmlDebugDumpAttrDecl(output, (xmlAttributePtr) node, depth);
666            return;
667        case XML_ENTITY_DECL:
668            xmlDebugDumpEntityDecl(output, (xmlEntityPtr) node, depth);
669            return;
670        case XML_NAMESPACE_DECL:
671            xmlDebugDumpNamespace(output, (xmlNsPtr) node, depth);
672            return;
673        case XML_XINCLUDE_START:
674            fprintf(output, shift);
675            fprintf(output, "INCLUDE START\n");
676            return;
677        case XML_XINCLUDE_END:
678            fprintf(output, shift);
679            fprintf(output, "INCLUDE END\n");
680            return;
681        default:
682            fprintf(output, shift);
683            fprintf(output, "NODE_%d !!!\n", node->type);
684            return;
685    }
686    if (node->doc == NULL) {
687        fprintf(output, shift);
688        fprintf(output, "doc == NULL !!!\n");
689    }
690    if (node->nsDef != NULL)
691        xmlDebugDumpNamespaceList(output, node->nsDef, depth + 1);
692    if (node->properties != NULL)
693        xmlDebugDumpAttrList(output, node->properties, depth + 1);
694    if (node->type != XML_ENTITY_REF_NODE) {
695        if ((node->type != XML_ELEMENT_NODE) && (node->content != NULL)) {
696            shift[2 * i] = shift[2 * i + 1] = ' ';
697            shift[2 * i + 2] = shift[2 * i + 3] = 0;
698            fprintf(output, shift);
699            fprintf(output, "content=");
700            xmlDebugDumpString(output, node->content);
701            fprintf(output, "\n");
702        }
703    } else {
704        xmlEntityPtr ent;
705
706        ent = xmlGetDocEntity(node->doc, node->name);
707        if (ent != NULL)
708            xmlDebugDumpEntity(output, ent, depth + 1);
709    }
710    /*
711     * Do a bit of checking
712     */
713    if (node->parent == NULL)
714        fprintf(output, "PBM: Node has no parent\n");
715    if (node->doc == NULL)
716        fprintf(output, "PBM: Node has no doc\n");
717    if ((node->parent != NULL) && (node->doc != node->parent->doc))
718        fprintf(output, "PBM: Node doc differs from parent's one\n");
719    if (node->prev == NULL) {
720        if ((node->parent != NULL) && (node->parent->children != node))
721            fprintf(output,
722                    "PBM: Node has no prev and not first of list\n");
723    } else {
724        if (node->prev->next != node)
725            fprintf(output, "PBM: Node prev->next : back link wrong\n");
726    }
727    if (node->next == NULL) {
728        if ((node->parent != NULL) && (node->parent->last != node))
729            fprintf(output,
730                    "PBM: Node has no next and not last of list\n");
731    } else {
732        if (node->next->prev != node)
733            fprintf(output, "PBM: Node next->prev : forward link wrong\n");
734    }
735}
736
737/**
738 * xmlDebugDumpNode:
739 * @output:  the FILE * for the output
740 * @node:  the node
741 * @depth:  the indentation level.
742 *
743 * Dumps debug information for the element node, it is recursive
744 */
745void
746xmlDebugDumpNode(FILE * output, xmlNodePtr node, int depth)
747{
748    if (output == NULL)
749	output = stdout;
750    if (node == NULL) {
751	int i;
752	char shift[100];
753
754	for (i = 0; ((i < depth) && (i < 25)); i++)
755	    shift[2 * i] = shift[2 * i + 1] = ' ';
756	shift[2 * i] = shift[2 * i + 1] = 0;
757
758	fprintf(output, shift);
759	fprintf(output, "node is NULL\n");
760	return;
761    }
762    xmlDebugDumpOneNode(output, node, depth);
763    if ((node->children != NULL) && (node->type != XML_ENTITY_REF_NODE))
764        xmlDebugDumpNodeList(output, node->children, depth + 1);
765}
766
767/**
768 * xmlDebugDumpNodeList:
769 * @output:  the FILE * for the output
770 * @node:  the node list
771 * @depth:  the indentation level.
772 *
773 * Dumps debug information for the list of element node, it is recursive
774 */
775void
776xmlDebugDumpNodeList(FILE * output, xmlNodePtr node, int depth)
777{
778    if (output == NULL)
779	output = stdout;
780    while (node != NULL) {
781        xmlDebugDumpNode(output, node, depth);
782        node = node->next;
783    }
784}
785
786
787/**
788 * xmlDebugDumpDocumentHead:
789 * @output:  the FILE * for the output
790 * @doc:  the document
791 *
792 * Dumps debug information cncerning the document, not recursive
793 */
794void
795xmlDebugDumpDocumentHead(FILE * output, xmlDocPtr doc)
796{
797    if (output == NULL)
798        output = stdout;
799    if (doc == NULL) {
800        fprintf(output, "DOCUMENT == NULL !\n");
801        return;
802    }
803
804    switch (doc->type) {
805        case XML_ELEMENT_NODE:
806            fprintf(output, "Error, ELEMENT found here ");
807            break;
808        case XML_ATTRIBUTE_NODE:
809            fprintf(output, "Error, ATTRIBUTE found here\n");
810            break;
811        case XML_TEXT_NODE:
812            fprintf(output, "Error, TEXT\n");
813            break;
814        case XML_CDATA_SECTION_NODE:
815            fprintf(output, "Error, CDATA_SECTION\n");
816            break;
817        case XML_ENTITY_REF_NODE:
818            fprintf(output, "Error, ENTITY_REF\n");
819            break;
820        case XML_ENTITY_NODE:
821            fprintf(output, "Error, ENTITY\n");
822            break;
823        case XML_PI_NODE:
824            fprintf(output, "Error, PI\n");
825            break;
826        case XML_COMMENT_NODE:
827            fprintf(output, "Error, COMMENT\n");
828            break;
829        case XML_DOCUMENT_NODE:
830            fprintf(output, "DOCUMENT\n");
831            break;
832        case XML_HTML_DOCUMENT_NODE:
833            fprintf(output, "HTML DOCUMENT\n");
834            break;
835        case XML_DOCUMENT_TYPE_NODE:
836            fprintf(output, "Error, DOCUMENT_TYPE\n");
837            break;
838        case XML_DOCUMENT_FRAG_NODE:
839            fprintf(output, "Error, DOCUMENT_FRAG\n");
840            break;
841        case XML_NOTATION_NODE:
842            fprintf(output, "Error, NOTATION\n");
843            break;
844        default:
845            fprintf(output, "NODE_%d\n", doc->type);
846    }
847    if (doc->name != NULL) {
848        fprintf(output, "name=");
849        xmlDebugDumpString(output, BAD_CAST doc->name);
850        fprintf(output, "\n");
851    }
852    if (doc->version != NULL) {
853        fprintf(output, "version=");
854        xmlDebugDumpString(output, doc->version);
855        fprintf(output, "\n");
856    }
857    if (doc->encoding != NULL) {
858        fprintf(output, "encoding=");
859        xmlDebugDumpString(output, doc->encoding);
860        fprintf(output, "\n");
861    }
862    if (doc->URL != NULL) {
863        fprintf(output, "URL=");
864        xmlDebugDumpString(output, doc->URL);
865        fprintf(output, "\n");
866    }
867    if (doc->standalone)
868        fprintf(output, "standalone=true\n");
869    if (doc->oldNs != NULL)
870        xmlDebugDumpNamespaceList(output, doc->oldNs, 0);
871}
872
873/**
874 * xmlDebugDumpDocument:
875 * @output:  the FILE * for the output
876 * @doc:  the document
877 *
878 * Dumps debug information for the document, it's recursive
879 */
880void
881xmlDebugDumpDocument(FILE * output, xmlDocPtr doc)
882{
883    if (output == NULL)
884        output = stdout;
885    if (doc == NULL) {
886        fprintf(output, "DOCUMENT == NULL !\n");
887        return;
888    }
889    xmlDebugDumpDocumentHead(output, doc);
890    if (((doc->type == XML_DOCUMENT_NODE) ||
891         (doc->type == XML_HTML_DOCUMENT_NODE)) && (doc->children != NULL))
892        xmlDebugDumpNodeList(output, doc->children, 1);
893}
894
895/**
896 * xmlDebugDumpDTD:
897 * @output:  the FILE * for the output
898 * @dtd:  the DTD
899 *
900 * Dumps debug information for the DTD
901 */
902void
903xmlDebugDumpDTD(FILE * output, xmlDtdPtr dtd)
904{
905    if (output == NULL)
906	output = stdout;
907    if (dtd == NULL) {
908	fprintf(output, "DTD is NULL\n");
909        return;
910    }
911    if (dtd->type != XML_DTD_NODE) {
912        fprintf(output, "PBM: not a DTD\n");
913        return;
914    }
915    if (dtd->name != NULL)
916        fprintf(output, "DTD(%s)", dtd->name);
917    else
918        fprintf(output, "DTD");
919    if (dtd->ExternalID != NULL)
920        fprintf(output, ", PUBLIC %s", dtd->ExternalID);
921    if (dtd->SystemID != NULL)
922        fprintf(output, ", SYSTEM %s", dtd->SystemID);
923    fprintf(output, "\n");
924    /*
925     * Do a bit of checking
926     */
927    if ((dtd->parent != NULL) && (dtd->doc != dtd->parent->doc))
928        fprintf(output, "PBM: DTD doc differs from parent's one\n");
929    if (dtd->prev == NULL) {
930        if ((dtd->parent != NULL)
931            && (dtd->parent->children != (xmlNodePtr) dtd))
932            fprintf(output,
933                    "PBM: DTD has no prev and not first of list\n");
934    } else {
935        if (dtd->prev->next != (xmlNodePtr) dtd)
936            fprintf(output, "PBM: DTD prev->next : back link wrong\n");
937    }
938    if (dtd->next == NULL) {
939        if ((dtd->parent != NULL)
940            && (dtd->parent->last != (xmlNodePtr) dtd))
941            fprintf(output, "PBM: DTD has no next and not last of list\n");
942    } else {
943        if (dtd->next->prev != (xmlNodePtr) dtd)
944            fprintf(output, "PBM: DTD next->prev : forward link wrong\n");
945    }
946    if (dtd->children == NULL)
947        fprintf(output, "    DTD is empty\n");
948    else
949        xmlDebugDumpNodeList(output, dtd->children, 1);
950}
951
952static void
953xmlDebugDumpEntityCallback(xmlEntityPtr cur, FILE *output) {
954    if (cur == NULL) {
955	fprintf(output, "Entity is NULL");
956	return;
957    }
958    fprintf(output, "%s : ", cur->name);
959    switch (cur->etype) {
960	case XML_INTERNAL_GENERAL_ENTITY:
961	    fprintf(output, "INTERNAL GENERAL, ");
962	    break;
963	case XML_EXTERNAL_GENERAL_PARSED_ENTITY:
964	    fprintf(output, "EXTERNAL PARSED, ");
965	    break;
966	case XML_EXTERNAL_GENERAL_UNPARSED_ENTITY:
967	    fprintf(output, "EXTERNAL UNPARSED, ");
968	    break;
969	case XML_INTERNAL_PARAMETER_ENTITY:
970	    fprintf(output, "INTERNAL PARAMETER, ");
971	    break;
972	case XML_EXTERNAL_PARAMETER_ENTITY:
973	    fprintf(output, "EXTERNAL PARAMETER, ");
974	    break;
975	default:
976	    fprintf(output, "UNKNOWN TYPE %d",
977		    cur->etype);
978    }
979    if (cur->ExternalID != NULL)
980	fprintf(output, "ID \"%s\"", cur->ExternalID);
981    if (cur->SystemID != NULL)
982	fprintf(output, "SYSTEM \"%s\"", cur->SystemID);
983    if (cur->orig != NULL)
984	fprintf(output, "\n orig \"%s\"", cur->orig);
985    if ((cur->type != XML_ELEMENT_NODE) &&
986	(cur->content != NULL))
987	fprintf(output, "\n content \"%s\"", cur->content);
988    fprintf(output, "\n");
989}
990
991/**
992 * xmlDebugDumpEntities:
993 * @output:  the FILE * for the output
994 * @doc:  the document
995 *
996 * Dumps debug information for all the entities in use by the document
997 */
998void
999xmlDebugDumpEntities(FILE * output, xmlDocPtr doc)
1000{
1001    if (output == NULL)
1002        output = stdout;
1003    if (doc == NULL) {
1004        fprintf(output, "DOCUMENT == NULL !\n");
1005        return;
1006    }
1007
1008    switch (doc->type) {
1009        case XML_ELEMENT_NODE:
1010            fprintf(output, "Error, ELEMENT found here ");
1011            break;
1012        case XML_ATTRIBUTE_NODE:
1013            fprintf(output, "Error, ATTRIBUTE found here\n");
1014            break;
1015        case XML_TEXT_NODE:
1016            fprintf(output, "Error, TEXT\n");
1017            break;
1018        case XML_CDATA_SECTION_NODE:
1019            fprintf(output, "Error, CDATA_SECTION\n");
1020            break;
1021        case XML_ENTITY_REF_NODE:
1022            fprintf(output, "Error, ENTITY_REF\n");
1023            break;
1024        case XML_ENTITY_NODE:
1025            fprintf(output, "Error, ENTITY\n");
1026            break;
1027        case XML_PI_NODE:
1028            fprintf(output, "Error, PI\n");
1029            break;
1030        case XML_COMMENT_NODE:
1031            fprintf(output, "Error, COMMENT\n");
1032            break;
1033        case XML_DOCUMENT_NODE:
1034            fprintf(output, "DOCUMENT\n");
1035            break;
1036        case XML_HTML_DOCUMENT_NODE:
1037            fprintf(output, "HTML DOCUMENT\n");
1038            break;
1039        case XML_DOCUMENT_TYPE_NODE:
1040            fprintf(output, "Error, DOCUMENT_TYPE\n");
1041            break;
1042        case XML_DOCUMENT_FRAG_NODE:
1043            fprintf(output, "Error, DOCUMENT_FRAG\n");
1044            break;
1045        case XML_NOTATION_NODE:
1046            fprintf(output, "Error, NOTATION\n");
1047            break;
1048        default:
1049            fprintf(output, "NODE_%d\n", doc->type);
1050    }
1051    if ((doc->intSubset != NULL) && (doc->intSubset->entities != NULL)) {
1052        xmlEntitiesTablePtr table = (xmlEntitiesTablePtr)
1053            doc->intSubset->entities;
1054
1055        fprintf(output, "Entities in internal subset\n");
1056        xmlHashScan(table, (xmlHashScanner) xmlDebugDumpEntityCallback,
1057                    output);
1058    } else
1059        fprintf(output, "No entities in internal subset\n");
1060    if ((doc->extSubset != NULL) && (doc->extSubset->entities != NULL)) {
1061        xmlEntitiesTablePtr table = (xmlEntitiesTablePtr)
1062            doc->extSubset->entities;
1063
1064        fprintf(output, "Entities in external subset\n");
1065        xmlHashScan(table, (xmlHashScanner) xmlDebugDumpEntityCallback,
1066                    output);
1067    } else
1068        fprintf(output, "No entities in external subset\n");
1069}
1070
1071/**
1072 * xmlLsCountNode:
1073 * @node:  the node to count
1074 *
1075 * Count the children of @node.
1076 *
1077 * Returns the number of children of @node.
1078 */
1079int
1080xmlLsCountNode(xmlNodePtr node) {
1081    int ret = 0;
1082    xmlNodePtr list = NULL;
1083
1084    if (node == NULL)
1085	return(0);
1086
1087    switch (node->type) {
1088	case XML_ELEMENT_NODE:
1089	    list = node->children;
1090	    break;
1091	case XML_DOCUMENT_NODE:
1092	case XML_HTML_DOCUMENT_NODE:
1093#ifdef LIBXML_DOCB_ENABLED
1094	case XML_DOCB_DOCUMENT_NODE:
1095#endif
1096	    list = ((xmlDocPtr) node)->children;
1097	    break;
1098	case XML_ATTRIBUTE_NODE:
1099	    list = ((xmlAttrPtr) node)->children;
1100	    break;
1101	case XML_TEXT_NODE:
1102	case XML_CDATA_SECTION_NODE:
1103	case XML_PI_NODE:
1104	case XML_COMMENT_NODE:
1105	    if (node->content != NULL) {
1106		ret = xmlStrlen(node->content);
1107            }
1108	    break;
1109	case XML_ENTITY_REF_NODE:
1110	case XML_DOCUMENT_TYPE_NODE:
1111	case XML_ENTITY_NODE:
1112	case XML_DOCUMENT_FRAG_NODE:
1113	case XML_NOTATION_NODE:
1114	case XML_DTD_NODE:
1115        case XML_ELEMENT_DECL:
1116        case XML_ATTRIBUTE_DECL:
1117        case XML_ENTITY_DECL:
1118	case XML_NAMESPACE_DECL:
1119	case XML_XINCLUDE_START:
1120	case XML_XINCLUDE_END:
1121	    ret = 1;
1122	    break;
1123    }
1124    for (;list != NULL;ret++)
1125        list = list->next;
1126    return(ret);
1127}
1128
1129/**
1130 * xmlLsOneNode:
1131 * @output:  the FILE * for the output
1132 * @node:  the node to dump
1133 *
1134 * Dump to @output the type and name of @node.
1135 */
1136void
1137xmlLsOneNode(FILE *output, xmlNodePtr node) {
1138    if (node == NULL) {
1139	fprintf(output, "NULL\n");
1140	return;
1141    }
1142    switch (node->type) {
1143	case XML_ELEMENT_NODE:
1144	    fprintf(output, "-");
1145	    break;
1146	case XML_ATTRIBUTE_NODE:
1147	    fprintf(output, "a");
1148	    break;
1149	case XML_TEXT_NODE:
1150	    fprintf(output, "t");
1151	    break;
1152	case XML_CDATA_SECTION_NODE:
1153	    fprintf(output, "C");
1154	    break;
1155	case XML_ENTITY_REF_NODE:
1156	    fprintf(output, "e");
1157	    break;
1158	case XML_ENTITY_NODE:
1159	    fprintf(output, "E");
1160	    break;
1161	case XML_PI_NODE:
1162	    fprintf(output, "p");
1163	    break;
1164	case XML_COMMENT_NODE:
1165	    fprintf(output, "c");
1166	    break;
1167	case XML_DOCUMENT_NODE:
1168	    fprintf(output, "d");
1169	    break;
1170	case XML_HTML_DOCUMENT_NODE:
1171	    fprintf(output, "h");
1172	    break;
1173	case XML_DOCUMENT_TYPE_NODE:
1174	    fprintf(output, "T");
1175	    break;
1176	case XML_DOCUMENT_FRAG_NODE:
1177	    fprintf(output, "F");
1178	    break;
1179	case XML_NOTATION_NODE:
1180	    fprintf(output, "N");
1181	    break;
1182	case XML_NAMESPACE_DECL:
1183	    fprintf(output, "n");
1184	    break;
1185	default:
1186	    fprintf(output, "?");
1187    }
1188    if (node->type != XML_NAMESPACE_DECL) {
1189	if (node->properties != NULL)
1190	    fprintf(output, "a");
1191	else
1192	    fprintf(output, "-");
1193	if (node->nsDef != NULL)
1194	    fprintf(output, "n");
1195	else
1196	    fprintf(output, "-");
1197    }
1198
1199    fprintf(output, " %8d ", xmlLsCountNode(node));
1200
1201    switch (node->type) {
1202	case XML_ELEMENT_NODE:
1203	    if (node->name != NULL)
1204		fprintf(output, "%s", (const char *) node->name);
1205	    break;
1206	case XML_ATTRIBUTE_NODE:
1207	    if (node->name != NULL)
1208		fprintf(output, "%s", (const char *) node->name);
1209	    break;
1210	case XML_TEXT_NODE:
1211	    if (node->content != NULL) {
1212		xmlDebugDumpString(output, node->content);
1213            }
1214	    break;
1215	case XML_CDATA_SECTION_NODE:
1216	    break;
1217	case XML_ENTITY_REF_NODE:
1218	    if (node->name != NULL)
1219		fprintf(output, "%s", (const char *) node->name);
1220	    break;
1221	case XML_ENTITY_NODE:
1222	    if (node->name != NULL)
1223		fprintf(output, "%s", (const char *) node->name);
1224	    break;
1225	case XML_PI_NODE:
1226	    if (node->name != NULL)
1227		fprintf(output, "%s", (const char *) node->name);
1228	    break;
1229	case XML_COMMENT_NODE:
1230	    break;
1231	case XML_DOCUMENT_NODE:
1232	    break;
1233	case XML_HTML_DOCUMENT_NODE:
1234	    break;
1235	case XML_DOCUMENT_TYPE_NODE:
1236	    break;
1237	case XML_DOCUMENT_FRAG_NODE:
1238	    break;
1239	case XML_NOTATION_NODE:
1240	    break;
1241	case XML_NAMESPACE_DECL: {
1242	    xmlNsPtr ns = (xmlNsPtr) node;
1243
1244	    if (ns->prefix == NULL)
1245		fprintf(output, "default -> %s", ns->href);
1246	    else
1247		fprintf(output, "%s -> %s", ns->prefix, ns->href);
1248	    break;
1249	}
1250	default:
1251	    if (node->name != NULL)
1252		fprintf(output, "%s", (const char *) node->name);
1253    }
1254    fprintf(output, "\n");
1255}
1256
1257/**
1258 * xmlBoolToText:
1259 * @boolval: a bool to turn into text
1260 *
1261 * Convenient way to turn bool into text
1262 *
1263 * Returns a pointer to either "True" or "False"
1264 */
1265const char *
1266xmlBoolToText(int boolval)
1267{
1268    if (boolval)
1269        return("True");
1270    else
1271        return("False");
1272}
1273
1274/****************************************************************
1275 *								*
1276 *	 	The XML shell related functions			*
1277 *								*
1278 ****************************************************************/
1279
1280
1281
1282/*
1283 * TODO: Improvement/cleanups for the XML shell
1284 *     - allow to shell out an editor on a subpart
1285 *     - cleanup function registrations (with help) and calling
1286 *     - provide registration routines
1287 */
1288
1289/**
1290 * xmlShellPrintXPathError:
1291 * @errorType: valid xpath error id
1292 * @arg: the argument that cause xpath to fail
1293 *
1294 * Print the xpath error to libxml default error channel
1295 */
1296void
1297xmlShellPrintXPathError(int errorType, const char *arg)
1298{
1299    const char *default_arg = "Result";
1300
1301    if (!arg)
1302        arg = default_arg;
1303
1304    switch (errorType) {
1305        case XPATH_UNDEFINED:
1306            xmlGenericError(xmlGenericErrorContext,
1307                            "%s: no such node\n", arg);
1308            break;
1309
1310        case XPATH_BOOLEAN:
1311            xmlGenericError(xmlGenericErrorContext,
1312                            "%s is a Boolean\n", arg);
1313            break;
1314        case XPATH_NUMBER:
1315            xmlGenericError(xmlGenericErrorContext,
1316                            "%s is a number\n", arg);
1317            break;
1318        case XPATH_STRING:
1319            xmlGenericError(xmlGenericErrorContext,
1320                            "%s is a string\n", arg);
1321            break;
1322        case XPATH_POINT:
1323            xmlGenericError(xmlGenericErrorContext,
1324                            "%s is a point\n", arg);
1325            break;
1326        case XPATH_RANGE:
1327            xmlGenericError(xmlGenericErrorContext,
1328                            "%s is a range\n", arg);
1329            break;
1330        case XPATH_LOCATIONSET:
1331            xmlGenericError(xmlGenericErrorContext,
1332                            "%s is a range\n", arg);
1333            break;
1334        case XPATH_USERS:
1335            xmlGenericError(xmlGenericErrorContext,
1336                            "%s is user-defined\n", arg);
1337            break;
1338        case XPATH_XSLT_TREE:
1339            xmlGenericError(xmlGenericErrorContext,
1340                            "%s is an XSLT value tree\n", arg);
1341            break;
1342    }
1343    xmlGenericError(xmlGenericErrorContext,
1344                    "Try casting the result string function (xpath builtin)\n",
1345                    arg);
1346}
1347
1348
1349/**
1350 * xmlShellPrintNodeCtxt:
1351 * @ctxt : a non-null shell context
1352 * @node : a non-null node to print to the output FILE
1353 *
1354 * Print node to the output FILE
1355 */
1356static void
1357xmlShellPrintNodeCtxt(xmlShellCtxtPtr ctxt,xmlNodePtr node)
1358{
1359    FILE *fp;
1360
1361    if (!node)
1362        return;
1363    if (ctxt == NULL)
1364	fp = stdout;
1365    else
1366	fp = ctxt->output;
1367
1368    if (node->type == XML_DOCUMENT_NODE)
1369        xmlDocDump(fp, (xmlDocPtr) node);
1370    else if (node->type == XML_ATTRIBUTE_NODE)
1371        xmlDebugDumpAttrList(fp, (xmlAttrPtr) node, 0);
1372    else
1373        xmlElemDump(fp, node->doc, node);
1374
1375    fprintf(fp, "\n");
1376}
1377
1378/**
1379 * xmlShellPrintNode:
1380 * @node : a non-null node to print to the output FILE
1381 *
1382 * Print node to the output FILE
1383 */
1384void
1385xmlShellPrintNode(xmlNodePtr node)
1386{
1387    xmlShellPrintNodeCtxt(NULL, node);
1388}
1389
1390/**
1391 * xmlShellPrintXPathResultCtxt:
1392 * @ctxt: a valid shell context
1393 * @list: a valid result generated by an xpath evaluation
1394 *
1395 * Prints result to the output FILE
1396 */
1397static void
1398xmlShellPrintXPathResultCtxt(xmlShellCtxtPtr ctxt,xmlXPathObjectPtr list)
1399{
1400    int i = 0;
1401    if (!ctxt)
1402       return;
1403
1404    if (list != NULL) {
1405        switch (list->type) {
1406            case XPATH_NODESET:{
1407                    int indx;
1408
1409                    if (list->nodesetval) {
1410                        for (indx = 0; indx < list->nodesetval->nodeNr;
1411                             indx++) {
1412                            if (i > 0)
1413                                fprintf(stderr, " -------\n");
1414                            xmlShellPrintNodeCtxt(ctxt,
1415				    list->nodesetval->nodeTab[indx]);
1416                        }
1417                    } else {
1418                        xmlGenericError(xmlGenericErrorContext,
1419                                        "Empty node set\n");
1420                    }
1421                    break;
1422                }
1423            case XPATH_BOOLEAN:
1424                xmlGenericError(xmlGenericErrorContext,
1425                                "Is a Boolean:%s\n",
1426                                xmlBoolToText(list->boolval));
1427                break;
1428            case XPATH_NUMBER:
1429                xmlGenericError(xmlGenericErrorContext,
1430                                "Is a number:%0g\n", list->floatval);
1431                break;
1432            case XPATH_STRING:
1433                xmlGenericError(xmlGenericErrorContext,
1434                                "Is a string:%s\n", list->stringval);
1435                break;
1436
1437            default:
1438                xmlShellPrintXPathError(list->type, NULL);
1439        }
1440    }
1441}
1442
1443/**
1444 * xmlShellPrintXPathResult:
1445 * @list: a valid result generated by an xpath evaluation
1446 *
1447 * Prints result to the output FILE
1448 */
1449void
1450xmlShellPrintXPathResult(xmlXPathObjectPtr list)
1451{
1452    xmlShellPrintXPathResultCtxt(NULL, list);
1453}
1454
1455/**
1456 * xmlShellList:
1457 * @ctxt:  the shell context
1458 * @arg:  unused
1459 * @node:  a node
1460 * @node2:  unused
1461 *
1462 * Implements the XML shell function "ls"
1463 * Does an Unix like listing of the given node (like a directory)
1464 *
1465 * Returns 0
1466 */
1467int
1468xmlShellList(xmlShellCtxtPtr ctxt,
1469             char *arg ATTRIBUTE_UNUSED, xmlNodePtr node,
1470             xmlNodePtr node2 ATTRIBUTE_UNUSED)
1471{
1472    xmlNodePtr cur;
1473    if (!ctxt)
1474        return (0);
1475    if (node == NULL) {
1476	fprintf(ctxt->output, "NULL\n");
1477	return (0);
1478    }
1479    if ((node->type == XML_DOCUMENT_NODE) ||
1480        (node->type == XML_HTML_DOCUMENT_NODE)) {
1481        cur = ((xmlDocPtr) node)->children;
1482    } else if (node->type == XML_NAMESPACE_DECL) {
1483        xmlLsOneNode(ctxt->output, node);
1484        return (0);
1485    } else if (node->children != NULL) {
1486        cur = node->children;
1487    } else {
1488        xmlLsOneNode(ctxt->output, node);
1489        return (0);
1490    }
1491    while (cur != NULL) {
1492        xmlLsOneNode(ctxt->output, cur);
1493        cur = cur->next;
1494    }
1495    return (0);
1496}
1497
1498/**
1499 * xmlShellBase:
1500 * @ctxt:  the shell context
1501 * @arg:  unused
1502 * @node:  a node
1503 * @node2:  unused
1504 *
1505 * Implements the XML shell function "base"
1506 * dumps the current XML base of the node
1507 *
1508 * Returns 0
1509 */
1510int
1511xmlShellBase(xmlShellCtxtPtr ctxt,
1512             char *arg ATTRIBUTE_UNUSED, xmlNodePtr node,
1513             xmlNodePtr node2 ATTRIBUTE_UNUSED)
1514{
1515    xmlChar *base;
1516    if (!ctxt)
1517        return 0;
1518    if (node == NULL) {
1519	fprintf(ctxt->output, "NULL\n");
1520	return (0);
1521    }
1522
1523    base = xmlNodeGetBase(node->doc, node);
1524
1525    if (base == NULL) {
1526        fprintf(ctxt->output, " No base found !!!\n");
1527    } else {
1528        fprintf(ctxt->output, "%s\n", base);
1529        xmlFree(base);
1530    }
1531    return (0);
1532}
1533
1534/**
1535 * xmlShellSetBase:
1536 * @ctxt:  the shell context
1537 * @arg:  the new base
1538 * @node:  a node
1539 * @node2:  unused
1540 *
1541 * Implements the XML shell function "setbase"
1542 * change the current XML base of the node
1543 *
1544 * Returns 0
1545 */
1546static int
1547xmlShellSetBase(xmlShellCtxtPtr ctxt ATTRIBUTE_UNUSED,
1548             char *arg ATTRIBUTE_UNUSED, xmlNodePtr node,
1549             xmlNodePtr node2 ATTRIBUTE_UNUSED)
1550{
1551    xmlNodeSetBase(node, (xmlChar*) arg);
1552    return (0);
1553}
1554
1555/**
1556 * xmlShellGrep:
1557 * @ctxt:  the shell context
1558 * @arg:  the string or regular expression to find
1559 * @node:  a node
1560 * @node2:  unused
1561 *
1562 * Implements the XML shell function "grep"
1563 * dumps informations about the node (namespace, attributes, content).
1564 *
1565 * Returns 0
1566 */
1567static int
1568xmlShellGrep(xmlShellCtxtPtr ctxt ATTRIBUTE_UNUSED,
1569            char *arg, xmlNodePtr node, xmlNodePtr node2 ATTRIBUTE_UNUSED)
1570{
1571    if (!ctxt)
1572        return (0);
1573    if (node == NULL)
1574	return (0);
1575    if (arg == NULL)
1576	return (0);
1577#ifdef LIBXML_REGEXP_ENABLED
1578    if ((xmlStrchr((xmlChar *) arg, '?')) ||
1579	(xmlStrchr((xmlChar *) arg, '*')) ||
1580	(xmlStrchr((xmlChar *) arg, '.')) ||
1581	(xmlStrchr((xmlChar *) arg, '['))) {
1582    }
1583#endif
1584    while (node != NULL) {
1585        if (node->type == XML_COMMENT_NODE) {
1586	    if (xmlStrstr(node->content, (xmlChar *) arg)) {
1587
1588		fprintf(ctxt->output, "%s : ", xmlGetNodePath(node));
1589                xmlShellList(ctxt, NULL, node, NULL);
1590	    }
1591        } else if (node->type == XML_TEXT_NODE) {
1592	    if (xmlStrstr(node->content, (xmlChar *) arg)) {
1593
1594		fprintf(ctxt->output, "%s : ", xmlGetNodePath(node->parent));
1595                xmlShellList(ctxt, NULL, node->parent, NULL);
1596	    }
1597        }
1598
1599        /*
1600         * Browse the full subtree, deep first
1601         */
1602
1603        if ((node->type == XML_DOCUMENT_NODE) ||
1604            (node->type == XML_HTML_DOCUMENT_NODE)) {
1605            node = ((xmlDocPtr) node)->children;
1606        } else if ((node->children != NULL)
1607                   && (node->type != XML_ENTITY_REF_NODE)) {
1608            /* deep first */
1609            node = node->children;
1610        } else if (node->next != NULL) {
1611            /* then siblings */
1612            node = node->next;
1613        } else {
1614            /* go up to parents->next if needed */
1615            while (node != NULL) {
1616                if (node->parent != NULL) {
1617                    node = node->parent;
1618                }
1619                if (node->next != NULL) {
1620                    node = node->next;
1621                    break;
1622                }
1623                if (node->parent == NULL) {
1624                    node = NULL;
1625                    break;
1626                }
1627            }
1628	}
1629    }
1630    return (0);
1631}
1632
1633/**
1634 * xmlShellDir:
1635 * @ctxt:  the shell context
1636 * @arg:  unused
1637 * @node:  a node
1638 * @node2:  unused
1639 *
1640 * Implements the XML shell function "dir"
1641 * dumps informations about the node (namespace, attributes, content).
1642 *
1643 * Returns 0
1644 */
1645int
1646xmlShellDir(xmlShellCtxtPtr ctxt ATTRIBUTE_UNUSED,
1647            char *arg ATTRIBUTE_UNUSED, xmlNodePtr node,
1648            xmlNodePtr node2 ATTRIBUTE_UNUSED)
1649{
1650    if (!ctxt)
1651        return (0);
1652    if (node == NULL) {
1653	fprintf(ctxt->output, "NULL\n");
1654	return (0);
1655    }
1656    if ((node->type == XML_DOCUMENT_NODE) ||
1657        (node->type == XML_HTML_DOCUMENT_NODE)) {
1658        xmlDebugDumpDocumentHead(ctxt->output, (xmlDocPtr) node);
1659    } else if (node->type == XML_ATTRIBUTE_NODE) {
1660        xmlDebugDumpAttr(ctxt->output, (xmlAttrPtr) node, 0);
1661    } else {
1662        xmlDebugDumpOneNode(ctxt->output, node, 0);
1663    }
1664    return (0);
1665}
1666
1667/**
1668 * xmlShellCat:
1669 * @ctxt:  the shell context
1670 * @arg:  unused
1671 * @node:  a node
1672 * @node2:  unused
1673 *
1674 * Implements the XML shell function "cat"
1675 * dumps the serialization node content (XML or HTML).
1676 *
1677 * Returns 0
1678 */
1679int
1680xmlShellCat(xmlShellCtxtPtr ctxt, char *arg ATTRIBUTE_UNUSED,
1681            xmlNodePtr node, xmlNodePtr node2 ATTRIBUTE_UNUSED)
1682{
1683    if (!ctxt)
1684        return (0);
1685    if (node == NULL) {
1686	fprintf(ctxt->output, "NULL\n");
1687	return (0);
1688    }
1689    if (ctxt->doc->type == XML_HTML_DOCUMENT_NODE) {
1690#ifdef LIBXML_HTML_ENABLED
1691        if (node->type == XML_HTML_DOCUMENT_NODE)
1692            htmlDocDump(ctxt->output, (htmlDocPtr) node);
1693        else
1694            htmlNodeDumpFile(ctxt->output, ctxt->doc, node);
1695#else
1696        if (node->type == XML_DOCUMENT_NODE)
1697            xmlDocDump(ctxt->output, (xmlDocPtr) node);
1698        else
1699            xmlElemDump(ctxt->output, ctxt->doc, node);
1700#endif /* LIBXML_HTML_ENABLED */
1701    } else {
1702        if (node->type == XML_DOCUMENT_NODE)
1703            xmlDocDump(ctxt->output, (xmlDocPtr) node);
1704        else
1705            xmlElemDump(ctxt->output, ctxt->doc, node);
1706    }
1707    fprintf(ctxt->output, "\n");
1708    return (0);
1709}
1710
1711/**
1712 * xmlShellLoad:
1713 * @ctxt:  the shell context
1714 * @filename:  the file name
1715 * @node:  unused
1716 * @node2:  unused
1717 *
1718 * Implements the XML shell function "load"
1719 * loads a new document specified by the filename
1720 *
1721 * Returns 0 or -1 if loading failed
1722 */
1723int
1724xmlShellLoad(xmlShellCtxtPtr ctxt, char *filename,
1725             xmlNodePtr node ATTRIBUTE_UNUSED,
1726             xmlNodePtr node2 ATTRIBUTE_UNUSED)
1727{
1728    xmlDocPtr doc;
1729    int html = 0;
1730
1731    if (ctxt->doc != NULL)
1732        html = (ctxt->doc->type == XML_HTML_DOCUMENT_NODE);
1733
1734    if (html) {
1735#ifdef LIBXML_HTML_ENABLED
1736        doc = htmlParseFile(filename, NULL);
1737#else
1738        fprintf(ctxt->output, "HTML support not compiled in\n");
1739        doc = NULL;
1740#endif /* LIBXML_HTML_ENABLED */
1741    } else {
1742        doc = xmlParseFile(filename);
1743    }
1744    if (doc != NULL) {
1745        if (ctxt->loaded == 1) {
1746            xmlFreeDoc(ctxt->doc);
1747        }
1748        ctxt->loaded = 1;
1749#ifdef LIBXML_XPATH_ENABLED
1750        xmlXPathFreeContext(ctxt->pctxt);
1751#endif /* LIBXML_XPATH_ENABLED */
1752        xmlFree(ctxt->filename);
1753        ctxt->doc = doc;
1754        ctxt->node = (xmlNodePtr) doc;
1755#ifdef LIBXML_XPATH_ENABLED
1756        ctxt->pctxt = xmlXPathNewContext(doc);
1757#endif /* LIBXML_XPATH_ENABLED */
1758        ctxt->filename = (char *) xmlCanonicPath((xmlChar *) filename);
1759    } else
1760        return (-1);
1761    return (0);
1762}
1763
1764/**
1765 * xmlShellWrite:
1766 * @ctxt:  the shell context
1767 * @filename:  the file name
1768 * @node:  a node in the tree
1769 * @node2:  unused
1770 *
1771 * Implements the XML shell function "write"
1772 * Write the current node to the filename, it saves the serialization
1773 * of the subtree under the @node specified
1774 *
1775 * Returns 0 or -1 in case of error
1776 */
1777int
1778xmlShellWrite(xmlShellCtxtPtr ctxt, char *filename, xmlNodePtr node,
1779              xmlNodePtr node2 ATTRIBUTE_UNUSED)
1780{
1781    if (node == NULL)
1782        return (-1);
1783    if ((filename == NULL) || (filename[0] == 0)) {
1784        xmlGenericError(xmlGenericErrorContext,
1785                        "Write command requires a filename argument\n");
1786        return (-1);
1787    }
1788#ifdef W_OK
1789    if (access((char *) filename, W_OK)) {
1790        xmlGenericError(xmlGenericErrorContext,
1791                        "Cannot write to %s\n", filename);
1792        return (-1);
1793    }
1794#endif
1795    switch (node->type) {
1796        case XML_DOCUMENT_NODE:
1797            if (xmlSaveFile((char *) filename, ctxt->doc) < -1) {
1798                xmlGenericError(xmlGenericErrorContext,
1799                                "Failed to write to %s\n", filename);
1800                return (-1);
1801            }
1802            break;
1803        case XML_HTML_DOCUMENT_NODE:
1804#ifdef LIBXML_HTML_ENABLED
1805            if (htmlSaveFile((char *) filename, ctxt->doc) < 0) {
1806                xmlGenericError(xmlGenericErrorContext,
1807                                "Failed to write to %s\n", filename);
1808                return (-1);
1809            }
1810#else
1811            if (xmlSaveFile((char *) filename, ctxt->doc) < -1) {
1812                xmlGenericError(xmlGenericErrorContext,
1813                                "Failed to write to %s\n", filename);
1814                return (-1);
1815            }
1816#endif /* LIBXML_HTML_ENABLED */
1817            break;
1818        default:{
1819                FILE *f;
1820
1821                f = fopen((char *) filename, "w");
1822                if (f == NULL) {
1823                    xmlGenericError(xmlGenericErrorContext,
1824                                    "Failed to write to %s\n", filename);
1825                    return (-1);
1826                }
1827                xmlElemDump(f, ctxt->doc, node);
1828                fclose(f);
1829            }
1830    }
1831    return (0);
1832}
1833
1834/**
1835 * xmlShellSave:
1836 * @ctxt:  the shell context
1837 * @filename:  the file name (optional)
1838 * @node:  unused
1839 * @node2:  unused
1840 *
1841 * Implements the XML shell function "save"
1842 * Write the current document to the filename, or it's original name
1843 *
1844 * Returns 0 or -1 in case of error
1845 */
1846int
1847xmlShellSave(xmlShellCtxtPtr ctxt, char *filename,
1848             xmlNodePtr node ATTRIBUTE_UNUSED,
1849             xmlNodePtr node2 ATTRIBUTE_UNUSED)
1850{
1851    if (ctxt->doc == NULL)
1852        return (-1);
1853    if ((filename == NULL) || (filename[0] == 0))
1854        filename = ctxt->filename;
1855#ifdef W_OK
1856    if (access((char *) filename, W_OK)) {
1857        xmlGenericError(xmlGenericErrorContext,
1858                        "Cannot save to %s\n", filename);
1859        return (-1);
1860    }
1861#endif
1862    switch (ctxt->doc->type) {
1863        case XML_DOCUMENT_NODE:
1864            if (xmlSaveFile((char *) filename, ctxt->doc) < 0) {
1865                xmlGenericError(xmlGenericErrorContext,
1866                                "Failed to save to %s\n", filename);
1867            }
1868            break;
1869        case XML_HTML_DOCUMENT_NODE:
1870#ifdef LIBXML_HTML_ENABLED
1871            if (htmlSaveFile((char *) filename, ctxt->doc) < 0) {
1872                xmlGenericError(xmlGenericErrorContext,
1873                                "Failed to save to %s\n", filename);
1874            }
1875#else
1876            if (xmlSaveFile((char *) filename, ctxt->doc) < 0) {
1877                xmlGenericError(xmlGenericErrorContext,
1878                                "Failed to save to %s\n", filename);
1879            }
1880#endif /* LIBXML_HTML_ENABLED */
1881            break;
1882        default:
1883            xmlGenericError(xmlGenericErrorContext,
1884	    "To save to subparts of a document use the 'write' command\n");
1885            return (-1);
1886
1887    }
1888    return (0);
1889}
1890
1891/**
1892 * xmlShellValidate:
1893 * @ctxt:  the shell context
1894 * @dtd:  the DTD URI (optional)
1895 * @node:  unused
1896 * @node2:  unused
1897 *
1898 * Implements the XML shell function "validate"
1899 * Validate the document, if a DTD path is provided, then the validation
1900 * is done against the given DTD.
1901 *
1902 * Returns 0 or -1 in case of error
1903 */
1904int
1905xmlShellValidate(xmlShellCtxtPtr ctxt, char *dtd,
1906                 xmlNodePtr node ATTRIBUTE_UNUSED,
1907                 xmlNodePtr node2 ATTRIBUTE_UNUSED)
1908{
1909    xmlValidCtxt vctxt;
1910    int res = -1;
1911
1912    vctxt.userData = stderr;
1913    vctxt.error = (xmlValidityErrorFunc) fprintf;
1914    vctxt.warning = (xmlValidityWarningFunc) fprintf;
1915
1916    if ((dtd == NULL) || (dtd[0] == 0)) {
1917        res = xmlValidateDocument(&vctxt, ctxt->doc);
1918    } else {
1919        xmlDtdPtr subset;
1920
1921        subset = xmlParseDTD(NULL, (xmlChar *) dtd);
1922        if (subset != NULL) {
1923            res = xmlValidateDtd(&vctxt, ctxt->doc, subset);
1924
1925            xmlFreeDtd(subset);
1926        }
1927    }
1928    return (res);
1929}
1930
1931/**
1932 * xmlShellDu:
1933 * @ctxt:  the shell context
1934 * @arg:  unused
1935 * @tree:  a node defining a subtree
1936 * @node2:  unused
1937 *
1938 * Implements the XML shell function "du"
1939 * show the structure of the subtree under node @tree
1940 * If @tree is null, the command works on the current node.
1941 *
1942 * Returns 0 or -1 in case of error
1943 */
1944int
1945xmlShellDu(xmlShellCtxtPtr ctxt,
1946           char *arg ATTRIBUTE_UNUSED, xmlNodePtr tree,
1947           xmlNodePtr node2 ATTRIBUTE_UNUSED)
1948{
1949    xmlNodePtr node;
1950    int indent = 0, i;
1951
1952    if (!ctxt)
1953	return (-1);
1954
1955    if (tree == NULL)
1956        return (-1);
1957    node = tree;
1958    while (node != NULL) {
1959        if ((node->type == XML_DOCUMENT_NODE) ||
1960            (node->type == XML_HTML_DOCUMENT_NODE)) {
1961            fprintf(ctxt->output, "/\n");
1962        } else if (node->type == XML_ELEMENT_NODE) {
1963            for (i = 0; i < indent; i++)
1964                fprintf(ctxt->output, "  ");
1965            fprintf(ctxt->output, "%s\n", node->name);
1966        } else {
1967        }
1968
1969        /*
1970         * Browse the full subtree, deep first
1971         */
1972
1973        if ((node->type == XML_DOCUMENT_NODE) ||
1974            (node->type == XML_HTML_DOCUMENT_NODE)) {
1975            node = ((xmlDocPtr) node)->children;
1976        } else if ((node->children != NULL)
1977                   && (node->type != XML_ENTITY_REF_NODE)) {
1978            /* deep first */
1979            node = node->children;
1980            indent++;
1981        } else if ((node != tree) && (node->next != NULL)) {
1982            /* then siblings */
1983            node = node->next;
1984        } else if (node != tree) {
1985            /* go up to parents->next if needed */
1986            while (node != tree) {
1987                if (node->parent != NULL) {
1988                    node = node->parent;
1989                    indent--;
1990                }
1991                if ((node != tree) && (node->next != NULL)) {
1992                    node = node->next;
1993                    break;
1994                }
1995                if (node->parent == NULL) {
1996                    node = NULL;
1997                    break;
1998                }
1999                if (node == tree) {
2000                    node = NULL;
2001                    break;
2002                }
2003            }
2004            /* exit condition */
2005            if (node == tree)
2006                node = NULL;
2007        } else
2008            node = NULL;
2009    }
2010    return (0);
2011}
2012
2013/**
2014 * xmlShellPwd:
2015 * @ctxt:  the shell context
2016 * @buffer:  the output buffer
2017 * @node:  a node
2018 * @node2:  unused
2019 *
2020 * Implements the XML shell function "pwd"
2021 * Show the full path from the root to the node, if needed building
2022 * thumblers when similar elements exists at a given ancestor level.
2023 * The output is compatible with XPath commands.
2024 *
2025 * Returns 0 or -1 in case of error
2026 */
2027int
2028xmlShellPwd(xmlShellCtxtPtr ctxt ATTRIBUTE_UNUSED, char *buffer,
2029            xmlNodePtr node, xmlNodePtr node2 ATTRIBUTE_UNUSED)
2030{
2031    xmlChar *path;
2032
2033    if (node == NULL)
2034        return (-1);
2035
2036    path = xmlGetNodePath(node);
2037    if (path == NULL)
2038	return (-1);
2039
2040    /*
2041     * This test prevents buffer overflow, because this routine
2042     * is only called by xmlShell, in which the second argument is
2043     * 500 chars long.
2044     * It is a dirty hack before a cleaner solution is found.
2045     * Documentation should mention that the second argument must
2046     * be at least 500 chars long, and could be stripped if too long.
2047     */
2048    snprintf(buffer, 499, "%s", path);
2049    buffer[499] = '0';
2050    xmlFree(path);
2051
2052    return (0);
2053}
2054
2055/**
2056 * xmlShell:
2057 * @doc:  the initial document
2058 * @filename:  the output buffer
2059 * @input:  the line reading function
2060 * @output:  the output FILE*, defaults to stdout if NULL
2061 *
2062 * Implements the XML shell
2063 * This allow to load, validate, view, modify and save a document
2064 * using a environment similar to a UNIX commandline.
2065 */
2066void
2067xmlShell(xmlDocPtr doc, char *filename, xmlShellReadlineFunc input,
2068         FILE * output)
2069{
2070    char prompt[500] = "/ > ";
2071    char *cmdline = NULL, *cur;
2072    int nbargs;
2073    char command[100];
2074    char arg[400];
2075    int i;
2076    xmlShellCtxtPtr ctxt;
2077    xmlXPathObjectPtr list;
2078
2079    if (doc == NULL)
2080        return;
2081    if (filename == NULL)
2082        return;
2083    if (input == NULL)
2084        return;
2085    if (output == NULL)
2086        output = stdout;
2087    ctxt = (xmlShellCtxtPtr) xmlMalloc(sizeof(xmlShellCtxt));
2088    if (ctxt == NULL)
2089        return;
2090    ctxt->loaded = 0;
2091    ctxt->doc = doc;
2092    ctxt->input = input;
2093    ctxt->output = output;
2094    ctxt->filename = (char *) xmlStrdup((xmlChar *) filename);
2095    ctxt->node = (xmlNodePtr) ctxt->doc;
2096
2097#ifdef LIBXML_XPATH_ENABLED
2098    ctxt->pctxt = xmlXPathNewContext(ctxt->doc);
2099    if (ctxt->pctxt == NULL) {
2100        xmlFree(ctxt);
2101        return;
2102    }
2103#endif /* LIBXML_XPATH_ENABLED */
2104    while (1) {
2105        if (ctxt->node == (xmlNodePtr) ctxt->doc)
2106            snprintf(prompt, sizeof(prompt), "%s > ", "/");
2107        else if ((ctxt->node != NULL) && (ctxt->node->name))
2108            snprintf(prompt, sizeof(prompt), "%s > ", ctxt->node->name);
2109        else
2110            snprintf(prompt, sizeof(prompt), "? > ");
2111        prompt[sizeof(prompt) - 1] = 0;
2112
2113        /*
2114         * Get a new command line
2115         */
2116        cmdline = ctxt->input(prompt);
2117        if (cmdline == NULL)
2118            break;
2119
2120        /*
2121         * Parse the command itself
2122         */
2123        cur = cmdline;
2124        nbargs = 0;
2125        while ((*cur == ' ') || (*cur == '\t'))
2126            cur++;
2127        i = 0;
2128        while ((*cur != ' ') && (*cur != '\t') &&
2129               (*cur != '\n') && (*cur != '\r')) {
2130            if (*cur == 0)
2131                break;
2132            command[i++] = *cur++;
2133        }
2134        command[i] = 0;
2135        if (i == 0)
2136            continue;
2137        nbargs++;
2138
2139        /*
2140         * Parse the argument
2141         */
2142        while ((*cur == ' ') || (*cur == '\t'))
2143            cur++;
2144        i = 0;
2145        while ((*cur != '\n') && (*cur != '\r') && (*cur != 0)) {
2146            if (*cur == 0)
2147                break;
2148            arg[i++] = *cur++;
2149        }
2150        arg[i] = 0;
2151        if (i != 0)
2152            nbargs++;
2153
2154        /*
2155         * start interpreting the command
2156         */
2157        if (!strcmp(command, "exit"))
2158            break;
2159        if (!strcmp(command, "quit"))
2160            break;
2161        if (!strcmp(command, "bye"))
2162            break;
2163		if (!strcmp(command, "help")) {
2164		  fprintf(ctxt->output, "\tbase         display XML base of the node\n");
2165		  fprintf(ctxt->output, "\tsetbase URI  change the XML base of the node\n");
2166		  fprintf(ctxt->output, "\tbye          leave shell\n");
2167		  fprintf(ctxt->output, "\tcat [node]   display node or current node\n");
2168		  fprintf(ctxt->output, "\tcd [path]    change directory to path or to root\n");
2169		  fprintf(ctxt->output, "\tdir [path]   dumps informations about the node (namespace, attributes, content)\n");
2170		  fprintf(ctxt->output, "\tdu [path]    show the structure of the subtree under path or the current node\n");
2171		  fprintf(ctxt->output, "\texit         leave shell\n");
2172		  fprintf(ctxt->output, "\thelp         display this help\n");
2173		  fprintf(ctxt->output, "\tfree         display memory usage\n");
2174		  fprintf(ctxt->output, "\tload [name]  load a new document with name\n");
2175		  fprintf(ctxt->output, "\tls [path]    list contents of path or the current directory\n");
2176#ifdef LIBXML_XPATH_ENABLED
2177		  fprintf(ctxt->output, "\txpath expr   evaluate the XPath expression in that context and print the result\n");
2178#endif /* LIBXML_XPATH_ENABLED */
2179		  fprintf(ctxt->output, "\tpwd          display current working directory\n");
2180		  fprintf(ctxt->output, "\tquit         leave shell\n");
2181		  fprintf(ctxt->output, "\tsave [name]  save this document to name or the original name\n");
2182		  fprintf(ctxt->output, "\tvalidate     check the document for errors\n");
2183		  fprintf(ctxt->output, "\twrite [name] write the current node to the filename\n");
2184		  fprintf(ctxt->output, "\tgrep string  search for a string in the subtree\n");
2185        } else if (!strcmp(command, "validate")) {
2186            xmlShellValidate(ctxt, arg, NULL, NULL);
2187        } else if (!strcmp(command, "load")) {
2188            xmlShellLoad(ctxt, arg, NULL, NULL);
2189        } else if (!strcmp(command, "save")) {
2190            xmlShellSave(ctxt, arg, NULL, NULL);
2191        } else if (!strcmp(command, "write")) {
2192            xmlShellWrite(ctxt, arg, NULL, NULL);
2193        } else if (!strcmp(command, "grep")) {
2194            xmlShellGrep(ctxt, arg, ctxt->node, NULL);
2195        } else if (!strcmp(command, "free")) {
2196            if (arg[0] == 0) {
2197                xmlMemShow(ctxt->output, 0);
2198            } else {
2199                int len = 0;
2200
2201                sscanf(arg, "%d", &len);
2202                xmlMemShow(ctxt->output, len);
2203            }
2204        } else if (!strcmp(command, "pwd")) {
2205            char dir[500];
2206
2207            if (!xmlShellPwd(ctxt, dir, ctxt->node, NULL))
2208                fprintf(ctxt->output, "%s\n", dir);
2209        } else if (!strcmp(command, "du")) {
2210            xmlShellDu(ctxt, NULL, ctxt->node, NULL);
2211        } else if (!strcmp(command, "base")) {
2212            xmlShellBase(ctxt, NULL, ctxt->node, NULL);
2213#ifdef LIBXML_XPATH_ENABLED
2214        } else if (!strcmp(command, "xpath")) {
2215            if (arg[0] == 0) {
2216		xmlGenericError(xmlGenericErrorContext,
2217				"xpath: expression required\n");
2218	    } else {
2219                ctxt->pctxt->node = ctxt->node;
2220                list = xmlXPathEval((xmlChar *) arg, ctxt->pctxt);
2221		xmlXPathDebugDumpObject(ctxt->output, list, 0);
2222		xmlXPathFreeObject(list);
2223	    }
2224#endif /* LIBXML_XPATH_ENABLED */
2225        } else if (!strcmp(command, "setbase")) {
2226            xmlShellSetBase(ctxt, arg, ctxt->node, NULL);
2227        } else if ((!strcmp(command, "ls")) || (!strcmp(command, "dir"))) {
2228            int dir = (!strcmp(command, "dir"));
2229
2230            if (arg[0] == 0) {
2231                if (dir)
2232                    xmlShellDir(ctxt, NULL, ctxt->node, NULL);
2233                else
2234                    xmlShellList(ctxt, NULL, ctxt->node, NULL);
2235            } else {
2236                ctxt->pctxt->node = ctxt->node;
2237#ifdef LIBXML_XPATH_ENABLED
2238                ctxt->pctxt->node = ctxt->node;
2239                list = xmlXPathEval((xmlChar *) arg, ctxt->pctxt);
2240#else
2241                list = NULL;
2242#endif /* LIBXML_XPATH_ENABLED */
2243                if (list != NULL) {
2244                    switch (list->type) {
2245                        case XPATH_UNDEFINED:
2246                            xmlGenericError(xmlGenericErrorContext,
2247                                            "%s: no such node\n", arg);
2248                            break;
2249                        case XPATH_NODESET:{
2250                                int indx;
2251
2252				if (list->nodesetval == NULL)
2253				    break;
2254
2255                                for (indx = 0;
2256                                     indx < list->nodesetval->nodeNr;
2257                                     indx++) {
2258                                    if (dir)
2259                                        xmlShellDir(ctxt, NULL,
2260                                                    list->nodesetval->
2261                                                    nodeTab[indx], NULL);
2262                                    else
2263                                        xmlShellList(ctxt, NULL,
2264                                                     list->nodesetval->
2265                                                     nodeTab[indx], NULL);
2266                                }
2267                                break;
2268                            }
2269                        case XPATH_BOOLEAN:
2270                            xmlGenericError(xmlGenericErrorContext,
2271                                            "%s is a Boolean\n", arg);
2272                            break;
2273                        case XPATH_NUMBER:
2274                            xmlGenericError(xmlGenericErrorContext,
2275                                            "%s is a number\n", arg);
2276                            break;
2277                        case XPATH_STRING:
2278                            xmlGenericError(xmlGenericErrorContext,
2279                                            "%s is a string\n", arg);
2280                            break;
2281                        case XPATH_POINT:
2282                            xmlGenericError(xmlGenericErrorContext,
2283                                            "%s is a point\n", arg);
2284                            break;
2285                        case XPATH_RANGE:
2286                            xmlGenericError(xmlGenericErrorContext,
2287                                            "%s is a range\n", arg);
2288                            break;
2289                        case XPATH_LOCATIONSET:
2290                            xmlGenericError(xmlGenericErrorContext,
2291                                            "%s is a range\n", arg);
2292                            break;
2293                        case XPATH_USERS:
2294                            xmlGenericError(xmlGenericErrorContext,
2295                                            "%s is user-defined\n", arg);
2296                            break;
2297                        case XPATH_XSLT_TREE:
2298                            xmlGenericError(xmlGenericErrorContext,
2299                                            "%s is an XSLT value tree\n",
2300                                            arg);
2301                            break;
2302                    }
2303#ifdef LIBXML_XPATH_ENABLED
2304                    xmlXPathFreeObject(list);
2305#endif
2306                } else {
2307                    xmlGenericError(xmlGenericErrorContext,
2308                                    "%s: no such node\n", arg);
2309                }
2310                ctxt->pctxt->node = NULL;
2311            }
2312        } else if (!strcmp(command, "cd")) {
2313            if (arg[0] == 0) {
2314                ctxt->node = (xmlNodePtr) ctxt->doc;
2315            } else {
2316#ifdef LIBXML_XPATH_ENABLED
2317                ctxt->pctxt->node = ctxt->node;
2318                list = xmlXPathEval((xmlChar *) arg, ctxt->pctxt);
2319#else
2320                list = NULL;
2321#endif /* LIBXML_XPATH_ENABLED */
2322                if (list != NULL) {
2323                    switch (list->type) {
2324                        case XPATH_UNDEFINED:
2325                            xmlGenericError(xmlGenericErrorContext,
2326                                            "%s: no such node\n", arg);
2327                            break;
2328                        case XPATH_NODESET:
2329                            if (list->nodesetval != NULL) {
2330				if (list->nodesetval->nodeNr == 1) {
2331				    ctxt->node = list->nodesetval->nodeTab[0];
2332				    if ((ctxt->node != NULL) &&
2333				        (ctxt->node->type ==
2334					 XML_NAMESPACE_DECL)) {
2335					xmlGenericError(xmlGenericErrorContext,
2336						    "cannot cd to namespace\n");
2337					ctxt->node = NULL;
2338				    }
2339				} else
2340				    xmlGenericError(xmlGenericErrorContext,
2341						    "%s is a %d Node Set\n",
2342						    arg,
2343						    list->nodesetval->nodeNr);
2344                            } else
2345                                xmlGenericError(xmlGenericErrorContext,
2346                                                "%s is an empty Node Set\n",
2347                                                arg);
2348                            break;
2349                        case XPATH_BOOLEAN:
2350                            xmlGenericError(xmlGenericErrorContext,
2351                                            "%s is a Boolean\n", arg);
2352                            break;
2353                        case XPATH_NUMBER:
2354                            xmlGenericError(xmlGenericErrorContext,
2355                                            "%s is a number\n", arg);
2356                            break;
2357                        case XPATH_STRING:
2358                            xmlGenericError(xmlGenericErrorContext,
2359                                            "%s is a string\n", arg);
2360                            break;
2361                        case XPATH_POINT:
2362                            xmlGenericError(xmlGenericErrorContext,
2363                                            "%s is a point\n", arg);
2364                            break;
2365                        case XPATH_RANGE:
2366                            xmlGenericError(xmlGenericErrorContext,
2367                                            "%s is a range\n", arg);
2368                            break;
2369                        case XPATH_LOCATIONSET:
2370                            xmlGenericError(xmlGenericErrorContext,
2371                                            "%s is a range\n", arg);
2372                            break;
2373                        case XPATH_USERS:
2374                            xmlGenericError(xmlGenericErrorContext,
2375                                            "%s is user-defined\n", arg);
2376                            break;
2377                        case XPATH_XSLT_TREE:
2378                            xmlGenericError(xmlGenericErrorContext,
2379                                            "%s is an XSLT value tree\n",
2380                                            arg);
2381                            break;
2382                    }
2383#ifdef LIBXML_XPATH_ENABLED
2384                    xmlXPathFreeObject(list);
2385#endif
2386                } else {
2387                    xmlGenericError(xmlGenericErrorContext,
2388                                    "%s: no such node\n", arg);
2389                }
2390                ctxt->pctxt->node = NULL;
2391            }
2392        } else if (!strcmp(command, "cat")) {
2393            if (arg[0] == 0) {
2394                xmlShellCat(ctxt, NULL, ctxt->node, NULL);
2395            } else {
2396                ctxt->pctxt->node = ctxt->node;
2397#ifdef LIBXML_XPATH_ENABLED
2398                ctxt->pctxt->node = ctxt->node;
2399                list = xmlXPathEval((xmlChar *) arg, ctxt->pctxt);
2400#else
2401                list = NULL;
2402#endif /* LIBXML_XPATH_ENABLED */
2403                if (list != NULL) {
2404                    switch (list->type) {
2405                        case XPATH_UNDEFINED:
2406                            xmlGenericError(xmlGenericErrorContext,
2407                                            "%s: no such node\n", arg);
2408                            break;
2409                        case XPATH_NODESET:{
2410                                int indx;
2411
2412				if (list->nodesetval == NULL)
2413				    break;
2414
2415                                for (indx = 0;
2416                                     indx < list->nodesetval->nodeNr;
2417                                     indx++) {
2418                                    if (i > 0)
2419                                        fprintf(ctxt->output, " -------\n");
2420                                    xmlShellCat(ctxt, NULL,
2421                                                list->nodesetval->
2422                                                nodeTab[indx], NULL);
2423                                }
2424                                break;
2425                            }
2426                        case XPATH_BOOLEAN:
2427                            xmlGenericError(xmlGenericErrorContext,
2428                                            "%s is a Boolean\n", arg);
2429                            break;
2430                        case XPATH_NUMBER:
2431                            xmlGenericError(xmlGenericErrorContext,
2432                                            "%s is a number\n", arg);
2433                            break;
2434                        case XPATH_STRING:
2435                            xmlGenericError(xmlGenericErrorContext,
2436                                            "%s is a string\n", arg);
2437                            break;
2438                        case XPATH_POINT:
2439                            xmlGenericError(xmlGenericErrorContext,
2440                                            "%s is a point\n", arg);
2441                            break;
2442                        case XPATH_RANGE:
2443                            xmlGenericError(xmlGenericErrorContext,
2444                                            "%s is a range\n", arg);
2445                            break;
2446                        case XPATH_LOCATIONSET:
2447                            xmlGenericError(xmlGenericErrorContext,
2448                                            "%s is a range\n", arg);
2449                            break;
2450                        case XPATH_USERS:
2451                            xmlGenericError(xmlGenericErrorContext,
2452                                            "%s is user-defined\n", arg);
2453                            break;
2454                        case XPATH_XSLT_TREE:
2455                            xmlGenericError(xmlGenericErrorContext,
2456                                            "%s is an XSLT value tree\n",
2457                                            arg);
2458                            break;
2459                    }
2460#ifdef LIBXML_XPATH_ENABLED
2461                    xmlXPathFreeObject(list);
2462#endif
2463                } else {
2464                    xmlGenericError(xmlGenericErrorContext,
2465                                    "%s: no such node\n", arg);
2466                }
2467                ctxt->pctxt->node = NULL;
2468            }
2469        } else {
2470            xmlGenericError(xmlGenericErrorContext,
2471                            "Unknown command %s\n", command);
2472        }
2473        free(cmdline);          /* not xmlFree here ! */
2474    }
2475#ifdef LIBXML_XPATH_ENABLED
2476    xmlXPathFreeContext(ctxt->pctxt);
2477#endif /* LIBXML_XPATH_ENABLED */
2478    if (ctxt->loaded) {
2479        xmlFreeDoc(ctxt->doc);
2480    }
2481    if (ctxt->filename != NULL)
2482        xmlFree(ctxt->filename);
2483    xmlFree(ctxt);
2484    if (cmdline != NULL)
2485        free(cmdline);          /* not xmlFree here ! */
2486}
2487
2488#endif /* LIBXML_DEBUG_ENABLED */
2489