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