2 * catalog.c: set of generic Catalog related routines
4 * Reference: SGML Open Technical Resolution TR9401:1997.
5 * http://www.jclark.com/sp/catalog.htm
7 * XML Catalogs Working Draft 06 August 2001
8 * http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
10 * See Copyright for the status of this software.
12 * Daniel.Veillard@imag.fr
18 #ifdef LIBXML_CATALOG_ENABLED
19 #ifdef HAVE_SYS_TYPES_H
20 #include <sys/types.h>
22 #ifdef HAVE_SYS_STAT_H
35 #include <libxml/xmlmemory.h>
36 #include <libxml/hash.h>
37 #include <libxml/uri.h>
38 #include <libxml/parserInternals.h>
39 #include <libxml/catalog.h>
40 #include <libxml/xmlerror.h>
41 #include <libxml/threads.h>
42 #include <libxml/globals.h>
44 #define MAX_DELEGATE 50
45 #define MAX_CATAL_DEPTH 50
50 * macro to flag unimplemented blocks
51 * XML_CATALOG_PREFER user env to select between system/public prefered
52 * option. C.f. Richard Tobin <richard@cogsci.ed.ac.uk>
53 *> Just FYI, I am using an environment variable XML_CATALOG_PREFER with
54 *> values "system" and "public". I have made the default be "system" to
58 xmlGenericError(xmlGenericErrorContext, \
59 "Unimplemented block at %s:%d\n", \
62 #define XML_URN_PUBID "urn:publicid:"
63 #define XML_CATAL_BREAK ((xmlChar *) -1)
64 #ifndef XML_XML_DEFAULT_CATALOG
65 #define XML_XML_DEFAULT_CATALOG "file:///etc/xml/catalog"
67 #ifndef XML_SGML_DEFAULT_CATALOG
68 #define XML_SGML_DEFAULT_CATALOG "file:///etc/sgml/catalog"
71 static int xmlExpandCatalog(xmlCatalogPtr catal, const char *filename);
73 /************************************************************************
75 * Types, all private *
77 ************************************************************************/
80 XML_CATA_REMOVED = -1,
83 XML_CATA_BROKEN_CATALOG,
84 XML_CATA_NEXT_CATALOG,
87 XML_CATA_REWRITE_SYSTEM,
88 XML_CATA_DELEGATE_PUBLIC,
89 XML_CATA_DELEGATE_SYSTEM,
92 XML_CATA_DELEGATE_URI,
105 } xmlCatalogEntryType;
107 typedef struct _xmlCatalogEntry xmlCatalogEntry;
108 typedef xmlCatalogEntry *xmlCatalogEntryPtr;
109 struct _xmlCatalogEntry {
110 struct _xmlCatalogEntry *next;
111 struct _xmlCatalogEntry *parent;
112 struct _xmlCatalogEntry *children;
113 xmlCatalogEntryType type;
116 xmlChar *URL; /* The expanded URL using the base */
117 xmlCatalogPrefer prefer;
123 XML_XML_CATALOG_TYPE = 1,
124 XML_SGML_CATALOG_TYPE
127 #define XML_MAX_SGML_CATA_DEPTH 10
129 xmlCatalogType type; /* either XML or SGML */
132 * SGML Catalogs are stored as a simple hash table of catalog entries
133 * Catalog stack to check against overflows when building the
136 char *catalTab[XML_MAX_SGML_CATA_DEPTH]; /* stack of catals */
137 int catalNr; /* Number of current catal streams */
138 int catalMax; /* Max number of catal streams */
139 xmlHashTablePtr sgml;
142 * XML Catalogs are stored as a tree of Catalog entries
144 xmlCatalogPrefer prefer;
145 xmlCatalogEntryPtr xml;
148 /************************************************************************
152 ************************************************************************/
155 * Those are preferences
157 static int xmlDebugCatalogs = 0; /* used for debugging */
158 static xmlCatalogAllow xmlCatalogDefaultAllow = XML_CATA_ALLOW_ALL;
159 static xmlCatalogPrefer xmlCatalogDefaultPrefer = XML_CATA_PREFER_PUBLIC;
162 * Hash table containing all the trees of XML catalogs parsed by
165 static xmlHashTablePtr xmlCatalogXMLFiles = NULL;
168 * The default catalog in use by the application
170 static xmlCatalogPtr xmlDefaultCatalog = NULL;
173 * A mutex for modifying the shared global catalog(s)
174 * xmlDefaultCatalog tree.
175 * It also protects xmlCatalogXMLFiles
176 * The core of this readers/writer scheme is in xmlFetchXMLCatalogFile()
178 static xmlRMutexPtr xmlCatalogMutex = NULL;
181 * Whether the catalog support was initialized.
183 static int xmlCatalogInitialized = 0;
186 /************************************************************************
188 * Allocation and Freeing *
190 ************************************************************************/
193 * xmlNewCatalogEntry:
194 * @type: type of entry
195 * @name: name of the entry
196 * @value: value of the entry
197 * @prefer: the PUBLIC vs. SYSTEM current preference value
199 * create a new Catalog entry, this type is shared both by XML and
200 * SGML catalogs, but the acceptable types values differs.
202 * Returns the xmlCatalogEntryPtr or NULL in case of error
204 static xmlCatalogEntryPtr
205 xmlNewCatalogEntry(xmlCatalogEntryType type, const xmlChar *name,
206 const xmlChar *value, const xmlChar *URL, xmlCatalogPrefer prefer) {
207 xmlCatalogEntryPtr ret;
209 ret = (xmlCatalogEntryPtr) xmlMalloc(sizeof(xmlCatalogEntry));
211 xmlGenericError(xmlGenericErrorContext,
212 "malloc of %d byte failed\n", sizeof(xmlCatalogEntry));
217 ret->children = NULL;
220 ret->name = xmlStrdup(name);
224 ret->value = xmlStrdup(value);
230 ret->URL = xmlStrdup(URL);
233 ret->prefer = prefer;
240 xmlFreeCatalogEntryList(xmlCatalogEntryPtr ret);
243 * xmlFreeCatalogEntry:
244 * @ret: a Catalog entry
246 * Free the memory allocated to a Catalog entry
249 xmlFreeCatalogEntry(xmlCatalogEntryPtr ret) {
253 * Entries stored in the file hash must be deallocated
254 * only by the file hash cleaner !
256 if (ret->dealloc == 1)
259 if (xmlDebugCatalogs) {
260 if (ret->name != NULL)
261 xmlGenericError(xmlGenericErrorContext,
262 "Free catalog entry %s\n", ret->name);
263 else if (ret->value != NULL)
264 xmlGenericError(xmlGenericErrorContext,
265 "Free catalog entry %s\n", ret->value);
267 xmlGenericError(xmlGenericErrorContext,
268 "Free catalog entry\n");
271 if (ret->name != NULL)
273 if (ret->value != NULL)
275 if (ret->URL != NULL)
281 * xmlFreeCatalogEntryList:
282 * @ret: a Catalog entry list
284 * Free the memory allocated to a full chained list of Catalog entries
287 xmlFreeCatalogEntryList(xmlCatalogEntryPtr ret) {
288 xmlCatalogEntryPtr next;
290 while (ret != NULL) {
292 xmlFreeCatalogEntry(ret);
298 * xmlFreeCatalogHashEntryList:
299 * @ret: a Catalog entry list
301 * Free the memory allocated to list of Catalog entries from the
305 xmlFreeCatalogHashEntryList(xmlCatalogEntryPtr catal) {
306 xmlCatalogEntryPtr children, next;
311 children = catal->children;
312 while (children != NULL) {
313 next = children->next;
314 children->dealloc = 0;
315 children->children = NULL;
316 xmlFreeCatalogEntry(children);
320 xmlFreeCatalogEntry(catal);
324 * xmlCreateNewCatalog:
325 * @type: type of catalog
326 * @prefer: the PUBLIC vs. SYSTEM current preference value
328 * create a new Catalog, this type is shared both by XML and
329 * SGML catalogs, but the acceptable types values differs.
331 * Returns the xmlCatalogPtr or NULL in case of error
334 xmlCreateNewCatalog(xmlCatalogType type, xmlCatalogPrefer prefer) {
337 ret = (xmlCatalogPtr) xmlMalloc(sizeof(xmlCatalog));
339 xmlGenericError(xmlGenericErrorContext,
340 "malloc of %d byte failed\n", sizeof(xmlCatalog));
343 memset(ret, 0, sizeof(xmlCatalog));
346 ret->catalMax = XML_MAX_SGML_CATA_DEPTH;
347 ret->prefer = prefer;
348 if (ret->type == XML_SGML_CATALOG_TYPE)
349 ret->sgml = xmlHashCreate(10);
355 * @catal: a Catalog entry
357 * Free the memory allocated to a Catalog
360 xmlFreeCatalog(xmlCatalogPtr catal) {
363 if (catal->xml != NULL)
364 xmlFreeCatalogEntryList(catal->xml);
365 if (catal->sgml != NULL)
366 xmlHashFree(catal->sgml,
367 (xmlHashDeallocator) xmlFreeCatalogEntry);
371 /************************************************************************
373 * Serializing Catalogs *
375 ************************************************************************/
378 * xmlCatalogDumpEntry:
382 * Serialize an SGML Catalog entry
385 xmlCatalogDumpEntry(xmlCatalogEntryPtr entry, FILE *out) {
386 if ((entry == NULL) || (out == NULL))
388 switch (entry->type) {
389 case SGML_CATA_ENTITY:
390 fprintf(out, "ENTITY "); break;
391 case SGML_CATA_PENTITY:
392 fprintf(out, "ENTITY %%"); break;
393 case SGML_CATA_DOCTYPE:
394 fprintf(out, "DOCTYPE "); break;
395 case SGML_CATA_LINKTYPE:
396 fprintf(out, "LINKTYPE "); break;
397 case SGML_CATA_NOTATION:
398 fprintf(out, "NOTATION "); break;
399 case SGML_CATA_PUBLIC:
400 fprintf(out, "PUBLIC "); break;
401 case SGML_CATA_SYSTEM:
402 fprintf(out, "SYSTEM "); break;
403 case SGML_CATA_DELEGATE:
404 fprintf(out, "DELEGATE "); break;
406 fprintf(out, "BASE "); break;
407 case SGML_CATA_CATALOG:
408 fprintf(out, "CATALOG "); break;
409 case SGML_CATA_DOCUMENT:
410 fprintf(out, "DOCUMENT "); break;
411 case SGML_CATA_SGMLDECL:
412 fprintf(out, "SGMLDECL "); break;
416 switch (entry->type) {
417 case SGML_CATA_ENTITY:
418 case SGML_CATA_PENTITY:
419 case SGML_CATA_DOCTYPE:
420 case SGML_CATA_LINKTYPE:
421 case SGML_CATA_NOTATION:
422 fprintf(out, "%s", (const char *) entry->name); break;
423 case SGML_CATA_PUBLIC:
424 case SGML_CATA_SYSTEM:
425 case SGML_CATA_SGMLDECL:
426 case SGML_CATA_DOCUMENT:
427 case SGML_CATA_CATALOG:
429 case SGML_CATA_DELEGATE:
430 fprintf(out, "\"%s\"", entry->name); break;
434 switch (entry->type) {
435 case SGML_CATA_ENTITY:
436 case SGML_CATA_PENTITY:
437 case SGML_CATA_DOCTYPE:
438 case SGML_CATA_LINKTYPE:
439 case SGML_CATA_NOTATION:
440 case SGML_CATA_PUBLIC:
441 case SGML_CATA_SYSTEM:
442 case SGML_CATA_DELEGATE:
443 fprintf(out, " \"%s\"", entry->value); break;
451 xmlDumpXMLCatalog(FILE *out, xmlCatalogEntryPtr catal) {
456 xmlNodePtr node, catalog;
457 xmlOutputBufferPtr buf;
458 xmlCatalogEntryPtr cur;
463 doc = xmlNewDoc(NULL);
466 dtd = xmlNewDtd(doc, BAD_CAST "catalog",
467 BAD_CAST "-//OASIS//DTD Entity Resolution XML Catalog V1.0//EN",
468 BAD_CAST "http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd");
470 xmlAddChild((xmlNodePtr) doc, (xmlNodePtr) dtd);
472 ns = xmlNewNs(NULL, XML_CATALOGS_NAMESPACE, NULL);
477 catalog = xmlNewDocNode(doc, ns, BAD_CAST "catalog", NULL);
478 if (catalog == NULL) {
484 xmlAddChild((xmlNodePtr) doc, catalog);
487 * add all the catalog entries
490 while (cur != NULL) {
492 case XML_CATA_REMOVED:
494 case XML_CATA_BROKEN_CATALOG:
495 case XML_CATA_CATALOG:
501 case XML_CATA_NEXT_CATALOG:
502 node = xmlNewDocNode(doc, ns, BAD_CAST "nextCatalog", NULL);
503 xmlSetProp(node, BAD_CAST "catalog", cur->value);
504 xmlAddChild(catalog, node);
508 case XML_CATA_PUBLIC:
509 node = xmlNewDocNode(doc, ns, BAD_CAST "public", NULL);
510 xmlSetProp(node, BAD_CAST "publicId", cur->name);
511 xmlSetProp(node, BAD_CAST "uri", cur->value);
512 xmlAddChild(catalog, node);
514 case XML_CATA_SYSTEM:
515 node = xmlNewDocNode(doc, ns, BAD_CAST "system", NULL);
516 xmlSetProp(node, BAD_CAST "systemId", cur->name);
517 xmlSetProp(node, BAD_CAST "uri", cur->value);
518 xmlAddChild(catalog, node);
520 case XML_CATA_REWRITE_SYSTEM:
521 node = xmlNewDocNode(doc, ns, BAD_CAST "rewriteSystem", NULL);
522 xmlSetProp(node, BAD_CAST "systemIdStartString", cur->name);
523 xmlSetProp(node, BAD_CAST "rewritePrefix", cur->value);
524 xmlAddChild(catalog, node);
526 case XML_CATA_DELEGATE_PUBLIC:
527 node = xmlNewDocNode(doc, ns, BAD_CAST "delegatePublic", NULL);
528 xmlSetProp(node, BAD_CAST "publicIdStartString", cur->name);
529 xmlSetProp(node, BAD_CAST "catalog", cur->value);
530 xmlAddChild(catalog, node);
532 case XML_CATA_DELEGATE_SYSTEM:
533 node = xmlNewDocNode(doc, ns, BAD_CAST "delegateSystem", NULL);
534 xmlSetProp(node, BAD_CAST "systemIdStartString", cur->name);
535 xmlSetProp(node, BAD_CAST "catalog", cur->value);
536 xmlAddChild(catalog, node);
539 node = xmlNewDocNode(doc, ns, BAD_CAST "uri", NULL);
540 xmlSetProp(node, BAD_CAST "name", cur->name);
541 xmlSetProp(node, BAD_CAST "uri", cur->value);
542 xmlAddChild(catalog, node);
544 case XML_CATA_REWRITE_URI:
545 node = xmlNewDocNode(doc, ns, BAD_CAST "rewriteURI", NULL);
546 xmlSetProp(node, BAD_CAST "uriStartString", cur->name);
547 xmlSetProp(node, BAD_CAST "rewritePrefix", cur->value);
548 xmlAddChild(catalog, node);
550 case XML_CATA_DELEGATE_URI:
551 node = xmlNewDocNode(doc, ns, BAD_CAST "delegateURI", NULL);
552 xmlSetProp(node, BAD_CAST "uriStartString", cur->name);
553 xmlSetProp(node, BAD_CAST "catalog", cur->value);
554 xmlAddChild(catalog, node);
556 case SGML_CATA_SYSTEM:
557 case SGML_CATA_PUBLIC:
558 case SGML_CATA_ENTITY:
559 case SGML_CATA_PENTITY:
560 case SGML_CATA_DOCTYPE:
561 case SGML_CATA_LINKTYPE:
562 case SGML_CATA_NOTATION:
563 case SGML_CATA_DELEGATE:
565 case SGML_CATA_CATALOG:
566 case SGML_CATA_DOCUMENT:
567 case SGML_CATA_SGMLDECL:
576 buf = xmlOutputBufferCreateFile(out, NULL);
581 ret = xmlSaveFormatFileTo(buf, doc, NULL, 1);
591 /************************************************************************
593 * Converting SGML Catalogs to XML *
595 ************************************************************************/
598 * xmlCatalogConvertEntry:
600 * @catal: pointer to the catalog being converted
602 * Convert one entry from the catalog
605 xmlCatalogConvertEntry(xmlCatalogEntryPtr entry, xmlCatalogPtr catal) {
606 if ((entry == NULL) || (catal == NULL) || (catal->sgml == NULL) ||
607 (catal->xml == NULL))
609 switch (entry->type) {
610 case SGML_CATA_ENTITY:
611 entry->type = XML_CATA_PUBLIC;
613 case SGML_CATA_PENTITY:
614 entry->type = XML_CATA_PUBLIC;
616 case SGML_CATA_DOCTYPE:
617 entry->type = XML_CATA_PUBLIC;
619 case SGML_CATA_LINKTYPE:
620 entry->type = XML_CATA_PUBLIC;
622 case SGML_CATA_NOTATION:
623 entry->type = XML_CATA_PUBLIC;
625 case SGML_CATA_PUBLIC:
626 entry->type = XML_CATA_PUBLIC;
628 case SGML_CATA_SYSTEM:
629 entry->type = XML_CATA_SYSTEM;
631 case SGML_CATA_DELEGATE:
632 entry->type = XML_CATA_DELEGATE_PUBLIC;
634 case SGML_CATA_CATALOG:
635 entry->type = XML_CATA_CATALOG;
638 xmlHashRemoveEntry(catal->sgml, entry->name,
639 (xmlHashDeallocator) xmlFreeCatalogEntry);
643 * Conversion successful, remove from the SGML catalog
644 * and add it to the default XML one
646 xmlHashRemoveEntry(catal->sgml, entry->name, NULL);
647 entry->parent = catal->xml;
649 if (catal->xml->children == NULL)
650 catal->xml->children = entry;
652 xmlCatalogEntryPtr prev;
654 prev = catal->xml->children;
655 while (prev->next != NULL)
662 * xmlConvertSGMLCatalog:
663 * @catal: the catalog
665 * Convert all the SGML catalog entries as XML ones
667 * Returns the number of entries converted if successful, -1 otherwise
670 xmlConvertSGMLCatalog(xmlCatalogPtr catal) {
672 if ((catal == NULL) || (catal->type != XML_SGML_CATALOG_TYPE))
675 if (xmlDebugCatalogs) {
676 xmlGenericError(xmlGenericErrorContext,
677 "Converting SGML catalog to XML\n");
679 xmlHashScan(catal->sgml,
680 (xmlHashScanner) xmlCatalogConvertEntry,
685 /************************************************************************
689 ************************************************************************/
692 * xmlCatalogUnWrapURN:
693 * @urn: an "urn:publicid:" to unwrap
695 * Expand the URN into the equivalent Public Identifier
697 * Returns the new identifier or NULL, the string must be deallocated
701 xmlCatalogUnWrapURN(const xmlChar *urn) {
702 xmlChar result[2000];
705 if (xmlStrncmp(urn, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1))
707 urn += sizeof(XML_URN_PUBID) - 1;
710 if (i > sizeof(result) - 3)
715 } else if (*urn == ':') {
719 } else if (*urn == ';') {
723 } else if (*urn == '%') {
724 if ((urn[1] == '2') && (urn[1] == 'B'))
726 else if ((urn[1] == '3') && (urn[1] == 'A'))
728 else if ((urn[1] == '2') && (urn[1] == 'F'))
730 else if ((urn[1] == '3') && (urn[1] == 'B'))
732 else if ((urn[1] == '2') && (urn[1] == '7'))
734 else if ((urn[1] == '3') && (urn[1] == 'F'))
736 else if ((urn[1] == '2') && (urn[1] == '3'))
738 else if ((urn[1] == '2') && (urn[1] == '5'))
753 return(xmlStrdup(result));
757 * xmlParseCatalogFile:
758 * @filename: the filename
760 * parse an XML file and build a tree. It's like xmlParseFile()
761 * except it bypass all catalog lookups.
763 * Returns the resulting document tree or NULL in case of error
767 xmlParseCatalogFile(const char *filename) {
769 xmlParserCtxtPtr ctxt;
770 char *directory = NULL;
771 xmlParserInputPtr inputStream;
772 xmlParserInputBufferPtr buf;
774 ctxt = xmlNewParserCtxt();
776 if (xmlDefaultSAXHandler.error != NULL) {
777 xmlDefaultSAXHandler.error(NULL, "out of memory\n");
782 buf = xmlParserInputBufferCreateFilename(filename, XML_CHAR_ENCODING_NONE);
784 xmlFreeParserCtxt(ctxt);
788 inputStream = xmlNewInputStream(ctxt);
789 if (inputStream == NULL) {
790 xmlFreeParserCtxt(ctxt);
794 inputStream->filename = (char *) xmlCanonicPath((const xmlChar *)filename);
795 inputStream->buf = buf;
796 inputStream->base = inputStream->buf->buffer->content;
797 inputStream->cur = inputStream->buf->buffer->content;
799 &inputStream->buf->buffer->content[inputStream->buf->buffer->use];
801 inputPush(ctxt, inputStream);
802 if ((ctxt->directory == NULL) && (directory == NULL))
803 directory = xmlParserGetDirectory(filename);
804 if ((ctxt->directory == NULL) && (directory != NULL))
805 ctxt->directory = directory;
808 ctxt->loadsubset = 0;
811 xmlParseDocument(ctxt);
813 if (ctxt->wellFormed)
817 xmlFreeDoc(ctxt->myDoc);
820 xmlFreeParserCtxt(ctxt);
826 * xmlLoadFileContent:
827 * @filename: a file path
829 * Load a file content into memory.
831 * Returns a pointer to the 0 terminated string or NULL in case of error
834 xmlLoadFileContent(const char *filename)
849 if (filename == NULL)
853 if (stat(filename, &info) < 0)
858 if ((fd = open(filename, O_RDONLY)) < 0)
860 if ((fd = fopen(filename, "rb")) == NULL)
868 if (fseek(fd, 0, SEEK_END) || (size = ftell(fd)) == EOF || fseek(fd, 0, SEEK_SET)) { /* File operations denied? ok, just close and return failure */
873 content = xmlMallocAtomic(size + 10);
874 if (content == NULL) {
875 xmlGenericError(xmlGenericErrorContext,
876 "malloc of %d byte failed\n", size + 10);
880 len = read(fd, content, size);
882 len = fread(content, 1, size, fd);
898 /************************************************************************
900 * The XML Catalog parser *
902 ************************************************************************/
904 static xmlCatalogEntryPtr
905 xmlParseXMLCatalogFile(xmlCatalogPrefer prefer, const xmlChar *filename);
907 xmlParseXMLCatalogNodeList(xmlNodePtr cur, xmlCatalogPrefer prefer,
908 xmlCatalogEntryPtr parent);
910 xmlCatalogListXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,
911 const xmlChar *sysID);
913 xmlCatalogListXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI);
917 * xmlGetXMLCatalogEntryType:
920 * lookup the internal type associated to an XML catalog entry name
922 * Returns the type associate with that name
924 static xmlCatalogEntryType
925 xmlGetXMLCatalogEntryType(const xmlChar *name) {
926 xmlCatalogEntryType type = XML_CATA_NONE;
927 if (xmlStrEqual(name, (const xmlChar *) "system"))
928 type = XML_CATA_SYSTEM;
929 else if (xmlStrEqual(name, (const xmlChar *) "public"))
930 type = XML_CATA_PUBLIC;
931 else if (xmlStrEqual(name, (const xmlChar *) "rewriteSystem"))
932 type = XML_CATA_REWRITE_SYSTEM;
933 else if (xmlStrEqual(name, (const xmlChar *) "delegatePublic"))
934 type = XML_CATA_DELEGATE_PUBLIC;
935 else if (xmlStrEqual(name, (const xmlChar *) "delegateSystem"))
936 type = XML_CATA_DELEGATE_SYSTEM;
937 else if (xmlStrEqual(name, (const xmlChar *) "uri"))
939 else if (xmlStrEqual(name, (const xmlChar *) "rewriteURI"))
940 type = XML_CATA_REWRITE_URI;
941 else if (xmlStrEqual(name, (const xmlChar *) "delegateURI"))
942 type = XML_CATA_DELEGATE_URI;
943 else if (xmlStrEqual(name, (const xmlChar *) "nextCatalog"))
944 type = XML_CATA_NEXT_CATALOG;
945 else if (xmlStrEqual(name, (const xmlChar *) "catalog"))
946 type = XML_CATA_CATALOG;
951 * xmlParseXMLCatalogOneNode:
953 * @type: the type of Catalog entry
954 * @name: the name of the node
955 * @attrName: the attribute holding the value
956 * @uriAttrName: the attribute holding the URI-Reference
957 * @prefer: the PUBLIC vs. SYSTEM current preference value
959 * Finishes the examination of an XML tree node of a catalog and build
960 * a Catalog entry from it.
962 * Returns the new Catalog entry node or NULL in case of error.
964 static xmlCatalogEntryPtr
965 xmlParseXMLCatalogOneNode(xmlNodePtr cur, xmlCatalogEntryType type,
966 const xmlChar *name, const xmlChar *attrName,
967 const xmlChar *uriAttrName, xmlCatalogPrefer prefer) {
970 xmlChar *nameValue = NULL;
971 xmlChar *base = NULL;
973 xmlCatalogEntryPtr ret = NULL;
975 if (attrName != NULL) {
976 nameValue = xmlGetProp(cur, attrName);
977 if (nameValue == NULL) {
978 xmlGenericError(xmlGenericErrorContext,
979 "%s entry lacks '%s'\n", name, attrName);
983 uriValue = xmlGetProp(cur, uriAttrName);
984 if (uriValue == NULL) {
985 xmlGenericError(xmlGenericErrorContext,
986 "%s entry lacks '%s'\n", name, uriAttrName);
990 if (nameValue != NULL)
992 if (uriValue != NULL)
997 base = xmlNodeGetBase(cur->doc, cur);
998 URL = xmlBuildURI(uriValue, base);
1000 if (xmlDebugCatalogs > 1) {
1001 if (nameValue != NULL)
1002 xmlGenericError(xmlGenericErrorContext,
1003 "Found %s: '%s' '%s'\n", name, nameValue, URL);
1005 xmlGenericError(xmlGenericErrorContext,
1006 "Found %s: '%s'\n", name, URL);
1008 ret = xmlNewCatalogEntry(type, nameValue, uriValue, URL, prefer);
1010 xmlGenericError(xmlGenericErrorContext,
1011 "%s entry '%s' broken ?: %s\n", name, uriAttrName, uriValue);
1013 if (nameValue != NULL)
1015 if (uriValue != NULL)
1025 * xmlParseXMLCatalogNode:
1026 * @cur: the XML node
1027 * @prefer: the PUBLIC vs. SYSTEM current preference value
1028 * @parent: the parent Catalog entry
1030 * Examines an XML tree node of a catalog and build
1031 * a Catalog entry from it adding it to its parent. The examination can
1035 xmlParseXMLCatalogNode(xmlNodePtr cur, xmlCatalogPrefer prefer,
1036 xmlCatalogEntryPtr parent)
1038 xmlChar *uri = NULL;
1039 xmlChar *URL = NULL;
1040 xmlChar *base = NULL;
1041 xmlCatalogEntryPtr entry = NULL;
1045 if (xmlStrEqual(cur->name, BAD_CAST "group")) {
1048 prop = xmlGetProp(cur, BAD_CAST "prefer");
1050 if (xmlStrEqual(prop, BAD_CAST "system")) {
1051 prefer = XML_CATA_PREFER_SYSTEM;
1052 } else if (xmlStrEqual(prop, BAD_CAST "public")) {
1053 prefer = XML_CATA_PREFER_PUBLIC;
1055 xmlGenericError(xmlGenericErrorContext,
1056 "Invalid value for prefer: '%s'\n", prop);
1061 * Recurse to propagate prefer to the subtree
1062 * (xml:base handling is automated)
1064 xmlParseXMLCatalogNodeList(cur->children, prefer, parent);
1065 } else if (xmlStrEqual(cur->name, BAD_CAST "public")) {
1066 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_PUBLIC,
1067 BAD_CAST "public", BAD_CAST "publicId", BAD_CAST "uri", prefer);
1068 } else if (xmlStrEqual(cur->name, BAD_CAST "system")) {
1069 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_SYSTEM,
1070 BAD_CAST "system", BAD_CAST "systemId", BAD_CAST "uri", prefer);
1071 } else if (xmlStrEqual(cur->name, BAD_CAST "rewriteSystem")) {
1072 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_REWRITE_SYSTEM,
1073 BAD_CAST "rewriteSystem", BAD_CAST "systemIdStartString",
1074 BAD_CAST "rewritePrefix", prefer);
1075 } else if (xmlStrEqual(cur->name, BAD_CAST "delegatePublic")) {
1076 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_PUBLIC,
1077 BAD_CAST "delegatePublic", BAD_CAST "publicIdStartString",
1078 BAD_CAST "catalog", prefer);
1079 } else if (xmlStrEqual(cur->name, BAD_CAST "delegateSystem")) {
1080 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_SYSTEM,
1081 BAD_CAST "delegateSystem", BAD_CAST "systemIdStartString",
1082 BAD_CAST "catalog", prefer);
1083 } else if (xmlStrEqual(cur->name, BAD_CAST "uri")) {
1084 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_URI,
1085 BAD_CAST "uri", BAD_CAST "name",
1086 BAD_CAST "uri", prefer);
1087 } else if (xmlStrEqual(cur->name, BAD_CAST "rewriteURI")) {
1088 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_REWRITE_URI,
1089 BAD_CAST "rewriteURI", BAD_CAST "uriStartString",
1090 BAD_CAST "rewritePrefix", prefer);
1091 } else if (xmlStrEqual(cur->name, BAD_CAST "delegateURI")) {
1092 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_URI,
1093 BAD_CAST "delegateURI", BAD_CAST "uriStartString",
1094 BAD_CAST "catalog", prefer);
1095 } else if (xmlStrEqual(cur->name, BAD_CAST "nextCatalog")) {
1096 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_NEXT_CATALOG,
1097 BAD_CAST "nextCatalog", NULL,
1098 BAD_CAST "catalog", prefer);
1100 if ((entry != NULL) && (parent != NULL)) {
1101 entry->parent = parent;
1102 if (parent->children == NULL)
1103 parent->children = entry;
1105 xmlCatalogEntryPtr prev;
1107 prev = parent->children;
1108 while (prev->next != NULL)
1122 * xmlParseXMLCatalogNodeList:
1123 * @cur: the XML node list of siblings
1124 * @prefer: the PUBLIC vs. SYSTEM current preference value
1125 * @parent: the parent Catalog entry
1127 * Examines a list of XML sibling nodes of a catalog and build
1128 * a list of Catalog entry from it adding it to the parent.
1129 * The examination will recurse to examine node subtrees.
1132 xmlParseXMLCatalogNodeList(xmlNodePtr cur, xmlCatalogPrefer prefer,
1133 xmlCatalogEntryPtr parent) {
1134 while (cur != NULL) {
1135 if ((cur->ns != NULL) && (cur->ns->href != NULL) &&
1136 (xmlStrEqual(cur->ns->href, XML_CATALOGS_NAMESPACE))) {
1137 xmlParseXMLCatalogNode(cur, prefer, parent);
1141 /* TODO: sort the list according to REWRITE lengths and prefer value */
1145 * xmlParseXMLCatalogFile:
1146 * @prefer: the PUBLIC vs. SYSTEM current preference value
1147 * @filename: the filename for the catalog
1149 * Parses the catalog file to extract the XML tree and then analyze the
1150 * tree to build a list of Catalog entries corresponding to this catalog
1152 * Returns the resulting Catalog entries list
1154 static xmlCatalogEntryPtr
1155 xmlParseXMLCatalogFile(xmlCatalogPrefer prefer, const xmlChar *filename) {
1159 xmlCatalogEntryPtr parent = NULL;
1161 if (filename == NULL)
1164 doc = xmlParseCatalogFile((const char *) filename);
1166 if (xmlDebugCatalogs)
1167 xmlGenericError(xmlGenericErrorContext,
1168 "Failed to parse catalog %s\n", filename);
1172 if (xmlDebugCatalogs)
1173 xmlGenericError(xmlGenericErrorContext,
1174 "%d Parsing catalog %s\n", xmlGetThreadId(), filename);
1176 cur = xmlDocGetRootElement(doc);
1177 if ((cur != NULL) && (xmlStrEqual(cur->name, BAD_CAST "catalog")) &&
1178 (cur->ns != NULL) && (cur->ns->href != NULL) &&
1179 (xmlStrEqual(cur->ns->href, XML_CATALOGS_NAMESPACE))) {
1181 parent = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
1182 (const xmlChar *)filename, NULL, prefer);
1183 if (parent == NULL) {
1188 prop = xmlGetProp(cur, BAD_CAST "prefer");
1190 if (xmlStrEqual(prop, BAD_CAST "system")) {
1191 prefer = XML_CATA_PREFER_SYSTEM;
1192 } else if (xmlStrEqual(prop, BAD_CAST "public")) {
1193 prefer = XML_CATA_PREFER_PUBLIC;
1195 xmlGenericError(xmlGenericErrorContext,
1196 "Invalid value for prefer: '%s'\n",
1201 cur = cur->children;
1202 xmlParseXMLCatalogNodeList(cur, prefer, parent);
1204 xmlGenericError(xmlGenericErrorContext,
1205 "File %s is not an XML Catalog\n", filename);
1214 * xmlFetchXMLCatalogFile:
1215 * @catal: an existing but incomplete catalog entry
1217 * Fetch and parse the subcatalog referenced by an entry
1219 * Returns 0 in case of success, -1 otherwise
1222 xmlFetchXMLCatalogFile(xmlCatalogEntryPtr catal) {
1223 xmlCatalogEntryPtr doc;
1227 if (catal->URL == NULL)
1229 if (catal->children != NULL)
1233 * lock the whole catalog for modification
1235 xmlRMutexLock(xmlCatalogMutex);
1236 if (catal->children != NULL) {
1237 /* Okay someone else did it in the meantime */
1238 xmlRMutexUnlock(xmlCatalogMutex);
1242 if (xmlCatalogXMLFiles != NULL) {
1243 doc = (xmlCatalogEntryPtr)
1244 xmlHashLookup(xmlCatalogXMLFiles, catal->URL);
1246 if (xmlDebugCatalogs)
1247 xmlGenericError(xmlGenericErrorContext,
1248 "Found %s in file hash\n", catal->URL);
1250 if (catal->type == XML_CATA_CATALOG)
1251 catal->children = doc->children;
1253 catal->children = doc;
1255 xmlRMutexUnlock(xmlCatalogMutex);
1258 if (xmlDebugCatalogs)
1259 xmlGenericError(xmlGenericErrorContext,
1260 "%s not found in file hash\n", catal->URL);
1264 * Fetch and parse. Note that xmlParseXMLCatalogFile does not
1265 * use the existing catalog, there is no recursion allowed at
1268 doc = xmlParseXMLCatalogFile(catal->prefer, catal->URL);
1270 catal->type = XML_CATA_BROKEN_CATALOG;
1271 xmlRMutexUnlock(xmlCatalogMutex);
1275 if (catal->type == XML_CATA_CATALOG)
1276 catal->children = doc->children;
1278 catal->children = doc;
1282 if (xmlCatalogXMLFiles == NULL)
1283 xmlCatalogXMLFiles = xmlHashCreate(10);
1284 if (xmlCatalogXMLFiles != NULL) {
1285 if (xmlDebugCatalogs)
1286 xmlGenericError(xmlGenericErrorContext,
1287 "%s added to file hash\n", catal->URL);
1288 xmlHashAddEntry(xmlCatalogXMLFiles, catal->URL, doc);
1290 xmlRMutexUnlock(xmlCatalogMutex);
1294 /************************************************************************
1296 * XML Catalog handling *
1298 ************************************************************************/
1302 * @catal: top of an XML catalog
1303 * @type: the type of record to add to the catalog
1304 * @orig: the system, public or prefix to match (or NULL)
1305 * @replace: the replacement value for the match
1307 * Add an entry in the XML catalog, it may overwrite existing but
1308 * different entries.
1310 * Returns 0 if successful, -1 otherwise
1313 xmlAddXMLCatalog(xmlCatalogEntryPtr catal, const xmlChar *type,
1314 const xmlChar *orig, const xmlChar *replace) {
1315 xmlCatalogEntryPtr cur;
1316 xmlCatalogEntryType typ;
1319 if ((catal == NULL) ||
1320 ((catal->type != XML_CATA_CATALOG) &&
1321 (catal->type != XML_CATA_BROKEN_CATALOG)))
1323 if (catal->children == NULL) {
1324 xmlFetchXMLCatalogFile(catal);
1326 if (catal->children == NULL)
1329 typ = xmlGetXMLCatalogEntryType(type);
1330 if (typ == XML_CATA_NONE) {
1331 if (xmlDebugCatalogs)
1332 xmlGenericError(xmlGenericErrorContext,
1333 "Failed to add unknown element %s to catalog\n", type);
1337 cur = catal->children;
1339 * Might be a simple "update in place"
1342 while (cur != NULL) {
1343 if ((orig != NULL) && (cur->type == typ) &&
1344 (xmlStrEqual(orig, cur->name))) {
1345 if (xmlDebugCatalogs)
1346 xmlGenericError(xmlGenericErrorContext,
1347 "Updating element %s to catalog\n", type);
1348 if (cur->value != NULL)
1349 xmlFree(cur->value);
1350 if (cur->URL != NULL)
1352 cur->value = xmlStrdup(replace);
1353 cur->URL = xmlStrdup(replace);
1356 if (cur->next == NULL)
1361 if (xmlDebugCatalogs)
1362 xmlGenericError(xmlGenericErrorContext,
1363 "Adding element %s to catalog\n", type);
1365 catal->children = xmlNewCatalogEntry(typ, orig, replace,
1366 NULL, catal->prefer);
1368 cur->next = xmlNewCatalogEntry(typ, orig, replace,
1369 NULL, catal->prefer);
1371 cur = xmlHashLookup(xmlCatalogXMLFiles, catal->URL);
1373 cur->children = catal->children;
1381 * @catal: top of an XML catalog
1382 * @value: the value to remove from the catalog
1384 * Remove entries in the XML catalog where the value or the URI
1385 * is equal to @value
1387 * Returns the number of entries removed if successful, -1 otherwise
1390 xmlDelXMLCatalog(xmlCatalogEntryPtr catal, const xmlChar *value) {
1391 xmlCatalogEntryPtr cur;
1394 if ((catal == NULL) ||
1395 ((catal->type != XML_CATA_CATALOG) &&
1396 (catal->type != XML_CATA_BROKEN_CATALOG)))
1400 if (catal->children == NULL) {
1401 xmlFetchXMLCatalogFile(catal);
1407 cur = catal->children;
1408 while (cur != NULL) {
1409 if (((cur->name != NULL) && (xmlStrEqual(value, cur->name))) ||
1410 (xmlStrEqual(value, cur->value))) {
1411 if (xmlDebugCatalogs) {
1412 if (cur->name != NULL)
1413 xmlGenericError(xmlGenericErrorContext,
1414 "Removing element %s from catalog\n", cur->name);
1416 xmlGenericError(xmlGenericErrorContext,
1417 "Removing element %s from catalog\n", cur->value);
1419 cur->type = XML_CATA_REMOVED;
1427 * xmlCatalogXMLResolve:
1428 * @catal: a catalog list
1429 * @pubId: the public ID string
1430 * @sysId: the system ID string
1432 * Do a complete resolution lookup of an External Identifier for a
1433 * list of catalog entries.
1435 * Implements (or tries to) 7.1. External Identifier Resolution
1436 * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
1438 * Returns the URI of the resource or NULL if not found
1441 xmlCatalogXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,
1442 const xmlChar *sysID) {
1443 xmlChar *ret = NULL;
1444 xmlCatalogEntryPtr cur;
1445 int haveDelegate = 0;
1449 * protection against loops
1451 if (catal->depth > MAX_CATAL_DEPTH) {
1452 if (catal->name != NULL)
1453 xmlGenericError(xmlGenericErrorContext,
1454 "Detected recursion in catalog %s\n", catal->name);
1456 xmlGenericError(xmlGenericErrorContext,
1457 "Detected recursion in catalog\n");
1463 * First tries steps 2/ 3/ 4/ if a system ID is provided.
1465 if (sysID != NULL) {
1466 xmlCatalogEntryPtr rewrite = NULL;
1467 int lenrewrite = 0, len;
1470 while (cur != NULL) {
1471 switch (cur->type) {
1472 case XML_CATA_SYSTEM:
1473 if (xmlStrEqual(sysID, cur->name)) {
1474 if (xmlDebugCatalogs)
1475 xmlGenericError(xmlGenericErrorContext,
1476 "Found system match %s\n", cur->name);
1478 return(xmlStrdup(cur->URL));
1481 case XML_CATA_REWRITE_SYSTEM:
1482 len = xmlStrlen(cur->name);
1483 if ((len > lenrewrite) &&
1484 (!xmlStrncmp(sysID, cur->name, len))) {
1489 case XML_CATA_DELEGATE_SYSTEM:
1490 if (!xmlStrncmp(sysID, cur->name, xmlStrlen(cur->name)))
1493 case XML_CATA_NEXT_CATALOG:
1501 if (rewrite != NULL) {
1502 if (xmlDebugCatalogs)
1503 xmlGenericError(xmlGenericErrorContext,
1504 "Using rewriting rule %s\n", rewrite->name);
1505 ret = xmlStrdup(rewrite->URL);
1507 ret = xmlStrcat(ret, &sysID[lenrewrite]);
1512 const xmlChar *delegates[MAX_DELEGATE];
1516 * Assume the entries have been sorted by decreasing substring
1517 * matches when the list was produced.
1520 while (cur != NULL) {
1521 if ((cur->type == XML_CATA_DELEGATE_SYSTEM) &&
1522 (!xmlStrncmp(sysID, cur->name, xmlStrlen(cur->name)))) {
1523 for (i = 0;i < nbList;i++)
1524 if (xmlStrEqual(cur->URL, delegates[i]))
1530 if (nbList < MAX_DELEGATE)
1531 delegates[nbList++] = cur->URL;
1533 if (cur->children == NULL) {
1534 xmlFetchXMLCatalogFile(cur);
1536 if (cur->children != NULL) {
1537 if (xmlDebugCatalogs)
1538 xmlGenericError(xmlGenericErrorContext,
1539 "Trying system delegate %s\n", cur->URL);
1540 ret = xmlCatalogListXMLResolve(
1541 cur->children, NULL, sysID);
1551 * Apply the cut algorithm explained in 4/
1554 return(XML_CATAL_BREAK);
1558 * Then tries 5/ 6/ if a public ID is provided
1560 if (pubID != NULL) {
1563 while (cur != NULL) {
1564 switch (cur->type) {
1565 case XML_CATA_PUBLIC:
1566 if (xmlStrEqual(pubID, cur->name)) {
1567 if (xmlDebugCatalogs)
1568 xmlGenericError(xmlGenericErrorContext,
1569 "Found public match %s\n", cur->name);
1571 return(xmlStrdup(cur->URL));
1574 case XML_CATA_DELEGATE_PUBLIC:
1575 if (!xmlStrncmp(pubID, cur->name, xmlStrlen(cur->name)) &&
1576 (cur->prefer == XML_CATA_PREFER_PUBLIC))
1579 case XML_CATA_NEXT_CATALOG:
1589 const xmlChar *delegates[MAX_DELEGATE];
1593 * Assume the entries have been sorted by decreasing substring
1594 * matches when the list was produced.
1597 while (cur != NULL) {
1598 if ((cur->type == XML_CATA_DELEGATE_PUBLIC) &&
1599 (cur->prefer == XML_CATA_PREFER_PUBLIC) &&
1600 (!xmlStrncmp(pubID, cur->name, xmlStrlen(cur->name)))) {
1602 for (i = 0;i < nbList;i++)
1603 if (xmlStrEqual(cur->URL, delegates[i]))
1609 if (nbList < MAX_DELEGATE)
1610 delegates[nbList++] = cur->URL;
1612 if (cur->children == NULL) {
1613 xmlFetchXMLCatalogFile(cur);
1615 if (cur->children != NULL) {
1616 if (xmlDebugCatalogs)
1617 xmlGenericError(xmlGenericErrorContext,
1618 "Trying public delegate %s\n", cur->URL);
1619 ret = xmlCatalogListXMLResolve(
1620 cur->children, pubID, NULL);
1630 * Apply the cut algorithm explained in 4/
1633 return(XML_CATAL_BREAK);
1638 while (cur != NULL) {
1639 if (cur->type == XML_CATA_NEXT_CATALOG) {
1640 if (cur->children == NULL) {
1641 xmlFetchXMLCatalogFile(cur);
1643 if (cur->children != NULL) {
1644 ret = xmlCatalogListXMLResolve(cur->children, pubID, sysID);
1660 * xmlCatalogXMLResolveURI:
1661 * @catal: a catalog list
1663 * @sysId: the system ID string
1665 * Do a complete resolution lookup of an External Identifier for a
1666 * list of catalog entries.
1668 * Implements (or tries to) 7.2.2. URI Resolution
1669 * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
1671 * Returns the URI of the resource or NULL if not found
1674 xmlCatalogXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI) {
1675 xmlChar *ret = NULL;
1676 xmlCatalogEntryPtr cur;
1677 int haveDelegate = 0;
1679 xmlCatalogEntryPtr rewrite = NULL;
1680 int lenrewrite = 0, len;
1689 * First tries steps 2/ 3/ 4/ if a system ID is provided.
1693 while (cur != NULL) {
1694 switch (cur->type) {
1696 if (xmlStrEqual(URI, cur->name)) {
1697 if (xmlDebugCatalogs)
1698 xmlGenericError(xmlGenericErrorContext,
1699 "Found URI match %s\n", cur->name);
1700 return(xmlStrdup(cur->URL));
1703 case XML_CATA_REWRITE_URI:
1704 len = xmlStrlen(cur->name);
1705 if ((len > lenrewrite) &&
1706 (!xmlStrncmp(URI, cur->name, len))) {
1711 case XML_CATA_DELEGATE_URI:
1712 if (!xmlStrncmp(URI, cur->name, xmlStrlen(cur->name)))
1715 case XML_CATA_NEXT_CATALOG:
1723 if (rewrite != NULL) {
1724 if (xmlDebugCatalogs)
1725 xmlGenericError(xmlGenericErrorContext,
1726 "Using rewriting rule %s\n", rewrite->name);
1727 ret = xmlStrdup(rewrite->URL);
1729 ret = xmlStrcat(ret, &URI[lenrewrite]);
1733 const xmlChar *delegates[MAX_DELEGATE];
1737 * Assume the entries have been sorted by decreasing substring
1738 * matches when the list was produced.
1741 while (cur != NULL) {
1742 if (((cur->type == XML_CATA_DELEGATE_SYSTEM) ||
1743 (cur->type == XML_CATA_DELEGATE_URI)) &&
1744 (!xmlStrncmp(URI, cur->name, xmlStrlen(cur->name)))) {
1745 for (i = 0;i < nbList;i++)
1746 if (xmlStrEqual(cur->URL, delegates[i]))
1752 if (nbList < MAX_DELEGATE)
1753 delegates[nbList++] = cur->URL;
1755 if (cur->children == NULL) {
1756 xmlFetchXMLCatalogFile(cur);
1758 if (cur->children != NULL) {
1759 if (xmlDebugCatalogs)
1760 xmlGenericError(xmlGenericErrorContext,
1761 "Trying URI delegate %s\n", cur->URL);
1762 ret = xmlCatalogListXMLResolveURI(
1763 cur->children, URI);
1771 * Apply the cut algorithm explained in 4/
1773 return(XML_CATAL_BREAK);
1777 while (cur != NULL) {
1778 if (cur->type == XML_CATA_NEXT_CATALOG) {
1779 if (cur->children == NULL) {
1780 xmlFetchXMLCatalogFile(cur);
1782 if (cur->children != NULL) {
1783 ret = xmlCatalogListXMLResolveURI(cur->children, URI);
1796 * xmlCatalogListXMLResolve:
1797 * @catal: a catalog list
1798 * @pubId: the public ID string
1799 * @sysId: the system ID string
1801 * Do a complete resolution lookup of an External Identifier for a
1804 * Implements (or tries to) 7.1. External Identifier Resolution
1805 * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
1807 * Returns the URI of the resource or NULL if not found
1810 xmlCatalogListXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,
1811 const xmlChar *sysID) {
1812 xmlChar *ret = NULL;
1813 xmlChar *urnID = NULL;
1817 if ((pubID == NULL) && (sysID == NULL))
1820 if (!xmlStrncmp(pubID, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) {
1821 urnID = xmlCatalogUnWrapURN(pubID);
1822 if (xmlDebugCatalogs) {
1824 xmlGenericError(xmlGenericErrorContext,
1825 "Public URN ID %s expanded to NULL\n", pubID);
1827 xmlGenericError(xmlGenericErrorContext,
1828 "Public URN ID expanded to %s\n", urnID);
1830 ret = xmlCatalogListXMLResolve(catal, urnID, sysID);
1835 if (!xmlStrncmp(sysID, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) {
1836 urnID = xmlCatalogUnWrapURN(sysID);
1837 if (xmlDebugCatalogs) {
1839 xmlGenericError(xmlGenericErrorContext,
1840 "System URN ID %s expanded to NULL\n", sysID);
1842 xmlGenericError(xmlGenericErrorContext,
1843 "System URN ID expanded to %s\n", urnID);
1846 ret = xmlCatalogListXMLResolve(catal, urnID, NULL);
1847 else if (xmlStrEqual(pubID, urnID))
1848 ret = xmlCatalogListXMLResolve(catal, pubID, NULL);
1850 ret = xmlCatalogListXMLResolve(catal, pubID, NULL);
1856 while (catal != NULL) {
1857 if (catal->type == XML_CATA_CATALOG) {
1858 if (catal->children == NULL) {
1859 xmlFetchXMLCatalogFile(catal);
1861 if (catal->children != NULL) {
1862 ret = xmlCatalogXMLResolve(catal->children, pubID, sysID);
1867 catal = catal->next;
1873 * xmlCatalogListXMLResolveURI:
1874 * @catal: a catalog list
1877 * Do a complete resolution lookup of an URI for a list of catalogs
1879 * Implements (or tries to) 7.2. URI Resolution
1880 * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
1882 * Returns the URI of the resource or NULL if not found
1885 xmlCatalogListXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI) {
1886 xmlChar *ret = NULL;
1887 xmlChar *urnID = NULL;
1894 if (!xmlStrncmp(URI, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) {
1895 urnID = xmlCatalogUnWrapURN(URI);
1896 if (xmlDebugCatalogs) {
1898 xmlGenericError(xmlGenericErrorContext,
1899 "URN ID %s expanded to NULL\n", URI);
1901 xmlGenericError(xmlGenericErrorContext,
1902 "URN ID expanded to %s\n", urnID);
1904 ret = xmlCatalogListXMLResolve(catal, urnID, NULL);
1909 while (catal != NULL) {
1910 if (catal->type == XML_CATA_CATALOG) {
1911 if (catal->children == NULL) {
1912 xmlFetchXMLCatalogFile(catal);
1914 if (catal->children != NULL) {
1915 ret = xmlCatalogXMLResolveURI(catal->children, URI);
1920 catal = catal->next;
1925 /************************************************************************
1927 * The SGML Catalog parser *
1929 ************************************************************************/
1934 #define SKIP(x) cur += x;
1936 #define SKIP_BLANKS while (IS_BLANK(*cur)) NEXT;
1939 * xmlParseSGMLCatalogComment:
1940 * @cur: the current character
1942 * Skip a comment in an SGML catalog
1944 * Returns new current character
1946 static const xmlChar *
1947 xmlParseSGMLCatalogComment(const xmlChar *cur) {
1948 if ((cur[0] != '-') || (cur[1] != '-'))
1951 while ((cur[0] != 0) && ((cur[0] != '-') || ((cur[1] != '-'))))
1960 * xmlParseSGMLCatalogPubid:
1961 * @cur: the current character
1962 * @id: the return location
1964 * Parse an SGML catalog ID
1966 * Returns new current character and store the value in @id
1968 static const xmlChar *
1969 xmlParseSGMLCatalogPubid(const xmlChar *cur, xmlChar **id) {
1970 xmlChar *buf = NULL;
1981 } else if (RAW == '\'') {
1987 buf = (xmlChar *) xmlMallocAtomic(size * sizeof(xmlChar));
1989 xmlGenericError(xmlGenericErrorContext,
1990 "malloc of %d byte failed\n", size);
1993 while (xmlIsPubidChar(*cur) || (*cur == '?')) {
1994 if ((*cur == stop) && (stop != ' '))
1996 if ((stop == ' ') && (IS_BLANK(*cur)))
1998 if (len + 1 >= size) {
2000 buf = (xmlChar *) xmlRealloc(buf, size * sizeof(xmlChar));
2002 xmlGenericError(xmlGenericErrorContext,
2003 "realloc of %d byte failed\n", size);
2013 if (!IS_BLANK(*cur)) {
2029 * xmlParseSGMLCatalogName:
2030 * @cur: the current character
2031 * @name: the return location
2033 * Parse an SGML catalog name
2035 * Returns new current character and store the value in @name
2037 static const xmlChar *
2038 xmlParseSGMLCatalogName(const xmlChar *cur, xmlChar **name) {
2039 xmlChar buf[XML_MAX_NAMELEN + 5];
2046 * Handler for more complex cases
2049 if ((!IS_LETTER(c) && (c != '_') && (c != ':'))) {
2053 while (((IS_LETTER(c)) || (IS_DIGIT(c)) ||
2054 (c == '.') || (c == '-') ||
2055 (c == '_') || (c == ':'))) {
2059 if (len >= XML_MAX_NAMELEN)
2062 *name = xmlStrndup(buf, len);
2067 * xmlGetSGMLCatalogEntryType:
2068 * @name: the entry name
2070 * Get the Catalog entry type for a given SGML Catalog name
2072 * Returns Catalog entry type
2074 static xmlCatalogEntryType
2075 xmlGetSGMLCatalogEntryType(const xmlChar *name) {
2076 xmlCatalogEntryType type = XML_CATA_NONE;
2077 if (xmlStrEqual(name, (const xmlChar *) "SYSTEM"))
2078 type = SGML_CATA_SYSTEM;
2079 else if (xmlStrEqual(name, (const xmlChar *) "PUBLIC"))
2080 type = SGML_CATA_PUBLIC;
2081 else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE"))
2082 type = SGML_CATA_DELEGATE;
2083 else if (xmlStrEqual(name, (const xmlChar *) "ENTITY"))
2084 type = SGML_CATA_ENTITY;
2085 else if (xmlStrEqual(name, (const xmlChar *) "DOCTYPE"))
2086 type = SGML_CATA_DOCTYPE;
2087 else if (xmlStrEqual(name, (const xmlChar *) "LINKTYPE"))
2088 type = SGML_CATA_LINKTYPE;
2089 else if (xmlStrEqual(name, (const xmlChar *) "NOTATION"))
2090 type = SGML_CATA_NOTATION;
2091 else if (xmlStrEqual(name, (const xmlChar *) "SGMLDECL"))
2092 type = SGML_CATA_SGMLDECL;
2093 else if (xmlStrEqual(name, (const xmlChar *) "DOCUMENT"))
2094 type = SGML_CATA_DOCUMENT;
2095 else if (xmlStrEqual(name, (const xmlChar *) "CATALOG"))
2096 type = SGML_CATA_CATALOG;
2097 else if (xmlStrEqual(name, (const xmlChar *) "BASE"))
2098 type = SGML_CATA_BASE;
2099 else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE"))
2100 type = SGML_CATA_DELEGATE;
2105 * xmlParseSGMLCatalog:
2106 * @catal: the SGML Catalog
2107 * @value: the content of the SGML Catalog serialization
2108 * @file: the filepath for the catalog
2109 * @super: should this be handled as a Super Catalog in which case
2110 * parsing is not recursive
2112 * Parse an SGML catalog content and fill up the @catal hash table with
2113 * the new entries found.
2115 * Returns 0 in case of success, -1 in case of error.
2118 xmlParseSGMLCatalog(xmlCatalogPtr catal, const xmlChar *value,
2119 const char *file, int super) {
2120 const xmlChar *cur = value;
2121 xmlChar *base = NULL;
2124 if ((cur == NULL) || (file == NULL))
2126 base = xmlStrdup((const xmlChar *) file);
2128 while ((cur != NULL) && (cur[0] != 0)) {
2132 if ((cur[0] == '-') && (cur[1] == '-')) {
2133 cur = xmlParseSGMLCatalogComment(cur);
2139 xmlChar *sysid = NULL;
2140 xmlChar *name = NULL;
2141 xmlCatalogEntryType type = XML_CATA_NONE;
2143 cur = xmlParseSGMLCatalogName(cur, &name);
2148 if (!IS_BLANK(*cur)) {
2153 if (xmlStrEqual(name, (const xmlChar *) "SYSTEM"))
2154 type = SGML_CATA_SYSTEM;
2155 else if (xmlStrEqual(name, (const xmlChar *) "PUBLIC"))
2156 type = SGML_CATA_PUBLIC;
2157 else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE"))
2158 type = SGML_CATA_DELEGATE;
2159 else if (xmlStrEqual(name, (const xmlChar *) "ENTITY"))
2160 type = SGML_CATA_ENTITY;
2161 else if (xmlStrEqual(name, (const xmlChar *) "DOCTYPE"))
2162 type = SGML_CATA_DOCTYPE;
2163 else if (xmlStrEqual(name, (const xmlChar *) "LINKTYPE"))
2164 type = SGML_CATA_LINKTYPE;
2165 else if (xmlStrEqual(name, (const xmlChar *) "NOTATION"))
2166 type = SGML_CATA_NOTATION;
2167 else if (xmlStrEqual(name, (const xmlChar *) "SGMLDECL"))
2168 type = SGML_CATA_SGMLDECL;
2169 else if (xmlStrEqual(name, (const xmlChar *) "DOCUMENT"))
2170 type = SGML_CATA_DOCUMENT;
2171 else if (xmlStrEqual(name, (const xmlChar *) "CATALOG"))
2172 type = SGML_CATA_CATALOG;
2173 else if (xmlStrEqual(name, (const xmlChar *) "BASE"))
2174 type = SGML_CATA_BASE;
2175 else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE"))
2176 type = SGML_CATA_DELEGATE;
2177 else if (xmlStrEqual(name, (const xmlChar *) "OVERRIDE")) {
2179 cur = xmlParseSGMLCatalogName(cur, &name);
2191 case SGML_CATA_ENTITY:
2193 type = SGML_CATA_PENTITY;
2194 case SGML_CATA_PENTITY:
2195 case SGML_CATA_DOCTYPE:
2196 case SGML_CATA_LINKTYPE:
2197 case SGML_CATA_NOTATION:
2198 cur = xmlParseSGMLCatalogName(cur, &name);
2203 if (!IS_BLANK(*cur)) {
2208 cur = xmlParseSGMLCatalogPubid(cur, &sysid);
2214 case SGML_CATA_PUBLIC:
2215 case SGML_CATA_SYSTEM:
2216 case SGML_CATA_DELEGATE:
2217 cur = xmlParseSGMLCatalogPubid(cur, &name);
2222 if (!IS_BLANK(*cur)) {
2227 cur = xmlParseSGMLCatalogPubid(cur, &sysid);
2233 case SGML_CATA_BASE:
2234 case SGML_CATA_CATALOG:
2235 case SGML_CATA_DOCUMENT:
2236 case SGML_CATA_SGMLDECL:
2237 cur = xmlParseSGMLCatalogPubid(cur, &sysid);
2252 } else if (type == SGML_CATA_BASE) {
2255 base = xmlStrdup(sysid);
2256 } else if ((type == SGML_CATA_PUBLIC) ||
2257 (type == SGML_CATA_SYSTEM)) {
2260 filename = xmlBuildURI(sysid, base);
2261 if (filename != NULL) {
2262 xmlCatalogEntryPtr entry;
2264 entry = xmlNewCatalogEntry(type, name, filename,
2265 NULL, XML_CATA_PREFER_NONE);
2266 res = xmlHashAddEntry(catal->sgml, name, entry);
2268 xmlFreeCatalogEntry(entry);
2273 } else if (type == SGML_CATA_CATALOG) {
2275 xmlCatalogEntryPtr entry;
2277 entry = xmlNewCatalogEntry(type, sysid, NULL, NULL,
2278 XML_CATA_PREFER_NONE);
2279 res = xmlHashAddEntry(catal->sgml, sysid, entry);
2281 xmlFreeCatalogEntry(entry);
2286 filename = xmlBuildURI(sysid, base);
2287 if (filename != NULL) {
2288 xmlExpandCatalog(catal, (const char *)filename);
2294 * drop anything else we won't handle it
2309 /************************************************************************
2311 * SGML Catalog handling *
2313 ************************************************************************/
2316 * xmlCatalogGetSGMLPublic:
2317 * @catal: an SGML catalog hash
2318 * @pubId: the public ID string
2320 * Try to lookup the system ID associated to a public ID
2322 * Returns the system ID if found or NULL otherwise.
2324 static const xmlChar *
2325 xmlCatalogGetSGMLPublic(xmlHashTablePtr catal, const xmlChar *pubID) {
2326 xmlCatalogEntryPtr entry;
2331 entry = (xmlCatalogEntryPtr) xmlHashLookup(catal, pubID);
2334 if (entry->type == SGML_CATA_PUBLIC)
2340 * xmlCatalogGetSGMLSystem:
2341 * @catal: an SGML catalog hash
2342 * @sysId: the public ID string
2344 * Try to lookup the catalog local reference for a system ID
2346 * Returns the system ID if found or NULL otherwise.
2348 static const xmlChar *
2349 xmlCatalogGetSGMLSystem(xmlHashTablePtr catal, const xmlChar *sysID) {
2350 xmlCatalogEntryPtr entry;
2355 entry = (xmlCatalogEntryPtr) xmlHashLookup(catal, sysID);
2358 if (entry->type == SGML_CATA_SYSTEM)
2364 * xmlCatalogSGMLResolve:
2365 * @catal: the SGML catalog
2366 * @pubId: the public ID string
2367 * @sysId: the system ID string
2369 * Do a complete resolution lookup of an External Identifier
2371 * Returns the URI of the resource or NULL if not found
2373 static const xmlChar *
2374 xmlCatalogSGMLResolve(xmlCatalogPtr catal, const xmlChar *pubID,
2375 const xmlChar *sysID) {
2376 const xmlChar *ret = NULL;
2378 if (catal->sgml == NULL)
2382 ret = xmlCatalogGetSGMLPublic(catal->sgml, pubID);
2386 ret = xmlCatalogGetSGMLSystem(catal->sgml, sysID);
2390 /************************************************************************
2392 * Specific Public interfaces *
2394 ************************************************************************/
2397 * xmlLoadSGMLSuperCatalog:
2398 * @filename: a file path
2400 * Load an SGML super catalog. It won't expand CATALOG or DELEGATE
2401 * references. This is only needed for manipulating SGML Super Catalogs
2402 * like adding and removing CATALOG or DELEGATE entries.
2404 * Returns the catalog parsed or NULL in case of error
2407 xmlLoadSGMLSuperCatalog(const char *filename)
2410 xmlCatalogPtr catal;
2413 content = xmlLoadFileContent(filename);
2414 if (content == NULL)
2417 catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE, xmlCatalogDefaultPrefer);
2418 if (catal == NULL) {
2423 ret = xmlParseSGMLCatalog(catal, content, filename, 1);
2426 xmlFreeCatalog(catal);
2434 * @filename: a file path
2436 * Load the catalog and build the associated data structures.
2437 * This can be either an XML Catalog or an SGML Catalog
2438 * It will recurse in SGML CATALOG entries. On the other hand XML
2439 * Catalogs are not handled recursively.
2441 * Returns the catalog parsed or NULL in case of error
2444 xmlLoadACatalog(const char *filename)
2448 xmlCatalogPtr catal;
2451 content = xmlLoadFileContent(filename);
2452 if (content == NULL)
2458 while ((*first != 0) && (*first != '-') && (*first != '<') &&
2459 (!(((*first >= 'A') && (*first <= 'Z')) ||
2460 ((*first >= 'a') && (*first <= 'z')))))
2463 if (*first != '<') {
2464 catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE, xmlCatalogDefaultPrefer);
2465 if (catal == NULL) {
2469 ret = xmlParseSGMLCatalog(catal, content, filename, 0);
2471 xmlFreeCatalog(catal);
2476 catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE, xmlCatalogDefaultPrefer);
2477 if (catal == NULL) {
2481 catal->xml = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
2482 NULL, BAD_CAST filename, xmlCatalogDefaultPrefer);
2491 * @filename: a file path
2493 * Load the catalog and expand the existing catal structure.
2494 * This can be either an XML Catalog or an SGML Catalog
2496 * Returns 0 in case of success, -1 in case of error
2499 xmlExpandCatalog(xmlCatalogPtr catal, const char *filename)
2503 if ((catal == NULL) || (filename == NULL))
2507 if (catal->type == XML_SGML_CATALOG_TYPE) {
2510 content = xmlLoadFileContent(filename);
2511 if (content == NULL)
2514 ret = xmlParseSGMLCatalog(catal, content, filename, 0);
2521 xmlCatalogEntryPtr tmp, cur;
2522 tmp = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
2523 NULL, BAD_CAST filename, xmlCatalogDefaultPrefer);
2529 while (cur->next != NULL) cur = cur->next;
2537 * xmlACatalogResolveSystem:
2539 * @sysID: the public ID string
2541 * Try to lookup the catalog resource for a system ID
2543 * Returns the system ID if found or NULL otherwise, the value returned
2544 * must be freed by the caller.
2547 xmlACatalogResolveSystem(xmlCatalogPtr catal, const xmlChar *sysID) {
2548 xmlChar *ret = NULL;
2550 if ((sysID == NULL) || (catal == NULL))
2553 if (xmlDebugCatalogs)
2554 xmlGenericError(xmlGenericErrorContext,
2555 "Resolve sysID %s\n", sysID);
2557 if (catal->type == XML_XML_CATALOG_TYPE) {
2558 ret = xmlCatalogListXMLResolve(catal->xml, NULL, sysID);
2559 if (ret == XML_CATAL_BREAK)
2562 const xmlChar *sgml;
2564 sgml = xmlCatalogGetSGMLSystem(catal->sgml, sysID);
2566 ret = xmlStrdup(sgml);
2572 * xmlACatalogResolvePublic:
2574 * @pubID: the public ID string
2576 * Try to lookup the system ID associated to a public ID in that catalog
2578 * Returns the system ID if found or NULL otherwise, the value returned
2579 * must be freed by the caller.
2582 xmlACatalogResolvePublic(xmlCatalogPtr catal, const xmlChar *pubID) {
2583 xmlChar *ret = NULL;
2585 if ((pubID == NULL) || (catal == NULL))
2588 if (xmlDebugCatalogs)
2589 xmlGenericError(xmlGenericErrorContext,
2590 "Resolve pubID %s\n", pubID);
2592 if (catal->type == XML_XML_CATALOG_TYPE) {
2593 ret = xmlCatalogListXMLResolve(catal->xml, pubID, NULL);
2594 if (ret == XML_CATAL_BREAK)
2597 const xmlChar *sgml;
2599 sgml = xmlCatalogGetSGMLPublic(catal->sgml, pubID);
2601 ret = xmlStrdup(sgml);
2607 * xmlACatalogResolve:
2609 * @pubID: the public ID string
2610 * @sysID: the system ID string
2612 * Do a complete resolution lookup of an External Identifier
2614 * Returns the URI of the resource or NULL if not found, it must be freed
2618 xmlACatalogResolve(xmlCatalogPtr catal, const xmlChar * pubID,
2619 const xmlChar * sysID)
2621 xmlChar *ret = NULL;
2623 if (((pubID == NULL) && (sysID == NULL)) || (catal == NULL))
2626 if (xmlDebugCatalogs) {
2627 if (pubID != NULL) {
2628 xmlGenericError(xmlGenericErrorContext,
2629 "Resolve: pubID %s\n", pubID);
2631 xmlGenericError(xmlGenericErrorContext,
2632 "Resolve: sysID %s\n", sysID);
2636 if (catal->type == XML_XML_CATALOG_TYPE) {
2637 ret = xmlCatalogListXMLResolve(catal->xml, pubID, sysID);
2638 if (ret == XML_CATAL_BREAK)
2641 const xmlChar *sgml;
2643 sgml = xmlCatalogSGMLResolve(catal, pubID, sysID);
2645 ret = xmlStrdup(sgml);
2651 * xmlACatalogResolveURI:
2655 * Do a complete resolution lookup of an URI
2657 * Returns the URI of the resource or NULL if not found, it must be freed
2661 xmlACatalogResolveURI(xmlCatalogPtr catal, const xmlChar *URI) {
2662 xmlChar *ret = NULL;
2664 if ((URI == NULL) || (catal == NULL))
2667 if (xmlDebugCatalogs)
2668 xmlGenericError(xmlGenericErrorContext,
2669 "Resolve URI %s\n", URI);
2671 if (catal->type == XML_XML_CATALOG_TYPE) {
2672 ret = xmlCatalogListXMLResolveURI(catal->xml, URI);
2673 if (ret == XML_CATAL_BREAK)
2676 const xmlChar *sgml;
2678 sgml = xmlCatalogSGMLResolve(catal, NULL, URI);
2680 sgml = xmlStrdup(sgml);
2690 * Free up all the memory associated with catalogs
2693 xmlACatalogDump(xmlCatalogPtr catal, FILE *out) {
2694 if ((out == NULL) || (catal == NULL))
2697 if (catal->type == XML_XML_CATALOG_TYPE) {
2698 xmlDumpXMLCatalog(out, catal->xml);
2700 xmlHashScan(catal->sgml,
2701 (xmlHashScanner) xmlCatalogDumpEntry, out);
2708 * @type: the type of record to add to the catalog
2709 * @orig: the system, public or prefix to match
2710 * @replace: the replacement value for the match
2712 * Add an entry in the catalog, it may overwrite existing but
2713 * different entries.
2715 * Returns 0 if successful, -1 otherwise
2718 xmlACatalogAdd(xmlCatalogPtr catal, const xmlChar * type,
2719 const xmlChar * orig, const xmlChar * replace)
2726 if (catal->type == XML_XML_CATALOG_TYPE) {
2727 res = xmlAddXMLCatalog(catal->xml, type, orig, replace);
2729 xmlCatalogEntryType cattype;
2731 cattype = xmlGetSGMLCatalogEntryType(type);
2732 if (cattype != XML_CATA_NONE) {
2733 xmlCatalogEntryPtr entry;
2735 entry = xmlNewCatalogEntry(cattype, orig, replace, NULL,
2736 XML_CATA_PREFER_NONE);
2737 if (catal->sgml == NULL)
2738 catal->sgml = xmlHashCreate(10);
2739 res = xmlHashAddEntry(catal->sgml, orig, entry);
2746 * xmlACatalogRemove:
2748 * @value: the value to remove
2750 * Remove an entry from the catalog
2752 * Returns the number of entries removed if successful, -1 otherwise
2755 xmlACatalogRemove(xmlCatalogPtr catal, const xmlChar *value) {
2758 if ((catal == NULL) || (value == NULL))
2761 if (catal->type == XML_XML_CATALOG_TYPE) {
2762 res = xmlDelXMLCatalog(catal->xml, value);
2764 res = xmlHashRemoveEntry(catal->sgml, value,
2765 (xmlHashDeallocator) xmlFreeCatalogEntry);
2774 * @sgml: should this create an SGML catalog
2776 * create a new Catalog.
2778 * Returns the xmlCatalogPtr or NULL in case of error
2781 xmlNewCatalog(int sgml) {
2782 xmlCatalogPtr catal = NULL;
2785 catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE,
2786 xmlCatalogDefaultPrefer);
2787 if ((catal != NULL) && (catal->sgml == NULL))
2788 catal->sgml = xmlHashCreate(10);
2790 catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE,
2791 xmlCatalogDefaultPrefer);
2796 * xmlCatalogIsEmpty:
2797 * @catal: should this create an SGML catalog
2799 * Check is a catalog is empty
2801 * Returns 1 if the catalog is empty, 0 if not, amd -1 in case of error.
2804 xmlCatalogIsEmpty(xmlCatalogPtr catal) {
2808 if (catal->type == XML_XML_CATALOG_TYPE) {
2809 if (catal->xml == NULL)
2811 if ((catal->xml->type != XML_CATA_CATALOG) &&
2812 (catal->xml->type != XML_CATA_BROKEN_CATALOG))
2814 if (catal->xml->children == NULL)
2820 if (catal->sgml == NULL)
2822 res = xmlHashSize(catal->sgml);
2831 /************************************************************************
2833 * Public interfaces manipulating the global shared default catalog *
2835 ************************************************************************/
2838 * xmlInitializeCatalogData:
2840 * Do the catalog initialization only of global data, doesn't try to load
2841 * any catalog actually.
2842 * this function is not thread safe, catalog initialization should
2843 * preferably be done once at startup
2846 xmlInitializeCatalogData(void) {
2847 if (xmlCatalogInitialized != 0)
2850 if (getenv("XML_DEBUG_CATALOG"))
2851 xmlDebugCatalogs = 1;
2852 xmlCatalogMutex = xmlNewRMutex();
2854 xmlCatalogInitialized = 1;
2857 * xmlInitializeCatalog:
2859 * Do the catalog initialization.
2860 * this function is not thread safe, catalog initialization should
2861 * preferably be done once at startup
2864 xmlInitializeCatalog(void) {
2865 if (xmlCatalogInitialized != 0)
2868 xmlInitializeCatalogData();
2869 xmlRMutexLock(xmlCatalogMutex);
2871 if (getenv("XML_DEBUG_CATALOG"))
2872 xmlDebugCatalogs = 1;
2874 if (xmlDefaultCatalog == NULL) {
2875 const char *catalogs;
2877 const char *cur, *paths;
2878 xmlCatalogPtr catal;
2879 xmlCatalogEntryPtr *nextent;
2881 catalogs = (const char *) getenv("XML_CATALOG_FILES");
2882 if (catalogs == NULL)
2883 catalogs = XML_XML_DEFAULT_CATALOG;
2885 catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE,
2886 xmlCatalogDefaultPrefer);
2887 if (catal != NULL) {
2888 /* the XML_CATALOG_FILES envvar is allowed to contain a
2889 space-separated list of entries. */
2891 nextent = &catal->xml;
2892 while (*cur != '\0') {
2893 while (IS_BLANK(*cur))
2897 while ((*cur != 0) && (!IS_BLANK(*cur)))
2899 path = (char *) xmlStrndup((const xmlChar *)paths, cur - paths);
2901 *nextent = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
2902 NULL, BAD_CAST path, xmlCatalogDefaultPrefer);
2903 if (*nextent != NULL)
2904 nextent = &((*nextent)->next);
2909 xmlDefaultCatalog = catal;
2913 xmlRMutexUnlock(xmlCatalogMutex);
2919 * @filename: a file path
2921 * Load the catalog and makes its definitions effective for the default
2922 * external entity loader. It will recurse in SGML CATALOG entries.
2923 * this function is not thread safe, catalog initialization should
2924 * preferably be done once at startup
2926 * Returns 0 in case of success -1 in case of error
2929 xmlLoadCatalog(const char *filename)
2932 xmlCatalogPtr catal;
2934 if (!xmlCatalogInitialized)
2935 xmlInitializeCatalogData();
2937 xmlRMutexLock(xmlCatalogMutex);
2939 if (xmlDefaultCatalog == NULL) {
2940 catal = xmlLoadACatalog(filename);
2941 if (catal == NULL) {
2942 xmlRMutexUnlock(xmlCatalogMutex);
2946 xmlDefaultCatalog = catal;
2947 xmlRMutexUnlock(xmlCatalogMutex);
2951 ret = xmlExpandCatalog(xmlDefaultCatalog, filename);
2952 xmlRMutexUnlock(xmlCatalogMutex);
2958 * @pathss: a list of directories separated by a colon or a space.
2960 * Load the catalogs and makes their definitions effective for the default
2961 * external entity loader.
2962 * this function is not thread safe, catalog initialization should
2963 * preferably be done once at startup
2966 xmlLoadCatalogs(const char *pathss) {
2975 while ((cur != NULL) && (*cur != 0)) {
2976 while (IS_BLANK(*cur)) cur++;
2979 while ((*cur != 0) && (*cur != ':') && (!IS_BLANK(*cur)))
2981 path = xmlStrndup((const xmlChar *)paths, cur - paths);
2983 xmlLoadCatalog((const char *) path);
2993 * xmlCatalogCleanup:
2995 * Free up all the memory associated with catalogs
2998 xmlCatalogCleanup(void) {
2999 if (xmlCatalogInitialized == 0)
3002 xmlRMutexLock(xmlCatalogMutex);
3003 if (xmlDebugCatalogs)
3004 xmlGenericError(xmlGenericErrorContext,
3005 "Catalogs cleanup\n");
3006 if (xmlCatalogXMLFiles != NULL)
3007 xmlHashFree(xmlCatalogXMLFiles,
3008 (xmlHashDeallocator)xmlFreeCatalogHashEntryList);
3009 xmlCatalogXMLFiles = NULL;
3010 if (xmlDefaultCatalog != NULL)
3011 xmlFreeCatalog(xmlDefaultCatalog);
3012 xmlDefaultCatalog = NULL;
3013 xmlDebugCatalogs = 0;
3014 xmlCatalogInitialized = 0;
3015 xmlRMutexUnlock(xmlCatalogMutex);
3016 xmlFreeRMutex(xmlCatalogMutex);
3020 * xmlCatalogResolveSystem:
3021 * @sysID: the public ID string
3023 * Try to lookup the catalog resource for a system ID
3025 * Returns the system ID if found or NULL otherwise, the value returned
3026 * must be freed by the caller.
3029 xmlCatalogResolveSystem(const xmlChar *sysID) {
3032 if (!xmlCatalogInitialized)
3033 xmlInitializeCatalog();
3035 ret = xmlACatalogResolveSystem(xmlDefaultCatalog, sysID);
3040 * xmlCatalogResolvePublic:
3041 * @pubID: the public ID string
3043 * Try to lookup the system ID associated to a public ID
3045 * Returns the system ID if found or NULL otherwise, the value returned
3046 * must be freed by the caller.
3049 xmlCatalogResolvePublic(const xmlChar *pubID) {
3052 if (!xmlCatalogInitialized)
3053 xmlInitializeCatalog();
3055 ret = xmlACatalogResolvePublic(xmlDefaultCatalog, pubID);
3060 * xmlCatalogResolve:
3061 * @pubID: the public ID string
3062 * @sysID: the system ID string
3064 * Do a complete resolution lookup of an External Identifier
3066 * Returns the URI of the resource or NULL if not found, it must be freed
3070 xmlCatalogResolve(const xmlChar *pubID, const xmlChar *sysID) {
3073 if (!xmlCatalogInitialized)
3074 xmlInitializeCatalog();
3076 ret = xmlACatalogResolve(xmlDefaultCatalog, pubID, sysID);
3081 * xmlCatalogResolveURI:
3084 * Do a complete resolution lookup of an URI
3086 * Returns the URI of the resource or NULL if not found, it must be freed
3090 xmlCatalogResolveURI(const xmlChar *URI) {
3093 if (!xmlCatalogInitialized)
3094 xmlInitializeCatalog();
3096 ret = xmlACatalogResolveURI(xmlDefaultCatalog, URI);
3104 * Free up all the memory associated with catalogs
3107 xmlCatalogDump(FILE *out) {
3111 if (!xmlCatalogInitialized)
3112 xmlInitializeCatalog();
3114 xmlACatalogDump(xmlDefaultCatalog, out);
3119 * @type: the type of record to add to the catalog
3120 * @orig: the system, public or prefix to match
3121 * @replace: the replacement value for the match
3123 * Add an entry in the catalog, it may overwrite existing but
3124 * different entries.
3125 * If called before any other catalog routine, allows to override the
3126 * default shared catalog put in place by xmlInitializeCatalog();
3128 * Returns 0 if successful, -1 otherwise
3131 xmlCatalogAdd(const xmlChar *type, const xmlChar *orig, const xmlChar *replace) {
3134 if (!xmlCatalogInitialized)
3135 xmlInitializeCatalogData();
3137 xmlRMutexLock(xmlCatalogMutex);
3139 * Specific case where one want to override the default catalog
3140 * put in place by xmlInitializeCatalog();
3142 if ((xmlDefaultCatalog == NULL) &&
3143 (xmlStrEqual(type, BAD_CAST "catalog"))) {
3144 xmlDefaultCatalog = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE,
3145 xmlCatalogDefaultPrefer);
3146 xmlDefaultCatalog->xml = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
3147 orig, NULL, xmlCatalogDefaultPrefer);
3149 xmlRMutexUnlock(xmlCatalogMutex);
3153 res = xmlACatalogAdd(xmlDefaultCatalog, type, orig, replace);
3154 xmlRMutexUnlock(xmlCatalogMutex);
3160 * @value: the value to remove
3162 * Remove an entry from the catalog
3164 * Returns the number of entries removed if successful, -1 otherwise
3167 xmlCatalogRemove(const xmlChar *value) {
3170 if (!xmlCatalogInitialized)
3171 xmlInitializeCatalog();
3173 xmlRMutexLock(xmlCatalogMutex);
3174 res = xmlACatalogRemove(xmlDefaultCatalog, value);
3175 xmlRMutexUnlock(xmlCatalogMutex);
3180 * xmlCatalogConvert:
3182 * Convert all the SGML catalog entries as XML ones
3184 * Returns the number of entries converted if successful, -1 otherwise
3187 xmlCatalogConvert(void) {
3190 if (!xmlCatalogInitialized)
3191 xmlInitializeCatalog();
3193 xmlRMutexLock(xmlCatalogMutex);
3194 res = xmlConvertSGMLCatalog(xmlDefaultCatalog);
3195 xmlRMutexUnlock(xmlCatalogMutex);
3199 /************************************************************************
3201 * Public interface manipulating the common preferences *
3203 ************************************************************************/
3206 * xmlCatalogGetDefaults:
3208 * Used to get the user preference w.r.t. to what catalogs should
3211 * Returns the current xmlCatalogAllow value
3214 xmlCatalogGetDefaults(void) {
3215 return(xmlCatalogDefaultAllow);
3219 * xmlCatalogSetDefaults:
3220 * @allow: what catalogs should be accepted
3222 * Used to set the user preference w.r.t. to what catalogs should
3226 xmlCatalogSetDefaults(xmlCatalogAllow allow) {
3227 if (xmlDebugCatalogs) {
3229 case XML_CATA_ALLOW_NONE:
3230 xmlGenericError(xmlGenericErrorContext,
3231 "Disabling catalog usage\n");
3233 case XML_CATA_ALLOW_GLOBAL:
3234 xmlGenericError(xmlGenericErrorContext,
3235 "Allowing only global catalogs\n");
3237 case XML_CATA_ALLOW_DOCUMENT:
3238 xmlGenericError(xmlGenericErrorContext,
3239 "Allowing only catalogs from the document\n");
3241 case XML_CATA_ALLOW_ALL:
3242 xmlGenericError(xmlGenericErrorContext,
3243 "Allowing all catalogs\n");
3247 xmlCatalogDefaultAllow = allow;
3251 * xmlCatalogSetDefaultPrefer:
3252 * @prefer: the default preference for delegation
3254 * Allows to set the preference between public and system for deletion
3255 * in XML Catalog resolution. C.f. section 4.1.1 of the spec
3256 * Values accepted are XML_CATA_PREFER_PUBLIC or XML_CATA_PREFER_SYSTEM
3258 * Returns the previous value of the default preference for delegation
3261 xmlCatalogSetDefaultPrefer(xmlCatalogPrefer prefer) {
3262 xmlCatalogPrefer ret = xmlCatalogDefaultPrefer;
3264 if (prefer == XML_CATA_PREFER_NONE)
3267 if (xmlDebugCatalogs) {
3269 case XML_CATA_PREFER_PUBLIC:
3270 xmlGenericError(xmlGenericErrorContext,
3271 "Setting catalog preference to PUBLIC\n");
3273 case XML_CATA_PREFER_SYSTEM:
3274 xmlGenericError(xmlGenericErrorContext,
3275 "Setting catalog preference to SYSTEM\n");
3277 case XML_CATA_PREFER_NONE:
3281 xmlCatalogDefaultPrefer = prefer;
3286 * xmlCatalogSetDebug:
3287 * @level: the debug level of catalogs required
3289 * Used to set the debug level for catalog operation, 0 disable
3290 * debugging, 1 enable it
3292 * Returns the previous value of the catalog debugging level
3295 xmlCatalogSetDebug(int level) {
3296 int ret = xmlDebugCatalogs;
3299 xmlDebugCatalogs = 0;
3301 xmlDebugCatalogs = level;
3305 /************************************************************************
3307 * Minimal interfaces used for per-document catalogs by the parser *
3309 ************************************************************************/
3312 * xmlCatalogFreeLocal:
3313 * @catalogs: a document's list of catalogs
3315 * Free up the memory associated to the catalog list
3318 xmlCatalogFreeLocal(void *catalogs) {
3319 xmlCatalogEntryPtr catal;
3321 if (!xmlCatalogInitialized)
3322 xmlInitializeCatalog();
3324 catal = (xmlCatalogEntryPtr) catalogs;
3326 xmlFreeCatalogEntryList(catal);
3331 * xmlCatalogAddLocal:
3332 * @catalogs: a document's list of catalogs
3333 * @URL: the URL to a new local catalog
3335 * Add the new entry to the catalog list
3337 * Returns the updated list
3340 xmlCatalogAddLocal(void *catalogs, const xmlChar *URL) {
3341 xmlCatalogEntryPtr catal, add;
3343 if (!xmlCatalogInitialized)
3344 xmlInitializeCatalog();
3349 if (xmlDebugCatalogs)
3350 xmlGenericError(xmlGenericErrorContext,
3351 "Adding document catalog %s\n", URL);
3353 add = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL, URL, NULL,
3354 xmlCatalogDefaultPrefer);
3358 catal = (xmlCatalogEntryPtr) catalogs;
3360 return((void *) add);
3362 while (catal->next != NULL)
3363 catal = catal->next;
3369 * xmlCatalogLocalResolve:
3370 * @catalogs: a document's list of catalogs
3371 * @pubID: the public ID string
3372 * @sysID: the system ID string
3374 * Do a complete resolution lookup of an External Identifier using a
3375 * document's private catalog list
3377 * Returns the URI of the resource or NULL if not found, it must be freed
3381 xmlCatalogLocalResolve(void *catalogs, const xmlChar *pubID,
3382 const xmlChar *sysID) {
3383 xmlCatalogEntryPtr catal;
3386 if (!xmlCatalogInitialized)
3387 xmlInitializeCatalog();
3389 if ((pubID == NULL) && (sysID == NULL))
3392 if (xmlDebugCatalogs) {
3393 if (pubID != NULL) {
3394 xmlGenericError(xmlGenericErrorContext,
3395 "Local resolve: pubID %s\n", pubID);
3397 xmlGenericError(xmlGenericErrorContext,
3398 "Local resolve: sysID %s\n", sysID);
3402 catal = (xmlCatalogEntryPtr) catalogs;
3405 ret = xmlCatalogListXMLResolve(catal, pubID, sysID);
3406 if ((ret != NULL) && (ret != XML_CATAL_BREAK))
3412 * xmlCatalogLocalResolveURI:
3413 * @catalogs: a document's list of catalogs
3416 * Do a complete resolution lookup of an URI using a
3417 * document's private catalog list
3419 * Returns the URI of the resource or NULL if not found, it must be freed
3423 xmlCatalogLocalResolveURI(void *catalogs, const xmlChar *URI) {
3424 xmlCatalogEntryPtr catal;
3427 if (!xmlCatalogInitialized)
3428 xmlInitializeCatalog();
3433 if (xmlDebugCatalogs)
3434 xmlGenericError(xmlGenericErrorContext,
3435 "Resolve URI %s\n", URI);
3437 catal = (xmlCatalogEntryPtr) catalogs;
3440 ret = xmlCatalogListXMLResolveURI(catal, URI);
3441 if ((ret != NULL) && (ret != XML_CATAL_BREAK))
3446 /************************************************************************
3448 * Deprecated interfaces *
3450 ************************************************************************/
3452 * xmlCatalogGetSystem:
3453 * @sysID: the system ID string
3455 * Try to lookup the system ID associated to a public ID
3456 * DEPRECATED, use xmlCatalogResolveSystem()
3458 * Returns the system ID if found or NULL otherwise.
3461 xmlCatalogGetSystem(const xmlChar *sysID) {
3463 static xmlChar result[1000];
3466 if (!xmlCatalogInitialized)
3467 xmlInitializeCatalog();
3470 xmlGenericError(xmlGenericErrorContext,
3471 "Use of deprecated xmlCatalogGetSystem() call\n");
3479 * Check first the XML catalogs
3481 if (xmlDefaultCatalog != NULL) {
3482 ret = xmlCatalogListXMLResolve(xmlDefaultCatalog->xml, NULL, sysID);
3483 if ((ret != NULL) && (ret != XML_CATAL_BREAK)) {
3484 snprintf((char *) result, sizeof(result) - 1, "%s", (char *) ret);
3485 result[sizeof(result) - 1] = 0;
3490 if (xmlDefaultCatalog != NULL)
3491 return(xmlCatalogGetSGMLSystem(xmlDefaultCatalog->sgml, sysID));
3496 * xmlCatalogGetPublic:
3497 * @pubID: the public ID string
3499 * Try to lookup the system ID associated to a public ID
3500 * DEPRECATED, use xmlCatalogResolvePublic()
3502 * Returns the system ID if found or NULL otherwise.
3505 xmlCatalogGetPublic(const xmlChar *pubID) {
3507 static xmlChar result[1000];
3510 if (!xmlCatalogInitialized)
3511 xmlInitializeCatalog();
3514 xmlGenericError(xmlGenericErrorContext,
3515 "Use of deprecated xmlCatalogGetPublic() call\n");
3523 * Check first the XML catalogs
3525 if (xmlDefaultCatalog != NULL) {
3526 ret = xmlCatalogListXMLResolve(xmlDefaultCatalog->xml, pubID, NULL);
3527 if ((ret != NULL) && (ret != XML_CATAL_BREAK)) {
3528 snprintf((char *) result, sizeof(result) - 1, "%s", (char *) ret);
3529 result[sizeof(result) - 1] = 0;
3534 if (xmlDefaultCatalog != NULL)
3535 return(xmlCatalogGetSGMLPublic(xmlDefaultCatalog->sgml, pubID));
3539 #endif /* LIBXML_CATALOG_ENABLED */