debugXML.c revision dbfd641b78b5a98e790459e13d126e2784a7adeb
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#include <stdio.h>
16#ifdef HAVE_STDLIB_H
17#include <stdlib.h>
18#endif
19#include "xmlmemory.h"
20#include "tree.h"
21#include "parser.h"
22#include "debugXML.h"
23#include "HTMLtree.h"
24#include "HTMLparser.h"
25
26#define IS_BLANK(c)							\
27  (((c) == '\n') || ((c) == '\r') || ((c) == '\t') || ((c) == ' '))
28
29void xmlDebugDumpString(FILE *output, const xmlChar *str) {
30    int i;
31    for (i = 0;i < 40;i++)
32        if (str[i] == 0) return;
33	else if (IS_BLANK(str[i])) fputc(' ', output);
34	else fputc(str[i], output);
35    fprintf(output, "...");
36}
37
38void xmlDebugDumpNamespace(FILE *output, xmlNsPtr ns, int depth) {
39    int i;
40    char shift[100];
41
42    for (i = 0;((i < depth) && (i < 25));i++)
43        shift[2 * i] = shift[2 * i + 1] = ' ';
44    shift[2 * i] = shift[2 * i + 1] = 0;
45
46    fprintf(output, shift);
47    if (ns->type == XML_GLOBAL_NAMESPACE)
48        fprintf(output, "old ");
49    if (ns->prefix != NULL)
50	fprintf(output, "namespace %s href=", ns->prefix);
51    else
52	fprintf(output, "default namespace href=");
53
54    xmlDebugDumpString(output, ns->href);
55    fprintf(output, "\n");
56}
57
58void xmlDebugDumpNamespaceList(FILE *output, xmlNsPtr ns, int depth) {
59    while (ns != NULL) {
60        xmlDebugDumpNamespace(output, ns, depth);
61	ns = ns->next;
62    }
63}
64
65void xmlDebugDumpEntity(FILE *output, xmlEntityPtr ent, int depth) {
66    int i;
67    char shift[100];
68
69    for (i = 0;((i < depth) && (i < 25));i++)
70        shift[2 * i] = shift[2 * i + 1] = ' ';
71    shift[2 * i] = shift[2 * i + 1] = 0;
72
73    fprintf(output, shift);
74    switch (ent->type) {
75        case XML_INTERNAL_GENERAL_ENTITY:
76	    fprintf(output, "INTERNAL_GENERAL_ENTITY ");
77	    break;
78        case XML_EXTERNAL_GENERAL_PARSED_ENTITY:
79	    fprintf(output, "EXTERNAL_GENERAL_PARSED_ENTITY ");
80	    break;
81        case XML_EXTERNAL_GENERAL_UNPARSED_ENTITY:
82	    fprintf(output, "EXTERNAL_GENERAL_UNPARSED_ENTITY ");
83	    break;
84        case XML_INTERNAL_PARAMETER_ENTITY:
85	    fprintf(output, "INTERNAL_PARAMETER_ENTITY ");
86	    break;
87        case XML_EXTERNAL_PARAMETER_ENTITY:
88	    fprintf(output, "EXTERNAL_PARAMETER_ENTITY ");
89	    break;
90	default:
91	    fprintf(output, "ENTITY_%d ! ", ent->type);
92    }
93    fprintf(output, "%s\n", ent->name);
94    if (ent->ExternalID) {
95        fprintf(output, shift);
96        fprintf(output, "ExternalID=%s\n", ent->ExternalID);
97    }
98    if (ent->SystemID) {
99        fprintf(output, shift);
100        fprintf(output, "SystemID=%s\n", ent->SystemID);
101    }
102    if (ent->content) {
103        fprintf(output, shift);
104	fprintf(output, "content=");
105	xmlDebugDumpString(output, ent->content);
106	fprintf(output, "\n");
107    }
108}
109
110void xmlDebugDumpAttr(FILE *output, xmlAttrPtr attr, int depth) {
111    int i;
112    char shift[100];
113
114    for (i = 0;((i < depth) && (i < 25));i++)
115        shift[2 * i] = shift[2 * i + 1] = ' ';
116    shift[2 * i] = shift[2 * i + 1] = 0;
117
118    fprintf(output, shift);
119    fprintf(output, "ATTRIBUTE %s\n", attr->name);
120    if (attr->val != NULL)
121        xmlDebugDumpNodeList(output, attr->val, depth + 1);
122}
123
124void xmlDebugDumpAttrList(FILE *output, xmlAttrPtr attr, int depth) {
125    while (attr != NULL) {
126        xmlDebugDumpAttr(output, attr, depth);
127	attr = attr->next;
128    }
129}
130
131void xmlDebugDumpOneNode(FILE *output, xmlNodePtr node, int depth) {
132    int i;
133    char shift[100];
134
135    for (i = 0;((i < depth) && (i < 25));i++)
136        shift[2 * i] = shift[2 * i + 1] = ' ';
137    shift[2 * i] = shift[2 * i + 1] = 0;
138
139    fprintf(output, shift);
140    switch (node->type) {
141	case XML_ELEMENT_NODE:
142	    fprintf(output, "ELEMENT ");
143	    if (node->ns != NULL)
144	        fprintf(output, "%s:%s\n", node->ns->prefix, node->name);
145	    else
146	        fprintf(output, "%s\n", node->name);
147	    break;
148	case XML_ATTRIBUTE_NODE:
149	    fprintf(output, "Error, ATTRIBUTE found here\n");
150	    break;
151	case XML_TEXT_NODE:
152	    fprintf(output, "TEXT\n");
153	    break;
154	case XML_CDATA_SECTION_NODE:
155	    fprintf(output, "CDATA_SECTION\n");
156	    break;
157	case XML_ENTITY_REF_NODE:
158	    fprintf(output, "ENTITY_REF\n");
159	    break;
160	case XML_ENTITY_NODE:
161	    fprintf(output, "ENTITY\n");
162	    break;
163	case XML_PI_NODE:
164	    fprintf(output, "PI %s\n", node->name);
165	    break;
166	case XML_COMMENT_NODE:
167	    fprintf(output, "COMMENT\n");
168	    break;
169	case XML_DOCUMENT_NODE:
170	case XML_HTML_DOCUMENT_NODE:
171	    fprintf(output, "Error, DOCUMENT found here\n");
172	    break;
173	case XML_DOCUMENT_TYPE_NODE:
174	    fprintf(output, "DOCUMENT_TYPE\n");
175	    break;
176	case XML_DOCUMENT_FRAG_NODE:
177	    fprintf(output, "DOCUMENT_FRAG\n");
178	    break;
179	case XML_NOTATION_NODE:
180	    fprintf(output, "NOTATION\n");
181	    break;
182	default:
183	    fprintf(output, "NODE_%d\n", node->type);
184    }
185    if (node->doc == NULL) {
186        fprintf(output, shift);
187	fprintf(output, "doc == NULL !!!\n");
188    }
189    if (node->nsDef != NULL)
190        xmlDebugDumpNamespaceList(output, node->nsDef, depth + 1);
191    if (node->properties != NULL)
192	xmlDebugDumpAttrList(output, node->properties, depth + 1);
193    if (node->type != XML_ENTITY_REF_NODE) {
194	if (node->content != NULL) {
195	    fprintf(output, shift);
196	    fprintf(output, "content=");
197#ifndef XML_USE_BUFFER_CONTENT
198	    xmlDebugDumpString(output, node->content);
199#else
200	    xmlDebugDumpString(output, xmlBufferContent(node->content));
201#endif
202	    fprintf(output, "\n");
203	}
204    } else {
205        xmlEntityPtr ent;
206	ent = xmlGetDocEntity(node->doc, node->name);
207	if (ent != NULL)
208	    xmlDebugDumpEntity(output, ent, depth + 1);
209    }
210}
211
212void xmlDebugDumpNode(FILE *output, xmlNodePtr node, int depth) {
213    xmlDebugDumpOneNode(output, node, depth);
214    if (node->childs != NULL)
215	xmlDebugDumpNodeList(output, node->childs, depth + 1);
216}
217
218void xmlDebugDumpNodeList(FILE *output, xmlNodePtr node, int depth) {
219    while (node != NULL) {
220        xmlDebugDumpNode(output, node, depth);
221	node = node->next;
222    }
223}
224
225
226void xmlDebugDumpDocumentHead(FILE *output, xmlDocPtr doc) {
227    if (output == NULL) output = stdout;
228    if (doc == NULL) {
229        fprintf(output, "DOCUMENT == NULL !\n");
230	return;
231    }
232
233    switch (doc->type) {
234	case XML_ELEMENT_NODE:
235	    fprintf(output, "Error, ELEMENT found here ");
236	    break;
237	case XML_ATTRIBUTE_NODE:
238	    fprintf(output, "Error, ATTRIBUTE found here\n");
239	    break;
240	case XML_TEXT_NODE:
241	    fprintf(output, "Error, TEXT\n");
242	    break;
243	case XML_CDATA_SECTION_NODE:
244	    fprintf(output, "Error, CDATA_SECTION\n");
245	    break;
246	case XML_ENTITY_REF_NODE:
247	    fprintf(output, "Error, ENTITY_REF\n");
248	    break;
249	case XML_ENTITY_NODE:
250	    fprintf(output, "Error, ENTITY\n");
251	    break;
252	case XML_PI_NODE:
253	    fprintf(output, "Error, PI\n");
254	    break;
255	case XML_COMMENT_NODE:
256	    fprintf(output, "Error, COMMENT\n");
257	    break;
258	case XML_DOCUMENT_NODE:
259	    fprintf(output, "DOCUMENT\n");
260	    break;
261	case XML_HTML_DOCUMENT_NODE:
262	    fprintf(output, "HTML DOCUMENT\n");
263	    break;
264	case XML_DOCUMENT_TYPE_NODE:
265	    fprintf(output, "Error, DOCUMENT_TYPE\n");
266	    break;
267	case XML_DOCUMENT_FRAG_NODE:
268	    fprintf(output, "Error, DOCUMENT_FRAG\n");
269	    break;
270	case XML_NOTATION_NODE:
271	    fprintf(output, "Error, NOTATION\n");
272	    break;
273	default:
274	    fprintf(output, "NODE_%d\n", doc->type);
275    }
276    if (doc->name != NULL) {
277	fprintf(output, "name=");
278        xmlDebugDumpString(output, BAD_CAST doc->name);
279	fprintf(output, "\n");
280    }
281    if (doc->version != NULL) {
282	fprintf(output, "version=");
283        xmlDebugDumpString(output, doc->version);
284	fprintf(output, "\n");
285    }
286    if (doc->encoding != NULL) {
287	fprintf(output, "encoding=");
288        xmlDebugDumpString(output, doc->encoding);
289	fprintf(output, "\n");
290    }
291    if (doc->standalone)
292        fprintf(output, "standalone=true\n");
293    if (doc->oldNs != NULL)
294        xmlDebugDumpNamespaceList(output, doc->oldNs, 0);
295}
296
297void xmlDebugDumpDocument(FILE *output, xmlDocPtr doc) {
298    if (output == NULL) output = stdout;
299    if (doc == NULL) {
300        fprintf(output, "DOCUMENT == NULL !\n");
301	return;
302    }
303    xmlDebugDumpDocumentHead(output, doc);
304    if (((doc->type == XML_DOCUMENT_NODE) ||
305         (doc->type == XML_HTML_DOCUMENT_NODE)) &&
306        (doc->root != NULL))
307        xmlDebugDumpNodeList(output, doc->root, 1);
308}
309
310void xmlDebugDumpEntities(FILE *output, xmlDocPtr doc) {
311    int i;
312    xmlEntityPtr cur;
313
314    if (output == NULL) output = stdout;
315    if (doc == NULL) {
316        fprintf(output, "DOCUMENT == NULL !\n");
317	return;
318    }
319
320    switch (doc->type) {
321	case XML_ELEMENT_NODE:
322	    fprintf(output, "Error, ELEMENT found here ");
323	    break;
324	case XML_ATTRIBUTE_NODE:
325	    fprintf(output, "Error, ATTRIBUTE found here\n");
326	    break;
327	case XML_TEXT_NODE:
328	    fprintf(output, "Error, TEXT\n");
329	    break;
330	case XML_CDATA_SECTION_NODE:
331	    fprintf(output, "Error, CDATA_SECTION\n");
332	    break;
333	case XML_ENTITY_REF_NODE:
334	    fprintf(output, "Error, ENTITY_REF\n");
335	    break;
336	case XML_ENTITY_NODE:
337	    fprintf(output, "Error, ENTITY\n");
338	    break;
339	case XML_PI_NODE:
340	    fprintf(output, "Error, PI\n");
341	    break;
342	case XML_COMMENT_NODE:
343	    fprintf(output, "Error, COMMENT\n");
344	    break;
345	case XML_DOCUMENT_NODE:
346	    fprintf(output, "DOCUMENT\n");
347	    break;
348	case XML_HTML_DOCUMENT_NODE:
349	    fprintf(output, "HTML DOCUMENT\n");
350	    break;
351	case XML_DOCUMENT_TYPE_NODE:
352	    fprintf(output, "Error, DOCUMENT_TYPE\n");
353	    break;
354	case XML_DOCUMENT_FRAG_NODE:
355	    fprintf(output, "Error, DOCUMENT_FRAG\n");
356	    break;
357	case XML_NOTATION_NODE:
358	    fprintf(output, "Error, NOTATION\n");
359	    break;
360	default:
361	    fprintf(output, "NODE_%d\n", doc->type);
362    }
363    if ((doc->intSubset != NULL) && (doc->intSubset->entities != NULL)) {
364        xmlEntitiesTablePtr table = (xmlEntitiesTablePtr)
365	                            doc->intSubset->entities;
366	fprintf(output, "Entities in internal subset\n");
367	for (i = 0;i < table->nb_entities;i++) {
368	    cur = &table->table[i];
369	    fprintf(output, "%d : %s : ", i, cur->name);
370	    switch (cur->type) {
371		case XML_INTERNAL_GENERAL_ENTITY:
372		    fprintf(output, "INTERNAL GENERAL");
373		    break;
374		case XML_EXTERNAL_GENERAL_PARSED_ENTITY:
375		    fprintf(output, "EXTERNAL PARSED");
376		    break;
377		case XML_EXTERNAL_GENERAL_UNPARSED_ENTITY:
378		    fprintf(output, "EXTERNAL UNPARSED");
379		    break;
380		case XML_INTERNAL_PARAMETER_ENTITY:
381		    fprintf(output, "INTERNAL PARAMETER");
382		    break;
383		case XML_EXTERNAL_PARAMETER_ENTITY:
384		    fprintf(output, "EXTERNAL PARAMETER");
385		    break;
386		default:
387		    fprintf(output, "UNKNOWN TYPE %d",
388			    cur->type);
389	    }
390	    if (cur->ExternalID != NULL)
391	        fprintf(output, "ID \"%s\"", cur->ExternalID);
392	    if (cur->SystemID != NULL)
393	        fprintf(output, "SYSTEM \"%s\"", cur->SystemID);
394	    if (cur->orig != NULL)
395	        fprintf(output, "\n orig \"%s\"", cur->orig);
396	    if (cur->content != NULL)
397	        fprintf(output, "\n content \"%s\"", cur->content);
398	    fprintf(output, "\n");
399	}
400    } else
401	fprintf(output, "No entities in internal subset\n");
402    if ((doc->extSubset != NULL) && (doc->extSubset->entities != NULL)) {
403        xmlEntitiesTablePtr table = (xmlEntitiesTablePtr)
404	                            doc->extSubset->entities;
405	fprintf(output, "Entities in external subset\n");
406	for (i = 0;i < table->nb_entities;i++) {
407	    cur = &table->table[i];
408	    fprintf(output, "%d : %s : ", i, cur->name);
409	    switch (cur->type) {
410		case XML_INTERNAL_GENERAL_ENTITY:
411		    fprintf(output, "INTERNAL GENERAL");
412		    break;
413		case XML_EXTERNAL_GENERAL_PARSED_ENTITY:
414		    fprintf(output, "EXTERNAL PARSED");
415		    break;
416		case XML_EXTERNAL_GENERAL_UNPARSED_ENTITY:
417		    fprintf(output, "EXTERNAL UNPARSED");
418		    break;
419		case XML_INTERNAL_PARAMETER_ENTITY:
420		    fprintf(output, "INTERNAL PARAMETER");
421		    break;
422		case XML_EXTERNAL_PARAMETER_ENTITY:
423		    fprintf(output, "EXTERNAL PARAMETER");
424		    break;
425		default:
426		    fprintf(output, "UNKNOWN TYPE %d",
427			    cur->type);
428	    }
429	    if (cur->ExternalID != NULL)
430	        fprintf(output, "ID \"%s\"", cur->ExternalID);
431	    if (cur->SystemID != NULL)
432	        fprintf(output, "SYSTEM \"%s\"", cur->SystemID);
433	    if (cur->orig != NULL)
434	        fprintf(output, "\n orig \"%s\"", cur->orig);
435	    if (cur->content != NULL)
436	        fprintf(output, "\n content \"%s\"", cur->content);
437	    fprintf(output, "\n");
438	}
439    } else
440	fprintf(output, "No entities in external subset\n");
441}
442
443static int xmlLsCountNode(xmlNodePtr node) {
444    int ret = 0;
445    xmlNodePtr list = NULL;
446
447    switch (node->type) {
448	case XML_ELEMENT_NODE:
449	    list = node->childs;
450	    break;
451	case XML_DOCUMENT_NODE:
452	case XML_HTML_DOCUMENT_NODE:
453	    list = ((xmlDocPtr) node)->root;
454	    break;
455	case XML_ATTRIBUTE_NODE:
456	    list = ((xmlAttrPtr) node)->val;
457	    break;
458	case XML_TEXT_NODE:
459	case XML_CDATA_SECTION_NODE:
460	case XML_PI_NODE:
461	case XML_COMMENT_NODE:
462	    if (node->content != NULL) {
463#ifndef XML_USE_BUFFER_CONTENT
464		ret = xmlStrlen(node->content);
465#else
466		ret = xmlBufferLength(node->content);
467#endif
468            }
469	    break;
470	case XML_ENTITY_REF_NODE:
471	case XML_DOCUMENT_TYPE_NODE:
472	case XML_ENTITY_NODE:
473	case XML_DOCUMENT_FRAG_NODE:
474	case XML_NOTATION_NODE:
475	    ret = 1;
476	    break;
477    }
478    for (;list != NULL;ret++)
479        list = list->next;
480    return(ret);
481}
482
483void xmlLsOneNode(FILE *output, xmlNodePtr node) {
484    switch (node->type) {
485	case XML_ELEMENT_NODE:
486	    fprintf(output, "-");
487	    break;
488	case XML_ATTRIBUTE_NODE:
489	    fprintf(output, "a");
490	    break;
491	case XML_TEXT_NODE:
492	    fprintf(output, "t");
493	    break;
494	case XML_CDATA_SECTION_NODE:
495	    fprintf(output, "c");
496	    break;
497	case XML_ENTITY_REF_NODE:
498	    fprintf(output, "e");
499	    break;
500	case XML_ENTITY_NODE:
501	    fprintf(output, "E");
502	    break;
503	case XML_PI_NODE:
504	    fprintf(output, "p");
505	    break;
506	case XML_COMMENT_NODE:
507	    fprintf(output, "c");
508	    break;
509	case XML_DOCUMENT_NODE:
510	    fprintf(output, "d");
511	    break;
512	case XML_HTML_DOCUMENT_NODE:
513	    fprintf(output, "h");
514	    break;
515	case XML_DOCUMENT_TYPE_NODE:
516	    fprintf(output, "T");
517	    break;
518	case XML_DOCUMENT_FRAG_NODE:
519	    fprintf(output, "F");
520	    break;
521	case XML_NOTATION_NODE:
522	    fprintf(output, "N");
523	    break;
524	default:
525	    fprintf(output, "?");
526    }
527    if (node->properties != NULL)
528	fprintf(output, "a");
529    else
530	fprintf(output, "-");
531    if (node->nsDef != NULL)
532	fprintf(output, "n");
533    else
534	fprintf(output, "-");
535
536    fprintf(output, " %8d ", xmlLsCountNode(node));
537
538    switch (node->type) {
539	case XML_ELEMENT_NODE:
540	    if (node->name != NULL)
541		fprintf(output, "%s", node->name);
542	    break;
543	case XML_ATTRIBUTE_NODE:
544	    if (node->name != NULL)
545		fprintf(output, "%s", node->name);
546	    break;
547	case XML_TEXT_NODE:
548	    if (node->content != NULL) {
549#ifndef XML_USE_BUFFER_CONTENT
550		xmlDebugDumpString(output, node->content);
551#else
552		xmlDebugDumpString(output, xmlBufferContent(node->content));
553#endif
554            }
555	    break;
556	case XML_CDATA_SECTION_NODE:
557	    break;
558	case XML_ENTITY_REF_NODE:
559	    if (node->name != NULL)
560		fprintf(output, "%s", node->name);
561	    break;
562	case XML_ENTITY_NODE:
563	    if (node->name != NULL)
564		fprintf(output, "%s", node->name);
565	    break;
566	case XML_PI_NODE:
567	    if (node->name != NULL)
568		fprintf(output, "%s", node->name);
569	    break;
570	case XML_COMMENT_NODE:
571	    break;
572	case XML_DOCUMENT_NODE:
573	    break;
574	case XML_HTML_DOCUMENT_NODE:
575	    break;
576	case XML_DOCUMENT_TYPE_NODE:
577	    break;
578	case XML_DOCUMENT_FRAG_NODE:
579	    break;
580	case XML_NOTATION_NODE:
581	    break;
582	default:
583	    if (node->name != NULL)
584		fprintf(output, "%s", node->name);
585    }
586    fprintf(output, "\n");
587}
588
589/****************************************************************
590 *								*
591 *	 	The XML shell related functions			*
592 *								*
593 ****************************************************************/
594
595/*
596 * TODO: Improvement/cleanups for the XML shell
597 *     - allow to shell out an editor on a subpart
598 *     - cleanup function registrations (with help) and calling
599 *     - provide registration routines
600 */
601
602/**
603 * xmlShellList:
604 * @ctxt:  the shell context
605 * @arg:  unused
606 * @node:  a node
607 * @node2:  unused
608 *
609 * Implements the XML shell function "ls"
610 * Does an Unix like listing of the given node (like a directory)
611 *
612 * Returns 0
613 */
614int
615xmlShellList(xmlShellCtxtPtr ctxt, char *arg, xmlNodePtr node,
616                  xmlNodePtr node2) {
617    xmlNodePtr cur;
618
619    if ((node->type == XML_DOCUMENT_NODE) ||
620        (node->type == XML_HTML_DOCUMENT_NODE)) {
621        cur = ((xmlDocPtr) node)->root;
622    } else if (node->childs != NULL) {
623        cur = node->childs;
624    } else {
625	xmlLsOneNode(stdout, node);
626        return(0);
627    }
628    while (cur != NULL) {
629	xmlLsOneNode(stdout, cur);
630	cur = cur->next;
631    }
632    return(0);
633}
634
635/**
636 * xmlShellDir:
637 * @ctxt:  the shell context
638 * @arg:  unused
639 * @node:  a node
640 * @node2:  unused
641 *
642 * Implements the XML shell function "dir"
643 * dumps informations about the node (namespace, attributes, content).
644 *
645 * Returns 0
646 */
647int
648xmlShellDir(xmlShellCtxtPtr ctxt, char *arg, xmlNodePtr node,
649                  xmlNodePtr node2) {
650    if ((node->type == XML_DOCUMENT_NODE) ||
651        (node->type == XML_HTML_DOCUMENT_NODE)) {
652	xmlDebugDumpDocumentHead(stdout, (xmlDocPtr) node);
653    } else if (node->type == XML_ATTRIBUTE_NODE) {
654	xmlDebugDumpAttr(stdout, (xmlAttrPtr) node, 0);
655    } else {
656	xmlDebugDumpOneNode(stdout, node, 0);
657    }
658    return(0);
659}
660
661/**
662 * xmlShellCat:
663 * @ctxt:  the shell context
664 * @arg:  unused
665 * @node:  a node
666 * @node2:  unused
667 *
668 * Implements the XML shell function "cat"
669 * dumps the serialization node content (XML or HTML).
670 *
671 * Returns 0
672 */
673int
674xmlShellCat(xmlShellCtxtPtr ctxt, char *arg, xmlNodePtr node,
675                  xmlNodePtr node2) {
676    xmlElemDump(stdout, ctxt->doc, node);
677    printf("\n");
678    return(0);
679}
680
681/**
682 * xmlShellLoad:
683 * @ctxt:  the shell context
684 * @filename:  the file name
685 * @node:  unused
686 * @node2:  unused
687 *
688 * Implements the XML shell function "load"
689 * loads a new document specified by the filename
690 *
691 * Returns 0 or -1 if loading failed
692 */
693int
694xmlShellLoad(xmlShellCtxtPtr ctxt, char *filename, xmlNodePtr node,
695             xmlNodePtr node2) {
696    xmlDocPtr doc;
697    int html = 0;
698
699    if (ctxt->doc != NULL)
700	html = (ctxt->doc->type == XML_HTML_DOCUMENT_NODE);
701
702    if (html) {
703	doc = htmlParseFile(filename, NULL);
704    } else {
705	doc = xmlParseFile(filename);
706    }
707    if (doc != NULL) {
708        if (ctxt->loaded == 1) {
709	    xmlFreeDoc(ctxt->doc);
710	}
711	ctxt->loaded = 1;
712	xmlXPathFreeContext(ctxt->pctxt);
713	xmlFree(ctxt->filename);
714	ctxt->doc = doc;
715	ctxt->node = (xmlNodePtr) doc;
716	ctxt->pctxt = xmlXPathNewContext(doc);
717	ctxt->filename = (char *) xmlStrdup((xmlChar *) filename);
718    } else
719        return(-1);
720    return(0);
721}
722
723/**
724 * xmlShellWrite:
725 * @ctxt:  the shell context
726 * @filename:  the file name
727 * @node:  a node in the tree
728 * @node2:  unused
729 *
730 * Implements the XML shell function "write"
731 * Write the current node to the filename, it saves the serailization
732 * of the subtree under the @node specified
733 *
734 * Returns 0 or -1 in case of error
735 */
736int
737xmlShellWrite(xmlShellCtxtPtr ctxt, char *filename, xmlNodePtr node,
738                  xmlNodePtr node2) {
739    if (node == NULL)
740        return(-1);
741    if ((filename == NULL) || (filename[0] == 0)) {
742        fprintf(stderr, "Write command requires a filename argument\n");
743	return(-1);
744    }
745#ifdef W_OK
746    if (access((char *) filename, W_OK)) {
747        fprintf(stderr, "Cannot write to %s\n", filename);
748	return(-1);
749    }
750#endif
751    switch(node->type) {
752        case XML_DOCUMENT_NODE:
753	    if (xmlSaveFile((char *) filename, ctxt->doc) < -1) {
754		fprintf(stderr, "Failed to write to %s\n", filename);
755		return(-1);
756	    }
757	    break;
758        case XML_HTML_DOCUMENT_NODE:
759	    if (htmlSaveFile((char *) filename, ctxt->doc) < 0) {
760		fprintf(stderr, "Failed to write to %s\n", filename);
761		return(-1);
762	    }
763	    break;
764	default: {
765	    FILE *f;
766
767	    f = fopen((char *) filename, "w");
768	    if (f == NULL) {
769		fprintf(stderr, "Failed to write to %s\n", filename);
770		return(-1);
771	    }
772	    xmlElemDump(f, ctxt->doc, node);
773	    fclose(f);
774	}
775    }
776    return(0);
777}
778
779/**
780 * xmlShellSave:
781 * @ctxt:  the shell context
782 * @filename:  the file name (optionnal)
783 * @node:  unused
784 * @node2:  unused
785 *
786 * Implements the XML shell function "save"
787 * Write the current document to the filename, or it's original name
788 *
789 * Returns 0 or -1 in case of error
790 */
791int
792xmlShellSave(xmlShellCtxtPtr ctxt, char *filename, xmlNodePtr node,
793             xmlNodePtr node2) {
794    if (ctxt->doc == NULL)
795	return(-1);
796    if ((filename == NULL) || (filename[0] == 0))
797        filename = ctxt->filename;
798#ifdef W_OK
799    if (access((char *) filename, W_OK)) {
800        fprintf(stderr, "Cannot save to %s\n", filename);
801	return(-1);
802    }
803#endif
804    switch(ctxt->doc->type) {
805        case XML_DOCUMENT_NODE:
806	    if (xmlSaveFile((char *) filename, ctxt->doc) < 0) {
807		fprintf(stderr, "Failed to save to %s\n", filename);
808	    }
809	    break;
810        case XML_HTML_DOCUMENT_NODE:
811	    if (htmlSaveFile((char *) filename, ctxt->doc) < 0) {
812		fprintf(stderr, "Failed to save to %s\n", filename);
813	    }
814	    break;
815	default:
816	    fprintf(stderr,
817	      "To save to subparts of a document use the 'write' command\n");
818	    return(-1);
819
820    }
821    return(0);
822}
823
824/**
825 * xmlShellValidate:
826 * @ctxt:  the shell context
827 * @dtd:  the DTD URI (optionnal)
828 * @node:  unused
829 * @node2:  unused
830 *
831 * Implements the XML shell function "validate"
832 * Validate the document, if a DTD path is provided, then the validation
833 * is done against the given DTD.
834 *
835 * Returns 0 or -1 in case of error
836 */
837int
838xmlShellValidate(xmlShellCtxtPtr ctxt, char *dtd, xmlNodePtr node,
839                 xmlNodePtr node2) {
840    xmlValidCtxt vctxt;
841    int res = -1;
842
843    vctxt.userData = stderr;
844    vctxt.error = (xmlValidityErrorFunc) fprintf;
845    vctxt.warning = (xmlValidityWarningFunc) fprintf;
846
847    if ((dtd == NULL) || (dtd[0] == 0)) {
848        res = xmlValidateDocument(&vctxt, ctxt->doc);
849    } else {
850        xmlDtdPtr subset;
851
852	subset = xmlParseDTD(NULL, (xmlChar *) dtd);
853	if (subset != NULL) {
854            res = xmlValidateDtd(&vctxt, ctxt->doc, subset);
855
856	    xmlFreeDtd(subset);
857	}
858    }
859    return(res);
860}
861
862/**
863 * xmlShellDu:
864 * @ctxt:  the shell context
865 * @arg:  unused
866 * @tree:  a node defining a subtree
867 * @node2:  unused
868 *
869 * Implements the XML shell function "du"
870 * show the structure of the subtree under node @tree
871 * If @tree is null, the command works on the current node.
872 *
873 * Returns 0 or -1 in case of error
874 */
875int
876xmlShellDu(xmlShellCtxtPtr ctxt, char *arg, xmlNodePtr tree,
877                  xmlNodePtr node2) {
878    xmlNodePtr node;
879    int indent = 0,i;
880
881    if (tree == NULL) return(-1);
882    node = tree;
883    while (node != NULL) {
884        if ((node->type == XML_DOCUMENT_NODE) ||
885            (node->type == XML_HTML_DOCUMENT_NODE)) {
886	    printf("/\n");
887	} else if (node->type == XML_ELEMENT_NODE) {
888	    for (i = 0;i < indent;i++)
889	        printf("  ");
890	    printf("%s\n", node->name);
891	} else {
892	}
893
894	/*
895	 * Browse the full subtree, deep first
896	 */
897
898        if ((node->type == XML_DOCUMENT_NODE) ||
899            (node->type == XML_HTML_DOCUMENT_NODE)) {
900	    node = ((xmlDocPtr) node)->root;
901        } else if (node->childs != NULL) {
902	    /* deep first */
903	    node = node->childs;
904	    indent++;
905	} else if ((node != tree) && (node->next != NULL)) {
906	    /* then siblings */
907	    node = node->next;
908	} else if (node != tree) {
909	    /* go up to parents->next if needed */
910	    while (node != tree) {
911	        if (node->parent != NULL) {
912		    node = node->parent;
913		    indent--;
914		}
915		if ((node != tree) && (node->next != NULL)) {
916		    node = node->next;
917		    break;
918		}
919		if (node->parent == NULL) {
920		    node = NULL;
921		    break;
922		}
923		if (node == tree) {
924		    node = NULL;
925		    break;
926		}
927	    }
928	    /* exit condition */
929	    if (node == tree)
930	        node = NULL;
931	} else
932	    node = NULL;
933    }
934    return(0);
935}
936
937/**
938 * xmlShellPwd:
939 * @ctxt:  the shell context
940 * @buffer:  the output buffer
941 * @tree:  a node
942 * @node2:  unused
943 *
944 * Implements the XML shell function "pwd"
945 * Show the full path from the root to the node, if needed building
946 * thumblers when similar elements exists at a given ancestor level.
947 * The output is compatible with XPath commands.
948 *
949 * Returns 0 or -1 in case of error
950 */
951int
952xmlShellPwd(xmlShellCtxtPtr ctxt, char *buffer, xmlNodePtr node,
953                  xmlNodePtr node2) {
954    xmlNodePtr cur, tmp, next;
955    char buf[500];
956    char sep;
957    const char *name;
958    int occur = 0;
959
960    buffer[0] = 0;
961    if (node == NULL) return(-1);
962    cur = node;
963    do {
964	name = "";
965	sep= '?';
966	occur = 0;
967	if ((cur->type == XML_DOCUMENT_NODE) ||
968	    (cur->type == XML_HTML_DOCUMENT_NODE)) {
969	    sep = '/';
970	    next = NULL;
971	} else if (cur->type == XML_ELEMENT_NODE) {
972	    sep = '/';
973	    name = (const char *)cur->name;
974	    next = cur->parent;
975
976	    /*
977	     * Thumbler index computation
978	     */
979	    tmp = cur->prev;
980            while (tmp != NULL) {
981	        if (!xmlStrcmp(cur->name, tmp->name))
982		    occur++;
983	        tmp = tmp->prev;
984	    }
985	    if (occur == 0) {
986	        tmp = cur->next;
987		while (tmp != NULL) {
988		    if (!xmlStrcmp(cur->name, tmp->name))
989			occur++;
990		    tmp = tmp->next;
991		}
992		if (occur != 0) occur = 1;
993	    } else
994	        occur++;
995	} else if (cur->type == XML_ATTRIBUTE_NODE) {
996	    sep = '@';
997	    name = (const char *) (((xmlAttrPtr) cur)->name);
998	    next = ((xmlAttrPtr) cur)->node;
999	} else {
1000	    next = cur->parent;
1001	}
1002	if (occur == 0)
1003	    sprintf(buf, "%c%s%s", sep, name, buffer);
1004	else
1005	    sprintf(buf, "%c%s[%d]%s", sep, name, occur, buffer);
1006	strcpy(buffer, buf);
1007        cur = next;
1008    } while (cur != NULL);
1009    return(0);
1010}
1011
1012/**
1013 * xmlShell
1014 * @doc:  the initial document
1015 * @filename:  the output buffer
1016 * @input:  the line reading function
1017 * @output:  the output FILE*
1018 *
1019 * Implements the XML shell
1020 * This allow to load, validate, view, modify and save a document
1021 * using a environment similar to a UNIX commandline.
1022 */
1023void
1024xmlShell(xmlDocPtr doc, char *filename, xmlShellReadlineFunc input,
1025         FILE *output) {
1026    char prompt[500] = "/ > ";
1027    char *cmdline = NULL;
1028    int nbargs;
1029    char command[100];
1030    char arg[400];
1031    xmlShellCtxtPtr ctxt;
1032    xmlXPathObjectPtr list;
1033
1034    if (doc == NULL)
1035        return;
1036    if (filename == NULL)
1037        return;
1038    if (input == NULL)
1039        return;
1040    if (output == NULL)
1041        return;
1042    ctxt = (xmlShellCtxtPtr) xmlMalloc(sizeof(xmlShellCtxt));
1043    if (ctxt == NULL)
1044        return;
1045    ctxt->loaded = 0;
1046    ctxt->doc = doc;
1047    ctxt->input = input;
1048    ctxt->output = output;
1049    ctxt->filename = (char *) xmlStrdup((xmlChar *) filename);
1050    ctxt->node = (xmlNodePtr) ctxt->doc;
1051
1052    ctxt->pctxt = xmlXPathNewContext(ctxt->doc);
1053    if (ctxt->pctxt == NULL) {
1054	xmlFree(ctxt);
1055	return;
1056    }
1057    while (1) {
1058        if (ctxt->node == (xmlNodePtr) ctxt->doc)
1059	    sprintf(prompt, "%s > ", "/");
1060	else if (ctxt->node->name)
1061	    sprintf(prompt, "%s > ", ctxt->node->name);
1062	else
1063	    sprintf(prompt, "? > ");
1064
1065        cmdline = ctxt->input(prompt);
1066        if (cmdline == NULL) break;
1067
1068	command[0] = 0;
1069	arg[0] = 0;
1070	nbargs = sscanf(cmdline, "%s %s", command, arg);
1071
1072	if (command[0] == 0) continue;
1073        if (!strcmp(command, "exit"))
1074	    break;
1075        if (!strcmp(command, "quit"))
1076	    break;
1077        if (!strcmp(command, "bye"))
1078	    break;
1079	if (!strcmp(command, "validate")) {
1080	    xmlShellValidate(ctxt, arg, NULL, NULL);
1081	} else if (!strcmp(command, "load")) {
1082	    xmlShellLoad(ctxt, arg, NULL, NULL);
1083	} else if (!strcmp(command, "save")) {
1084	    xmlShellSave(ctxt, arg, NULL, NULL);
1085	} else if (!strcmp(command, "write")) {
1086	    xmlShellWrite(ctxt, arg, NULL, NULL);
1087	} else if (!strcmp(command, "free")) {
1088	    if (arg[0] == 0) {
1089		xmlMemShow(stdout, 0);
1090	    } else {
1091	        int len = 0;
1092		sscanf(arg, "%d", &len);
1093		xmlMemShow(stdout, len);
1094	    }
1095	} else if (!strcmp(command, "pwd")) {
1096	    char dir[500];
1097	    if (!xmlShellPwd(ctxt, dir, ctxt->node, NULL))
1098		printf("%s\n", dir);
1099	} else  if (!strcmp(command, "du")) {
1100	    xmlShellDu(ctxt, NULL, ctxt->node, NULL);
1101	} else  if ((!strcmp(command, "ls")) ||
1102	      (!strcmp(command, "dir"))) {
1103	    int dir = (!strcmp(command, "dir"));
1104	    if (arg[0] == 0) {
1105		if (dir)
1106		    xmlShellDir(ctxt, NULL, ctxt->node, NULL);
1107		else
1108		    xmlShellList(ctxt, NULL, ctxt->node, NULL);
1109	    } else {
1110	        ctxt->pctxt->node = ctxt->node;
1111		if (ctxt->pctxt->nodelist != NULL)
1112		    xmlXPathFreeNodeSet(ctxt->pctxt->nodelist);
1113	        ctxt->pctxt->nodelist = xmlXPathNodeSetCreate(ctxt->node);
1114		list = xmlXPathEval((xmlChar *) arg, ctxt->pctxt);
1115		if (list != NULL) {
1116		    switch (list->type) {
1117			case XPATH_UNDEFINED:
1118			    fprintf(stderr, "%s: no such node\n", arg);
1119			    break;
1120			case XPATH_NODESET: {
1121			    int i;
1122
1123			    for (i = 0;i < list->nodesetval->nodeNr;i++) {
1124				if (dir)
1125				    xmlShellDir(ctxt, NULL,
1126				       list->nodesetval->nodeTab[i], NULL);
1127				else
1128				    xmlShellList(ctxt, NULL,
1129				       list->nodesetval->nodeTab[i], NULL);
1130			    }
1131			    break;
1132			}
1133			case XPATH_BOOLEAN:
1134			    fprintf(stderr, "%s is a Boolean\n", arg);
1135			    break;
1136			case XPATH_NUMBER:
1137			    fprintf(stderr, "%s is a number\n", arg);
1138			    break;
1139			case XPATH_STRING:
1140			    fprintf(stderr, "%s is a string\n", arg);
1141			    break;
1142		    }
1143		    xmlXPathFreeNodeSetList(list);
1144		} else {
1145		    fprintf(stderr, "%s: no such node\n", arg);
1146		}
1147		if (ctxt->pctxt->nodelist != NULL)
1148		    xmlXPathFreeNodeSet(ctxt->pctxt->nodelist);
1149		ctxt->pctxt->nodelist = NULL;
1150	    }
1151	} else if (!strcmp(command, "cd")) {
1152	    if (arg[0] == 0) {
1153		ctxt->node = (xmlNodePtr) ctxt->doc;
1154	    } else {
1155	        ctxt->pctxt->node = ctxt->node;
1156		if (ctxt->pctxt->nodelist != NULL)
1157		    xmlXPathFreeNodeSet(ctxt->pctxt->nodelist);
1158	        ctxt->pctxt->nodelist = xmlXPathNodeSetCreate(ctxt->node);
1159		list = xmlXPathEval((xmlChar *) arg, ctxt->pctxt);
1160		if (list != NULL) {
1161		    switch (list->type) {
1162			case XPATH_UNDEFINED:
1163			    fprintf(stderr, "%s: no such node\n", arg);
1164			    break;
1165			case XPATH_NODESET:
1166			    if (list->nodesetval->nodeNr == 1) {
1167				ctxt->node = list->nodesetval->nodeTab[0];
1168			    } else
1169				fprintf(stderr, "%s is a %d Node Set\n",
1170				        arg, list->nodesetval->nodeNr);
1171			    break;
1172			case XPATH_BOOLEAN:
1173			    fprintf(stderr, "%s is a Boolean\n", arg);
1174			    break;
1175			case XPATH_NUMBER:
1176			    fprintf(stderr, "%s is a number\n", arg);
1177			    break;
1178			case XPATH_STRING:
1179			    fprintf(stderr, "%s is a string\n", arg);
1180			    break;
1181		    }
1182		    xmlXPathFreeNodeSetList(list);
1183		} else {
1184		    fprintf(stderr, "%s: no such node\n", arg);
1185		}
1186		if (ctxt->pctxt->nodelist != NULL)
1187		    xmlXPathFreeNodeSet(ctxt->pctxt->nodelist);
1188		ctxt->pctxt->nodelist = NULL;
1189	    }
1190	} else if (!strcmp(command, "cat")) {
1191	    if (arg[0] == 0) {
1192		xmlShellCat(ctxt, NULL, ctxt->node, NULL);
1193	    } else {
1194	        ctxt->pctxt->node = ctxt->node;
1195		if (ctxt->pctxt->nodelist != NULL)
1196		    xmlXPathFreeNodeSet(ctxt->pctxt->nodelist);
1197	        ctxt->pctxt->nodelist = xmlXPathNodeSetCreate(ctxt->node);
1198		list = xmlXPathEval((xmlChar *) arg, ctxt->pctxt);
1199		if (list != NULL) {
1200		    switch (list->type) {
1201			case XPATH_UNDEFINED:
1202			    fprintf(stderr, "%s: no such node\n", arg);
1203			    break;
1204			case XPATH_NODESET: {
1205			    int i;
1206
1207			    for (i = 0;i < list->nodesetval->nodeNr;i++) {
1208			        if (i > 0) printf(" -------\n");
1209				xmlShellCat(ctxt, NULL,
1210				    list->nodesetval->nodeTab[i], NULL);
1211			    }
1212			    break;
1213			}
1214			case XPATH_BOOLEAN:
1215			    fprintf(stderr, "%s is a Boolean\n", arg);
1216			    break;
1217			case XPATH_NUMBER:
1218			    fprintf(stderr, "%s is a number\n", arg);
1219			    break;
1220			case XPATH_STRING:
1221			    fprintf(stderr, "%s is a string\n", arg);
1222			    break;
1223		    }
1224		    xmlXPathFreeNodeSetList(list);
1225		} else {
1226		    fprintf(stderr, "%s: no such node\n", arg);
1227		}
1228		if (ctxt->pctxt->nodelist != NULL)
1229		    xmlXPathFreeNodeSet(ctxt->pctxt->nodelist);
1230		ctxt->pctxt->nodelist = NULL;
1231	    }
1232	} else {
1233	    fprintf(stderr, "Unknown command %s\n", command);
1234	}
1235	free(cmdline); /* not xmlFree here ! */
1236    }
1237    xmlXPathFreeContext(ctxt->pctxt);
1238    if (ctxt->loaded) {
1239        xmlFreeDoc(ctxt->doc);
1240    }
1241    xmlFree(ctxt);
1242    if (cmdline != NULL)
1243        free(cmdline); /* not xmlFree here ! */
1244}
1245
1246