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