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