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