1 /**
2 * catalog.c: set of generic Catalog related routines
3 *
4 * Reference: SGML Open Technical Resolution TR9401:1997.
5 * http://www.jclark.com/sp/catalog.htm
6 *
7 * XML Catalogs Working Draft 06 August 2001
8 * http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
9 *
10 * See Copyright for the status of this software.
11 *
12 * Daniel.Veillard@imag.fr
13 */
14
15 #define IN_LIBXML
16 #include "libxml.h"
17
18 #ifdef LIBXML_CATALOG_ENABLED
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22
23 #include <fcntl.h>
24 #include <sys/stat.h>
25
26 #ifdef _WIN32
27 #include <io.h>
28 #else
29 #include <unistd.h>
30 #endif
31
32 #include <libxml/xmlmemory.h>
33 #include <libxml/hash.h>
34 #include <libxml/uri.h>
35 #include <libxml/parserInternals.h>
36 #include <libxml/catalog.h>
37 #include <libxml/xmlerror.h>
38 #include <libxml/threads.h>
39
40 #include "private/cata.h"
41 #include "private/buf.h"
42 #include "private/error.h"
43 #include "private/memory.h"
44 #include "private/parser.h"
45 #include "private/threads.h"
46
47 #define MAX_DELEGATE 50
48 #define MAX_CATAL_DEPTH 50
49
50 #ifdef _WIN32
51 # define PATH_SEPARATOR ';'
52 #else
53 # define PATH_SEPARATOR ':'
54 #endif
55
56 #define XML_URN_PUBID "urn:publicid:"
57 #define XML_CATAL_BREAK ((xmlChar *) -1)
58 #ifndef XML_XML_DEFAULT_CATALOG
59 #define XML_XML_DEFAULT_CATALOG "file://" XML_SYSCONFDIR "/xml/catalog"
60 #endif
61 #ifndef XML_SGML_DEFAULT_CATALOG
62 #define XML_SGML_DEFAULT_CATALOG "file://" XML_SYSCONFDIR "/sgml/catalog"
63 #endif
64
65 static xmlChar *xmlCatalogNormalizePublic(const xmlChar *pubID);
66 static int xmlExpandCatalog(xmlCatalogPtr catal, const char *filename);
67
68 /************************************************************************
69 * *
70 * Types, all private *
71 * *
72 ************************************************************************/
73
74 typedef enum {
75 XML_CATA_REMOVED = -1,
76 XML_CATA_NONE = 0,
77 XML_CATA_CATALOG,
78 XML_CATA_BROKEN_CATALOG,
79 XML_CATA_NEXT_CATALOG,
80 XML_CATA_GROUP,
81 XML_CATA_PUBLIC,
82 XML_CATA_SYSTEM,
83 XML_CATA_REWRITE_SYSTEM,
84 XML_CATA_DELEGATE_PUBLIC,
85 XML_CATA_DELEGATE_SYSTEM,
86 XML_CATA_URI,
87 XML_CATA_REWRITE_URI,
88 XML_CATA_DELEGATE_URI,
89 SGML_CATA_SYSTEM,
90 SGML_CATA_PUBLIC,
91 SGML_CATA_ENTITY,
92 SGML_CATA_PENTITY,
93 SGML_CATA_DOCTYPE,
94 SGML_CATA_LINKTYPE,
95 SGML_CATA_NOTATION,
96 SGML_CATA_DELEGATE,
97 SGML_CATA_BASE,
98 SGML_CATA_CATALOG,
99 SGML_CATA_DOCUMENT,
100 SGML_CATA_SGMLDECL
101 } xmlCatalogEntryType;
102
103 typedef struct _xmlCatalogEntry xmlCatalogEntry;
104 typedef xmlCatalogEntry *xmlCatalogEntryPtr;
105 struct _xmlCatalogEntry {
106 struct _xmlCatalogEntry *next;
107 struct _xmlCatalogEntry *parent;
108 struct _xmlCatalogEntry *children;
109 xmlCatalogEntryType type;
110 xmlChar *name;
111 xmlChar *value;
112 xmlChar *URL; /* The expanded URL using the base */
113 xmlCatalogPrefer prefer;
114 int dealloc;
115 int depth;
116 struct _xmlCatalogEntry *group;
117 };
118
119 typedef enum {
120 XML_XML_CATALOG_TYPE = 1,
121 XML_SGML_CATALOG_TYPE
122 } xmlCatalogType;
123
124 #define XML_MAX_SGML_CATA_DEPTH 10
125 struct _xmlCatalog {
126 xmlCatalogType type; /* either XML or SGML */
127
128 /*
129 * SGML Catalogs are stored as a simple hash table of catalog entries
130 * Catalog stack to check against overflows when building the
131 * SGML catalog
132 */
133 char *catalTab[XML_MAX_SGML_CATA_DEPTH]; /* stack of catals */
134 int catalNr; /* Number of current catal streams */
135 int catalMax; /* Max number of catal streams */
136 xmlHashTablePtr sgml;
137
138 /*
139 * XML Catalogs are stored as a tree of Catalog entries
140 */
141 xmlCatalogPrefer prefer;
142 xmlCatalogEntryPtr xml;
143 };
144
145 /************************************************************************
146 * *
147 * Global variables *
148 * *
149 ************************************************************************/
150
151 /*
152 * Those are preferences
153 */
154 static int xmlDebugCatalogs = 0; /* used for debugging */
155 static xmlCatalogAllow xmlCatalogDefaultAllow = XML_CATA_ALLOW_ALL;
156 static xmlCatalogPrefer xmlCatalogDefaultPrefer = XML_CATA_PREFER_PUBLIC;
157
158 /*
159 * Hash table containing all the trees of XML catalogs parsed by
160 * the application.
161 */
162 static xmlHashTablePtr xmlCatalogXMLFiles = NULL;
163
164 /*
165 * The default catalog in use by the application
166 */
167 static xmlCatalogPtr xmlDefaultCatalog = NULL;
168
169 /*
170 * A mutex for modifying the shared global catalog(s)
171 * xmlDefaultCatalog tree.
172 * It also protects xmlCatalogXMLFiles
173 * The core of this readers/writer scheme is in xmlFetchXMLCatalogFile()
174 */
175 static xmlRMutex xmlCatalogMutex;
176
177 /*
178 * Whether the default system catalog was initialized.
179 */
180 static int xmlCatalogInitialized = 0;
181
182 /************************************************************************
183 * *
184 * Catalog error handlers *
185 * *
186 ************************************************************************/
187
188 /**
189 * xmlCatalogErrMemory:
190 * @extra: extra information
191 *
192 * Handle an out of memory condition
193 */
194 static void
xmlCatalogErrMemory(void)195 xmlCatalogErrMemory(void)
196 {
197 xmlRaiseMemoryError(NULL, NULL, NULL, XML_FROM_CATALOG, NULL);
198 }
199
200 /**
201 * xmlCatalogErr:
202 * @catal: the Catalog entry
203 * @node: the context node
204 * @msg: the error message
205 * @extra: extra information
206 *
207 * Handle a catalog error
208 */
209 static void LIBXML_ATTR_FORMAT(4,0)
xmlCatalogErr(xmlCatalogEntryPtr catal,xmlNodePtr node,int error,const char * msg,const xmlChar * str1,const xmlChar * str2,const xmlChar * str3)210 xmlCatalogErr(xmlCatalogEntryPtr catal, xmlNodePtr node, int error,
211 const char *msg, const xmlChar *str1, const xmlChar *str2,
212 const xmlChar *str3)
213 {
214 int res;
215
216 res = xmlRaiseError(NULL, NULL, NULL, catal, node,
217 XML_FROM_CATALOG, error, XML_ERR_ERROR, NULL, 0,
218 (const char *) str1, (const char *) str2,
219 (const char *) str3, 0, 0,
220 msg, str1, str2, str3);
221 if (res < 0)
222 xmlCatalogErrMemory();
223 }
224
225 static void
xmlCatalogPrintDebug(const char * fmt,...)226 xmlCatalogPrintDebug(const char *fmt, ...) {
227 va_list ap;
228
229 va_start(ap, fmt);
230 xmlVPrintErrorMessage(fmt, ap);
231 va_end(ap);
232 }
233
234 /************************************************************************
235 * *
236 * Allocation and Freeing *
237 * *
238 ************************************************************************/
239
240 /**
241 * xmlNewCatalogEntry:
242 * @type: type of entry
243 * @name: name of the entry
244 * @value: value of the entry
245 * @prefer: the PUBLIC vs. SYSTEM current preference value
246 * @group: for members of a group, the group entry
247 *
248 * create a new Catalog entry, this type is shared both by XML and
249 * SGML catalogs, but the acceptable types values differs.
250 *
251 * Returns the xmlCatalogEntryPtr or NULL in case of error
252 */
253 static xmlCatalogEntryPtr
xmlNewCatalogEntry(xmlCatalogEntryType type,const xmlChar * name,const xmlChar * value,const xmlChar * URL,xmlCatalogPrefer prefer,xmlCatalogEntryPtr group)254 xmlNewCatalogEntry(xmlCatalogEntryType type, const xmlChar *name,
255 const xmlChar *value, const xmlChar *URL, xmlCatalogPrefer prefer,
256 xmlCatalogEntryPtr group) {
257 xmlCatalogEntryPtr ret;
258 xmlChar *normid = NULL;
259
260 ret = (xmlCatalogEntryPtr) xmlMalloc(sizeof(xmlCatalogEntry));
261 if (ret == NULL) {
262 xmlCatalogErrMemory();
263 return(NULL);
264 }
265 ret->next = NULL;
266 ret->parent = NULL;
267 ret->children = NULL;
268 ret->type = type;
269 if (type == XML_CATA_PUBLIC || type == XML_CATA_DELEGATE_PUBLIC) {
270 normid = xmlCatalogNormalizePublic(name);
271 if (normid != NULL)
272 name = (*normid != 0 ? normid : NULL);
273 }
274 if (name != NULL)
275 ret->name = xmlStrdup(name);
276 else
277 ret->name = NULL;
278 if (normid != NULL)
279 xmlFree(normid);
280 if (value != NULL)
281 ret->value = xmlStrdup(value);
282 else
283 ret->value = NULL;
284 if (URL == NULL)
285 URL = value;
286 if (URL != NULL)
287 ret->URL = xmlStrdup(URL);
288 else
289 ret->URL = NULL;
290 ret->prefer = prefer;
291 ret->dealloc = 0;
292 ret->depth = 0;
293 ret->group = group;
294 return(ret);
295 }
296
297 static void
298 xmlFreeCatalogEntryList(xmlCatalogEntryPtr ret);
299
300 /**
301 * xmlFreeCatalogEntry:
302 * @payload: a Catalog entry
303 *
304 * Free the memory allocated to a Catalog entry
305 */
306 static void
xmlFreeCatalogEntry(void * payload,const xmlChar * name ATTRIBUTE_UNUSED)307 xmlFreeCatalogEntry(void *payload, const xmlChar *name ATTRIBUTE_UNUSED) {
308 xmlCatalogEntryPtr ret = (xmlCatalogEntryPtr) payload;
309 if (ret == NULL)
310 return;
311 /*
312 * Entries stored in the file hash must be deallocated
313 * only by the file hash cleaner !
314 */
315 if (ret->dealloc == 1)
316 return;
317
318 if (xmlDebugCatalogs) {
319 if (ret->name != NULL)
320 xmlCatalogPrintDebug(
321 "Free catalog entry %s\n", ret->name);
322 else if (ret->value != NULL)
323 xmlCatalogPrintDebug(
324 "Free catalog entry %s\n", ret->value);
325 else
326 xmlCatalogPrintDebug(
327 "Free catalog entry\n");
328 }
329
330 if (ret->name != NULL)
331 xmlFree(ret->name);
332 if (ret->value != NULL)
333 xmlFree(ret->value);
334 if (ret->URL != NULL)
335 xmlFree(ret->URL);
336 xmlFree(ret);
337 }
338
339 /**
340 * xmlFreeCatalogEntryList:
341 * @ret: a Catalog entry list
342 *
343 * Free the memory allocated to a full chained list of Catalog entries
344 */
345 static void
xmlFreeCatalogEntryList(xmlCatalogEntryPtr ret)346 xmlFreeCatalogEntryList(xmlCatalogEntryPtr ret) {
347 xmlCatalogEntryPtr next;
348
349 while (ret != NULL) {
350 next = ret->next;
351 xmlFreeCatalogEntry(ret, NULL);
352 ret = next;
353 }
354 }
355
356 /**
357 * xmlFreeCatalogHashEntryList:
358 * @payload: a Catalog entry list
359 *
360 * Free the memory allocated to list of Catalog entries from the
361 * catalog file hash.
362 */
363 static void
xmlFreeCatalogHashEntryList(void * payload,const xmlChar * name ATTRIBUTE_UNUSED)364 xmlFreeCatalogHashEntryList(void *payload,
365 const xmlChar *name ATTRIBUTE_UNUSED) {
366 xmlCatalogEntryPtr catal = (xmlCatalogEntryPtr) payload;
367 xmlCatalogEntryPtr children, next;
368
369 if (catal == NULL)
370 return;
371
372 children = catal->children;
373 while (children != NULL) {
374 next = children->next;
375 children->dealloc = 0;
376 children->children = NULL;
377 xmlFreeCatalogEntry(children, NULL);
378 children = next;
379 }
380 catal->dealloc = 0;
381 xmlFreeCatalogEntry(catal, NULL);
382 }
383
384 /**
385 * xmlCreateNewCatalog:
386 * @type: type of catalog
387 * @prefer: the PUBLIC vs. SYSTEM current preference value
388 *
389 * create a new Catalog, this type is shared both by XML and
390 * SGML catalogs, but the acceptable types values differs.
391 *
392 * Returns the xmlCatalogPtr or NULL in case of error
393 */
394 static xmlCatalogPtr
xmlCreateNewCatalog(xmlCatalogType type,xmlCatalogPrefer prefer)395 xmlCreateNewCatalog(xmlCatalogType type, xmlCatalogPrefer prefer) {
396 xmlCatalogPtr ret;
397
398 ret = (xmlCatalogPtr) xmlMalloc(sizeof(xmlCatalog));
399 if (ret == NULL) {
400 xmlCatalogErrMemory();
401 return(NULL);
402 }
403 memset(ret, 0, sizeof(xmlCatalog));
404 ret->type = type;
405 ret->catalNr = 0;
406 ret->catalMax = XML_MAX_SGML_CATA_DEPTH;
407 ret->prefer = prefer;
408 if (ret->type == XML_SGML_CATALOG_TYPE)
409 ret->sgml = xmlHashCreate(10);
410 return(ret);
411 }
412
413 /**
414 * xmlFreeCatalog:
415 * @catal: a Catalog
416 *
417 * Free the memory allocated to a Catalog
418 */
419 void
xmlFreeCatalog(xmlCatalogPtr catal)420 xmlFreeCatalog(xmlCatalogPtr catal) {
421 if (catal == NULL)
422 return;
423 if (catal->xml != NULL)
424 xmlFreeCatalogEntryList(catal->xml);
425 if (catal->sgml != NULL)
426 xmlHashFree(catal->sgml, xmlFreeCatalogEntry);
427 xmlFree(catal);
428 }
429
430 /************************************************************************
431 * *
432 * Serializing Catalogs *
433 * *
434 ************************************************************************/
435
436 #ifdef LIBXML_OUTPUT_ENABLED
437 /**
438 * xmlCatalogDumpEntry:
439 * @entry: the catalog entry
440 * @out: the file.
441 *
442 * Serialize an SGML Catalog entry
443 */
444 static void
xmlCatalogDumpEntry(void * payload,void * data,const xmlChar * name ATTRIBUTE_UNUSED)445 xmlCatalogDumpEntry(void *payload, void *data,
446 const xmlChar *name ATTRIBUTE_UNUSED) {
447 xmlCatalogEntryPtr entry = (xmlCatalogEntryPtr) payload;
448 FILE *out = (FILE *) data;
449 if ((entry == NULL) || (out == NULL))
450 return;
451 switch (entry->type) {
452 case SGML_CATA_ENTITY:
453 fprintf(out, "ENTITY "); break;
454 case SGML_CATA_PENTITY:
455 fprintf(out, "ENTITY %%"); break;
456 case SGML_CATA_DOCTYPE:
457 fprintf(out, "DOCTYPE "); break;
458 case SGML_CATA_LINKTYPE:
459 fprintf(out, "LINKTYPE "); break;
460 case SGML_CATA_NOTATION:
461 fprintf(out, "NOTATION "); break;
462 case SGML_CATA_PUBLIC:
463 fprintf(out, "PUBLIC "); break;
464 case SGML_CATA_SYSTEM:
465 fprintf(out, "SYSTEM "); break;
466 case SGML_CATA_DELEGATE:
467 fprintf(out, "DELEGATE "); break;
468 case SGML_CATA_BASE:
469 fprintf(out, "BASE "); break;
470 case SGML_CATA_CATALOG:
471 fprintf(out, "CATALOG "); break;
472 case SGML_CATA_DOCUMENT:
473 fprintf(out, "DOCUMENT "); break;
474 case SGML_CATA_SGMLDECL:
475 fprintf(out, "SGMLDECL "); break;
476 default:
477 return;
478 }
479 switch (entry->type) {
480 case SGML_CATA_ENTITY:
481 case SGML_CATA_PENTITY:
482 case SGML_CATA_DOCTYPE:
483 case SGML_CATA_LINKTYPE:
484 case SGML_CATA_NOTATION:
485 fprintf(out, "%s", (const char *) entry->name); break;
486 case SGML_CATA_PUBLIC:
487 case SGML_CATA_SYSTEM:
488 case SGML_CATA_SGMLDECL:
489 case SGML_CATA_DOCUMENT:
490 case SGML_CATA_CATALOG:
491 case SGML_CATA_BASE:
492 case SGML_CATA_DELEGATE:
493 fprintf(out, "\"%s\"", entry->name); break;
494 default:
495 break;
496 }
497 switch (entry->type) {
498 case SGML_CATA_ENTITY:
499 case SGML_CATA_PENTITY:
500 case SGML_CATA_DOCTYPE:
501 case SGML_CATA_LINKTYPE:
502 case SGML_CATA_NOTATION:
503 case SGML_CATA_PUBLIC:
504 case SGML_CATA_SYSTEM:
505 case SGML_CATA_DELEGATE:
506 fprintf(out, " \"%s\"", entry->value); break;
507 default:
508 break;
509 }
510 fprintf(out, "\n");
511 }
512
513 /**
514 * xmlDumpXMLCatalogNode:
515 * @catal: top catalog entry
516 * @catalog: pointer to the xml tree
517 * @doc: the containing document
518 * @ns: the current namespace
519 * @cgroup: group node for group members
520 *
521 * Serializes a Catalog entry, called by xmlDumpXMLCatalog and recursively
522 * for group entries
523 */
xmlDumpXMLCatalogNode(xmlCatalogEntryPtr catal,xmlNodePtr catalog,xmlDocPtr doc,xmlNsPtr ns,xmlCatalogEntryPtr cgroup)524 static void xmlDumpXMLCatalogNode(xmlCatalogEntryPtr catal, xmlNodePtr catalog,
525 xmlDocPtr doc, xmlNsPtr ns, xmlCatalogEntryPtr cgroup) {
526 xmlNodePtr node;
527 xmlCatalogEntryPtr cur;
528 /*
529 * add all the catalog entries
530 */
531 cur = catal;
532 while (cur != NULL) {
533 if (cur->group == cgroup) {
534 switch (cur->type) {
535 case XML_CATA_REMOVED:
536 break;
537 case XML_CATA_BROKEN_CATALOG:
538 case XML_CATA_CATALOG:
539 if (cur == catal) {
540 cur = cur->children;
541 continue;
542 }
543 break;
544 case XML_CATA_NEXT_CATALOG:
545 node = xmlNewDocNode(doc, ns, BAD_CAST "nextCatalog", NULL);
546 xmlSetProp(node, BAD_CAST "catalog", cur->value);
547 xmlAddChild(catalog, node);
548 break;
549 case XML_CATA_NONE:
550 break;
551 case XML_CATA_GROUP:
552 node = xmlNewDocNode(doc, ns, BAD_CAST "group", NULL);
553 xmlSetProp(node, BAD_CAST "id", cur->name);
554 if (cur->value != NULL) {
555 xmlNsPtr xns;
556 xns = xmlSearchNsByHref(doc, node, XML_XML_NAMESPACE);
557 if (xns != NULL)
558 xmlSetNsProp(node, xns, BAD_CAST "base",
559 cur->value);
560 }
561 switch (cur->prefer) {
562 case XML_CATA_PREFER_NONE:
563 break;
564 case XML_CATA_PREFER_PUBLIC:
565 xmlSetProp(node, BAD_CAST "prefer", BAD_CAST "public");
566 break;
567 case XML_CATA_PREFER_SYSTEM:
568 xmlSetProp(node, BAD_CAST "prefer", BAD_CAST "system");
569 break;
570 }
571 xmlDumpXMLCatalogNode(cur->next, node, doc, ns, cur);
572 xmlAddChild(catalog, node);
573 break;
574 case XML_CATA_PUBLIC:
575 node = xmlNewDocNode(doc, ns, BAD_CAST "public", NULL);
576 xmlSetProp(node, BAD_CAST "publicId", cur->name);
577 xmlSetProp(node, BAD_CAST "uri", cur->value);
578 xmlAddChild(catalog, node);
579 break;
580 case XML_CATA_SYSTEM:
581 node = xmlNewDocNode(doc, ns, BAD_CAST "system", NULL);
582 xmlSetProp(node, BAD_CAST "systemId", cur->name);
583 xmlSetProp(node, BAD_CAST "uri", cur->value);
584 xmlAddChild(catalog, node);
585 break;
586 case XML_CATA_REWRITE_SYSTEM:
587 node = xmlNewDocNode(doc, ns, BAD_CAST "rewriteSystem", NULL);
588 xmlSetProp(node, BAD_CAST "systemIdStartString", cur->name);
589 xmlSetProp(node, BAD_CAST "rewritePrefix", cur->value);
590 xmlAddChild(catalog, node);
591 break;
592 case XML_CATA_DELEGATE_PUBLIC:
593 node = xmlNewDocNode(doc, ns, BAD_CAST "delegatePublic", NULL);
594 xmlSetProp(node, BAD_CAST "publicIdStartString", cur->name);
595 xmlSetProp(node, BAD_CAST "catalog", cur->value);
596 xmlAddChild(catalog, node);
597 break;
598 case XML_CATA_DELEGATE_SYSTEM:
599 node = xmlNewDocNode(doc, ns, BAD_CAST "delegateSystem", NULL);
600 xmlSetProp(node, BAD_CAST "systemIdStartString", cur->name);
601 xmlSetProp(node, BAD_CAST "catalog", cur->value);
602 xmlAddChild(catalog, node);
603 break;
604 case XML_CATA_URI:
605 node = xmlNewDocNode(doc, ns, BAD_CAST "uri", NULL);
606 xmlSetProp(node, BAD_CAST "name", cur->name);
607 xmlSetProp(node, BAD_CAST "uri", cur->value);
608 xmlAddChild(catalog, node);
609 break;
610 case XML_CATA_REWRITE_URI:
611 node = xmlNewDocNode(doc, ns, BAD_CAST "rewriteURI", NULL);
612 xmlSetProp(node, BAD_CAST "uriStartString", cur->name);
613 xmlSetProp(node, BAD_CAST "rewritePrefix", cur->value);
614 xmlAddChild(catalog, node);
615 break;
616 case XML_CATA_DELEGATE_URI:
617 node = xmlNewDocNode(doc, ns, BAD_CAST "delegateURI", NULL);
618 xmlSetProp(node, BAD_CAST "uriStartString", cur->name);
619 xmlSetProp(node, BAD_CAST "catalog", cur->value);
620 xmlAddChild(catalog, node);
621 break;
622 case SGML_CATA_SYSTEM:
623 case SGML_CATA_PUBLIC:
624 case SGML_CATA_ENTITY:
625 case SGML_CATA_PENTITY:
626 case SGML_CATA_DOCTYPE:
627 case SGML_CATA_LINKTYPE:
628 case SGML_CATA_NOTATION:
629 case SGML_CATA_DELEGATE:
630 case SGML_CATA_BASE:
631 case SGML_CATA_CATALOG:
632 case SGML_CATA_DOCUMENT:
633 case SGML_CATA_SGMLDECL:
634 break;
635 }
636 }
637 cur = cur->next;
638 }
639 }
640
641 static int
xmlDumpXMLCatalog(FILE * out,xmlCatalogEntryPtr catal)642 xmlDumpXMLCatalog(FILE *out, xmlCatalogEntryPtr catal) {
643 int ret;
644 xmlDocPtr doc;
645 xmlNsPtr ns;
646 xmlDtdPtr dtd;
647 xmlNodePtr catalog;
648 xmlOutputBufferPtr buf;
649
650 /*
651 * Rebuild a catalog
652 */
653 doc = xmlNewDoc(NULL);
654 if (doc == NULL)
655 return(-1);
656 dtd = xmlNewDtd(doc, BAD_CAST "catalog",
657 BAD_CAST "-//OASIS//DTD Entity Resolution XML Catalog V1.0//EN",
658 BAD_CAST "http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd");
659
660 xmlAddChild((xmlNodePtr) doc, (xmlNodePtr) dtd);
661
662 ns = xmlNewNs(NULL, XML_CATALOGS_NAMESPACE, NULL);
663 if (ns == NULL) {
664 xmlFreeDoc(doc);
665 return(-1);
666 }
667 catalog = xmlNewDocNode(doc, ns, BAD_CAST "catalog", NULL);
668 if (catalog == NULL) {
669 xmlFreeNs(ns);
670 xmlFreeDoc(doc);
671 return(-1);
672 }
673 catalog->nsDef = ns;
674 xmlAddChild((xmlNodePtr) doc, catalog);
675
676 xmlDumpXMLCatalogNode(catal, catalog, doc, ns, NULL);
677
678 /*
679 * reserialize it
680 */
681 buf = xmlOutputBufferCreateFile(out, NULL);
682 if (buf == NULL) {
683 xmlFreeDoc(doc);
684 return(-1);
685 }
686 ret = xmlSaveFormatFileTo(buf, doc, NULL, 1);
687
688 /*
689 * Free it
690 */
691 xmlFreeDoc(doc);
692
693 return(ret);
694 }
695 #endif /* LIBXML_OUTPUT_ENABLED */
696
697 /************************************************************************
698 * *
699 * Converting SGML Catalogs to XML *
700 * *
701 ************************************************************************/
702
703 /**
704 * xmlCatalogConvertEntry:
705 * @entry: the entry
706 * @catal: pointer to the catalog being converted
707 *
708 * Convert one entry from the catalog
709 */
710 static void
xmlCatalogConvertEntry(void * payload,void * data,const xmlChar * name ATTRIBUTE_UNUSED)711 xmlCatalogConvertEntry(void *payload, void *data,
712 const xmlChar *name ATTRIBUTE_UNUSED) {
713 xmlCatalogEntryPtr entry = (xmlCatalogEntryPtr) payload;
714 xmlCatalogPtr catal = (xmlCatalogPtr) data;
715 if ((entry == NULL) || (catal == NULL) || (catal->sgml == NULL) ||
716 (catal->xml == NULL))
717 return;
718 switch (entry->type) {
719 case SGML_CATA_ENTITY:
720 entry->type = XML_CATA_PUBLIC;
721 break;
722 case SGML_CATA_PENTITY:
723 entry->type = XML_CATA_PUBLIC;
724 break;
725 case SGML_CATA_DOCTYPE:
726 entry->type = XML_CATA_PUBLIC;
727 break;
728 case SGML_CATA_LINKTYPE:
729 entry->type = XML_CATA_PUBLIC;
730 break;
731 case SGML_CATA_NOTATION:
732 entry->type = XML_CATA_PUBLIC;
733 break;
734 case SGML_CATA_PUBLIC:
735 entry->type = XML_CATA_PUBLIC;
736 break;
737 case SGML_CATA_SYSTEM:
738 entry->type = XML_CATA_SYSTEM;
739 break;
740 case SGML_CATA_DELEGATE:
741 entry->type = XML_CATA_DELEGATE_PUBLIC;
742 break;
743 case SGML_CATA_CATALOG:
744 entry->type = XML_CATA_CATALOG;
745 break;
746 default:
747 xmlHashRemoveEntry(catal->sgml, entry->name, xmlFreeCatalogEntry);
748 return;
749 }
750 /*
751 * Conversion successful, remove from the SGML catalog
752 * and add it to the default XML one
753 */
754 xmlHashRemoveEntry(catal->sgml, entry->name, NULL);
755 entry->parent = catal->xml;
756 entry->next = NULL;
757 if (catal->xml->children == NULL)
758 catal->xml->children = entry;
759 else {
760 xmlCatalogEntryPtr prev;
761
762 prev = catal->xml->children;
763 while (prev->next != NULL)
764 prev = prev->next;
765 prev->next = entry;
766 }
767 }
768
769 /**
770 * xmlConvertSGMLCatalog:
771 * @catal: the catalog
772 *
773 * Convert all the SGML catalog entries as XML ones
774 *
775 * Returns the number of entries converted if successful, -1 otherwise
776 */
777 int
xmlConvertSGMLCatalog(xmlCatalogPtr catal)778 xmlConvertSGMLCatalog(xmlCatalogPtr catal) {
779
780 if ((catal == NULL) || (catal->type != XML_SGML_CATALOG_TYPE))
781 return(-1);
782
783 if (xmlDebugCatalogs) {
784 xmlCatalogPrintDebug(
785 "Converting SGML catalog to XML\n");
786 }
787 xmlHashScan(catal->sgml, xmlCatalogConvertEntry, &catal);
788 return(0);
789 }
790
791 /************************************************************************
792 * *
793 * Helper function *
794 * *
795 ************************************************************************/
796
797 /**
798 * xmlCatalogUnWrapURN:
799 * @urn: an "urn:publicid:" to unwrap
800 *
801 * Expand the URN into the equivalent Public Identifier
802 *
803 * Returns the new identifier or NULL, the string must be deallocated
804 * by the caller.
805 */
806 static xmlChar *
xmlCatalogUnWrapURN(const xmlChar * urn)807 xmlCatalogUnWrapURN(const xmlChar *urn) {
808 xmlChar result[2000];
809 unsigned int i = 0;
810
811 if (xmlStrncmp(urn, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1))
812 return(NULL);
813 urn += sizeof(XML_URN_PUBID) - 1;
814
815 while (*urn != 0) {
816 if (i > sizeof(result) - 4)
817 break;
818 if (*urn == '+') {
819 result[i++] = ' ';
820 urn++;
821 } else if (*urn == ':') {
822 result[i++] = '/';
823 result[i++] = '/';
824 urn++;
825 } else if (*urn == ';') {
826 result[i++] = ':';
827 result[i++] = ':';
828 urn++;
829 } else if (*urn == '%') {
830 if ((urn[1] == '2') && (urn[2] == 'B'))
831 result[i++] = '+';
832 else if ((urn[1] == '3') && (urn[2] == 'A'))
833 result[i++] = ':';
834 else if ((urn[1] == '2') && (urn[2] == 'F'))
835 result[i++] = '/';
836 else if ((urn[1] == '3') && (urn[2] == 'B'))
837 result[i++] = ';';
838 else if ((urn[1] == '2') && (urn[2] == '7'))
839 result[i++] = '\'';
840 else if ((urn[1] == '3') && (urn[2] == 'F'))
841 result[i++] = '?';
842 else if ((urn[1] == '2') && (urn[2] == '3'))
843 result[i++] = '#';
844 else if ((urn[1] == '2') && (urn[2] == '5'))
845 result[i++] = '%';
846 else {
847 result[i++] = *urn;
848 urn++;
849 continue;
850 }
851 urn += 3;
852 } else {
853 result[i++] = *urn;
854 urn++;
855 }
856 }
857 result[i] = 0;
858
859 return(xmlStrdup(result));
860 }
861
862 /**
863 * xmlParseCatalogFile:
864 * @filename: the filename
865 *
866 * parse an XML file and build a tree. It's like xmlParseFile()
867 * except it bypass all catalog lookups.
868 *
869 * Returns the resulting document tree or NULL in case of error
870 */
871
872 xmlDocPtr
xmlParseCatalogFile(const char * filename)873 xmlParseCatalogFile(const char *filename) {
874 xmlDocPtr ret;
875 xmlParserCtxtPtr ctxt;
876 xmlParserInputPtr inputStream;
877 xmlParserInputBufferPtr buf;
878
879 ctxt = xmlNewParserCtxt();
880 if (ctxt == NULL) {
881 xmlCatalogErrMemory();
882 return(NULL);
883 }
884
885 buf = xmlParserInputBufferCreateFilename(filename, XML_CHAR_ENCODING_NONE);
886 if (buf == NULL) {
887 xmlFreeParserCtxt(ctxt);
888 return(NULL);
889 }
890
891 inputStream = xmlNewInputStream(ctxt);
892 if (inputStream == NULL) {
893 xmlFreeParserInputBuffer(buf);
894 xmlFreeParserCtxt(ctxt);
895 return(NULL);
896 }
897
898 inputStream->filename = (char *) xmlCanonicPath((const xmlChar *)filename);
899 inputStream->buf = buf;
900 xmlBufResetInput(buf->buffer, inputStream);
901
902 if (xmlCtxtPushInput(ctxt, inputStream) < 0) {
903 xmlFreeInputStream(inputStream);
904 xmlFreeParserCtxt(ctxt);
905 return(NULL);
906 }
907
908 ctxt->valid = 0;
909 ctxt->validate = 0;
910 ctxt->loadsubset = 0;
911 ctxt->pedantic = 0;
912 ctxt->dictNames = 1;
913
914 xmlParseDocument(ctxt);
915
916 if (ctxt->wellFormed)
917 ret = ctxt->myDoc;
918 else {
919 ret = NULL;
920 xmlFreeDoc(ctxt->myDoc);
921 ctxt->myDoc = NULL;
922 }
923 xmlFreeParserCtxt(ctxt);
924
925 return(ret);
926 }
927
928 /**
929 * xmlLoadFileContent:
930 * @filename: a file path
931 *
932 * Load a file content into memory.
933 *
934 * Returns a pointer to the 0 terminated string or NULL in case of error
935 */
936 static xmlChar *
xmlLoadFileContent(const char * filename)937 xmlLoadFileContent(const char *filename)
938 {
939 int fd;
940 int len;
941 long size;
942
943 struct stat info;
944 xmlChar *content;
945
946 if (filename == NULL)
947 return (NULL);
948
949 if (stat(filename, &info) < 0)
950 return (NULL);
951
952 fd = open(filename, O_RDONLY);
953 if (fd < 0)
954 {
955 return (NULL);
956 }
957 size = info.st_size;
958 content = xmlMalloc(size + 10);
959 if (content == NULL) {
960 xmlCatalogErrMemory();
961 close(fd);
962 return (NULL);
963 }
964 len = read(fd, content, size);
965 close(fd);
966 if (len < 0) {
967 xmlFree(content);
968 return (NULL);
969 }
970 content[len] = 0;
971
972 return(content);
973 }
974
975 /**
976 * xmlCatalogNormalizePublic:
977 * @pubID: the public ID string
978 *
979 * Normalizes the Public Identifier
980 *
981 * Implements 6.2. Public Identifier Normalization
982 * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
983 *
984 * Returns the new string or NULL, the string must be deallocated
985 * by the caller.
986 */
987 static xmlChar *
xmlCatalogNormalizePublic(const xmlChar * pubID)988 xmlCatalogNormalizePublic(const xmlChar *pubID)
989 {
990 int ok = 1;
991 int white;
992 const xmlChar *p;
993 xmlChar *ret;
994 xmlChar *q;
995
996 if (pubID == NULL)
997 return(NULL);
998
999 white = 1;
1000 for (p = pubID;*p != 0 && ok;p++) {
1001 if (!xmlIsBlank_ch(*p))
1002 white = 0;
1003 else if (*p == 0x20 && !white)
1004 white = 1;
1005 else
1006 ok = 0;
1007 }
1008 if (ok && !white) /* is normalized */
1009 return(NULL);
1010
1011 ret = xmlStrdup(pubID);
1012 q = ret;
1013 white = 0;
1014 for (p = pubID;*p != 0;p++) {
1015 if (xmlIsBlank_ch(*p)) {
1016 if (q != ret)
1017 white = 1;
1018 } else {
1019 if (white) {
1020 *(q++) = 0x20;
1021 white = 0;
1022 }
1023 *(q++) = *p;
1024 }
1025 }
1026 *q = 0;
1027 return(ret);
1028 }
1029
1030 /************************************************************************
1031 * *
1032 * The XML Catalog parser *
1033 * *
1034 ************************************************************************/
1035
1036 static xmlCatalogEntryPtr
1037 xmlParseXMLCatalogFile(xmlCatalogPrefer prefer, const xmlChar *filename);
1038 static void
1039 xmlParseXMLCatalogNodeList(xmlNodePtr cur, xmlCatalogPrefer prefer,
1040 xmlCatalogEntryPtr parent, xmlCatalogEntryPtr cgroup);
1041 static xmlChar *
1042 xmlCatalogListXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,
1043 const xmlChar *sysID);
1044 static xmlChar *
1045 xmlCatalogListXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI);
1046
1047
1048 /**
1049 * xmlGetXMLCatalogEntryType:
1050 * @name: the name
1051 *
1052 * lookup the internal type associated to an XML catalog entry name
1053 *
1054 * Returns the type associated with that name
1055 */
1056 static xmlCatalogEntryType
xmlGetXMLCatalogEntryType(const xmlChar * name)1057 xmlGetXMLCatalogEntryType(const xmlChar *name) {
1058 xmlCatalogEntryType type = XML_CATA_NONE;
1059 if (xmlStrEqual(name, (const xmlChar *) "system"))
1060 type = XML_CATA_SYSTEM;
1061 else if (xmlStrEqual(name, (const xmlChar *) "public"))
1062 type = XML_CATA_PUBLIC;
1063 else if (xmlStrEqual(name, (const xmlChar *) "rewriteSystem"))
1064 type = XML_CATA_REWRITE_SYSTEM;
1065 else if (xmlStrEqual(name, (const xmlChar *) "delegatePublic"))
1066 type = XML_CATA_DELEGATE_PUBLIC;
1067 else if (xmlStrEqual(name, (const xmlChar *) "delegateSystem"))
1068 type = XML_CATA_DELEGATE_SYSTEM;
1069 else if (xmlStrEqual(name, (const xmlChar *) "uri"))
1070 type = XML_CATA_URI;
1071 else if (xmlStrEqual(name, (const xmlChar *) "rewriteURI"))
1072 type = XML_CATA_REWRITE_URI;
1073 else if (xmlStrEqual(name, (const xmlChar *) "delegateURI"))
1074 type = XML_CATA_DELEGATE_URI;
1075 else if (xmlStrEqual(name, (const xmlChar *) "nextCatalog"))
1076 type = XML_CATA_NEXT_CATALOG;
1077 else if (xmlStrEqual(name, (const xmlChar *) "catalog"))
1078 type = XML_CATA_CATALOG;
1079 return(type);
1080 }
1081
1082 /**
1083 * xmlParseXMLCatalogOneNode:
1084 * @cur: the XML node
1085 * @type: the type of Catalog entry
1086 * @name: the name of the node
1087 * @attrName: the attribute holding the value
1088 * @uriAttrName: the attribute holding the URI-Reference
1089 * @prefer: the PUBLIC vs. SYSTEM current preference value
1090 * @cgroup: the group which includes this node
1091 *
1092 * Finishes the examination of an XML tree node of a catalog and build
1093 * a Catalog entry from it.
1094 *
1095 * Returns the new Catalog entry node or NULL in case of error.
1096 */
1097 static xmlCatalogEntryPtr
xmlParseXMLCatalogOneNode(xmlNodePtr cur,xmlCatalogEntryType type,const xmlChar * name,const xmlChar * attrName,const xmlChar * uriAttrName,xmlCatalogPrefer prefer,xmlCatalogEntryPtr cgroup)1098 xmlParseXMLCatalogOneNode(xmlNodePtr cur, xmlCatalogEntryType type,
1099 const xmlChar *name, const xmlChar *attrName,
1100 const xmlChar *uriAttrName, xmlCatalogPrefer prefer,
1101 xmlCatalogEntryPtr cgroup) {
1102 int ok = 1;
1103 xmlChar *uriValue;
1104 xmlChar *nameValue = NULL;
1105 xmlChar *base = NULL;
1106 xmlChar *URL = NULL;
1107 xmlCatalogEntryPtr ret = NULL;
1108
1109 if (attrName != NULL) {
1110 nameValue = xmlGetProp(cur, attrName);
1111 if (nameValue == NULL) {
1112 xmlCatalogErr(ret, cur, XML_CATALOG_MISSING_ATTR,
1113 "%s entry lacks '%s'\n", name, attrName, NULL);
1114 ok = 0;
1115 }
1116 }
1117 uriValue = xmlGetProp(cur, uriAttrName);
1118 if (uriValue == NULL) {
1119 xmlCatalogErr(ret, cur, XML_CATALOG_MISSING_ATTR,
1120 "%s entry lacks '%s'\n", name, uriAttrName, NULL);
1121 ok = 0;
1122 }
1123 if (!ok) {
1124 if (nameValue != NULL)
1125 xmlFree(nameValue);
1126 if (uriValue != NULL)
1127 xmlFree(uriValue);
1128 return(NULL);
1129 }
1130
1131 base = xmlNodeGetBase(cur->doc, cur);
1132 URL = xmlBuildURI(uriValue, base);
1133 if (URL != NULL) {
1134 if (xmlDebugCatalogs > 1) {
1135 if (nameValue != NULL)
1136 xmlCatalogPrintDebug(
1137 "Found %s: '%s' '%s'\n", name, nameValue, URL);
1138 else
1139 xmlCatalogPrintDebug(
1140 "Found %s: '%s'\n", name, URL);
1141 }
1142 ret = xmlNewCatalogEntry(type, nameValue, uriValue, URL, prefer, cgroup);
1143 } else {
1144 xmlCatalogErr(ret, cur, XML_CATALOG_ENTRY_BROKEN,
1145 "%s entry '%s' broken ?: %s\n", name, uriAttrName, uriValue);
1146 }
1147 if (nameValue != NULL)
1148 xmlFree(nameValue);
1149 if (uriValue != NULL)
1150 xmlFree(uriValue);
1151 if (base != NULL)
1152 xmlFree(base);
1153 if (URL != NULL)
1154 xmlFree(URL);
1155 return(ret);
1156 }
1157
1158 /**
1159 * xmlParseXMLCatalogNode:
1160 * @cur: the XML node
1161 * @prefer: the PUBLIC vs. SYSTEM current preference value
1162 * @parent: the parent Catalog entry
1163 * @cgroup: the group which includes this node
1164 *
1165 * Examines an XML tree node of a catalog and build
1166 * a Catalog entry from it adding it to its parent. The examination can
1167 * be recursive.
1168 */
1169 static void
xmlParseXMLCatalogNode(xmlNodePtr cur,xmlCatalogPrefer prefer,xmlCatalogEntryPtr parent,xmlCatalogEntryPtr cgroup)1170 xmlParseXMLCatalogNode(xmlNodePtr cur, xmlCatalogPrefer prefer,
1171 xmlCatalogEntryPtr parent, xmlCatalogEntryPtr cgroup)
1172 {
1173 xmlChar *base = NULL;
1174 xmlCatalogEntryPtr entry = NULL;
1175
1176 if (cur == NULL)
1177 return;
1178 if (xmlStrEqual(cur->name, BAD_CAST "group")) {
1179 xmlChar *prop;
1180 xmlCatalogPrefer pref = XML_CATA_PREFER_NONE;
1181
1182 prop = xmlGetProp(cur, BAD_CAST "prefer");
1183 if (prop != NULL) {
1184 if (xmlStrEqual(prop, BAD_CAST "system")) {
1185 prefer = XML_CATA_PREFER_SYSTEM;
1186 } else if (xmlStrEqual(prop, BAD_CAST "public")) {
1187 prefer = XML_CATA_PREFER_PUBLIC;
1188 } else {
1189 xmlCatalogErr(parent, cur, XML_CATALOG_PREFER_VALUE,
1190 "Invalid value for prefer: '%s'\n",
1191 prop, NULL, NULL);
1192 }
1193 xmlFree(prop);
1194 pref = prefer;
1195 }
1196 prop = xmlGetProp(cur, BAD_CAST "id");
1197 base = xmlGetNsProp(cur, BAD_CAST "base", XML_XML_NAMESPACE);
1198 entry = xmlNewCatalogEntry(XML_CATA_GROUP, prop, base, NULL, pref, cgroup);
1199 xmlFree(prop);
1200 } else if (xmlStrEqual(cur->name, BAD_CAST "public")) {
1201 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_PUBLIC,
1202 BAD_CAST "public", BAD_CAST "publicId", BAD_CAST "uri", prefer, cgroup);
1203 } else if (xmlStrEqual(cur->name, BAD_CAST "system")) {
1204 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_SYSTEM,
1205 BAD_CAST "system", BAD_CAST "systemId", BAD_CAST "uri", prefer, cgroup);
1206 } else if (xmlStrEqual(cur->name, BAD_CAST "rewriteSystem")) {
1207 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_REWRITE_SYSTEM,
1208 BAD_CAST "rewriteSystem", BAD_CAST "systemIdStartString",
1209 BAD_CAST "rewritePrefix", prefer, cgroup);
1210 } else if (xmlStrEqual(cur->name, BAD_CAST "delegatePublic")) {
1211 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_PUBLIC,
1212 BAD_CAST "delegatePublic", BAD_CAST "publicIdStartString",
1213 BAD_CAST "catalog", prefer, cgroup);
1214 } else if (xmlStrEqual(cur->name, BAD_CAST "delegateSystem")) {
1215 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_SYSTEM,
1216 BAD_CAST "delegateSystem", BAD_CAST "systemIdStartString",
1217 BAD_CAST "catalog", prefer, cgroup);
1218 } else if (xmlStrEqual(cur->name, BAD_CAST "uri")) {
1219 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_URI,
1220 BAD_CAST "uri", BAD_CAST "name",
1221 BAD_CAST "uri", prefer, cgroup);
1222 } else if (xmlStrEqual(cur->name, BAD_CAST "rewriteURI")) {
1223 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_REWRITE_URI,
1224 BAD_CAST "rewriteURI", BAD_CAST "uriStartString",
1225 BAD_CAST "rewritePrefix", prefer, cgroup);
1226 } else if (xmlStrEqual(cur->name, BAD_CAST "delegateURI")) {
1227 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_URI,
1228 BAD_CAST "delegateURI", BAD_CAST "uriStartString",
1229 BAD_CAST "catalog", prefer, cgroup);
1230 } else if (xmlStrEqual(cur->name, BAD_CAST "nextCatalog")) {
1231 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_NEXT_CATALOG,
1232 BAD_CAST "nextCatalog", NULL,
1233 BAD_CAST "catalog", prefer, cgroup);
1234 }
1235 if (entry != NULL) {
1236 if (parent != NULL) {
1237 entry->parent = parent;
1238 if (parent->children == NULL)
1239 parent->children = entry;
1240 else {
1241 xmlCatalogEntryPtr prev;
1242
1243 prev = parent->children;
1244 while (prev->next != NULL)
1245 prev = prev->next;
1246 prev->next = entry;
1247 }
1248 }
1249 if (entry->type == XML_CATA_GROUP) {
1250 /*
1251 * Recurse to propagate prefer to the subtree
1252 * (xml:base handling is automated)
1253 */
1254 xmlParseXMLCatalogNodeList(cur->children, prefer, parent, entry);
1255 }
1256 }
1257 if (base != NULL)
1258 xmlFree(base);
1259 }
1260
1261 /**
1262 * xmlParseXMLCatalogNodeList:
1263 * @cur: the XML node list of siblings
1264 * @prefer: the PUBLIC vs. SYSTEM current preference value
1265 * @parent: the parent Catalog entry
1266 * @cgroup: the group which includes this list
1267 *
1268 * Examines a list of XML sibling nodes of a catalog and build
1269 * a list of Catalog entry from it adding it to the parent.
1270 * The examination will recurse to examine node subtrees.
1271 */
1272 static void
xmlParseXMLCatalogNodeList(xmlNodePtr cur,xmlCatalogPrefer prefer,xmlCatalogEntryPtr parent,xmlCatalogEntryPtr cgroup)1273 xmlParseXMLCatalogNodeList(xmlNodePtr cur, xmlCatalogPrefer prefer,
1274 xmlCatalogEntryPtr parent, xmlCatalogEntryPtr cgroup) {
1275 while (cur != NULL) {
1276 if ((cur->ns != NULL) && (cur->ns->href != NULL) &&
1277 (xmlStrEqual(cur->ns->href, XML_CATALOGS_NAMESPACE))) {
1278 xmlParseXMLCatalogNode(cur, prefer, parent, cgroup);
1279 }
1280 cur = cur->next;
1281 }
1282 /* TODO: sort the list according to REWRITE lengths and prefer value */
1283 }
1284
1285 /**
1286 * xmlParseXMLCatalogFile:
1287 * @prefer: the PUBLIC vs. SYSTEM current preference value
1288 * @filename: the filename for the catalog
1289 *
1290 * Parses the catalog file to extract the XML tree and then analyze the
1291 * tree to build a list of Catalog entries corresponding to this catalog
1292 *
1293 * Returns the resulting Catalog entries list
1294 */
1295 static xmlCatalogEntryPtr
xmlParseXMLCatalogFile(xmlCatalogPrefer prefer,const xmlChar * filename)1296 xmlParseXMLCatalogFile(xmlCatalogPrefer prefer, const xmlChar *filename) {
1297 xmlDocPtr doc;
1298 xmlNodePtr cur;
1299 xmlChar *prop;
1300 xmlCatalogEntryPtr parent = NULL;
1301
1302 if (filename == NULL)
1303 return(NULL);
1304
1305 doc = xmlParseCatalogFile((const char *) filename);
1306 if (doc == NULL) {
1307 if (xmlDebugCatalogs)
1308 xmlCatalogPrintDebug(
1309 "Failed to parse catalog %s\n", filename);
1310 return(NULL);
1311 }
1312
1313 if (xmlDebugCatalogs)
1314 xmlCatalogPrintDebug(
1315 "%d Parsing catalog %s\n", xmlGetThreadId(), filename);
1316
1317 cur = xmlDocGetRootElement(doc);
1318 if ((cur != NULL) && (xmlStrEqual(cur->name, BAD_CAST "catalog")) &&
1319 (cur->ns != NULL) && (cur->ns->href != NULL) &&
1320 (xmlStrEqual(cur->ns->href, XML_CATALOGS_NAMESPACE))) {
1321
1322 parent = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
1323 (const xmlChar *)filename, NULL, prefer, NULL);
1324 if (parent == NULL) {
1325 xmlFreeDoc(doc);
1326 return(NULL);
1327 }
1328
1329 prop = xmlGetProp(cur, BAD_CAST "prefer");
1330 if (prop != NULL) {
1331 if (xmlStrEqual(prop, BAD_CAST "system")) {
1332 prefer = XML_CATA_PREFER_SYSTEM;
1333 } else if (xmlStrEqual(prop, BAD_CAST "public")) {
1334 prefer = XML_CATA_PREFER_PUBLIC;
1335 } else {
1336 xmlCatalogErr(NULL, cur, XML_CATALOG_PREFER_VALUE,
1337 "Invalid value for prefer: '%s'\n",
1338 prop, NULL, NULL);
1339 }
1340 xmlFree(prop);
1341 }
1342 cur = cur->children;
1343 xmlParseXMLCatalogNodeList(cur, prefer, parent, NULL);
1344 } else {
1345 xmlCatalogErr(NULL, (xmlNodePtr) doc, XML_CATALOG_NOT_CATALOG,
1346 "File %s is not an XML Catalog\n",
1347 filename, NULL, NULL);
1348 xmlFreeDoc(doc);
1349 return(NULL);
1350 }
1351 xmlFreeDoc(doc);
1352 return(parent);
1353 }
1354
1355 /**
1356 * xmlFetchXMLCatalogFile:
1357 * @catal: an existing but incomplete catalog entry
1358 *
1359 * Fetch and parse the subcatalog referenced by an entry
1360 *
1361 * Returns 0 in case of success, -1 otherwise
1362 */
1363 static int
xmlFetchXMLCatalogFile(xmlCatalogEntryPtr catal)1364 xmlFetchXMLCatalogFile(xmlCatalogEntryPtr catal) {
1365 xmlCatalogEntryPtr doc;
1366
1367 if (catal == NULL)
1368 return(-1);
1369 if (catal->URL == NULL)
1370 return(-1);
1371
1372 /*
1373 * lock the whole catalog for modification
1374 */
1375 xmlRMutexLock(&xmlCatalogMutex);
1376 if (catal->children != NULL) {
1377 /* Okay someone else did it in the meantime */
1378 xmlRMutexUnlock(&xmlCatalogMutex);
1379 return(0);
1380 }
1381
1382 if (xmlCatalogXMLFiles != NULL) {
1383 doc = (xmlCatalogEntryPtr)
1384 xmlHashLookup(xmlCatalogXMLFiles, catal->URL);
1385 if (doc != NULL) {
1386 if (xmlDebugCatalogs)
1387 xmlCatalogPrintDebug(
1388 "Found %s in file hash\n", catal->URL);
1389
1390 if (catal->type == XML_CATA_CATALOG)
1391 catal->children = doc->children;
1392 else
1393 catal->children = doc;
1394 catal->dealloc = 0;
1395 xmlRMutexUnlock(&xmlCatalogMutex);
1396 return(0);
1397 }
1398 if (xmlDebugCatalogs)
1399 xmlCatalogPrintDebug(
1400 "%s not found in file hash\n", catal->URL);
1401 }
1402
1403 /*
1404 * Fetch and parse. Note that xmlParseXMLCatalogFile does not
1405 * use the existing catalog, there is no recursion allowed at
1406 * that level.
1407 */
1408 doc = xmlParseXMLCatalogFile(catal->prefer, catal->URL);
1409 if (doc == NULL) {
1410 catal->type = XML_CATA_BROKEN_CATALOG;
1411 xmlRMutexUnlock(&xmlCatalogMutex);
1412 return(-1);
1413 }
1414
1415 if (catal->type == XML_CATA_CATALOG)
1416 catal->children = doc->children;
1417 else
1418 catal->children = doc;
1419
1420 doc->dealloc = 1;
1421
1422 if (xmlCatalogXMLFiles == NULL)
1423 xmlCatalogXMLFiles = xmlHashCreate(10);
1424 if (xmlCatalogXMLFiles != NULL) {
1425 if (xmlDebugCatalogs)
1426 xmlCatalogPrintDebug(
1427 "%s added to file hash\n", catal->URL);
1428 xmlHashAddEntry(xmlCatalogXMLFiles, catal->URL, doc);
1429 }
1430 xmlRMutexUnlock(&xmlCatalogMutex);
1431 return(0);
1432 }
1433
1434 /************************************************************************
1435 * *
1436 * XML Catalog handling *
1437 * *
1438 ************************************************************************/
1439
1440 /**
1441 * xmlAddXMLCatalog:
1442 * @catal: top of an XML catalog
1443 * @type: the type of record to add to the catalog
1444 * @orig: the system, public or prefix to match (or NULL)
1445 * @replace: the replacement value for the match
1446 *
1447 * Add an entry in the XML catalog, it may overwrite existing but
1448 * different entries.
1449 *
1450 * Returns 0 if successful, -1 otherwise
1451 */
1452 static int
xmlAddXMLCatalog(xmlCatalogEntryPtr catal,const xmlChar * type,const xmlChar * orig,const xmlChar * replace)1453 xmlAddXMLCatalog(xmlCatalogEntryPtr catal, const xmlChar *type,
1454 const xmlChar *orig, const xmlChar *replace) {
1455 xmlCatalogEntryPtr cur;
1456 xmlCatalogEntryType typ;
1457 int doregister = 0;
1458
1459 if ((catal == NULL) ||
1460 ((catal->type != XML_CATA_CATALOG) &&
1461 (catal->type != XML_CATA_BROKEN_CATALOG)))
1462 return(-1);
1463 if (catal->children == NULL) {
1464 xmlFetchXMLCatalogFile(catal);
1465 }
1466 if (catal->children == NULL)
1467 doregister = 1;
1468
1469 typ = xmlGetXMLCatalogEntryType(type);
1470 if (typ == XML_CATA_NONE) {
1471 if (xmlDebugCatalogs)
1472 xmlCatalogPrintDebug(
1473 "Failed to add unknown element %s to catalog\n", type);
1474 return(-1);
1475 }
1476
1477 cur = catal->children;
1478 /*
1479 * Might be a simple "update in place"
1480 */
1481 if (cur != NULL) {
1482 while (cur != NULL) {
1483 if ((orig != NULL) && (cur->type == typ) &&
1484 (xmlStrEqual(orig, cur->name))) {
1485 if (xmlDebugCatalogs)
1486 xmlCatalogPrintDebug(
1487 "Updating element %s to catalog\n", type);
1488 if (cur->value != NULL)
1489 xmlFree(cur->value);
1490 if (cur->URL != NULL)
1491 xmlFree(cur->URL);
1492 cur->value = xmlStrdup(replace);
1493 cur->URL = xmlStrdup(replace);
1494 return(0);
1495 }
1496 if (cur->next == NULL)
1497 break;
1498 cur = cur->next;
1499 }
1500 }
1501 if (xmlDebugCatalogs)
1502 xmlCatalogPrintDebug(
1503 "Adding element %s to catalog\n", type);
1504 if (cur == NULL)
1505 catal->children = xmlNewCatalogEntry(typ, orig, replace,
1506 NULL, catal->prefer, NULL);
1507 else
1508 cur->next = xmlNewCatalogEntry(typ, orig, replace,
1509 NULL, catal->prefer, NULL);
1510 if (doregister) {
1511 catal->type = XML_CATA_CATALOG;
1512 cur = (xmlCatalogEntryPtr)xmlHashLookup(xmlCatalogXMLFiles, catal->URL);
1513 if (cur != NULL)
1514 cur->children = catal->children;
1515 }
1516
1517 return(0);
1518 }
1519
1520 /**
1521 * xmlDelXMLCatalog:
1522 * @catal: top of an XML catalog
1523 * @value: the value to remove from the catalog
1524 *
1525 * Remove entries in the XML catalog where the value or the URI
1526 * is equal to @value
1527 *
1528 * Returns the number of entries removed if successful, -1 otherwise
1529 */
1530 static int
xmlDelXMLCatalog(xmlCatalogEntryPtr catal,const xmlChar * value)1531 xmlDelXMLCatalog(xmlCatalogEntryPtr catal, const xmlChar *value) {
1532 xmlCatalogEntryPtr cur;
1533 int ret = 0;
1534
1535 if ((catal == NULL) ||
1536 ((catal->type != XML_CATA_CATALOG) &&
1537 (catal->type != XML_CATA_BROKEN_CATALOG)))
1538 return(-1);
1539 if (value == NULL)
1540 return(-1);
1541 if (catal->children == NULL) {
1542 xmlFetchXMLCatalogFile(catal);
1543 }
1544
1545 /*
1546 * Scan the children
1547 */
1548 cur = catal->children;
1549 while (cur != NULL) {
1550 if (((cur->name != NULL) && (xmlStrEqual(value, cur->name))) ||
1551 (xmlStrEqual(value, cur->value))) {
1552 if (xmlDebugCatalogs) {
1553 if (cur->name != NULL)
1554 xmlCatalogPrintDebug(
1555 "Removing element %s from catalog\n", cur->name);
1556 else
1557 xmlCatalogPrintDebug(
1558 "Removing element %s from catalog\n", cur->value);
1559 }
1560 cur->type = XML_CATA_REMOVED;
1561 }
1562 cur = cur->next;
1563 }
1564 return(ret);
1565 }
1566
1567 /**
1568 * xmlCatalogXMLResolve:
1569 * @catal: a catalog list
1570 * @pubID: the public ID string
1571 * @sysID: the system ID string
1572 *
1573 * Do a complete resolution lookup of an External Identifier for a
1574 * list of catalog entries.
1575 *
1576 * Implements (or tries to) 7.1. External Identifier Resolution
1577 * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
1578 *
1579 * Returns the URI of the resource or NULL if not found
1580 */
1581 static xmlChar *
xmlCatalogXMLResolve(xmlCatalogEntryPtr catal,const xmlChar * pubID,const xmlChar * sysID)1582 xmlCatalogXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,
1583 const xmlChar *sysID) {
1584 xmlChar *ret = NULL;
1585 xmlCatalogEntryPtr cur;
1586 int haveDelegate = 0;
1587 int haveNext = 0;
1588
1589 /*
1590 * protection against loops
1591 */
1592 if (catal->depth > MAX_CATAL_DEPTH) {
1593 xmlCatalogErr(catal, NULL, XML_CATALOG_RECURSION,
1594 "Detected recursion in catalog %s\n",
1595 catal->name, NULL, NULL);
1596 return(NULL);
1597 }
1598 catal->depth++;
1599
1600 /*
1601 * First tries steps 2/ 3/ 4/ if a system ID is provided.
1602 */
1603 if (sysID != NULL) {
1604 xmlCatalogEntryPtr rewrite = NULL;
1605 int lenrewrite = 0, len;
1606 cur = catal;
1607 haveDelegate = 0;
1608 while (cur != NULL) {
1609 switch (cur->type) {
1610 case XML_CATA_SYSTEM:
1611 if (xmlStrEqual(sysID, cur->name)) {
1612 if (xmlDebugCatalogs)
1613 xmlCatalogPrintDebug(
1614 "Found system match %s, using %s\n",
1615 cur->name, cur->URL);
1616 catal->depth--;
1617 return(xmlStrdup(cur->URL));
1618 }
1619 break;
1620 case XML_CATA_REWRITE_SYSTEM:
1621 len = xmlStrlen(cur->name);
1622 if ((len > lenrewrite) &&
1623 (!xmlStrncmp(sysID, cur->name, len))) {
1624 lenrewrite = len;
1625 rewrite = cur;
1626 }
1627 break;
1628 case XML_CATA_DELEGATE_SYSTEM:
1629 if (!xmlStrncmp(sysID, cur->name, xmlStrlen(cur->name)))
1630 haveDelegate++;
1631 break;
1632 case XML_CATA_NEXT_CATALOG:
1633 haveNext++;
1634 break;
1635 default:
1636 break;
1637 }
1638 cur = cur->next;
1639 }
1640 if (rewrite != NULL) {
1641 if (xmlDebugCatalogs)
1642 xmlCatalogPrintDebug(
1643 "Using rewriting rule %s\n", rewrite->name);
1644 ret = xmlStrdup(rewrite->URL);
1645 if (ret != NULL)
1646 ret = xmlStrcat(ret, &sysID[lenrewrite]);
1647 catal->depth--;
1648 return(ret);
1649 }
1650 if (haveDelegate) {
1651 const xmlChar *delegates[MAX_DELEGATE];
1652 int nbList = 0, i;
1653
1654 /*
1655 * Assume the entries have been sorted by decreasing substring
1656 * matches when the list was produced.
1657 */
1658 cur = catal;
1659 while (cur != NULL) {
1660 if ((cur->type == XML_CATA_DELEGATE_SYSTEM) &&
1661 (!xmlStrncmp(sysID, cur->name, xmlStrlen(cur->name)))) {
1662 for (i = 0;i < nbList;i++)
1663 if (xmlStrEqual(cur->URL, delegates[i]))
1664 break;
1665 if (i < nbList) {
1666 cur = cur->next;
1667 continue;
1668 }
1669 if (nbList < MAX_DELEGATE)
1670 delegates[nbList++] = cur->URL;
1671
1672 if (cur->children == NULL) {
1673 xmlFetchXMLCatalogFile(cur);
1674 }
1675 if (cur->children != NULL) {
1676 if (xmlDebugCatalogs)
1677 xmlCatalogPrintDebug(
1678 "Trying system delegate %s\n", cur->URL);
1679 ret = xmlCatalogListXMLResolve(
1680 cur->children, NULL, sysID);
1681 if (ret != NULL) {
1682 catal->depth--;
1683 return(ret);
1684 }
1685 }
1686 }
1687 cur = cur->next;
1688 }
1689 /*
1690 * Apply the cut algorithm explained in 4/
1691 */
1692 catal->depth--;
1693 return(XML_CATAL_BREAK);
1694 }
1695 }
1696 /*
1697 * Then tries 5/ 6/ if a public ID is provided
1698 */
1699 if (pubID != NULL) {
1700 cur = catal;
1701 haveDelegate = 0;
1702 while (cur != NULL) {
1703 switch (cur->type) {
1704 case XML_CATA_PUBLIC:
1705 if (xmlStrEqual(pubID, cur->name)) {
1706 if (xmlDebugCatalogs)
1707 xmlCatalogPrintDebug(
1708 "Found public match %s\n", cur->name);
1709 catal->depth--;
1710 return(xmlStrdup(cur->URL));
1711 }
1712 break;
1713 case XML_CATA_DELEGATE_PUBLIC:
1714 if (!xmlStrncmp(pubID, cur->name, xmlStrlen(cur->name)) &&
1715 (cur->prefer == XML_CATA_PREFER_PUBLIC))
1716 haveDelegate++;
1717 break;
1718 case XML_CATA_NEXT_CATALOG:
1719 if (sysID == NULL)
1720 haveNext++;
1721 break;
1722 default:
1723 break;
1724 }
1725 cur = cur->next;
1726 }
1727 if (haveDelegate) {
1728 const xmlChar *delegates[MAX_DELEGATE];
1729 int nbList = 0, i;
1730
1731 /*
1732 * Assume the entries have been sorted by decreasing substring
1733 * matches when the list was produced.
1734 */
1735 cur = catal;
1736 while (cur != NULL) {
1737 if ((cur->type == XML_CATA_DELEGATE_PUBLIC) &&
1738 (cur->prefer == XML_CATA_PREFER_PUBLIC) &&
1739 (!xmlStrncmp(pubID, cur->name, xmlStrlen(cur->name)))) {
1740
1741 for (i = 0;i < nbList;i++)
1742 if (xmlStrEqual(cur->URL, delegates[i]))
1743 break;
1744 if (i < nbList) {
1745 cur = cur->next;
1746 continue;
1747 }
1748 if (nbList < MAX_DELEGATE)
1749 delegates[nbList++] = cur->URL;
1750
1751 if (cur->children == NULL) {
1752 xmlFetchXMLCatalogFile(cur);
1753 }
1754 if (cur->children != NULL) {
1755 if (xmlDebugCatalogs)
1756 xmlCatalogPrintDebug(
1757 "Trying public delegate %s\n", cur->URL);
1758 ret = xmlCatalogListXMLResolve(
1759 cur->children, pubID, NULL);
1760 if (ret != NULL) {
1761 catal->depth--;
1762 return(ret);
1763 }
1764 }
1765 }
1766 cur = cur->next;
1767 }
1768 /*
1769 * Apply the cut algorithm explained in 4/
1770 */
1771 catal->depth--;
1772 return(XML_CATAL_BREAK);
1773 }
1774 }
1775 if (haveNext) {
1776 cur = catal;
1777 while (cur != NULL) {
1778 if (cur->type == XML_CATA_NEXT_CATALOG) {
1779 if (cur->children == NULL) {
1780 xmlFetchXMLCatalogFile(cur);
1781 }
1782 if (cur->children != NULL) {
1783 ret = xmlCatalogListXMLResolve(cur->children, pubID, sysID);
1784 if (ret != NULL) {
1785 catal->depth--;
1786 return(ret);
1787 } else if (catal->depth > MAX_CATAL_DEPTH) {
1788 return(NULL);
1789 }
1790 }
1791 }
1792 cur = cur->next;
1793 }
1794 }
1795
1796 catal->depth--;
1797 return(NULL);
1798 }
1799
1800 /**
1801 * xmlCatalogXMLResolveURI:
1802 * @catal: a catalog list
1803 * @URI: the URI
1804 * @sysID: the system ID string
1805 *
1806 * Do a complete resolution lookup of an External Identifier for a
1807 * list of catalog entries.
1808 *
1809 * Implements (or tries to) 7.2.2. URI Resolution
1810 * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
1811 *
1812 * Returns the URI of the resource or NULL if not found
1813 */
1814 static xmlChar *
xmlCatalogXMLResolveURI(xmlCatalogEntryPtr catal,const xmlChar * URI)1815 xmlCatalogXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI) {
1816 xmlChar *ret = NULL;
1817 xmlCatalogEntryPtr cur;
1818 int haveDelegate = 0;
1819 int haveNext = 0;
1820 xmlCatalogEntryPtr rewrite = NULL;
1821 int lenrewrite = 0, len;
1822
1823 if (catal == NULL)
1824 return(NULL);
1825
1826 if (URI == NULL)
1827 return(NULL);
1828
1829 if (catal->depth > MAX_CATAL_DEPTH) {
1830 xmlCatalogErr(catal, NULL, XML_CATALOG_RECURSION,
1831 "Detected recursion in catalog %s\n",
1832 catal->name, NULL, NULL);
1833 return(NULL);
1834 }
1835
1836 /*
1837 * First tries steps 2/ 3/ 4/ if a system ID is provided.
1838 */
1839 cur = catal;
1840 haveDelegate = 0;
1841 while (cur != NULL) {
1842 switch (cur->type) {
1843 case XML_CATA_URI:
1844 if (xmlStrEqual(URI, cur->name)) {
1845 if (xmlDebugCatalogs)
1846 xmlCatalogPrintDebug(
1847 "Found URI match %s\n", cur->name);
1848 return(xmlStrdup(cur->URL));
1849 }
1850 break;
1851 case XML_CATA_REWRITE_URI:
1852 len = xmlStrlen(cur->name);
1853 if ((len > lenrewrite) &&
1854 (!xmlStrncmp(URI, cur->name, len))) {
1855 lenrewrite = len;
1856 rewrite = cur;
1857 }
1858 break;
1859 case XML_CATA_DELEGATE_URI:
1860 if (!xmlStrncmp(URI, cur->name, xmlStrlen(cur->name)))
1861 haveDelegate++;
1862 break;
1863 case XML_CATA_NEXT_CATALOG:
1864 haveNext++;
1865 break;
1866 default:
1867 break;
1868 }
1869 cur = cur->next;
1870 }
1871 if (rewrite != NULL) {
1872 if (xmlDebugCatalogs)
1873 xmlCatalogPrintDebug(
1874 "Using rewriting rule %s\n", rewrite->name);
1875 ret = xmlStrdup(rewrite->URL);
1876 if (ret != NULL)
1877 ret = xmlStrcat(ret, &URI[lenrewrite]);
1878 return(ret);
1879 }
1880 if (haveDelegate) {
1881 const xmlChar *delegates[MAX_DELEGATE];
1882 int nbList = 0, i;
1883
1884 /*
1885 * Assume the entries have been sorted by decreasing substring
1886 * matches when the list was produced.
1887 */
1888 cur = catal;
1889 while (cur != NULL) {
1890 if (((cur->type == XML_CATA_DELEGATE_SYSTEM) ||
1891 (cur->type == XML_CATA_DELEGATE_URI)) &&
1892 (!xmlStrncmp(URI, cur->name, xmlStrlen(cur->name)))) {
1893 for (i = 0;i < nbList;i++)
1894 if (xmlStrEqual(cur->URL, delegates[i]))
1895 break;
1896 if (i < nbList) {
1897 cur = cur->next;
1898 continue;
1899 }
1900 if (nbList < MAX_DELEGATE)
1901 delegates[nbList++] = cur->URL;
1902
1903 if (cur->children == NULL) {
1904 xmlFetchXMLCatalogFile(cur);
1905 }
1906 if (cur->children != NULL) {
1907 if (xmlDebugCatalogs)
1908 xmlCatalogPrintDebug(
1909 "Trying URI delegate %s\n", cur->URL);
1910 ret = xmlCatalogListXMLResolveURI(
1911 cur->children, URI);
1912 if (ret != NULL)
1913 return(ret);
1914 }
1915 }
1916 cur = cur->next;
1917 }
1918 /*
1919 * Apply the cut algorithm explained in 4/
1920 */
1921 return(XML_CATAL_BREAK);
1922 }
1923 if (haveNext) {
1924 cur = catal;
1925 while (cur != NULL) {
1926 if (cur->type == XML_CATA_NEXT_CATALOG) {
1927 if (cur->children == NULL) {
1928 xmlFetchXMLCatalogFile(cur);
1929 }
1930 if (cur->children != NULL) {
1931 ret = xmlCatalogListXMLResolveURI(cur->children, URI);
1932 if (ret != NULL)
1933 return(ret);
1934 }
1935 }
1936 cur = cur->next;
1937 }
1938 }
1939
1940 return(NULL);
1941 }
1942
1943 /**
1944 * xmlCatalogListXMLResolve:
1945 * @catal: a catalog list
1946 * @pubID: the public ID string
1947 * @sysID: the system ID string
1948 *
1949 * Do a complete resolution lookup of an External Identifier for a
1950 * list of catalogs
1951 *
1952 * Implements (or tries to) 7.1. External Identifier Resolution
1953 * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
1954 *
1955 * Returns the URI of the resource or NULL if not found
1956 */
1957 static xmlChar *
xmlCatalogListXMLResolve(xmlCatalogEntryPtr catal,const xmlChar * pubID,const xmlChar * sysID)1958 xmlCatalogListXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,
1959 const xmlChar *sysID) {
1960 xmlChar *ret = NULL;
1961 xmlChar *urnID = NULL;
1962 xmlChar *normid;
1963
1964 if (catal == NULL)
1965 return(NULL);
1966 if ((pubID == NULL) && (sysID == NULL))
1967 return(NULL);
1968
1969 normid = xmlCatalogNormalizePublic(pubID);
1970 if (normid != NULL)
1971 pubID = (*normid != 0 ? normid : NULL);
1972
1973 if (!xmlStrncmp(pubID, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) {
1974 urnID = xmlCatalogUnWrapURN(pubID);
1975 if (xmlDebugCatalogs) {
1976 if (urnID == NULL)
1977 xmlCatalogPrintDebug(
1978 "Public URN ID %s expanded to NULL\n", pubID);
1979 else
1980 xmlCatalogPrintDebug(
1981 "Public URN ID expanded to %s\n", urnID);
1982 }
1983 ret = xmlCatalogListXMLResolve(catal, urnID, sysID);
1984 if (urnID != NULL)
1985 xmlFree(urnID);
1986 if (normid != NULL)
1987 xmlFree(normid);
1988 return(ret);
1989 }
1990 if (!xmlStrncmp(sysID, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) {
1991 urnID = xmlCatalogUnWrapURN(sysID);
1992 if (xmlDebugCatalogs) {
1993 if (urnID == NULL)
1994 xmlCatalogPrintDebug(
1995 "System URN ID %s expanded to NULL\n", sysID);
1996 else
1997 xmlCatalogPrintDebug(
1998 "System URN ID expanded to %s\n", urnID);
1999 }
2000 if (pubID == NULL)
2001 ret = xmlCatalogListXMLResolve(catal, urnID, NULL);
2002 else if (xmlStrEqual(pubID, urnID))
2003 ret = xmlCatalogListXMLResolve(catal, pubID, NULL);
2004 else {
2005 ret = xmlCatalogListXMLResolve(catal, pubID, urnID);
2006 }
2007 if (urnID != NULL)
2008 xmlFree(urnID);
2009 if (normid != NULL)
2010 xmlFree(normid);
2011 return(ret);
2012 }
2013 while (catal != NULL) {
2014 if (catal->type == XML_CATA_CATALOG) {
2015 if (catal->children == NULL) {
2016 xmlFetchXMLCatalogFile(catal);
2017 }
2018 if (catal->children != NULL) {
2019 ret = xmlCatalogXMLResolve(catal->children, pubID, sysID);
2020 if (ret != NULL) {
2021 break;
2022 } else if (catal->children->depth > MAX_CATAL_DEPTH) {
2023 ret = NULL;
2024 break;
2025 }
2026 }
2027 }
2028 catal = catal->next;
2029 }
2030 if (normid != NULL)
2031 xmlFree(normid);
2032 return(ret);
2033 }
2034
2035 /**
2036 * xmlCatalogListXMLResolveURI:
2037 * @catal: a catalog list
2038 * @URI: the URI
2039 *
2040 * Do a complete resolution lookup of an URI for a list of catalogs
2041 *
2042 * Implements (or tries to) 7.2. URI Resolution
2043 * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
2044 *
2045 * Returns the URI of the resource or NULL if not found
2046 */
2047 static xmlChar *
xmlCatalogListXMLResolveURI(xmlCatalogEntryPtr catal,const xmlChar * URI)2048 xmlCatalogListXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI) {
2049 xmlChar *ret = NULL;
2050 xmlChar *urnID = NULL;
2051
2052 if (catal == NULL)
2053 return(NULL);
2054 if (URI == NULL)
2055 return(NULL);
2056
2057 if (!xmlStrncmp(URI, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) {
2058 urnID = xmlCatalogUnWrapURN(URI);
2059 if (xmlDebugCatalogs) {
2060 if (urnID == NULL)
2061 xmlCatalogPrintDebug(
2062 "URN ID %s expanded to NULL\n", URI);
2063 else
2064 xmlCatalogPrintDebug(
2065 "URN ID expanded to %s\n", urnID);
2066 }
2067 ret = xmlCatalogListXMLResolve(catal, urnID, NULL);
2068 if (urnID != NULL)
2069 xmlFree(urnID);
2070 return(ret);
2071 }
2072 while (catal != NULL) {
2073 if (catal->type == XML_CATA_CATALOG) {
2074 if (catal->children == NULL) {
2075 xmlFetchXMLCatalogFile(catal);
2076 }
2077 if (catal->children != NULL) {
2078 ret = xmlCatalogXMLResolveURI(catal->children, URI);
2079 if (ret != NULL)
2080 return(ret);
2081 }
2082 }
2083 catal = catal->next;
2084 }
2085 return(ret);
2086 }
2087
2088 /************************************************************************
2089 * *
2090 * The SGML Catalog parser *
2091 * *
2092 ************************************************************************/
2093
2094
2095 #define RAW *cur
2096 #define NEXT cur++;
2097 #define SKIP(x) cur += x;
2098
2099 #define SKIP_BLANKS while (IS_BLANK_CH(*cur)) NEXT;
2100
2101 /**
2102 * xmlParseSGMLCatalogComment:
2103 * @cur: the current character
2104 *
2105 * Skip a comment in an SGML catalog
2106 *
2107 * Returns new current character
2108 */
2109 static const xmlChar *
xmlParseSGMLCatalogComment(const xmlChar * cur)2110 xmlParseSGMLCatalogComment(const xmlChar *cur) {
2111 if ((cur[0] != '-') || (cur[1] != '-'))
2112 return(cur);
2113 SKIP(2);
2114 while ((cur[0] != 0) && ((cur[0] != '-') || ((cur[1] != '-'))))
2115 NEXT;
2116 if (cur[0] == 0) {
2117 return(NULL);
2118 }
2119 return(cur + 2);
2120 }
2121
2122 /**
2123 * xmlParseSGMLCatalogPubid:
2124 * @cur: the current character
2125 * @id: the return location
2126 *
2127 * Parse an SGML catalog ID
2128 *
2129 * Returns new current character and store the value in @id
2130 */
2131 static const xmlChar *
xmlParseSGMLCatalogPubid(const xmlChar * cur,xmlChar ** id)2132 xmlParseSGMLCatalogPubid(const xmlChar *cur, xmlChar **id) {
2133 xmlChar *buf = NULL;
2134 int len = 0;
2135 int size = 50;
2136 xmlChar stop;
2137
2138 *id = NULL;
2139
2140 if (RAW == '"') {
2141 NEXT;
2142 stop = '"';
2143 } else if (RAW == '\'') {
2144 NEXT;
2145 stop = '\'';
2146 } else {
2147 stop = ' ';
2148 }
2149 buf = xmlMalloc(size);
2150 if (buf == NULL) {
2151 xmlCatalogErrMemory();
2152 return(NULL);
2153 }
2154 while (IS_PUBIDCHAR_CH(*cur) || (*cur == '?')) {
2155 if ((*cur == stop) && (stop != ' '))
2156 break;
2157 if ((stop == ' ') && (IS_BLANK_CH(*cur)))
2158 break;
2159 if (len + 1 >= size) {
2160 xmlChar *tmp;
2161 int newSize;
2162
2163 newSize = xmlGrowCapacity(size, 1, 1, XML_MAX_ITEMS);
2164 if (newSize < 0) {
2165 xmlCatalogErrMemory();
2166 xmlFree(buf);
2167 return(NULL);
2168 }
2169 tmp = xmlRealloc(buf, newSize);
2170 if (tmp == NULL) {
2171 xmlCatalogErrMemory();
2172 xmlFree(buf);
2173 return(NULL);
2174 }
2175 buf = tmp;
2176 size = newSize;
2177 }
2178 buf[len++] = *cur;
2179 NEXT;
2180 }
2181 buf[len] = 0;
2182 if (stop == ' ') {
2183 if (!IS_BLANK_CH(*cur)) {
2184 xmlFree(buf);
2185 return(NULL);
2186 }
2187 } else {
2188 if (*cur != stop) {
2189 xmlFree(buf);
2190 return(NULL);
2191 }
2192 NEXT;
2193 }
2194 *id = buf;
2195 return(cur);
2196 }
2197
2198 /**
2199 * xmlParseSGMLCatalogName:
2200 * @cur: the current character
2201 * @name: the return location
2202 *
2203 * Parse an SGML catalog name
2204 *
2205 * Returns new current character and store the value in @name
2206 */
2207 static const xmlChar *
xmlParseSGMLCatalogName(const xmlChar * cur,xmlChar ** name)2208 xmlParseSGMLCatalogName(const xmlChar *cur, xmlChar **name) {
2209 xmlChar buf[XML_MAX_NAMELEN + 5];
2210 int len = 0;
2211 int c;
2212
2213 *name = NULL;
2214
2215 /*
2216 * Handler for more complex cases
2217 */
2218 c = *cur;
2219 if ((!IS_LETTER(c) && (c != '_') && (c != ':'))) {
2220 return(NULL);
2221 }
2222
2223 while (((IS_LETTER(c)) || (IS_DIGIT(c)) ||
2224 (c == '.') || (c == '-') ||
2225 (c == '_') || (c == ':'))) {
2226 buf[len++] = c;
2227 cur++;
2228 c = *cur;
2229 if (len >= XML_MAX_NAMELEN)
2230 return(NULL);
2231 }
2232 *name = xmlStrndup(buf, len);
2233 return(cur);
2234 }
2235
2236 /**
2237 * xmlGetSGMLCatalogEntryType:
2238 * @name: the entry name
2239 *
2240 * Get the Catalog entry type for a given SGML Catalog name
2241 *
2242 * Returns Catalog entry type
2243 */
2244 static xmlCatalogEntryType
xmlGetSGMLCatalogEntryType(const xmlChar * name)2245 xmlGetSGMLCatalogEntryType(const xmlChar *name) {
2246 xmlCatalogEntryType type = XML_CATA_NONE;
2247 if (xmlStrEqual(name, (const xmlChar *) "SYSTEM"))
2248 type = SGML_CATA_SYSTEM;
2249 else if (xmlStrEqual(name, (const xmlChar *) "PUBLIC"))
2250 type = SGML_CATA_PUBLIC;
2251 else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE"))
2252 type = SGML_CATA_DELEGATE;
2253 else if (xmlStrEqual(name, (const xmlChar *) "ENTITY"))
2254 type = SGML_CATA_ENTITY;
2255 else if (xmlStrEqual(name, (const xmlChar *) "DOCTYPE"))
2256 type = SGML_CATA_DOCTYPE;
2257 else if (xmlStrEqual(name, (const xmlChar *) "LINKTYPE"))
2258 type = SGML_CATA_LINKTYPE;
2259 else if (xmlStrEqual(name, (const xmlChar *) "NOTATION"))
2260 type = SGML_CATA_NOTATION;
2261 else if (xmlStrEqual(name, (const xmlChar *) "SGMLDECL"))
2262 type = SGML_CATA_SGMLDECL;
2263 else if (xmlStrEqual(name, (const xmlChar *) "DOCUMENT"))
2264 type = SGML_CATA_DOCUMENT;
2265 else if (xmlStrEqual(name, (const xmlChar *) "CATALOG"))
2266 type = SGML_CATA_CATALOG;
2267 else if (xmlStrEqual(name, (const xmlChar *) "BASE"))
2268 type = SGML_CATA_BASE;
2269 return(type);
2270 }
2271
2272 /**
2273 * xmlParseSGMLCatalog:
2274 * @catal: the SGML Catalog
2275 * @value: the content of the SGML Catalog serialization
2276 * @file: the filepath for the catalog
2277 * @super: should this be handled as a Super Catalog in which case
2278 * parsing is not recursive
2279 *
2280 * Parse an SGML catalog content and fill up the @catal hash table with
2281 * the new entries found.
2282 *
2283 * Returns 0 in case of success, -1 in case of error.
2284 */
2285 static int
xmlParseSGMLCatalog(xmlCatalogPtr catal,const xmlChar * value,const char * file,int super)2286 xmlParseSGMLCatalog(xmlCatalogPtr catal, const xmlChar *value,
2287 const char *file, int super) {
2288 const xmlChar *cur = value;
2289 xmlChar *base = NULL;
2290 int res;
2291
2292 if ((cur == NULL) || (file == NULL))
2293 return(-1);
2294 base = xmlStrdup((const xmlChar *) file);
2295
2296 while ((cur != NULL) && (cur[0] != 0)) {
2297 SKIP_BLANKS;
2298 if (cur[0] == 0)
2299 break;
2300 if ((cur[0] == '-') && (cur[1] == '-')) {
2301 cur = xmlParseSGMLCatalogComment(cur);
2302 if (cur == NULL) {
2303 /* error */
2304 break;
2305 }
2306 } else {
2307 xmlChar *sysid = NULL;
2308 xmlChar *name = NULL;
2309 xmlCatalogEntryType type = XML_CATA_NONE;
2310
2311 cur = xmlParseSGMLCatalogName(cur, &name);
2312 if (cur == NULL || name == NULL) {
2313 /* error */
2314 break;
2315 }
2316 if (!IS_BLANK_CH(*cur)) {
2317 /* error */
2318 xmlFree(name);
2319 break;
2320 }
2321 SKIP_BLANKS;
2322 if (xmlStrEqual(name, (const xmlChar *) "SYSTEM"))
2323 type = SGML_CATA_SYSTEM;
2324 else if (xmlStrEqual(name, (const xmlChar *) "PUBLIC"))
2325 type = SGML_CATA_PUBLIC;
2326 else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE"))
2327 type = SGML_CATA_DELEGATE;
2328 else if (xmlStrEqual(name, (const xmlChar *) "ENTITY"))
2329 type = SGML_CATA_ENTITY;
2330 else if (xmlStrEqual(name, (const xmlChar *) "DOCTYPE"))
2331 type = SGML_CATA_DOCTYPE;
2332 else if (xmlStrEqual(name, (const xmlChar *) "LINKTYPE"))
2333 type = SGML_CATA_LINKTYPE;
2334 else if (xmlStrEqual(name, (const xmlChar *) "NOTATION"))
2335 type = SGML_CATA_NOTATION;
2336 else if (xmlStrEqual(name, (const xmlChar *) "SGMLDECL"))
2337 type = SGML_CATA_SGMLDECL;
2338 else if (xmlStrEqual(name, (const xmlChar *) "DOCUMENT"))
2339 type = SGML_CATA_DOCUMENT;
2340 else if (xmlStrEqual(name, (const xmlChar *) "CATALOG"))
2341 type = SGML_CATA_CATALOG;
2342 else if (xmlStrEqual(name, (const xmlChar *) "BASE"))
2343 type = SGML_CATA_BASE;
2344 else if (xmlStrEqual(name, (const xmlChar *) "OVERRIDE")) {
2345 xmlFree(name);
2346 cur = xmlParseSGMLCatalogName(cur, &name);
2347 if (name == NULL) {
2348 /* error */
2349 break;
2350 }
2351 xmlFree(name);
2352 continue;
2353 }
2354 xmlFree(name);
2355 name = NULL;
2356
2357 switch(type) {
2358 case SGML_CATA_ENTITY:
2359 if (*cur == '%')
2360 type = SGML_CATA_PENTITY;
2361 /* Falls through. */
2362 case SGML_CATA_PENTITY:
2363 case SGML_CATA_DOCTYPE:
2364 case SGML_CATA_LINKTYPE:
2365 case SGML_CATA_NOTATION:
2366 cur = xmlParseSGMLCatalogName(cur, &name);
2367 if (cur == NULL) {
2368 /* error */
2369 break;
2370 }
2371 if (!IS_BLANK_CH(*cur)) {
2372 /* error */
2373 break;
2374 }
2375 SKIP_BLANKS;
2376 cur = xmlParseSGMLCatalogPubid(cur, &sysid);
2377 if (cur == NULL) {
2378 /* error */
2379 break;
2380 }
2381 break;
2382 case SGML_CATA_PUBLIC:
2383 case SGML_CATA_SYSTEM:
2384 case SGML_CATA_DELEGATE:
2385 cur = xmlParseSGMLCatalogPubid(cur, &name);
2386 if (cur == NULL) {
2387 /* error */
2388 break;
2389 }
2390 if (type != SGML_CATA_SYSTEM) {
2391 xmlChar *normid;
2392
2393 normid = xmlCatalogNormalizePublic(name);
2394 if (normid != NULL) {
2395 if (name != NULL)
2396 xmlFree(name);
2397 if (*normid != 0)
2398 name = normid;
2399 else {
2400 xmlFree(normid);
2401 name = NULL;
2402 }
2403 }
2404 }
2405 if (!IS_BLANK_CH(*cur)) {
2406 /* error */
2407 break;
2408 }
2409 SKIP_BLANKS;
2410 cur = xmlParseSGMLCatalogPubid(cur, &sysid);
2411 if (cur == NULL) {
2412 /* error */
2413 break;
2414 }
2415 break;
2416 case SGML_CATA_BASE:
2417 case SGML_CATA_CATALOG:
2418 case SGML_CATA_DOCUMENT:
2419 case SGML_CATA_SGMLDECL:
2420 cur = xmlParseSGMLCatalogPubid(cur, &sysid);
2421 if (cur == NULL) {
2422 /* error */
2423 break;
2424 }
2425 break;
2426 default:
2427 break;
2428 }
2429 if (cur == NULL) {
2430 if (name != NULL)
2431 xmlFree(name);
2432 if (sysid != NULL)
2433 xmlFree(sysid);
2434 break;
2435 } else if (type == SGML_CATA_BASE) {
2436 if (base != NULL)
2437 xmlFree(base);
2438 base = xmlStrdup(sysid);
2439 } else if ((type == SGML_CATA_PUBLIC) ||
2440 (type == SGML_CATA_SYSTEM)) {
2441 xmlChar *filename;
2442
2443 filename = xmlBuildURI(sysid, base);
2444 if (filename != NULL) {
2445 xmlCatalogEntryPtr entry;
2446
2447 entry = xmlNewCatalogEntry(type, name, filename,
2448 NULL, XML_CATA_PREFER_NONE, NULL);
2449 res = xmlHashAddEntry(catal->sgml, name, entry);
2450 if (res < 0) {
2451 xmlFreeCatalogEntry(entry, NULL);
2452 }
2453 xmlFree(filename);
2454 }
2455
2456 } else if (type == SGML_CATA_CATALOG) {
2457 if (super) {
2458 xmlCatalogEntryPtr entry;
2459
2460 entry = xmlNewCatalogEntry(type, sysid, NULL, NULL,
2461 XML_CATA_PREFER_NONE, NULL);
2462 res = xmlHashAddEntry(catal->sgml, sysid, entry);
2463 if (res < 0) {
2464 xmlFreeCatalogEntry(entry, NULL);
2465 }
2466 } else {
2467 xmlChar *filename;
2468
2469 filename = xmlBuildURI(sysid, base);
2470 if (filename != NULL) {
2471 xmlExpandCatalog(catal, (const char *)filename);
2472 xmlFree(filename);
2473 }
2474 }
2475 }
2476 /*
2477 * drop anything else we won't handle it
2478 */
2479 if (name != NULL)
2480 xmlFree(name);
2481 if (sysid != NULL)
2482 xmlFree(sysid);
2483 }
2484 }
2485 if (base != NULL)
2486 xmlFree(base);
2487 if (cur == NULL)
2488 return(-1);
2489 return(0);
2490 }
2491
2492 /************************************************************************
2493 * *
2494 * SGML Catalog handling *
2495 * *
2496 ************************************************************************/
2497
2498 /**
2499 * xmlCatalogGetSGMLPublic:
2500 * @catal: an SGML catalog hash
2501 * @pubID: the public ID string
2502 *
2503 * Try to lookup the catalog local reference associated to a public ID
2504 *
2505 * Returns the local resource if found or NULL otherwise.
2506 */
2507 static const xmlChar *
xmlCatalogGetSGMLPublic(xmlHashTablePtr catal,const xmlChar * pubID)2508 xmlCatalogGetSGMLPublic(xmlHashTablePtr catal, const xmlChar *pubID) {
2509 xmlCatalogEntryPtr entry;
2510 xmlChar *normid;
2511
2512 if (catal == NULL)
2513 return(NULL);
2514
2515 normid = xmlCatalogNormalizePublic(pubID);
2516 if (normid != NULL)
2517 pubID = (*normid != 0 ? normid : NULL);
2518
2519 entry = (xmlCatalogEntryPtr) xmlHashLookup(catal, pubID);
2520 if (entry == NULL) {
2521 if (normid != NULL)
2522 xmlFree(normid);
2523 return(NULL);
2524 }
2525 if (entry->type == SGML_CATA_PUBLIC) {
2526 if (normid != NULL)
2527 xmlFree(normid);
2528 return(entry->URL);
2529 }
2530 if (normid != NULL)
2531 xmlFree(normid);
2532 return(NULL);
2533 }
2534
2535 /**
2536 * xmlCatalogGetSGMLSystem:
2537 * @catal: an SGML catalog hash
2538 * @sysID: the system ID string
2539 *
2540 * Try to lookup the catalog local reference for a system ID
2541 *
2542 * Returns the local resource if found or NULL otherwise.
2543 */
2544 static const xmlChar *
xmlCatalogGetSGMLSystem(xmlHashTablePtr catal,const xmlChar * sysID)2545 xmlCatalogGetSGMLSystem(xmlHashTablePtr catal, const xmlChar *sysID) {
2546 xmlCatalogEntryPtr entry;
2547
2548 if (catal == NULL)
2549 return(NULL);
2550
2551 entry = (xmlCatalogEntryPtr) xmlHashLookup(catal, sysID);
2552 if (entry == NULL)
2553 return(NULL);
2554 if (entry->type == SGML_CATA_SYSTEM)
2555 return(entry->URL);
2556 return(NULL);
2557 }
2558
2559 /**
2560 * xmlCatalogSGMLResolve:
2561 * @catal: the SGML catalog
2562 * @pubID: the public ID string
2563 * @sysID: the system ID string
2564 *
2565 * Do a complete resolution lookup of an External Identifier
2566 *
2567 * Returns the URI of the resource or NULL if not found
2568 */
2569 static const xmlChar *
xmlCatalogSGMLResolve(xmlCatalogPtr catal,const xmlChar * pubID,const xmlChar * sysID)2570 xmlCatalogSGMLResolve(xmlCatalogPtr catal, const xmlChar *pubID,
2571 const xmlChar *sysID) {
2572 const xmlChar *ret = NULL;
2573
2574 if (catal->sgml == NULL)
2575 return(NULL);
2576
2577 if (pubID != NULL)
2578 ret = xmlCatalogGetSGMLPublic(catal->sgml, pubID);
2579 if (ret != NULL)
2580 return(ret);
2581 if (sysID != NULL)
2582 ret = xmlCatalogGetSGMLSystem(catal->sgml, sysID);
2583 if (ret != NULL)
2584 return(ret);
2585 return(NULL);
2586 }
2587
2588 /************************************************************************
2589 * *
2590 * Specific Public interfaces *
2591 * *
2592 ************************************************************************/
2593
2594 /**
2595 * xmlLoadSGMLSuperCatalog:
2596 * @filename: a file path
2597 *
2598 * Load an SGML super catalog. It won't expand CATALOG or DELEGATE
2599 * references. This is only needed for manipulating SGML Super Catalogs
2600 * like adding and removing CATALOG or DELEGATE entries.
2601 *
2602 * Returns the catalog parsed or NULL in case of error
2603 */
2604 xmlCatalogPtr
xmlLoadSGMLSuperCatalog(const char * filename)2605 xmlLoadSGMLSuperCatalog(const char *filename)
2606 {
2607 xmlChar *content;
2608 xmlCatalogPtr catal;
2609 int ret;
2610
2611 content = xmlLoadFileContent(filename);
2612 if (content == NULL)
2613 return(NULL);
2614
2615 catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE, xmlCatalogDefaultPrefer);
2616 if (catal == NULL) {
2617 xmlFree(content);
2618 return(NULL);
2619 }
2620
2621 ret = xmlParseSGMLCatalog(catal, content, filename, 1);
2622 xmlFree(content);
2623 if (ret < 0) {
2624 xmlFreeCatalog(catal);
2625 return(NULL);
2626 }
2627 return (catal);
2628 }
2629
2630 /**
2631 * xmlLoadACatalog:
2632 * @filename: a file path
2633 *
2634 * Load the catalog and build the associated data structures.
2635 * This can be either an XML Catalog or an SGML Catalog
2636 * It will recurse in SGML CATALOG entries. On the other hand XML
2637 * Catalogs are not handled recursively.
2638 *
2639 * Returns the catalog parsed or NULL in case of error
2640 */
2641 xmlCatalogPtr
xmlLoadACatalog(const char * filename)2642 xmlLoadACatalog(const char *filename)
2643 {
2644 xmlChar *content;
2645 xmlChar *first;
2646 xmlCatalogPtr catal;
2647 int ret;
2648
2649 content = xmlLoadFileContent(filename);
2650 if (content == NULL)
2651 return(NULL);
2652
2653
2654 first = content;
2655
2656 while ((*first != 0) && (*first != '-') && (*first != '<') &&
2657 (!(((*first >= 'A') && (*first <= 'Z')) ||
2658 ((*first >= 'a') && (*first <= 'z')))))
2659 first++;
2660
2661 if (*first != '<') {
2662 catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE, xmlCatalogDefaultPrefer);
2663 if (catal == NULL) {
2664 xmlFree(content);
2665 return(NULL);
2666 }
2667 ret = xmlParseSGMLCatalog(catal, content, filename, 0);
2668 if (ret < 0) {
2669 xmlFreeCatalog(catal);
2670 xmlFree(content);
2671 return(NULL);
2672 }
2673 } else {
2674 catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE, xmlCatalogDefaultPrefer);
2675 if (catal == NULL) {
2676 xmlFree(content);
2677 return(NULL);
2678 }
2679 catal->xml = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
2680 NULL, BAD_CAST filename, xmlCatalogDefaultPrefer, NULL);
2681 }
2682 xmlFree(content);
2683 return (catal);
2684 }
2685
2686 /**
2687 * xmlExpandCatalog:
2688 * @catal: a catalog
2689 * @filename: a file path
2690 *
2691 * Load the catalog and expand the existing catal structure.
2692 * This can be either an XML Catalog or an SGML Catalog
2693 *
2694 * Returns 0 in case of success, -1 in case of error
2695 */
2696 static int
xmlExpandCatalog(xmlCatalogPtr catal,const char * filename)2697 xmlExpandCatalog(xmlCatalogPtr catal, const char *filename)
2698 {
2699 int ret;
2700
2701 if ((catal == NULL) || (filename == NULL))
2702 return(-1);
2703
2704
2705 if (catal->type == XML_SGML_CATALOG_TYPE) {
2706 xmlChar *content;
2707
2708 content = xmlLoadFileContent(filename);
2709 if (content == NULL)
2710 return(-1);
2711
2712 ret = xmlParseSGMLCatalog(catal, content, filename, 0);
2713 if (ret < 0) {
2714 xmlFree(content);
2715 return(-1);
2716 }
2717 xmlFree(content);
2718 } else {
2719 xmlCatalogEntryPtr tmp, cur;
2720 tmp = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
2721 NULL, BAD_CAST filename, xmlCatalogDefaultPrefer, NULL);
2722
2723 cur = catal->xml;
2724 if (cur == NULL) {
2725 catal->xml = tmp;
2726 } else {
2727 while (cur->next != NULL) cur = cur->next;
2728 cur->next = tmp;
2729 }
2730 }
2731 return (0);
2732 }
2733
2734 /**
2735 * xmlACatalogResolveSystem:
2736 * @catal: a Catalog
2737 * @sysID: the system ID string
2738 *
2739 * Try to lookup the catalog resource for a system ID
2740 *
2741 * Returns the resource if found or NULL otherwise, the value returned
2742 * must be freed by the caller.
2743 */
2744 xmlChar *
xmlACatalogResolveSystem(xmlCatalogPtr catal,const xmlChar * sysID)2745 xmlACatalogResolveSystem(xmlCatalogPtr catal, const xmlChar *sysID) {
2746 xmlChar *ret = NULL;
2747
2748 if ((sysID == NULL) || (catal == NULL))
2749 return(NULL);
2750
2751 if (xmlDebugCatalogs)
2752 xmlCatalogPrintDebug(
2753 "Resolve sysID %s\n", sysID);
2754
2755 if (catal->type == XML_XML_CATALOG_TYPE) {
2756 ret = xmlCatalogListXMLResolve(catal->xml, NULL, sysID);
2757 if (ret == XML_CATAL_BREAK)
2758 ret = NULL;
2759 } else {
2760 const xmlChar *sgml;
2761
2762 sgml = xmlCatalogGetSGMLSystem(catal->sgml, sysID);
2763 if (sgml != NULL)
2764 ret = xmlStrdup(sgml);
2765 }
2766 return(ret);
2767 }
2768
2769 /**
2770 * xmlACatalogResolvePublic:
2771 * @catal: a Catalog
2772 * @pubID: the public ID string
2773 *
2774 * Try to lookup the catalog local reference associated to a public ID in that catalog
2775 *
2776 * Returns the local resource if found or NULL otherwise, the value returned
2777 * must be freed by the caller.
2778 */
2779 xmlChar *
xmlACatalogResolvePublic(xmlCatalogPtr catal,const xmlChar * pubID)2780 xmlACatalogResolvePublic(xmlCatalogPtr catal, const xmlChar *pubID) {
2781 xmlChar *ret = NULL;
2782
2783 if ((pubID == NULL) || (catal == NULL))
2784 return(NULL);
2785
2786 if (xmlDebugCatalogs)
2787 xmlCatalogPrintDebug(
2788 "Resolve pubID %s\n", pubID);
2789
2790 if (catal->type == XML_XML_CATALOG_TYPE) {
2791 ret = xmlCatalogListXMLResolve(catal->xml, pubID, NULL);
2792 if (ret == XML_CATAL_BREAK)
2793 ret = NULL;
2794 } else {
2795 const xmlChar *sgml;
2796
2797 sgml = xmlCatalogGetSGMLPublic(catal->sgml, pubID);
2798 if (sgml != NULL)
2799 ret = xmlStrdup(sgml);
2800 }
2801 return(ret);
2802 }
2803
2804 /**
2805 * xmlACatalogResolve:
2806 * @catal: a Catalog
2807 * @pubID: the public ID string
2808 * @sysID: the system ID string
2809 *
2810 * Do a complete resolution lookup of an External Identifier
2811 *
2812 * Returns the URI of the resource or NULL if not found, it must be freed
2813 * by the caller.
2814 */
2815 xmlChar *
xmlACatalogResolve(xmlCatalogPtr catal,const xmlChar * pubID,const xmlChar * sysID)2816 xmlACatalogResolve(xmlCatalogPtr catal, const xmlChar * pubID,
2817 const xmlChar * sysID)
2818 {
2819 xmlChar *ret = NULL;
2820
2821 if (((pubID == NULL) && (sysID == NULL)) || (catal == NULL))
2822 return (NULL);
2823
2824 if (xmlDebugCatalogs) {
2825 if ((pubID != NULL) && (sysID != NULL)) {
2826 xmlCatalogPrintDebug(
2827 "Resolve: pubID %s sysID %s\n", pubID, sysID);
2828 } else if (pubID != NULL) {
2829 xmlCatalogPrintDebug(
2830 "Resolve: pubID %s\n", pubID);
2831 } else {
2832 xmlCatalogPrintDebug(
2833 "Resolve: sysID %s\n", sysID);
2834 }
2835 }
2836
2837 if (catal->type == XML_XML_CATALOG_TYPE) {
2838 ret = xmlCatalogListXMLResolve(catal->xml, pubID, sysID);
2839 if (ret == XML_CATAL_BREAK)
2840 ret = NULL;
2841 } else {
2842 const xmlChar *sgml;
2843
2844 sgml = xmlCatalogSGMLResolve(catal, pubID, sysID);
2845 if (sgml != NULL)
2846 ret = xmlStrdup(sgml);
2847 }
2848 return (ret);
2849 }
2850
2851 /**
2852 * xmlACatalogResolveURI:
2853 * @catal: a Catalog
2854 * @URI: the URI
2855 *
2856 * Do a complete resolution lookup of an URI
2857 *
2858 * Returns the URI of the resource or NULL if not found, it must be freed
2859 * by the caller.
2860 */
2861 xmlChar *
xmlACatalogResolveURI(xmlCatalogPtr catal,const xmlChar * URI)2862 xmlACatalogResolveURI(xmlCatalogPtr catal, const xmlChar *URI) {
2863 xmlChar *ret = NULL;
2864
2865 if ((URI == NULL) || (catal == NULL))
2866 return(NULL);
2867
2868 if (xmlDebugCatalogs)
2869 xmlCatalogPrintDebug(
2870 "Resolve URI %s\n", URI);
2871
2872 if (catal->type == XML_XML_CATALOG_TYPE) {
2873 ret = xmlCatalogListXMLResolveURI(catal->xml, URI);
2874 if (ret == XML_CATAL_BREAK)
2875 ret = NULL;
2876 } else {
2877 const xmlChar *sgml;
2878
2879 sgml = xmlCatalogSGMLResolve(catal, NULL, URI);
2880 if (sgml != NULL)
2881 ret = xmlStrdup(sgml);
2882 }
2883 return(ret);
2884 }
2885
2886 #ifdef LIBXML_OUTPUT_ENABLED
2887 /**
2888 * xmlACatalogDump:
2889 * @catal: a Catalog
2890 * @out: the file.
2891 *
2892 * Dump the given catalog to the given file.
2893 */
2894 void
xmlACatalogDump(xmlCatalogPtr catal,FILE * out)2895 xmlACatalogDump(xmlCatalogPtr catal, FILE *out) {
2896 if ((out == NULL) || (catal == NULL))
2897 return;
2898
2899 if (catal->type == XML_XML_CATALOG_TYPE) {
2900 xmlDumpXMLCatalog(out, catal->xml);
2901 } else {
2902 xmlHashScan(catal->sgml, xmlCatalogDumpEntry, out);
2903 }
2904 }
2905 #endif /* LIBXML_OUTPUT_ENABLED */
2906
2907 /**
2908 * xmlACatalogAdd:
2909 * @catal: a Catalog
2910 * @type: the type of record to add to the catalog
2911 * @orig: the system, public or prefix to match
2912 * @replace: the replacement value for the match
2913 *
2914 * Add an entry in the catalog, it may overwrite existing but
2915 * different entries.
2916 *
2917 * Returns 0 if successful, -1 otherwise
2918 */
2919 int
xmlACatalogAdd(xmlCatalogPtr catal,const xmlChar * type,const xmlChar * orig,const xmlChar * replace)2920 xmlACatalogAdd(xmlCatalogPtr catal, const xmlChar * type,
2921 const xmlChar * orig, const xmlChar * replace)
2922 {
2923 int res = -1;
2924
2925 if (catal == NULL)
2926 return(-1);
2927
2928 if (catal->type == XML_XML_CATALOG_TYPE) {
2929 res = xmlAddXMLCatalog(catal->xml, type, orig, replace);
2930 } else {
2931 xmlCatalogEntryType cattype;
2932
2933 cattype = xmlGetSGMLCatalogEntryType(type);
2934 if (cattype != XML_CATA_NONE) {
2935 xmlCatalogEntryPtr entry;
2936
2937 entry = xmlNewCatalogEntry(cattype, orig, replace, NULL,
2938 XML_CATA_PREFER_NONE, NULL);
2939 if (catal->sgml == NULL)
2940 catal->sgml = xmlHashCreate(10);
2941 res = xmlHashAddEntry(catal->sgml, orig, entry);
2942 if (res < 0)
2943 xmlFreeCatalogEntry(entry, NULL);
2944 }
2945 }
2946 return (res);
2947 }
2948
2949 /**
2950 * xmlACatalogRemove:
2951 * @catal: a Catalog
2952 * @value: the value to remove
2953 *
2954 * Remove an entry from the catalog
2955 *
2956 * Returns the number of entries removed if successful, -1 otherwise
2957 */
2958 int
xmlACatalogRemove(xmlCatalogPtr catal,const xmlChar * value)2959 xmlACatalogRemove(xmlCatalogPtr catal, const xmlChar *value) {
2960 int res = -1;
2961
2962 if ((catal == NULL) || (value == NULL))
2963 return(-1);
2964
2965 if (catal->type == XML_XML_CATALOG_TYPE) {
2966 res = xmlDelXMLCatalog(catal->xml, value);
2967 } else {
2968 res = xmlHashRemoveEntry(catal->sgml, value, xmlFreeCatalogEntry);
2969 if (res == 0)
2970 res = 1;
2971 }
2972 return(res);
2973 }
2974
2975 /**
2976 * xmlNewCatalog:
2977 * @sgml: should this create an SGML catalog
2978 *
2979 * create a new Catalog.
2980 *
2981 * Returns the xmlCatalogPtr or NULL in case of error
2982 */
2983 xmlCatalogPtr
xmlNewCatalog(int sgml)2984 xmlNewCatalog(int sgml) {
2985 xmlCatalogPtr catal = NULL;
2986
2987 if (sgml) {
2988 catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE,
2989 xmlCatalogDefaultPrefer);
2990 if ((catal != NULL) && (catal->sgml == NULL))
2991 catal->sgml = xmlHashCreate(10);
2992 } else
2993 catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE,
2994 xmlCatalogDefaultPrefer);
2995 return(catal);
2996 }
2997
2998 /**
2999 * xmlCatalogIsEmpty:
3000 * @catal: should this create an SGML catalog
3001 *
3002 * Check is a catalog is empty
3003 *
3004 * Returns 1 if the catalog is empty, 0 if not, amd -1 in case of error.
3005 */
3006 int
xmlCatalogIsEmpty(xmlCatalogPtr catal)3007 xmlCatalogIsEmpty(xmlCatalogPtr catal) {
3008 if (catal == NULL)
3009 return(-1);
3010
3011 if (catal->type == XML_XML_CATALOG_TYPE) {
3012 if (catal->xml == NULL)
3013 return(1);
3014 if ((catal->xml->type != XML_CATA_CATALOG) &&
3015 (catal->xml->type != XML_CATA_BROKEN_CATALOG))
3016 return(-1);
3017 if (catal->xml->children == NULL)
3018 return(1);
3019 return(0);
3020 } else {
3021 int res;
3022
3023 if (catal->sgml == NULL)
3024 return(1);
3025 res = xmlHashSize(catal->sgml);
3026 if (res == 0)
3027 return(1);
3028 if (res < 0)
3029 return(-1);
3030 }
3031 return(0);
3032 }
3033
3034 /************************************************************************
3035 * *
3036 * Public interfaces manipulating the global shared default catalog *
3037 * *
3038 ************************************************************************/
3039
3040 /**
3041 * xmlInitCatalogInternal:
3042 *
3043 * Do the catalog initialization only of global data, doesn't try to load
3044 * any catalog actually.
3045 */
3046 void
xmlInitCatalogInternal(void)3047 xmlInitCatalogInternal(void) {
3048 if (getenv("XML_DEBUG_CATALOG"))
3049 xmlDebugCatalogs = 1;
3050 xmlInitRMutex(&xmlCatalogMutex);
3051 }
3052
3053 /**
3054 * xmlInitializeCatalog:
3055 *
3056 * Load the default system catalog.
3057 */
3058 void
xmlInitializeCatalog(void)3059 xmlInitializeCatalog(void) {
3060 if (xmlCatalogInitialized != 0)
3061 return;
3062
3063 xmlInitParser();
3064
3065 xmlRMutexLock(&xmlCatalogMutex);
3066
3067 if (xmlDefaultCatalog == NULL) {
3068 const char *catalogs;
3069 char *path;
3070 const char *cur, *paths;
3071 xmlCatalogPtr catal;
3072 xmlCatalogEntryPtr *nextent;
3073
3074 catalogs = (const char *) getenv("XML_CATALOG_FILES");
3075 if (catalogs == NULL)
3076 catalogs = XML_XML_DEFAULT_CATALOG;
3077
3078 catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE,
3079 xmlCatalogDefaultPrefer);
3080 if (catal != NULL) {
3081 /* the XML_CATALOG_FILES envvar is allowed to contain a
3082 space-separated list of entries. */
3083 cur = catalogs;
3084 nextent = &catal->xml;
3085 while (*cur != '\0') {
3086 while (xmlIsBlank_ch(*cur))
3087 cur++;
3088 if (*cur != 0) {
3089 paths = cur;
3090 while ((*cur != 0) && (!xmlIsBlank_ch(*cur)))
3091 cur++;
3092 path = (char *) xmlStrndup((const xmlChar *)paths, cur - paths);
3093 if (path != NULL) {
3094 *nextent = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
3095 NULL, BAD_CAST path, xmlCatalogDefaultPrefer, NULL);
3096 if (*nextent != NULL)
3097 nextent = &((*nextent)->next);
3098 xmlFree(path);
3099 }
3100 }
3101 }
3102 xmlDefaultCatalog = catal;
3103 }
3104 }
3105
3106 xmlRMutexUnlock(&xmlCatalogMutex);
3107
3108 xmlCatalogInitialized = 1;
3109 }
3110
3111
3112 /**
3113 * xmlLoadCatalog:
3114 * @filename: a file path
3115 *
3116 * Load the catalog and makes its definitions effective for the default
3117 * external entity loader. It will recurse in SGML CATALOG entries.
3118 * this function is not thread safe, catalog initialization should
3119 * preferably be done once at startup
3120 *
3121 * Returns 0 in case of success -1 in case of error
3122 */
3123 int
xmlLoadCatalog(const char * filename)3124 xmlLoadCatalog(const char *filename)
3125 {
3126 int ret;
3127 xmlCatalogPtr catal;
3128
3129 xmlInitParser();
3130
3131 xmlRMutexLock(&xmlCatalogMutex);
3132
3133 if (xmlDefaultCatalog == NULL) {
3134 catal = xmlLoadACatalog(filename);
3135 if (catal == NULL) {
3136 xmlRMutexUnlock(&xmlCatalogMutex);
3137 return(-1);
3138 }
3139
3140 xmlDefaultCatalog = catal;
3141 xmlRMutexUnlock(&xmlCatalogMutex);
3142 xmlCatalogInitialized = 1;
3143 return(0);
3144 }
3145
3146 ret = xmlExpandCatalog(xmlDefaultCatalog, filename);
3147 xmlRMutexUnlock(&xmlCatalogMutex);
3148 return(ret);
3149 }
3150
3151 /**
3152 * xmlLoadCatalogs:
3153 * @pathss: a list of directories separated by a colon or a space.
3154 *
3155 * Load the catalogs and makes their definitions effective for the default
3156 * external entity loader.
3157 * this function is not thread safe, catalog initialization should
3158 * preferably be done once at startup
3159 */
3160 void
xmlLoadCatalogs(const char * pathss)3161 xmlLoadCatalogs(const char *pathss) {
3162 const char *cur;
3163 const char *paths;
3164 xmlChar *path;
3165 #ifdef _WIN32
3166 int i, iLen;
3167 #endif
3168
3169 if (pathss == NULL)
3170 return;
3171
3172 cur = pathss;
3173 while (*cur != 0) {
3174 while (xmlIsBlank_ch(*cur)) cur++;
3175 if (*cur != 0) {
3176 paths = cur;
3177 while ((*cur != 0) && (*cur != PATH_SEPARATOR) && (!xmlIsBlank_ch(*cur)))
3178 cur++;
3179 path = xmlStrndup((const xmlChar *)paths, cur - paths);
3180 if (path != NULL) {
3181 #ifdef _WIN32
3182 iLen = strlen((const char*)path);
3183 for(i = 0; i < iLen; i++) {
3184 if(path[i] == '\\') {
3185 path[i] = '/';
3186 }
3187 }
3188 #endif
3189 xmlLoadCatalog((const char *) path);
3190 xmlFree(path);
3191 }
3192 }
3193 while (*cur == PATH_SEPARATOR)
3194 cur++;
3195 }
3196 }
3197
3198 /**
3199 * xmlCatalogCleanup:
3200 *
3201 * Free up all the memory associated with catalogs
3202 */
3203 void
xmlCatalogCleanup(void)3204 xmlCatalogCleanup(void) {
3205 xmlRMutexLock(&xmlCatalogMutex);
3206 if (xmlDebugCatalogs)
3207 xmlCatalogPrintDebug(
3208 "Catalogs cleanup\n");
3209 if (xmlCatalogXMLFiles != NULL)
3210 xmlHashFree(xmlCatalogXMLFiles, xmlFreeCatalogHashEntryList);
3211 xmlCatalogXMLFiles = NULL;
3212 if (xmlDefaultCatalog != NULL)
3213 xmlFreeCatalog(xmlDefaultCatalog);
3214 xmlDefaultCatalog = NULL;
3215 xmlDebugCatalogs = 0;
3216 xmlCatalogInitialized = 0;
3217 xmlRMutexUnlock(&xmlCatalogMutex);
3218 }
3219
3220 /**
3221 * xmlCleanupCatalogInternal:
3222 *
3223 * Free global data.
3224 */
3225 void
xmlCleanupCatalogInternal(void)3226 xmlCleanupCatalogInternal(void) {
3227 xmlCleanupRMutex(&xmlCatalogMutex);
3228 }
3229
3230 /**
3231 * xmlCatalogResolveSystem:
3232 * @sysID: the system ID string
3233 *
3234 * Try to lookup the catalog resource for a system ID
3235 *
3236 * Returns the resource if found or NULL otherwise, the value returned
3237 * must be freed by the caller.
3238 */
3239 xmlChar *
xmlCatalogResolveSystem(const xmlChar * sysID)3240 xmlCatalogResolveSystem(const xmlChar *sysID) {
3241 xmlChar *ret;
3242
3243 if (!xmlCatalogInitialized)
3244 xmlInitializeCatalog();
3245
3246 ret = xmlACatalogResolveSystem(xmlDefaultCatalog, sysID);
3247 return(ret);
3248 }
3249
3250 /**
3251 * xmlCatalogResolvePublic:
3252 * @pubID: the public ID string
3253 *
3254 * Try to lookup the catalog reference associated to a public ID
3255 *
3256 * Returns the resource if found or NULL otherwise, the value returned
3257 * must be freed by the caller.
3258 */
3259 xmlChar *
xmlCatalogResolvePublic(const xmlChar * pubID)3260 xmlCatalogResolvePublic(const xmlChar *pubID) {
3261 xmlChar *ret;
3262
3263 if (!xmlCatalogInitialized)
3264 xmlInitializeCatalog();
3265
3266 ret = xmlACatalogResolvePublic(xmlDefaultCatalog, pubID);
3267 return(ret);
3268 }
3269
3270 /**
3271 * xmlCatalogResolve:
3272 * @pubID: the public ID string
3273 * @sysID: the system ID string
3274 *
3275 * Do a complete resolution lookup of an External Identifier
3276 *
3277 * Returns the URI of the resource or NULL if not found, it must be freed
3278 * by the caller.
3279 */
3280 xmlChar *
xmlCatalogResolve(const xmlChar * pubID,const xmlChar * sysID)3281 xmlCatalogResolve(const xmlChar *pubID, const xmlChar *sysID) {
3282 xmlChar *ret;
3283
3284 if (!xmlCatalogInitialized)
3285 xmlInitializeCatalog();
3286
3287 ret = xmlACatalogResolve(xmlDefaultCatalog, pubID, sysID);
3288 return(ret);
3289 }
3290
3291 /**
3292 * xmlCatalogResolveURI:
3293 * @URI: the URI
3294 *
3295 * Do a complete resolution lookup of an URI
3296 *
3297 * Returns the URI of the resource or NULL if not found, it must be freed
3298 * by the caller.
3299 */
3300 xmlChar *
xmlCatalogResolveURI(const xmlChar * URI)3301 xmlCatalogResolveURI(const xmlChar *URI) {
3302 xmlChar *ret;
3303
3304 if (!xmlCatalogInitialized)
3305 xmlInitializeCatalog();
3306
3307 ret = xmlACatalogResolveURI(xmlDefaultCatalog, URI);
3308 return(ret);
3309 }
3310
3311 #ifdef LIBXML_OUTPUT_ENABLED
3312 /**
3313 * xmlCatalogDump:
3314 * @out: the file.
3315 *
3316 * Dump all the global catalog content to the given file.
3317 */
3318 void
xmlCatalogDump(FILE * out)3319 xmlCatalogDump(FILE *out) {
3320 if (out == NULL)
3321 return;
3322
3323 if (!xmlCatalogInitialized)
3324 xmlInitializeCatalog();
3325
3326 xmlACatalogDump(xmlDefaultCatalog, out);
3327 }
3328 #endif /* LIBXML_OUTPUT_ENABLED */
3329
3330 /**
3331 * xmlCatalogAdd:
3332 * @type: the type of record to add to the catalog
3333 * @orig: the system, public or prefix to match
3334 * @replace: the replacement value for the match
3335 *
3336 * Add an entry in the catalog, it may overwrite existing but
3337 * different entries.
3338 * If called before any other catalog routine, allows to override the
3339 * default shared catalog put in place by xmlInitializeCatalog();
3340 *
3341 * Returns 0 if successful, -1 otherwise
3342 */
3343 int
xmlCatalogAdd(const xmlChar * type,const xmlChar * orig,const xmlChar * replace)3344 xmlCatalogAdd(const xmlChar *type, const xmlChar *orig, const xmlChar *replace) {
3345 int res = -1;
3346
3347 xmlInitParser();
3348
3349 xmlRMutexLock(&xmlCatalogMutex);
3350 /*
3351 * Specific case where one want to override the default catalog
3352 * put in place by xmlInitializeCatalog();
3353 */
3354 if ((xmlDefaultCatalog == NULL) &&
3355 (xmlStrEqual(type, BAD_CAST "catalog"))) {
3356 xmlDefaultCatalog = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE,
3357 xmlCatalogDefaultPrefer);
3358 if (xmlDefaultCatalog != NULL) {
3359 xmlDefaultCatalog->xml = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
3360 orig, NULL, xmlCatalogDefaultPrefer, NULL);
3361 }
3362 xmlRMutexUnlock(&xmlCatalogMutex);
3363 xmlCatalogInitialized = 1;
3364 return(0);
3365 }
3366
3367 res = xmlACatalogAdd(xmlDefaultCatalog, type, orig, replace);
3368 xmlRMutexUnlock(&xmlCatalogMutex);
3369 return(res);
3370 }
3371
3372 /**
3373 * xmlCatalogRemove:
3374 * @value: the value to remove
3375 *
3376 * Remove an entry from the catalog
3377 *
3378 * Returns the number of entries removed if successful, -1 otherwise
3379 */
3380 int
xmlCatalogRemove(const xmlChar * value)3381 xmlCatalogRemove(const xmlChar *value) {
3382 int res;
3383
3384 if (!xmlCatalogInitialized)
3385 xmlInitializeCatalog();
3386
3387 xmlRMutexLock(&xmlCatalogMutex);
3388 res = xmlACatalogRemove(xmlDefaultCatalog, value);
3389 xmlRMutexUnlock(&xmlCatalogMutex);
3390 return(res);
3391 }
3392
3393 /**
3394 * xmlCatalogConvert:
3395 *
3396 * Convert all the SGML catalog entries as XML ones
3397 *
3398 * Returns the number of entries converted if successful, -1 otherwise
3399 */
3400 int
xmlCatalogConvert(void)3401 xmlCatalogConvert(void) {
3402 int res = -1;
3403
3404 if (!xmlCatalogInitialized)
3405 xmlInitializeCatalog();
3406
3407 xmlRMutexLock(&xmlCatalogMutex);
3408 res = xmlConvertSGMLCatalog(xmlDefaultCatalog);
3409 xmlRMutexUnlock(&xmlCatalogMutex);
3410 return(res);
3411 }
3412
3413 /************************************************************************
3414 * *
3415 * Public interface manipulating the common preferences *
3416 * *
3417 ************************************************************************/
3418
3419 /**
3420 * xmlCatalogGetDefaults:
3421 *
3422 * DEPRECATED: Use XML_PARSE_NO_SYS_CATALOG and
3423 * XML_PARSE_CATALOG_PI.
3424 *
3425 * Used to get the user preference w.r.t. to what catalogs should
3426 * be accepted
3427 *
3428 * Returns the current xmlCatalogAllow value
3429 */
3430 xmlCatalogAllow
xmlCatalogGetDefaults(void)3431 xmlCatalogGetDefaults(void) {
3432 return(xmlCatalogDefaultAllow);
3433 }
3434
3435 /**
3436 * xmlCatalogSetDefaults:
3437 * @allow: what catalogs should be accepted
3438 *
3439 * DEPRECATED: Use XML_PARSE_NO_SYS_CATALOG and
3440 * XML_PARSE_CATALOG_PI.
3441 *
3442 * Used to set the user preference w.r.t. to what catalogs should
3443 * be accepted
3444 */
3445 void
xmlCatalogSetDefaults(xmlCatalogAllow allow)3446 xmlCatalogSetDefaults(xmlCatalogAllow allow) {
3447 if (xmlDebugCatalogs) {
3448 switch (allow) {
3449 case XML_CATA_ALLOW_NONE:
3450 xmlCatalogPrintDebug(
3451 "Disabling catalog usage\n");
3452 break;
3453 case XML_CATA_ALLOW_GLOBAL:
3454 xmlCatalogPrintDebug(
3455 "Allowing only global catalogs\n");
3456 break;
3457 case XML_CATA_ALLOW_DOCUMENT:
3458 xmlCatalogPrintDebug(
3459 "Allowing only catalogs from the document\n");
3460 break;
3461 case XML_CATA_ALLOW_ALL:
3462 xmlCatalogPrintDebug(
3463 "Allowing all catalogs\n");
3464 break;
3465 }
3466 }
3467 xmlCatalogDefaultAllow = allow;
3468 }
3469
3470 /**
3471 * xmlCatalogSetDefaultPrefer:
3472 * @prefer: the default preference for delegation
3473 *
3474 * DEPRECATED: This setting is global and not thread-safe.
3475 *
3476 * Allows to set the preference between public and system for deletion
3477 * in XML Catalog resolution. C.f. section 4.1.1 of the spec
3478 * Values accepted are XML_CATA_PREFER_PUBLIC or XML_CATA_PREFER_SYSTEM
3479 *
3480 * Returns the previous value of the default preference for delegation
3481 */
3482 xmlCatalogPrefer
xmlCatalogSetDefaultPrefer(xmlCatalogPrefer prefer)3483 xmlCatalogSetDefaultPrefer(xmlCatalogPrefer prefer) {
3484 xmlCatalogPrefer ret = xmlCatalogDefaultPrefer;
3485
3486 if (prefer == XML_CATA_PREFER_NONE)
3487 return(ret);
3488
3489 if (xmlDebugCatalogs) {
3490 switch (prefer) {
3491 case XML_CATA_PREFER_PUBLIC:
3492 xmlCatalogPrintDebug(
3493 "Setting catalog preference to PUBLIC\n");
3494 break;
3495 case XML_CATA_PREFER_SYSTEM:
3496 xmlCatalogPrintDebug(
3497 "Setting catalog preference to SYSTEM\n");
3498 break;
3499 default:
3500 return(ret);
3501 }
3502 }
3503 xmlCatalogDefaultPrefer = prefer;
3504 return(ret);
3505 }
3506
3507 /**
3508 * xmlCatalogSetDebug:
3509 * @level: the debug level of catalogs required
3510 *
3511 * Used to set the debug level for catalog operation, 0 disable
3512 * debugging, 1 enable it
3513 *
3514 * Returns the previous value of the catalog debugging level
3515 */
3516 int
xmlCatalogSetDebug(int level)3517 xmlCatalogSetDebug(int level) {
3518 int ret = xmlDebugCatalogs;
3519
3520 if (level <= 0)
3521 xmlDebugCatalogs = 0;
3522 else
3523 xmlDebugCatalogs = level;
3524 return(ret);
3525 }
3526
3527 /************************************************************************
3528 * *
3529 * Minimal interfaces used for per-document catalogs by the parser *
3530 * *
3531 ************************************************************************/
3532
3533 /**
3534 * xmlCatalogFreeLocal:
3535 * @catalogs: a document's list of catalogs
3536 *
3537 * Free up the memory associated to the catalog list
3538 */
3539 void
xmlCatalogFreeLocal(void * catalogs)3540 xmlCatalogFreeLocal(void *catalogs) {
3541 xmlCatalogEntryPtr catal;
3542
3543 catal = (xmlCatalogEntryPtr) catalogs;
3544 if (catal != NULL)
3545 xmlFreeCatalogEntryList(catal);
3546 }
3547
3548
3549 /**
3550 * xmlCatalogAddLocal:
3551 * @catalogs: a document's list of catalogs
3552 * @URL: the URL to a new local catalog
3553 *
3554 * Add the new entry to the catalog list
3555 *
3556 * Returns the updated list
3557 */
3558 void *
xmlCatalogAddLocal(void * catalogs,const xmlChar * URL)3559 xmlCatalogAddLocal(void *catalogs, const xmlChar *URL) {
3560 xmlCatalogEntryPtr catal, add;
3561
3562 xmlInitParser();
3563
3564 if (URL == NULL)
3565 return(catalogs);
3566
3567 if (xmlDebugCatalogs)
3568 xmlCatalogPrintDebug(
3569 "Adding document catalog %s\n", URL);
3570
3571 add = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL, URL, NULL,
3572 xmlCatalogDefaultPrefer, NULL);
3573 if (add == NULL)
3574 return(catalogs);
3575
3576 catal = (xmlCatalogEntryPtr) catalogs;
3577 if (catal == NULL)
3578 return((void *) add);
3579
3580 while (catal->next != NULL)
3581 catal = catal->next;
3582 catal->next = add;
3583 return(catalogs);
3584 }
3585
3586 /**
3587 * xmlCatalogLocalResolve:
3588 * @catalogs: a document's list of catalogs
3589 * @pubID: the public ID string
3590 * @sysID: the system ID string
3591 *
3592 * Do a complete resolution lookup of an External Identifier using a
3593 * document's private catalog list
3594 *
3595 * Returns the URI of the resource or NULL if not found, it must be freed
3596 * by the caller.
3597 */
3598 xmlChar *
xmlCatalogLocalResolve(void * catalogs,const xmlChar * pubID,const xmlChar * sysID)3599 xmlCatalogLocalResolve(void *catalogs, const xmlChar *pubID,
3600 const xmlChar *sysID) {
3601 xmlCatalogEntryPtr catal;
3602 xmlChar *ret;
3603
3604 if ((pubID == NULL) && (sysID == NULL))
3605 return(NULL);
3606
3607 if (xmlDebugCatalogs) {
3608 if ((pubID != NULL) && (sysID != NULL)) {
3609 xmlCatalogPrintDebug(
3610 "Local Resolve: pubID %s sysID %s\n", pubID, sysID);
3611 } else if (pubID != NULL) {
3612 xmlCatalogPrintDebug(
3613 "Local Resolve: pubID %s\n", pubID);
3614 } else {
3615 xmlCatalogPrintDebug(
3616 "Local Resolve: sysID %s\n", sysID);
3617 }
3618 }
3619
3620 catal = (xmlCatalogEntryPtr) catalogs;
3621 if (catal == NULL)
3622 return(NULL);
3623 ret = xmlCatalogListXMLResolve(catal, pubID, sysID);
3624 if ((ret != NULL) && (ret != XML_CATAL_BREAK))
3625 return(ret);
3626 return(NULL);
3627 }
3628
3629 /**
3630 * xmlCatalogLocalResolveURI:
3631 * @catalogs: a document's list of catalogs
3632 * @URI: the URI
3633 *
3634 * Do a complete resolution lookup of an URI using a
3635 * document's private catalog list
3636 *
3637 * Returns the URI of the resource or NULL if not found, it must be freed
3638 * by the caller.
3639 */
3640 xmlChar *
xmlCatalogLocalResolveURI(void * catalogs,const xmlChar * URI)3641 xmlCatalogLocalResolveURI(void *catalogs, const xmlChar *URI) {
3642 xmlCatalogEntryPtr catal;
3643 xmlChar *ret;
3644
3645 if (URI == NULL)
3646 return(NULL);
3647
3648 if (xmlDebugCatalogs)
3649 xmlCatalogPrintDebug(
3650 "Resolve URI %s\n", URI);
3651
3652 catal = (xmlCatalogEntryPtr) catalogs;
3653 if (catal == NULL)
3654 return(NULL);
3655 ret = xmlCatalogListXMLResolveURI(catal, URI);
3656 if ((ret != NULL) && (ret != XML_CATAL_BREAK))
3657 return(ret);
3658 return(NULL);
3659 }
3660
3661 /************************************************************************
3662 * *
3663 * Deprecated interfaces *
3664 * *
3665 ************************************************************************/
3666 /**
3667 * xmlCatalogGetSystem:
3668 * @sysID: the system ID string
3669 *
3670 * Try to lookup the catalog reference associated to a system ID
3671 * DEPRECATED, use xmlCatalogResolveSystem()
3672 *
3673 * Returns the resource if found or NULL otherwise.
3674 */
3675 const xmlChar *
xmlCatalogGetSystem(const xmlChar * sysID)3676 xmlCatalogGetSystem(const xmlChar *sysID) {
3677 xmlChar *ret;
3678 static xmlChar result[1000];
3679 static int msg = 0;
3680
3681 if (!xmlCatalogInitialized)
3682 xmlInitializeCatalog();
3683
3684 if (msg == 0) {
3685 xmlPrintErrorMessage(
3686 "Use of deprecated xmlCatalogGetSystem() call\n");
3687 msg++;
3688 }
3689
3690 if (sysID == NULL)
3691 return(NULL);
3692
3693 /*
3694 * Check first the XML catalogs
3695 */
3696 if (xmlDefaultCatalog != NULL) {
3697 ret = xmlCatalogListXMLResolve(xmlDefaultCatalog->xml, NULL, sysID);
3698 if ((ret != NULL) && (ret != XML_CATAL_BREAK)) {
3699 snprintf((char *) result, sizeof(result) - 1, "%s", (char *) ret);
3700 result[sizeof(result) - 1] = 0;
3701 return(result);
3702 }
3703 }
3704
3705 if (xmlDefaultCatalog != NULL)
3706 return(xmlCatalogGetSGMLSystem(xmlDefaultCatalog->sgml, sysID));
3707 return(NULL);
3708 }
3709
3710 /**
3711 * xmlCatalogGetPublic:
3712 * @pubID: the public ID string
3713 *
3714 * Try to lookup the catalog reference associated to a public ID
3715 * DEPRECATED, use xmlCatalogResolvePublic()
3716 *
3717 * Returns the resource if found or NULL otherwise.
3718 */
3719 const xmlChar *
xmlCatalogGetPublic(const xmlChar * pubID)3720 xmlCatalogGetPublic(const xmlChar *pubID) {
3721 xmlChar *ret;
3722 static xmlChar result[1000];
3723 static int msg = 0;
3724
3725 if (!xmlCatalogInitialized)
3726 xmlInitializeCatalog();
3727
3728 if (msg == 0) {
3729 xmlPrintErrorMessage(
3730 "Use of deprecated xmlCatalogGetPublic() call\n");
3731 msg++;
3732 }
3733
3734 if (pubID == NULL)
3735 return(NULL);
3736
3737 /*
3738 * Check first the XML catalogs
3739 */
3740 if (xmlDefaultCatalog != NULL) {
3741 ret = xmlCatalogListXMLResolve(xmlDefaultCatalog->xml, pubID, NULL);
3742 if ((ret != NULL) && (ret != XML_CATAL_BREAK)) {
3743 snprintf((char *) result, sizeof(result) - 1, "%s", (char *) ret);
3744 result[sizeof(result) - 1] = 0;
3745 return(result);
3746 }
3747 }
3748
3749 if (xmlDefaultCatalog != NULL)
3750 return(xmlCatalogGetSGMLPublic(xmlDefaultCatalog->sgml, pubID));
3751 return(NULL);
3752 }
3753
3754 #endif /* LIBXML_CATALOG_ENABLED */
3755