debugXML.c revision d6d7f7bf96a87688cc4bf756cf98367018e3ef88
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@w3.org>
8 */
9
10#ifdef WIN32
11#include "win32config.h"
12#else
13#include "config.h"
14#endif
15
16#include <libxml/xmlversion.h>
17#ifdef LIBXML_DEBUG_ENABLED
18
19#include <stdio.h>
20#include <string.h>
21#ifdef HAVE_STDLIB_H
22#include <stdlib.h>
23#endif
24#ifdef HAVE_STRING_H
25#include <string.h>
26#endif
27#include <libxml/xmlmemory.h>
28#include <libxml/tree.h>
29#include <libxml/parser.h>
30#include <libxml/valid.h>
31#include <libxml/debugXML.h>
32#include <libxml/HTMLtree.h>
33#include <libxml/HTMLparser.h>
34#include <libxml/xmlerror.h>
35
36#define IS_BLANK(c)							\
37  (((c) == '\n') || ((c) == '\r') || ((c) == '\t') || ((c) == ' '))
38
39void xmlDebugDumpString(FILE *output, const xmlChar *str) {
40    int i;
41    if (str == NULL) {
42	fprintf(output, "(NULL)");
43	return;
44    }
45    for (i = 0;i < 40;i++)
46        if (str[i] == 0) return;
47	else if (IS_BLANK(str[i])) fputc(' ', output);
48	else if (str[i] >= 0x80)
49	     fprintf(output, "#%X", str[i]);
50	else fputc(str[i], output);
51    fprintf(output, "...");
52}
53
54void xmlDebugDumpDtd(FILE *output, xmlDtdPtr dtd, int depth) {
55    int i;
56    char shift[100];
57
58    for (i = 0;((i < depth) && (i < 25));i++)
59        shift[2 * i] = shift[2 * i + 1] = ' ';
60    shift[2 * i] = shift[2 * i + 1] = 0;
61
62    fprintf(output, shift);
63
64    if (dtd->type != XML_DTD_NODE) {
65	fprintf(output, "PBM: not a DTD\n");
66	return;
67    }
68    if (dtd->name != NULL)
69	fprintf(output, "DTD(%s)", dtd->name);
70    else
71	fprintf(output, "DTD");
72    if (dtd->ExternalID != NULL)
73	fprintf(output, ", PUBLIC %s", dtd->ExternalID);
74    if (dtd->SystemID != NULL)
75	fprintf(output, ", SYSTEM %s", dtd->SystemID);
76    fprintf(output, "\n");
77    /*
78     * Do a bit of checking
79     */
80    if (dtd->parent == NULL)
81	fprintf(output, "PBM: Dtd has no parent\n");
82    if (dtd->doc == NULL)
83	fprintf(output, "PBM: Dtd has no doc\n");
84    if ((dtd->parent != NULL) && (dtd->doc != dtd->parent->doc))
85	fprintf(output, "PBM: Dtd doc differs from parent's one\n");
86    if (dtd->prev == NULL) {
87	if ((dtd->parent != NULL) && (dtd->parent->children != (xmlNodePtr)dtd))
88	    fprintf(output, "PBM: Dtd has no prev and not first of list\n");
89    } else {
90	if (dtd->prev->next != (xmlNodePtr) dtd)
91	    fprintf(output, "PBM: Dtd prev->next : back link wrong\n");
92    }
93    if (dtd->next == NULL) {
94	if ((dtd->parent != NULL) && (dtd->parent->last != (xmlNodePtr) dtd))
95	    fprintf(output, "PBM: Dtd has no next and not last of list\n");
96    } else {
97	if (dtd->next->prev != (xmlNodePtr) dtd)
98	    fprintf(output, "PBM: Dtd next->prev : forward link wrong\n");
99    }
100}
101
102void xmlDebugDumpAttrDecl(FILE *output, xmlAttributePtr attr, int depth) {
103    int i;
104    char shift[100];
105
106    for (i = 0;((i < depth) && (i < 25));i++)
107        shift[2 * i] = shift[2 * i + 1] = ' ';
108    shift[2 * i] = shift[2 * i + 1] = 0;
109
110    fprintf(output, shift);
111
112    if (attr->type != XML_ATTRIBUTE_DECL) {
113	fprintf(output, "PBM: not a Attr\n");
114	return;
115    }
116    if (attr->name != NULL)
117	fprintf(output, "ATTRDECL(%s)", attr->name);
118    else
119	fprintf(output, "PBM ATTRDECL noname!!!");
120    if (attr->elem != NULL)
121	fprintf(output, " for %s", attr->elem);
122    else
123	fprintf(output, " PBM noelem!!!");
124    switch (attr->atype) {
125        case XML_ATTRIBUTE_CDATA:
126	    fprintf(output, " CDATA");
127	    break;
128        case XML_ATTRIBUTE_ID:
129	    fprintf(output, " ID");
130	    break;
131        case XML_ATTRIBUTE_IDREF:
132	    fprintf(output, " IDREF");
133	    break;
134        case XML_ATTRIBUTE_IDREFS:
135	    fprintf(output, " IDREFS");
136	    break;
137        case XML_ATTRIBUTE_ENTITY:
138	    fprintf(output, " ENTITY");
139	    break;
140        case XML_ATTRIBUTE_ENTITIES:
141	    fprintf(output, " ENTITIES");
142	    break;
143        case XML_ATTRIBUTE_NMTOKEN:
144	    fprintf(output, " NMTOKEN");
145	    break;
146        case XML_ATTRIBUTE_NMTOKENS:
147	    fprintf(output, " NMTOKENS");
148	    break;
149        case XML_ATTRIBUTE_ENUMERATION:
150	    fprintf(output, " ENUMERATION");
151	    break;
152        case XML_ATTRIBUTE_NOTATION:
153	    fprintf(output, " NOTATION ");
154	    break;
155    }
156    if (attr->tree != NULL) {
157	int i;
158	xmlEnumerationPtr cur = attr->tree;
159
160	for (i = 0;i < 5; i++) {
161	    if (i != 0)
162		fprintf(output, "|%s", cur->name);
163	    else
164		fprintf(output, " (%s", cur->name);
165	    cur = cur->next;
166	    if (cur == NULL) break;
167	}
168	if (cur == NULL)
169	    fprintf(output, ")");
170	else
171	    fprintf(output, "...)");
172    }
173    switch (attr->def) {
174        case XML_ATTRIBUTE_NONE:
175	    break;
176        case XML_ATTRIBUTE_REQUIRED:
177	    fprintf(output, " REQUIRED");
178	    break;
179        case XML_ATTRIBUTE_IMPLIED:
180	    fprintf(output, " IMPLIED");
181	    break;
182        case XML_ATTRIBUTE_FIXED:
183	    fprintf(output, " FIXED");
184	    break;
185    }
186    if (attr->defaultValue != NULL) {
187	fprintf(output, "\"");
188	xmlDebugDumpString(output, attr->defaultValue);
189	fprintf(output, "\"");
190    }
191    printf("\n");
192
193    /*
194     * Do a bit of checking
195     */
196    if (attr->parent == NULL)
197	fprintf(output, "PBM: Attr has no parent\n");
198    if (attr->doc == NULL)
199	fprintf(output, "PBM: Attr has no doc\n");
200    if ((attr->parent != NULL) && (attr->doc != attr->parent->doc))
201	fprintf(output, "PBM: Attr doc differs from parent's one\n");
202    if (attr->prev == NULL) {
203	if ((attr->parent != NULL) && (attr->parent->children != (xmlNodePtr)attr))
204	    fprintf(output, "PBM: Attr has no prev and not first of list\n");
205    } else {
206	if (attr->prev->next != (xmlNodePtr) attr)
207	    fprintf(output, "PBM: Attr prev->next : back link wrong\n");
208    }
209    if (attr->next == NULL) {
210	if ((attr->parent != NULL) && (attr->parent->last != (xmlNodePtr) attr))
211	    fprintf(output, "PBM: Attr has no next and not last of list\n");
212    } else {
213	if (attr->next->prev != (xmlNodePtr) attr)
214	    fprintf(output, "PBM: Attr next->prev : forward link wrong\n");
215    }
216}
217
218void xmlDebugDumpElemDecl(FILE *output, xmlElementPtr elem, int depth) {
219    int i;
220    char shift[100];
221
222    for (i = 0;((i < depth) && (i < 25));i++)
223        shift[2 * i] = shift[2 * i + 1] = ' ';
224    shift[2 * i] = shift[2 * i + 1] = 0;
225
226    fprintf(output, shift);
227
228    if (elem->type != XML_ELEMENT_DECL) {
229	fprintf(output, "PBM: not a Elem\n");
230	return;
231    }
232    if (elem->name != NULL) {
233	fprintf(output, "ELEMDECL(");
234	xmlDebugDumpString(output, elem->name);
235	fprintf(output, ")");
236    } else
237	fprintf(output, "PBM ELEMDECL noname!!!");
238    switch (elem->etype) {
239	case XML_ELEMENT_TYPE_EMPTY:
240	    fprintf(output, ", EMPTY");
241	    break;
242	case XML_ELEMENT_TYPE_ANY:
243	    fprintf(output, ", ANY");
244	    break;
245	case XML_ELEMENT_TYPE_MIXED:
246	    fprintf(output, ", MIXED ");
247	    break;
248	case XML_ELEMENT_TYPE_ELEMENT:
249	    fprintf(output, ", MIXED ");
250	    break;
251    }
252    if (elem->content != NULL) {
253	char buf[5001];
254
255	buf[0] = 0;
256	xmlSprintfElementContent(buf, elem->content, 1);
257	buf[5000] = 0;
258	fprintf(output, "%s", buf);
259    }
260    printf("\n");
261
262    /*
263     * Do a bit of checking
264     */
265    if (elem->parent == NULL)
266	fprintf(output, "PBM: Elem has no parent\n");
267    if (elem->doc == NULL)
268	fprintf(output, "PBM: Elem has no doc\n");
269    if ((elem->parent != NULL) && (elem->doc != elem->parent->doc))
270	fprintf(output, "PBM: Elem doc differs from parent's one\n");
271    if (elem->prev == NULL) {
272	if ((elem->parent != NULL) && (elem->parent->children != (xmlNodePtr)elem))
273	    fprintf(output, "PBM: Elem has no prev and not first of list\n");
274    } else {
275	if (elem->prev->next != (xmlNodePtr) elem)
276	    fprintf(output, "PBM: Elem prev->next : back link wrong\n");
277    }
278    if (elem->next == NULL) {
279	if ((elem->parent != NULL) && (elem->parent->last != (xmlNodePtr) elem))
280	    fprintf(output, "PBM: Elem has no next and not last of list\n");
281    } else {
282	if (elem->next->prev != (xmlNodePtr) elem)
283	    fprintf(output, "PBM: Elem next->prev : forward link wrong\n");
284    }
285}
286
287void xmlDebugDumpEntityDecl(FILE *output, xmlEntityPtr ent, int depth) {
288    int i;
289    char shift[100];
290
291    for (i = 0;((i < depth) && (i < 25));i++)
292        shift[2 * i] = shift[2 * i + 1] = ' ';
293    shift[2 * i] = shift[2 * i + 1] = 0;
294
295    fprintf(output, shift);
296
297    if (ent->type != XML_ENTITY_DECL) {
298	fprintf(output, "PBM: not a Entity decl\n");
299	return;
300    }
301    if (ent->name != NULL) {
302	fprintf(output, "ENTITYDECL(");
303	xmlDebugDumpString(output, ent->name);
304	fprintf(output, ")");
305    } else
306	fprintf(output, "PBM ENTITYDECL noname!!!");
307    switch (ent->etype) {
308	case XML_INTERNAL_GENERAL_ENTITY:
309	    fprintf(output, ", internal\n");
310	    break;
311	case XML_EXTERNAL_GENERAL_PARSED_ENTITY:
312	    fprintf(output, ", external parsed\n");
313	    break;
314	case XML_EXTERNAL_GENERAL_UNPARSED_ENTITY:
315	    fprintf(output, ", unparsed\n");
316	    break;
317	case XML_INTERNAL_PARAMETER_ENTITY:
318	    fprintf(output, ", parameter\n");
319	    break;
320	case XML_EXTERNAL_PARAMETER_ENTITY:
321	    fprintf(output, ", external parameter\n");
322	    break;
323	case XML_INTERNAL_PREDEFINED_ENTITY:
324	    fprintf(output, ", predefined\n");
325	    break;
326    }
327    if (ent->ExternalID) {
328        fprintf(output, shift);
329        fprintf(output, " ExternalID=%s\n", ent->ExternalID);
330    }
331    if (ent->SystemID) {
332        fprintf(output, shift);
333        fprintf(output, " SystemID=%s\n", ent->SystemID);
334    }
335    if (ent->URI != NULL) {
336        fprintf(output, shift);
337        fprintf(output, " URI=%s\n", ent->URI);
338    }
339    if (ent->content) {
340        fprintf(output, shift);
341	fprintf(output, " content=");
342	xmlDebugDumpString(output, ent->content);
343	fprintf(output, "\n");
344    }
345
346    /*
347     * Do a bit of checking
348     */
349    if (ent->parent == NULL)
350	fprintf(output, "PBM: Ent has no parent\n");
351    if (ent->doc == NULL)
352	fprintf(output, "PBM: Ent has no doc\n");
353    if ((ent->parent != NULL) && (ent->doc != ent->parent->doc))
354	fprintf(output, "PBM: Ent doc differs from parent's one\n");
355    if (ent->prev == NULL) {
356	if ((ent->parent != NULL) && (ent->parent->children != (xmlNodePtr)ent))
357	    fprintf(output, "PBM: Ent has no prev and not first of list\n");
358    } else {
359	if (ent->prev->next != (xmlNodePtr) ent)
360	    fprintf(output, "PBM: Ent prev->next : back link wrong\n");
361    }
362    if (ent->next == NULL) {
363	if ((ent->parent != NULL) && (ent->parent->last != (xmlNodePtr) ent))
364	    fprintf(output, "PBM: Ent has no next and not last of list\n");
365    } else {
366	if (ent->next->prev != (xmlNodePtr) ent)
367	    fprintf(output, "PBM: Ent next->prev : forward link wrong\n");
368    }
369}
370
371void xmlDebugDumpNamespace(FILE *output, xmlNsPtr ns, int depth) {
372    int i;
373    char shift[100];
374
375    for (i = 0;((i < depth) && (i < 25));i++)
376        shift[2 * i] = shift[2 * i + 1] = ' ';
377    shift[2 * i] = shift[2 * i + 1] = 0;
378
379    fprintf(output, shift);
380    if (ns->type == XML_GLOBAL_NAMESPACE)
381        fprintf(output, "old ");
382    if (ns->href == NULL) {
383	if (ns->prefix != NULL)
384	    fprintf(output, "incomplete namespace %s href=NULL\n", ns->prefix);
385	else
386	    fprintf(output, "incomplete default namespace href=NULL\n");
387    } else {
388	if (ns->prefix != NULL)
389	    fprintf(output, "namespace %s href=", ns->prefix);
390	else
391	    fprintf(output, "default namespace href=");
392
393	xmlDebugDumpString(output, ns->href);
394	fprintf(output, "\n");
395    }
396}
397
398void xmlDebugDumpNamespaceList(FILE *output, xmlNsPtr ns, int depth) {
399    while (ns != NULL) {
400        xmlDebugDumpNamespace(output, ns, depth);
401	ns = ns->next;
402    }
403}
404
405void xmlDebugDumpEntity(FILE *output, xmlEntityPtr ent, int depth) {
406    int i;
407    char shift[100];
408
409    for (i = 0;((i < depth) && (i < 25));i++)
410        shift[2 * i] = shift[2 * i + 1] = ' ';
411    shift[2 * i] = shift[2 * i + 1] = 0;
412
413    fprintf(output, shift);
414    switch (ent->etype) {
415        case XML_INTERNAL_GENERAL_ENTITY:
416	    fprintf(output, "INTERNAL_GENERAL_ENTITY ");
417	    break;
418        case XML_EXTERNAL_GENERAL_PARSED_ENTITY:
419	    fprintf(output, "EXTERNAL_GENERAL_PARSED_ENTITY ");
420	    break;
421        case XML_EXTERNAL_GENERAL_UNPARSED_ENTITY:
422	    fprintf(output, "EXTERNAL_GENERAL_UNPARSED_ENTITY ");
423	    break;
424        case XML_INTERNAL_PARAMETER_ENTITY:
425	    fprintf(output, "INTERNAL_PARAMETER_ENTITY ");
426	    break;
427        case XML_EXTERNAL_PARAMETER_ENTITY:
428	    fprintf(output, "EXTERNAL_PARAMETER_ENTITY ");
429	    break;
430	default:
431	    fprintf(output, "ENTITY_%d ! ", ent->etype);
432    }
433    fprintf(output, "%s\n", ent->name);
434    if (ent->ExternalID) {
435        fprintf(output, shift);
436        fprintf(output, "ExternalID=%s\n", ent->ExternalID);
437    }
438    if (ent->SystemID) {
439        fprintf(output, shift);
440        fprintf(output, "SystemID=%s\n", ent->SystemID);
441    }
442    if (ent->URI) {
443        fprintf(output, shift);
444        fprintf(output, "URI=%s\n", ent->URI);
445    }
446    if (ent->content) {
447        fprintf(output, shift);
448	fprintf(output, "content=");
449	xmlDebugDumpString(output, ent->content);
450	fprintf(output, "\n");
451    }
452}
453
454void xmlDebugDumpAttr(FILE *output, xmlAttrPtr attr, int depth) {
455    int i;
456    char shift[100];
457
458    for (i = 0;((i < depth) && (i < 25));i++)
459        shift[2 * i] = shift[2 * i + 1] = ' ';
460    shift[2 * i] = shift[2 * i + 1] = 0;
461
462    fprintf(output, shift);
463
464    fprintf(output, "ATTRIBUTE ");
465    xmlDebugDumpString(output, attr->name);
466    fprintf(output, "\n");
467    if (attr->children != NULL)
468        xmlDebugDumpNodeList(output, attr->children, depth + 1);
469
470    /*
471     * Do a bit of checking
472     */
473    if (attr->parent == NULL)
474	fprintf(output, "PBM: Attr has no parent\n");
475    if (attr->doc == NULL)
476	fprintf(output, "PBM: Attr has no doc\n");
477    if ((attr->parent != NULL) && (attr->doc != attr->parent->doc))
478	fprintf(output, "PBM: Attr doc differs from parent's one\n");
479    if (attr->prev == NULL) {
480	if ((attr->parent != NULL) && (attr->parent->properties != attr))
481	    fprintf(output, "PBM: Attr has no prev and not first of list\n");
482    } else {
483	if (attr->prev->next != attr)
484	    fprintf(output, "PBM: Attr prev->next : back link wrong\n");
485    }
486    if (attr->next != NULL) {
487	if (attr->next->prev != attr)
488	    fprintf(output, "PBM: Attr next->prev : forward link wrong\n");
489    }
490}
491
492void xmlDebugDumpAttrList(FILE *output, xmlAttrPtr attr, int depth) {
493    while (attr != NULL) {
494        xmlDebugDumpAttr(output, attr, depth);
495	attr = attr->next;
496    }
497}
498
499void xmlDebugDumpOneNode(FILE *output, xmlNodePtr node, int depth) {
500    int i;
501    char shift[100];
502
503    for (i = 0;((i < depth) && (i < 25));i++)
504        shift[2 * i] = shift[2 * i + 1] = ' ';
505    shift[2 * i] = shift[2 * i + 1] = 0;
506
507    switch (node->type) {
508	case XML_ELEMENT_NODE:
509	    fprintf(output, shift);
510	    fprintf(output, "ELEMENT ");
511	    if (node->ns != NULL) {
512		xmlDebugDumpString(output, node->ns->prefix);
513	        fprintf(output, ":");
514	    }
515	    xmlDebugDumpString(output, node->name);
516	    fprintf(output, "\n");
517	    break;
518	case XML_ATTRIBUTE_NODE:
519	    fprintf(output, shift);
520	    fprintf(output, "Error, ATTRIBUTE found here\n");
521	    break;
522	case XML_TEXT_NODE:
523	    fprintf(output, shift);
524	    fprintf(output, "TEXT\n");
525	    break;
526	case XML_CDATA_SECTION_NODE:
527	    fprintf(output, shift);
528	    fprintf(output, "CDATA_SECTION\n");
529	    break;
530	case XML_ENTITY_REF_NODE:
531	    fprintf(output, shift);
532	    fprintf(output, "ENTITY_REF(%s)\n", node->name);
533	    break;
534	case XML_ENTITY_NODE:
535	    fprintf(output, shift);
536	    fprintf(output, "ENTITY\n");
537	    break;
538	case XML_PI_NODE:
539	    fprintf(output, shift);
540	    fprintf(output, "PI %s\n", node->name);
541	    break;
542	case XML_COMMENT_NODE:
543	    fprintf(output, shift);
544	    fprintf(output, "COMMENT\n");
545	    break;
546	case XML_DOCUMENT_NODE:
547	case XML_HTML_DOCUMENT_NODE:
548	    fprintf(output, shift);
549	    fprintf(output, "Error, DOCUMENT found here\n");
550	    break;
551	case XML_DOCUMENT_TYPE_NODE:
552	    fprintf(output, shift);
553	    fprintf(output, "DOCUMENT_TYPE\n");
554	    break;
555	case XML_DOCUMENT_FRAG_NODE:
556	    fprintf(output, shift);
557	    fprintf(output, "DOCUMENT_FRAG\n");
558	    break;
559	case XML_NOTATION_NODE:
560	    fprintf(output, "NOTATION\n");
561	    break;
562	case XML_DTD_NODE:
563	    xmlDebugDumpDtd(output, (xmlDtdPtr) node, depth);
564	    return;
565	case XML_ELEMENT_DECL:
566	    xmlDebugDumpElemDecl(output, (xmlElementPtr) node, depth);
567	    return;
568	case XML_ATTRIBUTE_DECL:
569	    xmlDebugDumpAttrDecl(output, (xmlAttributePtr) node, depth);
570	    return;
571        case XML_ENTITY_DECL:
572	    xmlDebugDumpEntityDecl(output, (xmlEntityPtr) node, depth);
573	    return;
574	default:
575	    fprintf(output, shift);
576	    fprintf(output, "NODE_%d !!!\n", node->type);
577	    return;
578    }
579    if (node->doc == NULL) {
580        fprintf(output, shift);
581	fprintf(output, "doc == NULL !!!\n");
582    }
583    if (node->nsDef != NULL)
584        xmlDebugDumpNamespaceList(output, node->nsDef, depth + 1);
585    if (node->properties != NULL)
586	xmlDebugDumpAttrList(output, node->properties, depth + 1);
587    if (node->type != XML_ENTITY_REF_NODE) {
588	if (node->content != NULL) {
589            shift[2 * i] = shift[2 * i + 1] = ' ' ;
590            shift[2 * i + 2] = shift[2 * i + 3] = 0 ;
591	    fprintf(output, shift);
592	    fprintf(output, "content=");
593#ifndef XML_USE_BUFFER_CONTENT
594	    xmlDebugDumpString(output, node->content);
595#else
596	    xmlDebugDumpString(output, xmlBufferContent(node->content));
597#endif
598	    fprintf(output, "\n");
599	}
600    } else {
601        xmlEntityPtr ent;
602	ent = xmlGetDocEntity(node->doc, node->name);
603	if (ent != NULL)
604	    xmlDebugDumpEntity(output, ent, depth + 1);
605    }
606    /*
607     * Do a bit of checking
608     */
609    if (node->parent == NULL)
610	fprintf(output, "PBM: Node has no parent\n");
611    if (node->doc == NULL)
612	fprintf(output, "PBM: Node has no doc\n");
613    if ((node->parent != NULL) && (node->doc != node->parent->doc))
614	fprintf(output, "PBM: Node doc differs from parent's one\n");
615    if (node->prev == NULL) {
616	if ((node->parent != NULL) && (node->parent->children != node))
617	    fprintf(output, "PBM: Node has no prev and not first of list\n");
618    } else {
619	if (node->prev->next != node)
620	    fprintf(output, "PBM: Node prev->next : back link wrong\n");
621    }
622    if (node->next == NULL) {
623	if ((node->parent != NULL) && (node->parent->last != node))
624	    fprintf(output, "PBM: Node has no next and not last of list\n");
625    } else {
626	if (node->next->prev != node)
627	    fprintf(output, "PBM: Node next->prev : forward link wrong\n");
628    }
629}
630
631void xmlDebugDumpNode(FILE *output, xmlNodePtr node, int depth) {
632    xmlDebugDumpOneNode(output, node, depth);
633    if ((node->children != NULL) && (node->type != XML_ENTITY_REF_NODE))
634	xmlDebugDumpNodeList(output, node->children, depth + 1);
635}
636
637void xmlDebugDumpNodeList(FILE *output, xmlNodePtr node, int depth) {
638    while (node != NULL) {
639        xmlDebugDumpNode(output, node, depth);
640	node = node->next;
641    }
642}
643
644
645void xmlDebugDumpDocumentHead(FILE *output, xmlDocPtr doc) {
646    if (output == NULL) output = stdout;
647    if (doc == NULL) {
648        fprintf(output, "DOCUMENT == NULL !\n");
649	return;
650    }
651
652    switch (doc->type) {
653	case XML_ELEMENT_NODE:
654	    fprintf(output, "Error, ELEMENT found here ");
655	    break;
656	case XML_ATTRIBUTE_NODE:
657	    fprintf(output, "Error, ATTRIBUTE found here\n");
658	    break;
659	case XML_TEXT_NODE:
660	    fprintf(output, "Error, TEXT\n");
661	    break;
662	case XML_CDATA_SECTION_NODE:
663	    fprintf(output, "Error, CDATA_SECTION\n");
664	    break;
665	case XML_ENTITY_REF_NODE:
666	    fprintf(output, "Error, ENTITY_REF\n");
667	    break;
668	case XML_ENTITY_NODE:
669	    fprintf(output, "Error, ENTITY\n");
670	    break;
671	case XML_PI_NODE:
672	    fprintf(output, "Error, PI\n");
673	    break;
674	case XML_COMMENT_NODE:
675	    fprintf(output, "Error, COMMENT\n");
676	    break;
677	case XML_DOCUMENT_NODE:
678	    fprintf(output, "DOCUMENT\n");
679	    break;
680	case XML_HTML_DOCUMENT_NODE:
681	    fprintf(output, "HTML DOCUMENT\n");
682	    break;
683	case XML_DOCUMENT_TYPE_NODE:
684	    fprintf(output, "Error, DOCUMENT_TYPE\n");
685	    break;
686	case XML_DOCUMENT_FRAG_NODE:
687	    fprintf(output, "Error, DOCUMENT_FRAG\n");
688	    break;
689	case XML_NOTATION_NODE:
690	    fprintf(output, "Error, NOTATION\n");
691	    break;
692	default:
693	    fprintf(output, "NODE_%d\n", doc->type);
694    }
695    if (doc->name != NULL) {
696	fprintf(output, "name=");
697        xmlDebugDumpString(output, BAD_CAST doc->name);
698	fprintf(output, "\n");
699    }
700    if (doc->version != NULL) {
701	fprintf(output, "version=");
702        xmlDebugDumpString(output, doc->version);
703	fprintf(output, "\n");
704    }
705    if (doc->encoding != NULL) {
706	fprintf(output, "encoding=");
707        xmlDebugDumpString(output, doc->encoding);
708	fprintf(output, "\n");
709    }
710    if (doc->URL != NULL) {
711	fprintf(output, "URL=");
712        xmlDebugDumpString(output, doc->URL);
713	fprintf(output, "\n");
714    }
715    if (doc->standalone)
716        fprintf(output, "standalone=true\n");
717    if (doc->oldNs != NULL)
718        xmlDebugDumpNamespaceList(output, doc->oldNs, 0);
719}
720
721void xmlDebugDumpDocument(FILE *output, xmlDocPtr doc) {
722    if (output == NULL) output = stdout;
723    if (doc == NULL) {
724        fprintf(output, "DOCUMENT == NULL !\n");
725	return;
726    }
727    xmlDebugDumpDocumentHead(output, doc);
728    if (((doc->type == XML_DOCUMENT_NODE) ||
729         (doc->type == XML_HTML_DOCUMENT_NODE)) &&
730        (doc->children != NULL))
731        xmlDebugDumpNodeList(output, doc->children, 1);
732}
733
734void xmlDebugDumpDTD(FILE *output, xmlDtdPtr dtd) {
735    if (dtd == NULL)
736	return;
737    if (dtd->type != XML_DTD_NODE) {
738	fprintf(output, "PBM: not a DTD\n");
739	return;
740    }
741    if (dtd->name != NULL)
742	fprintf(output, "DTD(%s)", dtd->name);
743    else
744	fprintf(output, "DTD");
745    if (dtd->ExternalID != NULL)
746	fprintf(output, ", PUBLIC %s", dtd->ExternalID);
747    if (dtd->SystemID != NULL)
748	fprintf(output, ", SYSTEM %s", dtd->SystemID);
749    fprintf(output, "\n");
750    /*
751     * Do a bit of checking
752     */
753    if ((dtd->parent != NULL) && (dtd->doc != dtd->parent->doc))
754	fprintf(output, "PBM: Dtd doc differs from parent's one\n");
755    if (dtd->prev == NULL) {
756	if ((dtd->parent != NULL) && (dtd->parent->children != (xmlNodePtr)dtd))
757	    fprintf(output, "PBM: Dtd has no prev and not first of list\n");
758    } else {
759	if (dtd->prev->next != (xmlNodePtr) dtd)
760	    fprintf(output, "PBM: Dtd prev->next : back link wrong\n");
761    }
762    if (dtd->next == NULL) {
763	if ((dtd->parent != NULL) && (dtd->parent->last != (xmlNodePtr) dtd))
764	    fprintf(output, "PBM: Dtd has no next and not last of list\n");
765    } else {
766	if (dtd->next->prev != (xmlNodePtr) dtd)
767	    fprintf(output, "PBM: Dtd next->prev : forward link wrong\n");
768    }
769    if (dtd->children == NULL)
770	fprintf(output, "    DTD is empty\n");
771    else
772        xmlDebugDumpNodeList(output, dtd->children, 1);
773}
774
775void xmlDebugDumpEntityCallback(xmlEntityPtr cur, FILE *output,
776	                        const xmlChar *name) {
777    fprintf(output, "%s : ", cur->name);
778    switch (cur->etype) {
779	case XML_INTERNAL_GENERAL_ENTITY:
780	    fprintf(output, "INTERNAL GENERAL, ");
781	    break;
782	case XML_EXTERNAL_GENERAL_PARSED_ENTITY:
783	    fprintf(output, "EXTERNAL PARSED, ");
784	    break;
785	case XML_EXTERNAL_GENERAL_UNPARSED_ENTITY:
786	    fprintf(output, "EXTERNAL UNPARSED, ");
787	    break;
788	case XML_INTERNAL_PARAMETER_ENTITY:
789	    fprintf(output, "INTERNAL PARAMETER, ");
790	    break;
791	case XML_EXTERNAL_PARAMETER_ENTITY:
792	    fprintf(output, "EXTERNAL PARAMETER, ");
793	    break;
794	default:
795	    fprintf(output, "UNKNOWN TYPE %d",
796		    cur->etype);
797    }
798    if (cur->ExternalID != NULL)
799	fprintf(output, "ID \"%s\"", cur->ExternalID);
800    if (cur->SystemID != NULL)
801	fprintf(output, "SYSTEM \"%s\"", cur->SystemID);
802    if (cur->orig != NULL)
803	fprintf(output, "\n orig \"%s\"", cur->orig);
804    if (cur->content != NULL)
805	fprintf(output, "\n content \"%s\"", cur->content);
806    fprintf(output, "\n");
807}
808
809void xmlDebugDumpEntities(FILE *output, xmlDocPtr doc) {
810    if (output == NULL) output = stdout;
811    if (doc == NULL) {
812        fprintf(output, "DOCUMENT == NULL !\n");
813	return;
814    }
815
816    switch (doc->type) {
817	case XML_ELEMENT_NODE:
818	    fprintf(output, "Error, ELEMENT found here ");
819	    break;
820	case XML_ATTRIBUTE_NODE:
821	    fprintf(output, "Error, ATTRIBUTE found here\n");
822	    break;
823	case XML_TEXT_NODE:
824	    fprintf(output, "Error, TEXT\n");
825	    break;
826	case XML_CDATA_SECTION_NODE:
827	    fprintf(output, "Error, CDATA_SECTION\n");
828	    break;
829	case XML_ENTITY_REF_NODE:
830	    fprintf(output, "Error, ENTITY_REF\n");
831	    break;
832	case XML_ENTITY_NODE:
833	    fprintf(output, "Error, ENTITY\n");
834	    break;
835	case XML_PI_NODE:
836	    fprintf(output, "Error, PI\n");
837	    break;
838	case XML_COMMENT_NODE:
839	    fprintf(output, "Error, COMMENT\n");
840	    break;
841	case XML_DOCUMENT_NODE:
842	    fprintf(output, "DOCUMENT\n");
843	    break;
844	case XML_HTML_DOCUMENT_NODE:
845	    fprintf(output, "HTML DOCUMENT\n");
846	    break;
847	case XML_DOCUMENT_TYPE_NODE:
848	    fprintf(output, "Error, DOCUMENT_TYPE\n");
849	    break;
850	case XML_DOCUMENT_FRAG_NODE:
851	    fprintf(output, "Error, DOCUMENT_FRAG\n");
852	    break;
853	case XML_NOTATION_NODE:
854	    fprintf(output, "Error, NOTATION\n");
855	    break;
856	default:
857	    fprintf(output, "NODE_%d\n", doc->type);
858    }
859    if ((doc->intSubset != NULL) && (doc->intSubset->entities != NULL)) {
860        xmlEntitiesTablePtr table = (xmlEntitiesTablePtr)
861	                            doc->intSubset->entities;
862	fprintf(output, "Entities in internal subset\n");
863	xmlHashScan(table, (xmlHashScanner)xmlDebugDumpEntityCallback, output);
864    } else
865	fprintf(output, "No entities in internal subset\n");
866    if ((doc->extSubset != NULL) && (doc->extSubset->entities != NULL)) {
867        xmlEntitiesTablePtr table = (xmlEntitiesTablePtr)
868	                            doc->extSubset->entities;
869	fprintf(output, "Entities in external subset\n");
870	xmlHashScan(table, (xmlHashScanner)xmlDebugDumpEntityCallback, output);
871    } else
872	fprintf(output, "No entities in external subset\n");
873}
874
875static int xmlLsCountNode(xmlNodePtr node) {
876    int ret = 0;
877    xmlNodePtr list = NULL;
878
879    switch (node->type) {
880	case XML_ELEMENT_NODE:
881	    list = node->children;
882	    break;
883	case XML_DOCUMENT_NODE:
884	case XML_HTML_DOCUMENT_NODE:
885#ifdef LIBXML_SGML_ENABLED
886	case XML_SGML_DOCUMENT_NODE:
887#endif
888	    list = ((xmlDocPtr) node)->children;
889	    break;
890	case XML_ATTRIBUTE_NODE:
891	    list = ((xmlAttrPtr) node)->children;
892	    break;
893	case XML_TEXT_NODE:
894	case XML_CDATA_SECTION_NODE:
895	case XML_PI_NODE:
896	case XML_COMMENT_NODE:
897	    if (node->content != NULL) {
898#ifndef XML_USE_BUFFER_CONTENT
899		ret = xmlStrlen(node->content);
900#else
901		ret = xmlBufferLength(node->content);
902#endif
903            }
904	    break;
905	case XML_ENTITY_REF_NODE:
906	case XML_DOCUMENT_TYPE_NODE:
907	case XML_ENTITY_NODE:
908	case XML_DOCUMENT_FRAG_NODE:
909	case XML_NOTATION_NODE:
910	case XML_DTD_NODE:
911        case XML_ELEMENT_DECL:
912        case XML_ATTRIBUTE_DECL:
913        case XML_ENTITY_DECL:
914	    ret = 1;
915	    break;
916    }
917    for (;list != NULL;ret++)
918        list = list->next;
919    return(ret);
920}
921
922void xmlLsOneNode(FILE *output, xmlNodePtr node) {
923    switch (node->type) {
924	case XML_ELEMENT_NODE:
925	    fprintf(output, "-");
926	    break;
927	case XML_ATTRIBUTE_NODE:
928	    fprintf(output, "a");
929	    break;
930	case XML_TEXT_NODE:
931	    fprintf(output, "t");
932	    break;
933	case XML_CDATA_SECTION_NODE:
934	    fprintf(output, "c");
935	    break;
936	case XML_ENTITY_REF_NODE:
937	    fprintf(output, "e");
938	    break;
939	case XML_ENTITY_NODE:
940	    fprintf(output, "E");
941	    break;
942	case XML_PI_NODE:
943	    fprintf(output, "p");
944	    break;
945	case XML_COMMENT_NODE:
946	    fprintf(output, "c");
947	    break;
948	case XML_DOCUMENT_NODE:
949	    fprintf(output, "d");
950	    break;
951	case XML_HTML_DOCUMENT_NODE:
952	    fprintf(output, "h");
953	    break;
954	case XML_DOCUMENT_TYPE_NODE:
955	    fprintf(output, "T");
956	    break;
957	case XML_DOCUMENT_FRAG_NODE:
958	    fprintf(output, "F");
959	    break;
960	case XML_NOTATION_NODE:
961	    fprintf(output, "N");
962	    break;
963	default:
964	    fprintf(output, "?");
965    }
966    if (node->properties != NULL)
967	fprintf(output, "a");
968    else
969	fprintf(output, "-");
970    if (node->nsDef != NULL)
971	fprintf(output, "n");
972    else
973	fprintf(output, "-");
974
975    fprintf(output, " %8d ", xmlLsCountNode(node));
976
977    switch (node->type) {
978	case XML_ELEMENT_NODE:
979	    if (node->name != NULL)
980		fprintf(output, "%s", node->name);
981	    break;
982	case XML_ATTRIBUTE_NODE:
983	    if (node->name != NULL)
984		fprintf(output, "%s", node->name);
985	    break;
986	case XML_TEXT_NODE:
987	    if (node->content != NULL) {
988#ifndef XML_USE_BUFFER_CONTENT
989		xmlDebugDumpString(output, node->content);
990#else
991		xmlDebugDumpString(output, xmlBufferContent(node->content));
992#endif
993            }
994	    break;
995	case XML_CDATA_SECTION_NODE:
996	    break;
997	case XML_ENTITY_REF_NODE:
998	    if (node->name != NULL)
999		fprintf(output, "%s", node->name);
1000	    break;
1001	case XML_ENTITY_NODE:
1002	    if (node->name != NULL)
1003		fprintf(output, "%s", node->name);
1004	    break;
1005	case XML_PI_NODE:
1006	    if (node->name != NULL)
1007		fprintf(output, "%s", node->name);
1008	    break;
1009	case XML_COMMENT_NODE:
1010	    break;
1011	case XML_DOCUMENT_NODE:
1012	    break;
1013	case XML_HTML_DOCUMENT_NODE:
1014	    break;
1015	case XML_DOCUMENT_TYPE_NODE:
1016	    break;
1017	case XML_DOCUMENT_FRAG_NODE:
1018	    break;
1019	case XML_NOTATION_NODE:
1020	    break;
1021	default:
1022	    if (node->name != NULL)
1023		fprintf(output, "%s", node->name);
1024    }
1025    fprintf(output, "\n");
1026}
1027
1028/****************************************************************
1029 *								*
1030 *	 	The XML shell related functions			*
1031 *								*
1032 ****************************************************************/
1033
1034/*
1035 * TODO: Improvement/cleanups for the XML shell
1036 *     - allow to shell out an editor on a subpart
1037 *     - cleanup function registrations (with help) and calling
1038 *     - provide registration routines
1039 */
1040
1041/**
1042 * xmlShellList:
1043 * @ctxt:  the shell context
1044 * @arg:  unused
1045 * @node:  a node
1046 * @node2:  unused
1047 *
1048 * Implements the XML shell function "ls"
1049 * Does an Unix like listing of the given node (like a directory)
1050 *
1051 * Returns 0
1052 */
1053int
1054xmlShellList(xmlShellCtxtPtr ctxt, char *arg, xmlNodePtr node,
1055                  xmlNodePtr node2) {
1056    xmlNodePtr cur;
1057
1058    if ((node->type == XML_DOCUMENT_NODE) ||
1059        (node->type == XML_HTML_DOCUMENT_NODE)) {
1060        cur = ((xmlDocPtr) node)->children;
1061    } else if (node->children != NULL) {
1062        cur = node->children;
1063    } else {
1064	xmlLsOneNode(stdout, node);
1065        return(0);
1066    }
1067    while (cur != NULL) {
1068	xmlLsOneNode(stdout, cur);
1069	cur = cur->next;
1070    }
1071    return(0);
1072}
1073
1074/**
1075 * xmlShellDir:
1076 * @ctxt:  the shell context
1077 * @arg:  unused
1078 * @node:  a node
1079 * @node2:  unused
1080 *
1081 * Implements the XML shell function "dir"
1082 * dumps informations about the node (namespace, attributes, content).
1083 *
1084 * Returns 0
1085 */
1086int
1087xmlShellDir(xmlShellCtxtPtr ctxt, char *arg, xmlNodePtr node,
1088                  xmlNodePtr node2) {
1089    if ((node->type == XML_DOCUMENT_NODE) ||
1090        (node->type == XML_HTML_DOCUMENT_NODE)) {
1091	xmlDebugDumpDocumentHead(stdout, (xmlDocPtr) node);
1092    } else if (node->type == XML_ATTRIBUTE_NODE) {
1093	xmlDebugDumpAttr(stdout, (xmlAttrPtr) node, 0);
1094    } else {
1095	xmlDebugDumpOneNode(stdout, node, 0);
1096    }
1097    return(0);
1098}
1099
1100/**
1101 * xmlShellCat:
1102 * @ctxt:  the shell context
1103 * @arg:  unused
1104 * @node:  a node
1105 * @node2:  unused
1106 *
1107 * Implements the XML shell function "cat"
1108 * dumps the serialization node content (XML or HTML).
1109 *
1110 * Returns 0
1111 */
1112int
1113xmlShellCat(xmlShellCtxtPtr ctxt, char *arg, xmlNodePtr node,
1114                  xmlNodePtr node2) {
1115    if (ctxt->doc->type == XML_HTML_DOCUMENT_NODE) {
1116#ifdef LIBXML_HTML_ENABLED
1117	if (node->type == XML_HTML_DOCUMENT_NODE)
1118	    htmlDocDump(stdout, (htmlDocPtr) node);
1119	else
1120	    htmlNodeDumpFile(stdout, ctxt->doc, node);
1121#else
1122	if (node->type == XML_DOCUMENT_NODE)
1123	    xmlDocDump(stdout, (xmlDocPtr) node);
1124	else
1125	    xmlElemDump(stdout, ctxt->doc, node);
1126#endif /* LIBXML_HTML_ENABLED */
1127    } else {
1128	if (node->type == XML_DOCUMENT_NODE)
1129	    xmlDocDump(stdout, (xmlDocPtr) node);
1130	else
1131	    xmlElemDump(stdout, ctxt->doc, node);
1132    }
1133    printf("\n");
1134    return(0);
1135}
1136
1137/**
1138 * xmlShellLoad:
1139 * @ctxt:  the shell context
1140 * @filename:  the file name
1141 * @node:  unused
1142 * @node2:  unused
1143 *
1144 * Implements the XML shell function "load"
1145 * loads a new document specified by the filename
1146 *
1147 * Returns 0 or -1 if loading failed
1148 */
1149int
1150xmlShellLoad(xmlShellCtxtPtr ctxt, char *filename, xmlNodePtr node,
1151             xmlNodePtr node2) {
1152    xmlDocPtr doc;
1153    int html = 0;
1154
1155    if (ctxt->doc != NULL)
1156	html = (ctxt->doc->type == XML_HTML_DOCUMENT_NODE);
1157
1158    if (html) {
1159#ifdef LIBXML_HTML_ENABLED
1160	doc = htmlParseFile(filename, NULL);
1161#else
1162	printf("HTML support not compiled in\n");
1163	doc = NULL;
1164#endif /* LIBXML_HTML_ENABLED */
1165    } else {
1166	doc = xmlParseFile(filename);
1167    }
1168    if (doc != NULL) {
1169        if (ctxt->loaded == 1) {
1170	    xmlFreeDoc(ctxt->doc);
1171	}
1172	ctxt->loaded = 1;
1173#ifdef LIBXML_XPATH_ENABLED
1174	xmlXPathFreeContext(ctxt->pctxt);
1175#endif /* LIBXML_XPATH_ENABLED */
1176	xmlFree(ctxt->filename);
1177	ctxt->doc = doc;
1178	ctxt->node = (xmlNodePtr) doc;
1179#ifdef LIBXML_XPATH_ENABLED
1180	ctxt->pctxt = xmlXPathNewContext(doc);
1181#endif /* LIBXML_XPATH_ENABLED */
1182	ctxt->filename = (char *) xmlStrdup((xmlChar *) filename);
1183    } else
1184        return(-1);
1185    return(0);
1186}
1187
1188/**
1189 * xmlShellWrite:
1190 * @ctxt:  the shell context
1191 * @filename:  the file name
1192 * @node:  a node in the tree
1193 * @node2:  unused
1194 *
1195 * Implements the XML shell function "write"
1196 * Write the current node to the filename, it saves the serailization
1197 * of the subtree under the @node specified
1198 *
1199 * Returns 0 or -1 in case of error
1200 */
1201int
1202xmlShellWrite(xmlShellCtxtPtr ctxt, char *filename, xmlNodePtr node,
1203                  xmlNodePtr node2) {
1204    if (node == NULL)
1205        return(-1);
1206    if ((filename == NULL) || (filename[0] == 0)) {
1207        xmlGenericError(xmlGenericErrorContext,
1208		"Write command requires a filename argument\n");
1209	return(-1);
1210    }
1211#ifdef W_OK
1212    if (access((char *) filename, W_OK)) {
1213        xmlGenericError(xmlGenericErrorContext,
1214		"Cannot write to %s\n", filename);
1215	return(-1);
1216    }
1217#endif
1218    switch(node->type) {
1219        case XML_DOCUMENT_NODE:
1220	    if (xmlSaveFile((char *) filename, ctxt->doc) < -1) {
1221		xmlGenericError(xmlGenericErrorContext,
1222			"Failed to write to %s\n", filename);
1223		return(-1);
1224	    }
1225	    break;
1226        case XML_HTML_DOCUMENT_NODE:
1227#ifdef LIBXML_HTML_ENABLED
1228	    if (htmlSaveFile((char *) filename, ctxt->doc) < 0) {
1229		xmlGenericError(xmlGenericErrorContext,
1230			"Failed to write to %s\n", filename);
1231		return(-1);
1232	    }
1233#else
1234	    if (xmlSaveFile((char *) filename, ctxt->doc) < -1) {
1235		xmlGenericError(xmlGenericErrorContext,
1236			"Failed to write to %s\n", filename);
1237		return(-1);
1238	    }
1239#endif /* LIBXML_HTML_ENABLED */
1240	    break;
1241	default: {
1242	    FILE *f;
1243
1244	    f = fopen((char *) filename, "w");
1245	    if (f == NULL) {
1246		xmlGenericError(xmlGenericErrorContext,
1247			"Failed to write to %s\n", filename);
1248		return(-1);
1249	    }
1250	    xmlElemDump(f, ctxt->doc, node);
1251	    fclose(f);
1252	}
1253    }
1254    return(0);
1255}
1256
1257/**
1258 * xmlShellSave:
1259 * @ctxt:  the shell context
1260 * @filename:  the file name (optionnal)
1261 * @node:  unused
1262 * @node2:  unused
1263 *
1264 * Implements the XML shell function "save"
1265 * Write the current document to the filename, or it's original name
1266 *
1267 * Returns 0 or -1 in case of error
1268 */
1269int
1270xmlShellSave(xmlShellCtxtPtr ctxt, char *filename, xmlNodePtr node,
1271             xmlNodePtr node2) {
1272    if (ctxt->doc == NULL)
1273	return(-1);
1274    if ((filename == NULL) || (filename[0] == 0))
1275        filename = ctxt->filename;
1276#ifdef W_OK
1277    if (access((char *) filename, W_OK)) {
1278        xmlGenericError(xmlGenericErrorContext,
1279		"Cannot save to %s\n", filename);
1280	return(-1);
1281    }
1282#endif
1283    switch(ctxt->doc->type) {
1284        case XML_DOCUMENT_NODE:
1285	    if (xmlSaveFile((char *) filename, ctxt->doc) < 0) {
1286		xmlGenericError(xmlGenericErrorContext,
1287			"Failed to save to %s\n", filename);
1288	    }
1289	    break;
1290        case XML_HTML_DOCUMENT_NODE:
1291#ifdef LIBXML_HTML_ENABLED
1292	    if (htmlSaveFile((char *) filename, ctxt->doc) < 0) {
1293		xmlGenericError(xmlGenericErrorContext,
1294			"Failed to save to %s\n", filename);
1295	    }
1296#else
1297	    if (xmlSaveFile((char *) filename, ctxt->doc) < 0) {
1298		xmlGenericError(xmlGenericErrorContext,
1299			"Failed to save to %s\n", filename);
1300	    }
1301#endif /* LIBXML_HTML_ENABLED */
1302	    break;
1303	default:
1304	    xmlGenericError(xmlGenericErrorContext,
1305	      "To save to subparts of a document use the 'write' command\n");
1306	    return(-1);
1307
1308    }
1309    return(0);
1310}
1311
1312/**
1313 * xmlShellValidate:
1314 * @ctxt:  the shell context
1315 * @dtd:  the DTD URI (optionnal)
1316 * @node:  unused
1317 * @node2:  unused
1318 *
1319 * Implements the XML shell function "validate"
1320 * Validate the document, if a DTD path is provided, then the validation
1321 * is done against the given DTD.
1322 *
1323 * Returns 0 or -1 in case of error
1324 */
1325int
1326xmlShellValidate(xmlShellCtxtPtr ctxt, char *dtd, xmlNodePtr node,
1327                 xmlNodePtr node2) {
1328    xmlValidCtxt vctxt;
1329    int res = -1;
1330
1331    vctxt.userData = stderr;
1332    vctxt.error = (xmlValidityErrorFunc) fprintf;
1333    vctxt.warning = (xmlValidityWarningFunc) fprintf;
1334
1335    if ((dtd == NULL) || (dtd[0] == 0)) {
1336        res = xmlValidateDocument(&vctxt, ctxt->doc);
1337    } else {
1338        xmlDtdPtr subset;
1339
1340	subset = xmlParseDTD(NULL, (xmlChar *) dtd);
1341	if (subset != NULL) {
1342            res = xmlValidateDtd(&vctxt, ctxt->doc, subset);
1343
1344	    xmlFreeDtd(subset);
1345	}
1346    }
1347    return(res);
1348}
1349
1350/**
1351 * xmlShellDu:
1352 * @ctxt:  the shell context
1353 * @arg:  unused
1354 * @tree:  a node defining a subtree
1355 * @node2:  unused
1356 *
1357 * Implements the XML shell function "du"
1358 * show the structure of the subtree under node @tree
1359 * If @tree is null, the command works on the current node.
1360 *
1361 * Returns 0 or -1 in case of error
1362 */
1363int
1364xmlShellDu(xmlShellCtxtPtr ctxt, char *arg, xmlNodePtr tree,
1365                  xmlNodePtr node2) {
1366    xmlNodePtr node;
1367    int indent = 0,i;
1368
1369    if (tree == NULL) return(-1);
1370    node = tree;
1371    while (node != NULL) {
1372        if ((node->type == XML_DOCUMENT_NODE) ||
1373            (node->type == XML_HTML_DOCUMENT_NODE)) {
1374	    printf("/\n");
1375	} else if (node->type == XML_ELEMENT_NODE) {
1376	    for (i = 0;i < indent;i++)
1377	        printf("  ");
1378	    printf("%s\n", node->name);
1379	} else {
1380	}
1381
1382	/*
1383	 * Browse the full subtree, deep first
1384	 */
1385
1386        if ((node->type == XML_DOCUMENT_NODE) ||
1387            (node->type == XML_HTML_DOCUMENT_NODE)) {
1388	    node = ((xmlDocPtr) node)->children;
1389        } else if ((node->children != NULL) && (node->type != XML_ENTITY_REF_NODE)) {
1390	    /* deep first */
1391	    node = node->children;
1392	    indent++;
1393	} else if ((node != tree) && (node->next != NULL)) {
1394	    /* then siblings */
1395	    node = node->next;
1396	} else if (node != tree) {
1397	    /* go up to parents->next if needed */
1398	    while (node != tree) {
1399	        if (node->parent != NULL) {
1400		    node = node->parent;
1401		    indent--;
1402		}
1403		if ((node != tree) && (node->next != NULL)) {
1404		    node = node->next;
1405		    break;
1406		}
1407		if (node->parent == NULL) {
1408		    node = NULL;
1409		    break;
1410		}
1411		if (node == tree) {
1412		    node = NULL;
1413		    break;
1414		}
1415	    }
1416	    /* exit condition */
1417	    if (node == tree)
1418	        node = NULL;
1419	} else
1420	    node = NULL;
1421    }
1422    return(0);
1423}
1424
1425/**
1426 * xmlShellPwd:
1427 * @ctxt:  the shell context
1428 * @buffer:  the output buffer
1429 * @tree:  a node
1430 * @node2:  unused
1431 *
1432 * Implements the XML shell function "pwd"
1433 * Show the full path from the root to the node, if needed building
1434 * thumblers when similar elements exists at a given ancestor level.
1435 * The output is compatible with XPath commands.
1436 *
1437 * Returns 0 or -1 in case of error
1438 */
1439int
1440xmlShellPwd(xmlShellCtxtPtr ctxt, char *buffer, xmlNodePtr node,
1441                  xmlNodePtr node2) {
1442    xmlNodePtr cur, tmp, next;
1443    char buf[500];
1444    char sep;
1445    const char *name;
1446    int occur = 0;
1447
1448    buffer[0] = 0;
1449    if (node == NULL) return(-1);
1450    cur = node;
1451    do {
1452	name = "";
1453	sep= '?';
1454	occur = 0;
1455	if ((cur->type == XML_DOCUMENT_NODE) ||
1456	    (cur->type == XML_HTML_DOCUMENT_NODE)) {
1457	    sep = '/';
1458	    next = NULL;
1459	} else if (cur->type == XML_ELEMENT_NODE) {
1460	    sep = '/';
1461	    name = (const char *)cur->name;
1462	    next = cur->parent;
1463
1464	    /*
1465	     * Thumbler index computation
1466	     */
1467	    tmp = cur->prev;
1468            while (tmp != NULL) {
1469	        if (xmlStrEqual(cur->name, tmp->name))
1470		    occur++;
1471	        tmp = tmp->prev;
1472	    }
1473	    if (occur == 0) {
1474	        tmp = cur->next;
1475		while (tmp != NULL) {
1476		    if (xmlStrEqual(cur->name, tmp->name))
1477			occur++;
1478		    tmp = tmp->next;
1479		}
1480		if (occur != 0) occur = 1;
1481	    } else
1482	        occur++;
1483	} else if (cur->type == XML_ATTRIBUTE_NODE) {
1484	    sep = '@';
1485	    name = (const char *) (((xmlAttrPtr) cur)->name);
1486	    next = ((xmlAttrPtr) cur)->parent;
1487	} else {
1488	    next = cur->parent;
1489	}
1490	if (occur == 0)
1491#ifdef HAVE_SNPRINTF
1492	    snprintf(buf, sizeof(buf), "%c%s%s", sep, name, buffer);
1493#else
1494	    sprintf(buf, "%c%s%s", sep, name, buffer);
1495#endif
1496        else
1497#ifdef HAVE_SNPRINTF
1498	    snprintf(buf, sizeof(buf), "%c%s[%d]%s",
1499                    sep, name, occur, buffer);
1500#else
1501	    sprintf(buf, "%c%s[%d]%s", sep, name, occur, buffer);
1502#endif
1503        buf[sizeof(buf) - 1] = 0;
1504        /*
1505         * This test prevents buffer overflow, because this routine
1506         * is only called by xmlShell, in which the second argument is
1507         * 500 chars long.
1508         * It is a dirty hack before a cleaner solution is found.
1509         * Documentation should mention that the second argument must
1510         * be at least 500 chars long, and could be stripped if too long.
1511         */
1512        if (strlen(buffer) + strlen(buf) > 499)
1513           break;
1514	strcpy(buffer, buf);
1515        cur = next;
1516    } while (cur != NULL);
1517    return(0);
1518}
1519
1520/**
1521 * xmlShell
1522 * @doc:  the initial document
1523 * @filename:  the output buffer
1524 * @input:  the line reading function
1525 * @output:  the output FILE*
1526 *
1527 * Implements the XML shell
1528 * This allow to load, validate, view, modify and save a document
1529 * using a environment similar to a UNIX commandline.
1530 */
1531void
1532xmlShell(xmlDocPtr doc, char *filename, xmlShellReadlineFunc input,
1533         FILE *output) {
1534    char prompt[500] = "/ > ";
1535    char *cmdline = NULL, *cur;
1536    int nbargs;
1537    char command[100];
1538    char arg[400];
1539    int i;
1540    xmlShellCtxtPtr ctxt;
1541    xmlXPathObjectPtr list;
1542
1543    if (doc == NULL)
1544        return;
1545    if (filename == NULL)
1546        return;
1547    if (input == NULL)
1548        return;
1549    if (output == NULL)
1550        return;
1551    ctxt = (xmlShellCtxtPtr) xmlMalloc(sizeof(xmlShellCtxt));
1552    if (ctxt == NULL)
1553        return;
1554    ctxt->loaded = 0;
1555    ctxt->doc = doc;
1556    ctxt->input = input;
1557    ctxt->output = output;
1558    ctxt->filename = (char *) xmlStrdup((xmlChar *) filename);
1559    ctxt->node = (xmlNodePtr) ctxt->doc;
1560
1561#ifdef LIBXML_XPATH_ENABLED
1562    ctxt->pctxt = xmlXPathNewContext(ctxt->doc);
1563    if (ctxt->pctxt == NULL) {
1564	xmlFree(ctxt);
1565	return;
1566    }
1567#endif /* LIBXML_XPATH_ENABLED */
1568    while (1) {
1569        if (ctxt->node == (xmlNodePtr) ctxt->doc)
1570	    sprintf(prompt, "%s > ", "/");
1571	else if (ctxt->node->name)
1572#ifdef HAVE_SNPRINTF
1573	    snprintf(prompt, sizeof(prompt), "%s > ", ctxt->node->name);
1574#else
1575	    sprintf(prompt, "%s > ", ctxt->node->name);
1576#endif
1577        else
1578	    sprintf(prompt, "? > ");
1579        prompt[sizeof(prompt) - 1] = 0;
1580
1581	/*
1582	 * Get a new command line
1583	 */
1584        cmdline = ctxt->input(prompt);
1585        if (cmdline == NULL) break;
1586
1587	/*
1588	 * Parse the command itself
1589	 */
1590	cur = cmdline;
1591	while ((*cur == ' ') || (*cur == '\t')) cur++;
1592	i = 0;
1593	while ((*cur != ' ') && (*cur != '\t') &&
1594	       (*cur != '\n') && (*cur != '\r')) {
1595	    if (*cur == 0)
1596		break;
1597	    command[i++] = *cur++;
1598	}
1599	command[i] = 0;
1600	if (i == 0) continue;
1601	nbargs++;
1602
1603	/*
1604	 * Parse the argument
1605	 */
1606	while ((*cur == ' ') || (*cur == '\t')) cur++;
1607	i = 0;
1608	while ((*cur != '\n') && (*cur != '\r') && (*cur != 0)) {
1609	    if (*cur == 0)
1610		break;
1611	    arg[i++] = *cur++;
1612	}
1613	arg[i] = 0;
1614	if (i != 0)
1615	    nbargs++;
1616
1617	/*
1618	 * start interpreting the command
1619	 */
1620        if (!strcmp(command, "exit"))
1621	    break;
1622        if (!strcmp(command, "quit"))
1623	    break;
1624        if (!strcmp(command, "bye"))
1625	    break;
1626	if (!strcmp(command, "validate")) {
1627	    xmlShellValidate(ctxt, arg, NULL, NULL);
1628	} else if (!strcmp(command, "load")) {
1629	    xmlShellLoad(ctxt, arg, NULL, NULL);
1630	} else if (!strcmp(command, "save")) {
1631	    xmlShellSave(ctxt, arg, NULL, NULL);
1632	} else if (!strcmp(command, "write")) {
1633	    xmlShellWrite(ctxt, arg, NULL, NULL);
1634	} else if (!strcmp(command, "free")) {
1635	    if (arg[0] == 0) {
1636		xmlMemShow(stdout, 0);
1637	    } else {
1638	        int len = 0;
1639		sscanf(arg, "%d", &len);
1640		xmlMemShow(stdout, len);
1641	    }
1642	} else if (!strcmp(command, "pwd")) {
1643	    char dir[500];
1644	    if (!xmlShellPwd(ctxt, dir, ctxt->node, NULL))
1645		printf("%s\n", dir);
1646	} else  if (!strcmp(command, "du")) {
1647	    xmlShellDu(ctxt, NULL, ctxt->node, NULL);
1648	} else  if ((!strcmp(command, "ls")) ||
1649	      (!strcmp(command, "dir"))) {
1650	    int dir = (!strcmp(command, "dir"));
1651	    if (arg[0] == 0) {
1652		if (dir)
1653		    xmlShellDir(ctxt, NULL, ctxt->node, NULL);
1654		else
1655		    xmlShellList(ctxt, NULL, ctxt->node, NULL);
1656	    } else {
1657	        ctxt->pctxt->node = ctxt->node;
1658#ifdef LIBXML_XPATH_ENABLED
1659	        ctxt->pctxt->node = ctxt->node;
1660		list = xmlXPathEval((xmlChar *) arg, ctxt->pctxt);
1661#else
1662		list = NULL;
1663#endif /* LIBXML_XPATH_ENABLED */
1664		if (list != NULL) {
1665		    switch (list->type) {
1666			case XPATH_UNDEFINED:
1667			    xmlGenericError(xmlGenericErrorContext,
1668				    "%s: no such node\n", arg);
1669			    break;
1670			case XPATH_NODESET: {
1671			    int i;
1672
1673			    for (i = 0;i < list->nodesetval->nodeNr;i++) {
1674				if (dir)
1675				    xmlShellDir(ctxt, NULL,
1676				       list->nodesetval->nodeTab[i], NULL);
1677				else
1678				    xmlShellList(ctxt, NULL,
1679				       list->nodesetval->nodeTab[i], NULL);
1680			    }
1681			    break;
1682			}
1683			case XPATH_BOOLEAN:
1684			    xmlGenericError(xmlGenericErrorContext,
1685				    "%s is a Boolean\n", arg);
1686			    break;
1687			case XPATH_NUMBER:
1688			    xmlGenericError(xmlGenericErrorContext,
1689				    "%s is a number\n", arg);
1690			    break;
1691			case XPATH_STRING:
1692			    xmlGenericError(xmlGenericErrorContext,
1693				    "%s is a string\n", arg);
1694			    break;
1695			case XPATH_POINT:
1696			    xmlGenericError(xmlGenericErrorContext,
1697				    "%s is a point\n", arg);
1698			    break;
1699			case XPATH_RANGE:
1700			    xmlGenericError(xmlGenericErrorContext,
1701				    "%s is a range\n", arg);
1702			    break;
1703			case XPATH_LOCATIONSET:
1704			    xmlGenericError(xmlGenericErrorContext,
1705				    "%s is a range\n", arg);
1706			    break;
1707			case XPATH_USERS:
1708			    xmlGenericError(xmlGenericErrorContext,
1709				    "%s is user-defined\n", arg);
1710			    break;
1711		    }
1712		    xmlXPathFreeNodeSetList(list);
1713		} else {
1714		    xmlGenericError(xmlGenericErrorContext,
1715			    "%s: no such node\n", arg);
1716		}
1717		ctxt->pctxt->node = NULL;
1718	    }
1719	} else if (!strcmp(command, "cd")) {
1720	    if (arg[0] == 0) {
1721		ctxt->node = (xmlNodePtr) ctxt->doc;
1722	    } else {
1723#ifdef LIBXML_XPATH_ENABLED
1724	        ctxt->pctxt->node = ctxt->node;
1725		list = xmlXPathEval((xmlChar *) arg, ctxt->pctxt);
1726#else
1727		list = NULL;
1728#endif /* LIBXML_XPATH_ENABLED */
1729		if (list != NULL) {
1730		    switch (list->type) {
1731			case XPATH_UNDEFINED:
1732			    xmlGenericError(xmlGenericErrorContext,
1733				    "%s: no such node\n", arg);
1734			    break;
1735			case XPATH_NODESET:
1736			    if (list->nodesetval->nodeNr == 1) {
1737				ctxt->node = list->nodesetval->nodeTab[0];
1738			    } else
1739				xmlGenericError(xmlGenericErrorContext,
1740					"%s is a %d Node Set\n",
1741				        arg, list->nodesetval->nodeNr);
1742			    break;
1743			case XPATH_BOOLEAN:
1744			    xmlGenericError(xmlGenericErrorContext,
1745				    "%s is a Boolean\n", arg);
1746			    break;
1747			case XPATH_NUMBER:
1748			    xmlGenericError(xmlGenericErrorContext,
1749				    "%s is a number\n", arg);
1750			    break;
1751			case XPATH_STRING:
1752			    xmlGenericError(xmlGenericErrorContext,
1753				    "%s is a string\n", arg);
1754			    break;
1755			case XPATH_POINT:
1756			    xmlGenericError(xmlGenericErrorContext,
1757				    "%s is a point\n", arg);
1758			    break;
1759			case XPATH_RANGE:
1760			    xmlGenericError(xmlGenericErrorContext,
1761				    "%s is a range\n", arg);
1762			    break;
1763			case XPATH_LOCATIONSET:
1764			    xmlGenericError(xmlGenericErrorContext,
1765				    "%s is a range\n", arg);
1766			    break;
1767			case XPATH_USERS:
1768			    xmlGenericError(xmlGenericErrorContext,
1769				    "%s is user-defined\n", arg);
1770			    break;
1771		    }
1772		    xmlXPathFreeNodeSetList(list);
1773		} else {
1774		    xmlGenericError(xmlGenericErrorContext,
1775			    "%s: no such node\n", arg);
1776		}
1777		ctxt->pctxt->node = NULL;
1778	    }
1779	} else if (!strcmp(command, "cat")) {
1780	    if (arg[0] == 0) {
1781		xmlShellCat(ctxt, NULL, ctxt->node, NULL);
1782	    } else {
1783	        ctxt->pctxt->node = ctxt->node;
1784#ifdef LIBXML_XPATH_ENABLED
1785	        ctxt->pctxt->node = ctxt->node;
1786		list = xmlXPathEval((xmlChar *) arg, ctxt->pctxt);
1787#else
1788		list = NULL;
1789#endif /* LIBXML_XPATH_ENABLED */
1790		if (list != NULL) {
1791		    switch (list->type) {
1792			case XPATH_UNDEFINED:
1793			    xmlGenericError(xmlGenericErrorContext,
1794				    "%s: no such node\n", arg);
1795			    break;
1796			case XPATH_NODESET: {
1797			    int i;
1798
1799			    for (i = 0;i < list->nodesetval->nodeNr;i++) {
1800			        if (i > 0) printf(" -------\n");
1801				xmlShellCat(ctxt, NULL,
1802				    list->nodesetval->nodeTab[i], NULL);
1803			    }
1804			    break;
1805			}
1806			case XPATH_BOOLEAN:
1807			    xmlGenericError(xmlGenericErrorContext,
1808				    "%s is a Boolean\n", arg);
1809			    break;
1810			case XPATH_NUMBER:
1811			    xmlGenericError(xmlGenericErrorContext,
1812				    "%s is a number\n", arg);
1813			    break;
1814			case XPATH_STRING:
1815			    xmlGenericError(xmlGenericErrorContext,
1816				    "%s is a string\n", arg);
1817			    break;
1818			case XPATH_POINT:
1819			    xmlGenericError(xmlGenericErrorContext,
1820				    "%s is a point\n", arg);
1821			    break;
1822			case XPATH_RANGE:
1823			    xmlGenericError(xmlGenericErrorContext,
1824				    "%s is a range\n", arg);
1825			    break;
1826			case XPATH_LOCATIONSET:
1827			    xmlGenericError(xmlGenericErrorContext,
1828				    "%s is a range\n", arg);
1829			    break;
1830			case XPATH_USERS:
1831			    xmlGenericError(xmlGenericErrorContext,
1832				    "%s is user-defined\n", arg);
1833			    break;
1834		    }
1835		    xmlXPathFreeNodeSetList(list);
1836		} else {
1837		    xmlGenericError(xmlGenericErrorContext,
1838			    "%s: no such node\n", arg);
1839		}
1840		ctxt->pctxt->node = NULL;
1841	    }
1842	} else {
1843	    xmlGenericError(xmlGenericErrorContext,
1844		    "Unknown command %s\n", command);
1845	}
1846	free(cmdline); /* not xmlFree here ! */
1847    }
1848#ifdef LIBXML_XPATH_ENABLED
1849    xmlXPathFreeContext(ctxt->pctxt);
1850#endif /* LIBXML_XPATH_ENABLED */
1851    if (ctxt->loaded) {
1852        xmlFreeDoc(ctxt->doc);
1853    }
1854    xmlFree(ctxt);
1855    if (cmdline != NULL)
1856        free(cmdline); /* not xmlFree here ! */
1857}
1858
1859#endif /* LIBXML_DEBUG_ENABLED */
1860