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
49 * macro to flag unimplemented blocks
52 xmlGenericError(xmlGenericErrorContext, \
53 "Unimplemented block at %s:%d\n", \
56 #define XML_URN_PUBID "urn:publicid:"
57 #define XML_CATAL_BREAK ((xmlChar *) -1)
58 #ifndef XML_XML_DEFAULT_CATALOG
59 #define XML_XML_DEFAULT_CATALOG "file:///etc/xml/catalog"
61 #ifndef XML_SGML_DEFAULT_CATALOG
62 #define XML_SGML_DEFAULT_CATALOG "file:///etc/sgml/catalog"
65 static int xmlExpandCatalog(xmlCatalogPtr catal, const char *filename);
67 /************************************************************************
69 * Types, all private *
71 ************************************************************************/
74 XML_CATA_REMOVED = -1,
77 XML_CATA_BROKEN_CATALOG,
78 XML_CATA_NEXT_CATALOG,
81 XML_CATA_REWRITE_SYSTEM,
82 XML_CATA_DELEGATE_PUBLIC,
83 XML_CATA_DELEGATE_SYSTEM,
86 XML_CATA_DELEGATE_URI,
99 } xmlCatalogEntryType;
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;
110 xmlChar *URL; /* The expanded URL using the base */
111 xmlCatalogPrefer prefer;
116 XML_XML_CATALOG_TYPE = 1,
117 XML_SGML_CATALOG_TYPE
120 #define XML_MAX_SGML_CATA_DEPTH 10
122 xmlCatalogType type; /* either XML or SGML */
125 * SGML Catalogs are stored as a simple hash table of catalog entries
126 * Catalog stack to check against overflows when building the
129 char *catalTab[XML_MAX_SGML_CATA_DEPTH]; /* stack of catals */
130 int catalNr; /* Number of current catal streams */
131 int catalMax; /* Max number of catal streams */
132 xmlHashTablePtr sgml;
135 * XML Catalogs are stored as a tree of Catalog entries
137 xmlCatalogPrefer prefer;
138 xmlCatalogEntryPtr xml;
141 /************************************************************************
145 ************************************************************************/
148 * Those are preferences
150 static int xmlDebugCatalogs = 0; /* used for debugging */
151 static xmlCatalogAllow xmlCatalogDefaultAllow = XML_CATA_ALLOW_ALL;
152 static xmlCatalogPrefer xmlCatalogDefaultPrefer = XML_CATA_PREFER_PUBLIC;
155 * Hash table containing all the trees of XML catalogs parsed by
158 static xmlHashTablePtr xmlCatalogXMLFiles = NULL;
161 * The default catalog in use by the application
163 static xmlCatalogPtr xmlDefaultCatalog = NULL;
166 * A mutex for modifying the shared global catalog(s)
167 * xmlDefaultCatalog tree.
168 * It also protects xmlCatalogXMLFiles
169 * The core of this readers/writer scheme is in xmlFetchXMLCatalogFile()
171 static xmlRMutexPtr xmlCatalogMutex = NULL;
174 * Whether the catalog support was initialized.
176 static int xmlCatalogInitialized = 0;
179 /************************************************************************
181 * Allocation and Freeing *
183 ************************************************************************/
186 * xmlNewCatalogEntry:
187 * @type: type of entry
188 * @name: name of the entry
189 * @value: value of the entry
190 * @prefer: the PUBLIC vs. SYSTEM current preference value
192 * create a new Catalog entry, this type is shared both by XML and
193 * SGML catalogs, but the acceptable types values differs.
195 * Returns the xmlCatalogEntryPtr or NULL in case of error
197 static xmlCatalogEntryPtr
198 xmlNewCatalogEntry(xmlCatalogEntryType type, const xmlChar *name,
199 const xmlChar *value, const xmlChar *URL, xmlCatalogPrefer prefer) {
200 xmlCatalogEntryPtr ret;
202 ret = (xmlCatalogEntryPtr) xmlMalloc(sizeof(xmlCatalogEntry));
204 xmlGenericError(xmlGenericErrorContext,
205 "malloc of %d byte failed\n", sizeof(xmlCatalogEntry));
210 ret->children = NULL;
213 ret->name = xmlStrdup(name);
217 ret->value = xmlStrdup(value);
223 ret->URL = xmlStrdup(URL);
226 ret->prefer = prefer;
232 xmlFreeCatalogEntryList(xmlCatalogEntryPtr ret);
235 * xmlFreeCatalogEntry:
236 * @ret: a Catalog entry
238 * Free the memory allocated to a Catalog entry
241 xmlFreeCatalogEntry(xmlCatalogEntryPtr ret) {
245 * Entries stored in the file hash must be deallocated
246 * only by the file hash cleaner !
248 if (ret->dealloc == 1)
251 if (xmlDebugCatalogs) {
252 if (ret->name != NULL)
253 xmlGenericError(xmlGenericErrorContext,
254 "Free catalog entry %s\n", ret->name);
255 else if (ret->value != NULL)
256 xmlGenericError(xmlGenericErrorContext,
257 "Free catalog entry %s\n", ret->value);
259 xmlGenericError(xmlGenericErrorContext,
260 "Free catalog entry\n");
263 if (ret->name != NULL)
265 if (ret->value != NULL)
267 if (ret->URL != NULL)
273 * xmlFreeCatalogEntryList:
274 * @ret: a Catalog entry list
276 * Free the memory allocated to a full chained list of Catalog entries
279 xmlFreeCatalogEntryList(xmlCatalogEntryPtr ret) {
280 xmlCatalogEntryPtr next;
282 while (ret != NULL) {
284 xmlFreeCatalogEntry(ret);
290 * xmlFreeCatalogHashEntryList:
291 * @ret: a Catalog entry list
293 * Free the memory allocated to list of Catalog entries from the
297 xmlFreeCatalogHashEntryList(xmlCatalogEntryPtr catal) {
298 xmlCatalogEntryPtr children, next;
303 children = catal->children;
304 while (children != NULL) {
305 next = children->next;
306 children->dealloc = 0;
307 children->children = NULL;
308 xmlFreeCatalogEntry(children);
312 xmlFreeCatalogEntry(catal);
316 * xmlCreateNewCatalog:
317 * @type: type of catalog
318 * @prefer: the PUBLIC vs. SYSTEM current preference value
320 * create a new Catalog, this type is shared both by XML and
321 * SGML catalogs, but the acceptable types values differs.
323 * Returns the xmlCatalogPtr or NULL in case of error
326 xmlCreateNewCatalog(xmlCatalogType type, xmlCatalogPrefer prefer) {
329 ret = (xmlCatalogPtr) xmlMalloc(sizeof(xmlCatalog));
331 xmlGenericError(xmlGenericErrorContext,
332 "malloc of %d byte failed\n", sizeof(xmlCatalog));
335 memset(ret, 0, sizeof(xmlCatalog));
338 ret->catalMax = XML_MAX_SGML_CATA_DEPTH;
339 ret->prefer = prefer;
340 if (ret->type == XML_SGML_CATALOG_TYPE)
341 ret->sgml = xmlHashCreate(10);
347 * @catal: a Catalog entry
349 * Free the memory allocated to a Catalog
352 xmlFreeCatalog(xmlCatalogPtr catal) {
355 if (catal->xml != NULL)
356 xmlFreeCatalogEntryList(catal->xml);
357 if (catal->sgml != NULL)
358 xmlHashFree(catal->sgml,
359 (xmlHashDeallocator) xmlFreeCatalogEntry);
363 /************************************************************************
365 * Serializing Catalogs *
367 ************************************************************************/
370 * xmlCatalogDumpEntry:
374 * Serialize an SGML Catalog entry
377 xmlCatalogDumpEntry(xmlCatalogEntryPtr entry, FILE *out) {
378 if ((entry == NULL) || (out == NULL))
380 switch (entry->type) {
381 case SGML_CATA_ENTITY:
382 fprintf(out, "ENTITY "); break;
383 case SGML_CATA_PENTITY:
384 fprintf(out, "ENTITY %%"); break;
385 case SGML_CATA_DOCTYPE:
386 fprintf(out, "DOCTYPE "); break;
387 case SGML_CATA_LINKTYPE:
388 fprintf(out, "LINKTYPE "); break;
389 case SGML_CATA_NOTATION:
390 fprintf(out, "NOTATION "); break;
391 case SGML_CATA_PUBLIC:
392 fprintf(out, "PUBLIC "); break;
393 case SGML_CATA_SYSTEM:
394 fprintf(out, "SYSTEM "); break;
395 case SGML_CATA_DELEGATE:
396 fprintf(out, "DELEGATE "); break;
398 fprintf(out, "BASE "); break;
399 case SGML_CATA_CATALOG:
400 fprintf(out, "CATALOG "); break;
401 case SGML_CATA_DOCUMENT:
402 fprintf(out, "DOCUMENT "); break;
403 case SGML_CATA_SGMLDECL:
404 fprintf(out, "SGMLDECL "); break;
408 switch (entry->type) {
409 case SGML_CATA_ENTITY:
410 case SGML_CATA_PENTITY:
411 case SGML_CATA_DOCTYPE:
412 case SGML_CATA_LINKTYPE:
413 case SGML_CATA_NOTATION:
414 fprintf(out, "%s", entry->name); break;
415 case SGML_CATA_PUBLIC:
416 case SGML_CATA_SYSTEM:
417 case SGML_CATA_SGMLDECL:
418 case SGML_CATA_DOCUMENT:
419 case SGML_CATA_CATALOG:
421 case SGML_CATA_DELEGATE:
422 fprintf(out, "\"%s\"", entry->name); break;
426 switch (entry->type) {
427 case SGML_CATA_ENTITY:
428 case SGML_CATA_PENTITY:
429 case SGML_CATA_DOCTYPE:
430 case SGML_CATA_LINKTYPE:
431 case SGML_CATA_NOTATION:
432 case SGML_CATA_PUBLIC:
433 case SGML_CATA_SYSTEM:
434 case SGML_CATA_DELEGATE:
435 fprintf(out, " \"%s\"", entry->value); break;
443 xmlDumpXMLCatalog(FILE *out, xmlCatalogEntryPtr catal) {
448 xmlNodePtr node, catalog;
449 xmlOutputBufferPtr buf;
450 xmlCatalogEntryPtr cur;
455 doc = xmlNewDoc(NULL);
458 dtd = xmlNewDtd(doc, BAD_CAST "catalog",
459 BAD_CAST "-//OASIS//DTD Entity Resolution XML Catalog V1.0//EN",
460 BAD_CAST "http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd");
462 xmlAddChild((xmlNodePtr) doc, (xmlNodePtr) dtd);
464 ns = xmlNewNs(NULL, XML_CATALOGS_NAMESPACE, NULL);
469 catalog = xmlNewDocNode(doc, ns, BAD_CAST "catalog", NULL);
470 if (catalog == NULL) {
476 xmlAddChild((xmlNodePtr) doc, catalog);
479 * add all the catalog entries
482 while (cur != NULL) {
484 case XML_CATA_REMOVED:
486 case XML_CATA_BROKEN_CATALOG:
487 case XML_CATA_CATALOG:
493 case XML_CATA_NEXT_CATALOG:
494 node = xmlNewDocNode(doc, ns, BAD_CAST "nextCatalog", NULL);
495 xmlSetProp(node, BAD_CAST "catalog", cur->value);
496 xmlAddChild(catalog, node);
500 case XML_CATA_PUBLIC:
501 node = xmlNewDocNode(doc, ns, BAD_CAST "public", NULL);
502 xmlSetProp(node, BAD_CAST "publicId", cur->name);
503 xmlSetProp(node, BAD_CAST "uri", cur->value);
504 xmlAddChild(catalog, node);
506 case XML_CATA_SYSTEM:
507 node = xmlNewDocNode(doc, ns, BAD_CAST "system", NULL);
508 xmlSetProp(node, BAD_CAST "systemId", cur->name);
509 xmlSetProp(node, BAD_CAST "uri", cur->value);
510 xmlAddChild(catalog, node);
512 case XML_CATA_REWRITE_SYSTEM:
513 node = xmlNewDocNode(doc, ns, BAD_CAST "rewriteSystem", NULL);
514 xmlSetProp(node, BAD_CAST "systemIdStartString", cur->name);
515 xmlSetProp(node, BAD_CAST "rewritePrefix", cur->value);
516 xmlAddChild(catalog, node);
518 case XML_CATA_DELEGATE_PUBLIC:
519 node = xmlNewDocNode(doc, ns, BAD_CAST "delegatePublic", NULL);
520 xmlSetProp(node, BAD_CAST "publicIdStartString", cur->name);
521 xmlSetProp(node, BAD_CAST "catalog", cur->value);
522 xmlAddChild(catalog, node);
524 case XML_CATA_DELEGATE_SYSTEM:
525 node = xmlNewDocNode(doc, ns, BAD_CAST "delegateSystem", NULL);
526 xmlSetProp(node, BAD_CAST "systemIdStartString", cur->name);
527 xmlSetProp(node, BAD_CAST "catalog", cur->value);
528 xmlAddChild(catalog, node);
531 node = xmlNewDocNode(doc, ns, BAD_CAST "uri", NULL);
532 xmlSetProp(node, BAD_CAST "name", cur->name);
533 xmlSetProp(node, BAD_CAST "uri", cur->value);
534 xmlAddChild(catalog, node);
536 case XML_CATA_REWRITE_URI:
537 node = xmlNewDocNode(doc, ns, BAD_CAST "rewriteURI", NULL);
538 xmlSetProp(node, BAD_CAST "uriStartString", cur->name);
539 xmlSetProp(node, BAD_CAST "rewritePrefix", cur->value);
540 xmlAddChild(catalog, node);
542 case XML_CATA_DELEGATE_URI:
543 node = xmlNewDocNode(doc, ns, BAD_CAST "delegateURI", NULL);
544 xmlSetProp(node, BAD_CAST "uriStartString", cur->name);
545 xmlSetProp(node, BAD_CAST "catalog", cur->value);
546 xmlAddChild(catalog, node);
548 case SGML_CATA_SYSTEM:
549 case SGML_CATA_PUBLIC:
550 case SGML_CATA_ENTITY:
551 case SGML_CATA_PENTITY:
552 case SGML_CATA_DOCTYPE:
553 case SGML_CATA_LINKTYPE:
554 case SGML_CATA_NOTATION:
555 case SGML_CATA_DELEGATE:
557 case SGML_CATA_CATALOG:
558 case SGML_CATA_DOCUMENT:
559 case SGML_CATA_SGMLDECL:
568 buf = xmlOutputBufferCreateFile(out, NULL);
573 ret = xmlSaveFormatFileTo(buf, doc, NULL, 1);
583 /************************************************************************
585 * Converting SGML Catalogs to XML *
587 ************************************************************************/
590 * xmlCatalogConvertEntry:
592 * @catal: pointer to the catalog being converted
594 * Convert one entry from the catalog
597 xmlCatalogConvertEntry(xmlCatalogEntryPtr entry, xmlCatalogPtr catal) {
598 if ((entry == NULL) || (catal == NULL) || (catal->sgml == NULL) ||
599 (catal->xml == NULL))
601 switch (entry->type) {
602 case SGML_CATA_ENTITY:
603 entry->type = XML_CATA_PUBLIC;
605 case SGML_CATA_PENTITY:
606 entry->type = XML_CATA_PUBLIC;
608 case SGML_CATA_DOCTYPE:
609 entry->type = XML_CATA_PUBLIC;
611 case SGML_CATA_LINKTYPE:
612 entry->type = XML_CATA_PUBLIC;
614 case SGML_CATA_NOTATION:
615 entry->type = XML_CATA_PUBLIC;
617 case SGML_CATA_PUBLIC:
618 entry->type = XML_CATA_PUBLIC;
620 case SGML_CATA_SYSTEM:
621 entry->type = XML_CATA_SYSTEM;
623 case SGML_CATA_DELEGATE:
624 entry->type = XML_CATA_DELEGATE_PUBLIC;
626 case SGML_CATA_CATALOG:
627 entry->type = XML_CATA_CATALOG;
630 xmlHashRemoveEntry(catal->sgml, entry->name,
631 (xmlHashDeallocator) xmlFreeCatalogEntry);
635 * Conversion successful, remove from the SGML catalog
636 * and add it to the default XML one
638 xmlHashRemoveEntry(catal->sgml, entry->name, NULL);
639 entry->parent = catal->xml;
641 if (catal->xml->children == NULL)
642 catal->xml->children = entry;
644 xmlCatalogEntryPtr prev;
646 prev = catal->xml->children;
647 while (prev->next != NULL)
654 * xmlConvertSGMLCatalog:
655 * @catal: the catalog
657 * Convert all the SGML catalog entries as XML ones
659 * Returns the number of entries converted if successful, -1 otherwise
662 xmlConvertSGMLCatalog(xmlCatalogPtr catal) {
664 if ((catal == NULL) || (catal->type != XML_SGML_CATALOG_TYPE))
667 if (xmlDebugCatalogs) {
668 xmlGenericError(xmlGenericErrorContext,
669 "Converting SGML catalog to XML\n");
671 xmlHashScan(catal->sgml,
672 (xmlHashScanner) xmlCatalogConvertEntry,
677 /************************************************************************
681 ************************************************************************/
684 * xmlCatalogUnWrapURN:
685 * @urn: an "urn:publicid:" to unwrap
687 * Expand the URN into the equivalent Public Identifier
689 * Returns the new identifier or NULL, the string must be deallocated
693 xmlCatalogUnWrapURN(const xmlChar *urn) {
694 xmlChar result[2000];
697 if (xmlStrncmp(urn, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1))
699 urn += sizeof(XML_URN_PUBID) - 1;
702 if (i > sizeof(result) - 3)
707 } else if (*urn == ':') {
711 } else if (*urn == ';') {
715 } else if (*urn == '%') {
716 if ((urn[1] == '2') && (urn[1] == 'B'))
718 else if ((urn[1] == '3') && (urn[1] == 'A'))
720 else if ((urn[1] == '2') && (urn[1] == 'F'))
722 else if ((urn[1] == '3') && (urn[1] == 'B'))
724 else if ((urn[1] == '2') && (urn[1] == '7'))
726 else if ((urn[1] == '3') && (urn[1] == 'F'))
728 else if ((urn[1] == '2') && (urn[1] == '3'))
730 else if ((urn[1] == '2') && (urn[1] == '5'))
745 return(xmlStrdup(result));
749 * xmlParseCatalogFile:
750 * @filename: the filename
752 * parse an XML file and build a tree. It's like xmlParseFile()
753 * except it bypass all catalog lookups.
755 * Returns the resulting document tree or NULL in case of error
759 xmlParseCatalogFile(const char *filename) {
761 xmlParserCtxtPtr ctxt;
762 char *directory = NULL;
763 xmlParserInputPtr inputStream;
764 xmlParserInputBufferPtr buf;
766 ctxt = xmlNewParserCtxt();
768 if (xmlDefaultSAXHandler.error != NULL) {
769 xmlDefaultSAXHandler.error(NULL, "out of memory\n");
774 buf = xmlParserInputBufferCreateFilename(filename, XML_CHAR_ENCODING_NONE);
776 xmlFreeParserCtxt(ctxt);
780 inputStream = xmlNewInputStream(ctxt);
781 if (inputStream == NULL) {
782 xmlFreeParserCtxt(ctxt);
786 inputStream->filename = xmlMemStrdup(filename);
787 inputStream->buf = buf;
788 inputStream->base = inputStream->buf->buffer->content;
789 inputStream->cur = inputStream->buf->buffer->content;
791 &inputStream->buf->buffer->content[inputStream->buf->buffer->use];
793 inputPush(ctxt, inputStream);
794 if ((ctxt->directory == NULL) && (directory == NULL))
795 directory = xmlParserGetDirectory(filename);
796 if ((ctxt->directory == NULL) && (directory != NULL))
797 ctxt->directory = directory;
800 ctxt->loadsubset = 0;
803 xmlParseDocument(ctxt);
805 if (ctxt->wellFormed)
809 xmlFreeDoc(ctxt->myDoc);
812 xmlFreeParserCtxt(ctxt);
818 * xmlLoadFileContent:
819 * @filename: a file path
821 * Load a file content into memory.
823 * Returns a pointer to the 0 terminated string or NULL in case of error
826 xmlLoadFileContent(const char *filename)
841 if (filename == NULL)
845 if (stat(filename, &info) < 0)
850 if ((fd = open(filename, O_RDONLY)) < 0)
852 if ((fd = fopen(filename, "rb")) == NULL)
860 if (fseek(fd, 0, SEEK_END) || (size = ftell(fd)) == EOF || fseek(fd, 0, SEEK_SET)) { /* File operations denied? ok, just close and return failure */
865 content = xmlMalloc(size + 10);
866 if (content == NULL) {
867 xmlGenericError(xmlGenericErrorContext,
868 "malloc of %d byte failed\n", size + 10);
872 len = read(fd, content, size);
874 len = fread(content, 1, size, fd);
890 /************************************************************************
892 * The XML Catalog parser *
894 ************************************************************************/
896 static xmlCatalogEntryPtr
897 xmlParseXMLCatalogFile(xmlCatalogPrefer prefer, const xmlChar *filename);
899 xmlParseXMLCatalogNodeList(xmlNodePtr cur, xmlCatalogPrefer prefer,
900 xmlCatalogEntryPtr parent);
902 xmlCatalogListXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,
903 const xmlChar *sysID);
905 xmlCatalogListXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI);
909 * xmlGetXMLCatalogEntryType:
912 * lookup the internal type associated to an XML catalog entry name
914 * Returns the type associate with that name
916 static xmlCatalogEntryType
917 xmlGetXMLCatalogEntryType(const xmlChar *name) {
918 xmlCatalogEntryType type = XML_CATA_NONE;
919 if (xmlStrEqual(name, (const xmlChar *) "system"))
920 type = XML_CATA_SYSTEM;
921 else if (xmlStrEqual(name, (const xmlChar *) "public"))
922 type = XML_CATA_PUBLIC;
923 else if (xmlStrEqual(name, (const xmlChar *) "rewriteSystem"))
924 type = XML_CATA_REWRITE_SYSTEM;
925 else if (xmlStrEqual(name, (const xmlChar *) "delegatePublic"))
926 type = XML_CATA_DELEGATE_PUBLIC;
927 else if (xmlStrEqual(name, (const xmlChar *) "delegateSystem"))
928 type = XML_CATA_DELEGATE_SYSTEM;
929 else if (xmlStrEqual(name, (const xmlChar *) "uri"))
931 else if (xmlStrEqual(name, (const xmlChar *) "rewriteURI"))
932 type = XML_CATA_REWRITE_URI;
933 else if (xmlStrEqual(name, (const xmlChar *) "delegateURI"))
934 type = XML_CATA_DELEGATE_URI;
935 else if (xmlStrEqual(name, (const xmlChar *) "nextCatalog"))
936 type = XML_CATA_NEXT_CATALOG;
937 else if (xmlStrEqual(name, (const xmlChar *) "catalog"))
938 type = XML_CATA_CATALOG;
943 * xmlParseXMLCatalogOneNode:
945 * @type: the type of Catalog entry
946 * @name: the name of the node
947 * @attrName: the attribute holding the value
948 * @uriAttrName: the attribute holding the URI-Reference
949 * @prefer: the PUBLIC vs. SYSTEM current preference value
951 * Finishes the examination of an XML tree node of a catalog and build
952 * a Catalog entry from it.
954 * Returns the new Catalog entry node or NULL in case of error.
956 static xmlCatalogEntryPtr
957 xmlParseXMLCatalogOneNode(xmlNodePtr cur, xmlCatalogEntryType type,
958 const xmlChar *name, const xmlChar *attrName,
959 const xmlChar *uriAttrName, xmlCatalogPrefer prefer) {
962 xmlChar *nameValue = NULL;
963 xmlChar *base = NULL;
965 xmlCatalogEntryPtr ret = NULL;
967 if (attrName != NULL) {
968 nameValue = xmlGetProp(cur, attrName);
969 if (nameValue == NULL) {
970 xmlGenericError(xmlGenericErrorContext,
971 "%s entry lacks '%s'\n", name, attrName);
975 uriValue = xmlGetProp(cur, uriAttrName);
976 if (uriValue == NULL) {
977 xmlGenericError(xmlGenericErrorContext,
978 "%s entry lacks '%s'\n", name, uriAttrName);
982 if (nameValue != NULL)
984 if (uriValue != NULL)
989 base = xmlNodeGetBase(cur->doc, cur);
990 URL = xmlBuildURI(uriValue, base);
992 if (xmlDebugCatalogs > 1) {
993 if (nameValue != NULL)
994 xmlGenericError(xmlGenericErrorContext,
995 "Found %s: '%s' '%s'\n", name, nameValue, URL);
997 xmlGenericError(xmlGenericErrorContext,
998 "Found %s: '%s'\n", name, URL);
1000 ret = xmlNewCatalogEntry(type, nameValue, uriValue, URL, prefer);
1002 xmlGenericError(xmlGenericErrorContext,
1003 "%s entry '%s' broken ?: %s\n", name, uriAttrName, uriValue);
1005 if (nameValue != NULL)
1007 if (uriValue != NULL)
1017 * xmlParseXMLCatalogNode:
1018 * @cur: the XML node
1019 * @prefer: the PUBLIC vs. SYSTEM current preference value
1020 * @parent: the parent Catalog entry
1022 * Examines an XML tree node of a catalog and build
1023 * a Catalog entry from it adding it to its parent. The examination can
1027 xmlParseXMLCatalogNode(xmlNodePtr cur, xmlCatalogPrefer prefer,
1028 xmlCatalogEntryPtr parent)
1030 xmlChar *uri = NULL;
1031 xmlChar *URL = NULL;
1032 xmlChar *base = NULL;
1033 xmlCatalogEntryPtr entry = NULL;
1037 if (xmlStrEqual(cur->name, BAD_CAST "group")) {
1040 prop = xmlGetProp(cur, BAD_CAST "prefer");
1042 if (xmlStrEqual(prop, BAD_CAST "system")) {
1043 prefer = XML_CATA_PREFER_SYSTEM;
1044 } else if (xmlStrEqual(prop, BAD_CAST "public")) {
1045 prefer = XML_CATA_PREFER_PUBLIC;
1047 xmlGenericError(xmlGenericErrorContext,
1048 "Invalid value for prefer: '%s'\n", prop);
1053 * Recurse to propagate prefer to the subtree
1054 * (xml:base handling is automated)
1056 xmlParseXMLCatalogNodeList(cur->children, prefer, parent);
1057 } else if (xmlStrEqual(cur->name, BAD_CAST "public")) {
1058 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_PUBLIC,
1059 BAD_CAST "public", BAD_CAST "publicId", BAD_CAST "uri", prefer);
1060 } else if (xmlStrEqual(cur->name, BAD_CAST "system")) {
1061 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_SYSTEM,
1062 BAD_CAST "system", BAD_CAST "systemId", BAD_CAST "uri", prefer);
1063 } else if (xmlStrEqual(cur->name, BAD_CAST "rewriteSystem")) {
1064 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_REWRITE_SYSTEM,
1065 BAD_CAST "rewriteSystem", BAD_CAST "systemIdStartString",
1066 BAD_CAST "rewritePrefix", prefer);
1067 } else if (xmlStrEqual(cur->name, BAD_CAST "delegatePublic")) {
1068 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_PUBLIC,
1069 BAD_CAST "delegatePublic", BAD_CAST "publicIdStartString",
1070 BAD_CAST "catalog", prefer);
1071 } else if (xmlStrEqual(cur->name, BAD_CAST "delegateSystem")) {
1072 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_SYSTEM,
1073 BAD_CAST "delegateSystem", BAD_CAST "systemIdStartString",
1074 BAD_CAST "catalog", prefer);
1075 } else if (xmlStrEqual(cur->name, BAD_CAST "uri")) {
1076 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_URI,
1077 BAD_CAST "uri", BAD_CAST "name",
1078 BAD_CAST "uri", prefer);
1079 } else if (xmlStrEqual(cur->name, BAD_CAST "rewriteURI")) {
1080 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_REWRITE_URI,
1081 BAD_CAST "rewriteURI", BAD_CAST "uriStartString",
1082 BAD_CAST "rewritePrefix", prefer);
1083 } else if (xmlStrEqual(cur->name, BAD_CAST "delegateURI")) {
1084 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_URI,
1085 BAD_CAST "delegateURI", BAD_CAST "uriStartString",
1086 BAD_CAST "catalog", prefer);
1087 } else if (xmlStrEqual(cur->name, BAD_CAST "nextCatalog")) {
1088 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_NEXT_CATALOG,
1089 BAD_CAST "nextCatalog", NULL,
1090 BAD_CAST "catalog", prefer);
1092 if ((entry != NULL) && (parent != NULL)) {
1093 entry->parent = parent;
1094 if (parent->children == NULL)
1095 parent->children = entry;
1097 xmlCatalogEntryPtr prev;
1099 prev = parent->children;
1100 while (prev->next != NULL)
1114 * xmlParseXMLCatalogNodeList:
1115 * @cur: the XML node list of siblings
1116 * @prefer: the PUBLIC vs. SYSTEM current preference value
1117 * @parent: the parent Catalog entry
1119 * Examines a list of XML sibling nodes of a catalog and build
1120 * a list of Catalog entry from it adding it to the parent.
1121 * The examination will recurse to examine node subtrees.
1124 xmlParseXMLCatalogNodeList(xmlNodePtr cur, xmlCatalogPrefer prefer,
1125 xmlCatalogEntryPtr parent) {
1126 while (cur != NULL) {
1127 if ((cur->ns != NULL) && (cur->ns->href != NULL) &&
1128 (xmlStrEqual(cur->ns->href, XML_CATALOGS_NAMESPACE))) {
1129 xmlParseXMLCatalogNode(cur, prefer, parent);
1133 /* TODO: sort the list according to REWRITE lengths and prefer value */
1137 * xmlParseXMLCatalogFile:
1138 * @prefer: the PUBLIC vs. SYSTEM current preference value
1139 * @filename: the filename for the catalog
1141 * Parses the catalog file to extract the XML tree and then analyze the
1142 * tree to build a list of Catalog entries corresponding to this catalog
1144 * Returns the resulting Catalog entries list
1146 static xmlCatalogEntryPtr
1147 xmlParseXMLCatalogFile(xmlCatalogPrefer prefer, const xmlChar *filename) {
1151 xmlCatalogEntryPtr parent = NULL;
1153 if (filename == NULL)
1156 doc = xmlParseCatalogFile((const char *) filename);
1158 if (xmlDebugCatalogs)
1159 xmlGenericError(xmlGenericErrorContext,
1160 "Failed to parse catalog %s\n", filename);
1164 if (xmlDebugCatalogs)
1165 xmlGenericError(xmlGenericErrorContext,
1166 "%d Parsing catalog %s\n", xmlGetThreadId(), filename);
1168 cur = xmlDocGetRootElement(doc);
1169 if ((cur != NULL) && (xmlStrEqual(cur->name, BAD_CAST "catalog")) &&
1170 (cur->ns != NULL) && (cur->ns->href != NULL) &&
1171 (xmlStrEqual(cur->ns->href, XML_CATALOGS_NAMESPACE))) {
1173 parent = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
1174 (const xmlChar *)filename, NULL, prefer);
1175 if (parent == NULL) {
1180 prop = xmlGetProp(cur, BAD_CAST "prefer");
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;
1187 xmlGenericError(xmlGenericErrorContext,
1188 "Invalid value for prefer: '%s'\n",
1193 cur = cur->children;
1194 xmlParseXMLCatalogNodeList(cur, prefer, parent);
1196 xmlGenericError(xmlGenericErrorContext,
1197 "File %s is not an XML Catalog\n", filename);
1206 * xmlFetchXMLCatalogFile:
1207 * @catal: an existing but incomplete catalog entry
1209 * Fetch and parse the subcatalog referenced by an entry
1211 * Returns 0 in case of success, -1 otherwise
1214 xmlFetchXMLCatalogFile(xmlCatalogEntryPtr catal) {
1215 xmlCatalogEntryPtr doc;
1219 if (catal->URL == NULL)
1221 if (catal->children != NULL)
1225 * lock the whole catalog for modification
1227 xmlRMutexLock(xmlCatalogMutex);
1228 if (catal->children != NULL) {
1229 /* Okay someone else did it in the meantime */
1230 xmlRMutexUnlock(xmlCatalogMutex);
1234 if (xmlCatalogXMLFiles != NULL) {
1235 doc = (xmlCatalogEntryPtr)
1236 xmlHashLookup(xmlCatalogXMLFiles, catal->URL);
1238 if (xmlDebugCatalogs)
1239 xmlGenericError(xmlGenericErrorContext,
1240 "Found %s in file hash\n", catal->URL);
1242 if (catal->type == XML_CATA_CATALOG)
1243 catal->children = doc->children;
1245 catal->children = doc;
1247 xmlRMutexUnlock(xmlCatalogMutex);
1250 if (xmlDebugCatalogs)
1251 xmlGenericError(xmlGenericErrorContext,
1252 "%s not found in file hash\n", catal->URL);
1256 * Fetch and parse. Note that xmlParseXMLCatalogFile does not
1257 * use the existing catalog, there is no recursion allowed at
1260 doc = xmlParseXMLCatalogFile(catal->prefer, catal->URL);
1262 catal->type = XML_CATA_BROKEN_CATALOG;
1263 xmlRMutexUnlock(xmlCatalogMutex);
1267 if (catal->type == XML_CATA_CATALOG)
1268 catal->children = doc->children;
1270 catal->children = doc;
1274 if (xmlCatalogXMLFiles == NULL)
1275 xmlCatalogXMLFiles = xmlHashCreate(10);
1276 if (xmlCatalogXMLFiles != NULL) {
1277 if (xmlDebugCatalogs)
1278 xmlGenericError(xmlGenericErrorContext,
1279 "%s added to file hash\n", catal->URL);
1280 xmlHashAddEntry(xmlCatalogXMLFiles, catal->URL, doc);
1282 xmlRMutexUnlock(xmlCatalogMutex);
1286 /************************************************************************
1288 * XML Catalog handling *
1290 ************************************************************************/
1294 * @catal: top of an XML catalog
1295 * @type: the type of record to add to the catalog
1296 * @orig: the system, public or prefix to match (or NULL)
1297 * @replace: the replacement value for the match
1299 * Add an entry in the XML catalog, it may overwrite existing but
1300 * different entries.
1302 * Returns 0 if successful, -1 otherwise
1305 xmlAddXMLCatalog(xmlCatalogEntryPtr catal, const xmlChar *type,
1306 const xmlChar *orig, const xmlChar *replace) {
1307 xmlCatalogEntryPtr cur;
1308 xmlCatalogEntryType typ;
1311 if ((catal == NULL) ||
1312 ((catal->type != XML_CATA_CATALOG) &&
1313 (catal->type != XML_CATA_BROKEN_CATALOG)))
1315 if (catal->children == NULL) {
1316 xmlFetchXMLCatalogFile(catal);
1318 if (catal->children == NULL)
1321 typ = xmlGetXMLCatalogEntryType(type);
1322 if (typ == XML_CATA_NONE) {
1323 if (xmlDebugCatalogs)
1324 xmlGenericError(xmlGenericErrorContext,
1325 "Failed to add unknown element %s to catalog\n", type);
1329 cur = catal->children;
1331 * Might be a simple "update in place"
1334 while (cur != NULL) {
1335 if ((orig != NULL) && (cur->type == typ) &&
1336 (xmlStrEqual(orig, cur->name))) {
1337 if (xmlDebugCatalogs)
1338 xmlGenericError(xmlGenericErrorContext,
1339 "Updating element %s to catalog\n", type);
1340 if (cur->value != NULL)
1341 xmlFree(cur->value);
1342 if (cur->URL != NULL)
1344 cur->value = xmlStrdup(replace);
1345 cur->URL = xmlStrdup(replace);
1348 if (cur->next == NULL)
1353 if (xmlDebugCatalogs)
1354 xmlGenericError(xmlGenericErrorContext,
1355 "Adding element %s to catalog\n", type);
1357 catal->children = xmlNewCatalogEntry(typ, orig, replace,
1358 NULL, catal->prefer);
1360 cur->next = xmlNewCatalogEntry(typ, orig, replace,
1361 NULL, catal->prefer);
1363 cur = xmlHashLookup(xmlCatalogXMLFiles, catal->URL);
1365 cur->children = catal->children;
1373 * @catal: top of an XML catalog
1374 * @value: the value to remove from the catalog
1376 * Remove entries in the XML catalog where the value or the URI
1377 * is equal to @value
1379 * Returns the number of entries removed if successful, -1 otherwise
1382 xmlDelXMLCatalog(xmlCatalogEntryPtr catal, const xmlChar *value) {
1383 xmlCatalogEntryPtr cur;
1386 if ((catal == NULL) ||
1387 ((catal->type != XML_CATA_CATALOG) &&
1388 (catal->type != XML_CATA_BROKEN_CATALOG)))
1392 if (catal->children == NULL) {
1393 xmlFetchXMLCatalogFile(catal);
1399 cur = catal->children;
1400 while (cur != NULL) {
1401 if (((cur->name != NULL) && (xmlStrEqual(value, cur->name))) ||
1402 (xmlStrEqual(value, cur->value))) {
1403 if (xmlDebugCatalogs) {
1404 if (cur->name != NULL)
1405 xmlGenericError(xmlGenericErrorContext,
1406 "Removing element %s from catalog\n", cur->name);
1408 xmlGenericError(xmlGenericErrorContext,
1409 "Removing element %s from catalog\n", cur->value);
1411 cur->type = XML_CATA_REMOVED;
1419 * xmlCatalogXMLResolve:
1420 * @catal: a catalog list
1421 * @pubId: the public ID string
1422 * @sysId: the system ID string
1424 * Do a complete resolution lookup of an External Identifier for a
1425 * list of catalog entries.
1427 * Implements (or tries to) 7.1. External Identifier Resolution
1428 * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
1430 * Returns the URI of the resource or NULL if not found
1433 xmlCatalogXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,
1434 const xmlChar *sysID) {
1435 xmlChar *ret = NULL;
1436 xmlCatalogEntryPtr cur;
1437 int haveDelegate = 0;
1441 * First tries steps 2/ 3/ 4/ if a system ID is provided.
1443 if (sysID != NULL) {
1444 xmlCatalogEntryPtr rewrite = NULL;
1445 int lenrewrite = 0, len;
1448 while (cur != NULL) {
1449 switch (cur->type) {
1450 case XML_CATA_SYSTEM:
1451 if (xmlStrEqual(sysID, cur->name)) {
1452 if (xmlDebugCatalogs)
1453 xmlGenericError(xmlGenericErrorContext,
1454 "Found system match %s\n", cur->name);
1455 return(xmlStrdup(cur->URL));
1458 case XML_CATA_REWRITE_SYSTEM:
1459 len = xmlStrlen(cur->name);
1460 if ((len > lenrewrite) &&
1461 (!xmlStrncmp(sysID, cur->name, len))) {
1466 case XML_CATA_DELEGATE_SYSTEM:
1467 if (!xmlStrncmp(sysID, cur->name, xmlStrlen(cur->name)))
1470 case XML_CATA_NEXT_CATALOG:
1478 if (rewrite != NULL) {
1479 if (xmlDebugCatalogs)
1480 xmlGenericError(xmlGenericErrorContext,
1481 "Using rewriting rule %s\n", rewrite->name);
1482 ret = xmlStrdup(rewrite->URL);
1484 ret = xmlStrcat(ret, &sysID[lenrewrite]);
1488 const xmlChar *delegates[MAX_DELEGATE];
1492 * Assume the entries have been sorted by decreasing substring
1493 * matches when the list was produced.
1496 while (cur != NULL) {
1497 if ((cur->type == XML_CATA_DELEGATE_SYSTEM) &&
1498 (!xmlStrncmp(sysID, cur->name, xmlStrlen(cur->name)))) {
1499 for (i = 0;i < nbList;i++)
1500 if (xmlStrEqual(cur->URL, delegates[i]))
1506 if (nbList < MAX_DELEGATE)
1507 delegates[nbList++] = cur->URL;
1509 if (cur->children == NULL) {
1510 xmlFetchXMLCatalogFile(cur);
1512 if (cur->children != NULL) {
1513 if (xmlDebugCatalogs)
1514 xmlGenericError(xmlGenericErrorContext,
1515 "Trying system delegate %s\n", cur->URL);
1516 ret = xmlCatalogListXMLResolve(
1517 cur->children, NULL, sysID);
1525 * Apply the cut algorithm explained in 4/
1527 return(XML_CATAL_BREAK);
1531 * Then tries 5/ 6/ if a public ID is provided
1533 if (pubID != NULL) {
1536 while (cur != NULL) {
1537 switch (cur->type) {
1538 case XML_CATA_PUBLIC:
1539 if (xmlStrEqual(pubID, cur->name)) {
1540 if (xmlDebugCatalogs)
1541 xmlGenericError(xmlGenericErrorContext,
1542 "Found public match %s\n", cur->name);
1543 return(xmlStrdup(cur->URL));
1546 case XML_CATA_DELEGATE_PUBLIC:
1547 if (!xmlStrncmp(pubID, cur->name, xmlStrlen(cur->name)) &&
1548 (cur->prefer == XML_CATA_PREFER_PUBLIC))
1551 case XML_CATA_NEXT_CATALOG:
1561 const xmlChar *delegates[MAX_DELEGATE];
1565 * Assume the entries have been sorted by decreasing substring
1566 * matches when the list was produced.
1569 while (cur != NULL) {
1570 if ((cur->type == XML_CATA_DELEGATE_PUBLIC) &&
1571 (cur->prefer == XML_CATA_PREFER_PUBLIC) &&
1572 (!xmlStrncmp(pubID, cur->name, xmlStrlen(cur->name)))) {
1574 for (i = 0;i < nbList;i++)
1575 if (xmlStrEqual(cur->URL, delegates[i]))
1581 if (nbList < MAX_DELEGATE)
1582 delegates[nbList++] = cur->URL;
1584 if (cur->children == NULL) {
1585 xmlFetchXMLCatalogFile(cur);
1587 if (cur->children != NULL) {
1588 if (xmlDebugCatalogs)
1589 xmlGenericError(xmlGenericErrorContext,
1590 "Trying public delegate %s\n", cur->URL);
1591 ret = xmlCatalogListXMLResolve(
1592 cur->children, pubID, NULL);
1600 * Apply the cut algorithm explained in 4/
1602 return(XML_CATAL_BREAK);
1607 while (cur != NULL) {
1608 if (cur->type == XML_CATA_NEXT_CATALOG) {
1609 if (cur->children == NULL) {
1610 xmlFetchXMLCatalogFile(cur);
1612 if (cur->children != NULL) {
1613 ret = xmlCatalogListXMLResolve(cur->children, pubID, sysID);
1626 * xmlCatalogXMLResolveURI:
1627 * @catal: a catalog list
1629 * @sysId: the system ID string
1631 * Do a complete resolution lookup of an External Identifier for a
1632 * list of catalog entries.
1634 * Implements (or tries to) 7.2.2. URI Resolution
1635 * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
1637 * Returns the URI of the resource or NULL if not found
1640 xmlCatalogXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI) {
1641 xmlChar *ret = NULL;
1642 xmlCatalogEntryPtr cur;
1643 int haveDelegate = 0;
1645 xmlCatalogEntryPtr rewrite = NULL;
1646 int lenrewrite = 0, len;
1655 * First tries steps 2/ 3/ 4/ if a system ID is provided.
1659 while (cur != NULL) {
1660 switch (cur->type) {
1662 if (xmlStrEqual(URI, cur->name)) {
1663 if (xmlDebugCatalogs)
1664 xmlGenericError(xmlGenericErrorContext,
1665 "Found URI match %s\n", cur->name);
1666 return(xmlStrdup(cur->URL));
1669 case XML_CATA_REWRITE_URI:
1670 len = xmlStrlen(cur->name);
1671 if ((len > lenrewrite) &&
1672 (!xmlStrncmp(URI, cur->name, len))) {
1677 case XML_CATA_DELEGATE_URI:
1678 if (!xmlStrncmp(URI, cur->name, xmlStrlen(cur->name)))
1681 case XML_CATA_NEXT_CATALOG:
1689 if (rewrite != NULL) {
1690 if (xmlDebugCatalogs)
1691 xmlGenericError(xmlGenericErrorContext,
1692 "Using rewriting rule %s\n", rewrite->name);
1693 ret = xmlStrdup(rewrite->URL);
1695 ret = xmlStrcat(ret, &URI[lenrewrite]);
1699 const xmlChar *delegates[MAX_DELEGATE];
1703 * Assume the entries have been sorted by decreasing substring
1704 * matches when the list was produced.
1707 while (cur != NULL) {
1708 if (((cur->type == XML_CATA_DELEGATE_SYSTEM) ||
1709 (cur->type == XML_CATA_DELEGATE_URI)) &&
1710 (!xmlStrncmp(URI, cur->name, xmlStrlen(cur->name)))) {
1711 for (i = 0;i < nbList;i++)
1712 if (xmlStrEqual(cur->URL, delegates[i]))
1718 if (nbList < MAX_DELEGATE)
1719 delegates[nbList++] = cur->URL;
1721 if (cur->children == NULL) {
1722 xmlFetchXMLCatalogFile(cur);
1724 if (cur->children != NULL) {
1725 if (xmlDebugCatalogs)
1726 xmlGenericError(xmlGenericErrorContext,
1727 "Trying URI delegate %s\n", cur->URL);
1728 ret = xmlCatalogListXMLResolveURI(
1729 cur->children, URI);
1737 * Apply the cut algorithm explained in 4/
1739 return(XML_CATAL_BREAK);
1743 while (cur != NULL) {
1744 if (cur->type == XML_CATA_NEXT_CATALOG) {
1745 if (cur->children == NULL) {
1746 xmlFetchXMLCatalogFile(cur);
1748 if (cur->children != NULL) {
1749 ret = xmlCatalogListXMLResolveURI(cur->children, URI);
1762 * xmlCatalogListXMLResolve:
1763 * @catal: a catalog list
1764 * @pubId: the public ID string
1765 * @sysId: the system ID string
1767 * Do a complete resolution lookup of an External Identifier for a
1770 * Implements (or tries to) 7.1. External Identifier Resolution
1771 * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
1773 * Returns the URI of the resource or NULL if not found
1776 xmlCatalogListXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,
1777 const xmlChar *sysID) {
1778 xmlChar *ret = NULL;
1779 xmlChar *urnID = NULL;
1783 if ((pubID == NULL) && (sysID == NULL))
1786 if (!xmlStrncmp(pubID, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) {
1787 urnID = xmlCatalogUnWrapURN(pubID);
1788 if (xmlDebugCatalogs) {
1790 xmlGenericError(xmlGenericErrorContext,
1791 "Public URN ID %s expanded to NULL\n", pubID);
1793 xmlGenericError(xmlGenericErrorContext,
1794 "Public URN ID expanded to %s\n", urnID);
1796 ret = xmlCatalogListXMLResolve(catal, urnID, sysID);
1801 if (!xmlStrncmp(sysID, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) {
1802 urnID = xmlCatalogUnWrapURN(sysID);
1803 if (xmlDebugCatalogs) {
1805 xmlGenericError(xmlGenericErrorContext,
1806 "System URN ID %s expanded to NULL\n", sysID);
1808 xmlGenericError(xmlGenericErrorContext,
1809 "System URN ID expanded to %s\n", urnID);
1812 ret = xmlCatalogListXMLResolve(catal, urnID, NULL);
1813 else if (xmlStrEqual(pubID, urnID))
1814 ret = xmlCatalogListXMLResolve(catal, pubID, NULL);
1816 ret = xmlCatalogListXMLResolve(catal, pubID, NULL);
1822 while (catal != NULL) {
1823 if (catal->type == XML_CATA_CATALOG) {
1824 if (catal->children == NULL) {
1825 xmlFetchXMLCatalogFile(catal);
1827 if (catal->children != NULL) {
1828 ret = xmlCatalogXMLResolve(catal->children, pubID, sysID);
1833 catal = catal->next;
1839 * xmlCatalogListXMLResolveURI:
1840 * @catal: a catalog list
1843 * Do a complete resolution lookup of an URI for a list of catalogs
1845 * Implements (or tries to) 7.2. URI Resolution
1846 * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
1848 * Returns the URI of the resource or NULL if not found
1851 xmlCatalogListXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI) {
1852 xmlChar *ret = NULL;
1853 xmlChar *urnID = NULL;
1860 if (!xmlStrncmp(URI, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) {
1861 urnID = xmlCatalogUnWrapURN(URI);
1862 if (xmlDebugCatalogs) {
1864 xmlGenericError(xmlGenericErrorContext,
1865 "URN ID %s expanded to NULL\n", URI);
1867 xmlGenericError(xmlGenericErrorContext,
1868 "URN ID expanded to %s\n", urnID);
1870 ret = xmlCatalogListXMLResolve(catal, urnID, NULL);
1875 while (catal != NULL) {
1876 if (catal->type == XML_CATA_CATALOG) {
1877 if (catal->children == NULL) {
1878 xmlFetchXMLCatalogFile(catal);
1880 if (catal->children != NULL) {
1881 ret = xmlCatalogXMLResolveURI(catal->children, URI);
1886 catal = catal->next;
1891 /************************************************************************
1893 * The SGML Catalog parser *
1895 ************************************************************************/
1900 #define SKIP(x) cur += x;
1902 #define SKIP_BLANKS while (IS_BLANK(*cur)) NEXT;
1905 * xmlParseSGMLCatalogComment:
1906 * @cur: the current character
1908 * Skip a comment in an SGML catalog
1910 * Returns new current character
1912 static const xmlChar *
1913 xmlParseSGMLCatalogComment(const xmlChar *cur) {
1914 if ((cur[0] != '-') || (cur[1] != '-'))
1917 while ((cur[0] != 0) && ((cur[0] != '-') || ((cur[1] != '-'))))
1926 * xmlParseSGMLCatalogPubid:
1927 * @cur: the current character
1928 * @id: the return location
1930 * Parse an SGML catalog ID
1932 * Returns new current character and store the value in @id
1934 static const xmlChar *
1935 xmlParseSGMLCatalogPubid(const xmlChar *cur, xmlChar **id) {
1936 xmlChar *buf = NULL;
1947 } else if (RAW == '\'') {
1953 buf = (xmlChar *) xmlMalloc(size * sizeof(xmlChar));
1955 xmlGenericError(xmlGenericErrorContext,
1956 "malloc of %d byte failed\n", size);
1959 while (xmlIsPubidChar(*cur) || (*cur == '?')) {
1960 if ((*cur == stop) && (stop != ' '))
1962 if ((stop == ' ') && (IS_BLANK(*cur)))
1964 if (len + 1 >= size) {
1966 buf = (xmlChar *) xmlRealloc(buf, size * sizeof(xmlChar));
1968 xmlGenericError(xmlGenericErrorContext,
1969 "realloc of %d byte failed\n", size);
1979 if (!IS_BLANK(*cur)) {
1995 * xmlParseSGMLCatalogName:
1996 * @cur: the current character
1997 * @name: the return location
1999 * Parse an SGML catalog name
2001 * Returns new current character and store the value in @name
2003 static const xmlChar *
2004 xmlParseSGMLCatalogName(const xmlChar *cur, xmlChar **name) {
2005 xmlChar buf[XML_MAX_NAMELEN + 5];
2012 * Handler for more complex cases
2015 if ((!IS_LETTER(c) && (c != '_') && (c != ':'))) {
2019 while (((IS_LETTER(c)) || (IS_DIGIT(c)) ||
2020 (c == '.') || (c == '-') ||
2021 (c == '_') || (c == ':'))) {
2025 if (len >= XML_MAX_NAMELEN)
2028 *name = xmlStrndup(buf, len);
2033 * xmlGetSGMLCatalogEntryType:
2034 * @name: the entry name
2036 * Get the Catalog entry type for a given SGML Catalog name
2038 * Returns Catalog entry type
2040 static xmlCatalogEntryType
2041 xmlGetSGMLCatalogEntryType(const xmlChar *name) {
2042 xmlCatalogEntryType type = XML_CATA_NONE;
2043 if (xmlStrEqual(name, (const xmlChar *) "SYSTEM"))
2044 type = SGML_CATA_SYSTEM;
2045 else if (xmlStrEqual(name, (const xmlChar *) "PUBLIC"))
2046 type = SGML_CATA_PUBLIC;
2047 else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE"))
2048 type = SGML_CATA_DELEGATE;
2049 else if (xmlStrEqual(name, (const xmlChar *) "ENTITY"))
2050 type = SGML_CATA_ENTITY;
2051 else if (xmlStrEqual(name, (const xmlChar *) "DOCTYPE"))
2052 type = SGML_CATA_DOCTYPE;
2053 else if (xmlStrEqual(name, (const xmlChar *) "LINKTYPE"))
2054 type = SGML_CATA_LINKTYPE;
2055 else if (xmlStrEqual(name, (const xmlChar *) "NOTATION"))
2056 type = SGML_CATA_NOTATION;
2057 else if (xmlStrEqual(name, (const xmlChar *) "SGMLDECL"))
2058 type = SGML_CATA_SGMLDECL;
2059 else if (xmlStrEqual(name, (const xmlChar *) "DOCUMENT"))
2060 type = SGML_CATA_DOCUMENT;
2061 else if (xmlStrEqual(name, (const xmlChar *) "CATALOG"))
2062 type = SGML_CATA_CATALOG;
2063 else if (xmlStrEqual(name, (const xmlChar *) "BASE"))
2064 type = SGML_CATA_BASE;
2065 else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE"))
2066 type = SGML_CATA_DELEGATE;
2071 * xmlParseSGMLCatalog:
2072 * @catal: the SGML Catalog
2073 * @value: the content of the SGML Catalog serialization
2074 * @file: the filepath for the catalog
2075 * @super: should this be handled as a Super Catalog in which case
2076 * parsing is not recursive
2078 * Parse an SGML catalog content and fill up the @catal hash table with
2079 * the new entries found.
2081 * Returns 0 in case of success, -1 in case of error.
2084 xmlParseSGMLCatalog(xmlCatalogPtr catal, const xmlChar *value,
2085 const char *file, int super) {
2086 const xmlChar *cur = value;
2087 xmlChar *base = NULL;
2090 if ((cur == NULL) || (file == NULL))
2092 base = xmlStrdup((const xmlChar *) file);
2094 while ((cur != NULL) && (cur[0] != 0)) {
2098 if ((cur[0] == '-') && (cur[1] == '-')) {
2099 cur = xmlParseSGMLCatalogComment(cur);
2105 xmlChar *sysid = NULL;
2106 xmlChar *name = NULL;
2107 xmlCatalogEntryType type = XML_CATA_NONE;
2109 cur = xmlParseSGMLCatalogName(cur, &name);
2114 if (!IS_BLANK(*cur)) {
2119 if (xmlStrEqual(name, (const xmlChar *) "SYSTEM"))
2120 type = SGML_CATA_SYSTEM;
2121 else if (xmlStrEqual(name, (const xmlChar *) "PUBLIC"))
2122 type = SGML_CATA_PUBLIC;
2123 else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE"))
2124 type = SGML_CATA_DELEGATE;
2125 else if (xmlStrEqual(name, (const xmlChar *) "ENTITY"))
2126 type = SGML_CATA_ENTITY;
2127 else if (xmlStrEqual(name, (const xmlChar *) "DOCTYPE"))
2128 type = SGML_CATA_DOCTYPE;
2129 else if (xmlStrEqual(name, (const xmlChar *) "LINKTYPE"))
2130 type = SGML_CATA_LINKTYPE;
2131 else if (xmlStrEqual(name, (const xmlChar *) "NOTATION"))
2132 type = SGML_CATA_NOTATION;
2133 else if (xmlStrEqual(name, (const xmlChar *) "SGMLDECL"))
2134 type = SGML_CATA_SGMLDECL;
2135 else if (xmlStrEqual(name, (const xmlChar *) "DOCUMENT"))
2136 type = SGML_CATA_DOCUMENT;
2137 else if (xmlStrEqual(name, (const xmlChar *) "CATALOG"))
2138 type = SGML_CATA_CATALOG;
2139 else if (xmlStrEqual(name, (const xmlChar *) "BASE"))
2140 type = SGML_CATA_BASE;
2141 else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE"))
2142 type = SGML_CATA_DELEGATE;
2143 else if (xmlStrEqual(name, (const xmlChar *) "OVERRIDE")) {
2145 cur = xmlParseSGMLCatalogName(cur, &name);
2157 case SGML_CATA_ENTITY:
2159 type = SGML_CATA_PENTITY;
2160 case SGML_CATA_PENTITY:
2161 case SGML_CATA_DOCTYPE:
2162 case SGML_CATA_LINKTYPE:
2163 case SGML_CATA_NOTATION:
2164 cur = xmlParseSGMLCatalogName(cur, &name);
2169 if (!IS_BLANK(*cur)) {
2174 cur = xmlParseSGMLCatalogPubid(cur, &sysid);
2180 case SGML_CATA_PUBLIC:
2181 case SGML_CATA_SYSTEM:
2182 case SGML_CATA_DELEGATE:
2183 cur = xmlParseSGMLCatalogPubid(cur, &name);
2188 if (!IS_BLANK(*cur)) {
2193 cur = xmlParseSGMLCatalogPubid(cur, &sysid);
2199 case SGML_CATA_BASE:
2200 case SGML_CATA_CATALOG:
2201 case SGML_CATA_DOCUMENT:
2202 case SGML_CATA_SGMLDECL:
2203 cur = xmlParseSGMLCatalogPubid(cur, &sysid);
2218 } else if (type == SGML_CATA_BASE) {
2221 base = xmlStrdup(sysid);
2222 } else if ((type == SGML_CATA_PUBLIC) ||
2223 (type == SGML_CATA_SYSTEM)) {
2226 filename = xmlBuildURI(sysid, base);
2227 if (filename != NULL) {
2228 xmlCatalogEntryPtr entry;
2230 entry = xmlNewCatalogEntry(type, name, filename,
2231 NULL, XML_CATA_PREFER_NONE);
2232 res = xmlHashAddEntry(catal->sgml, name, entry);
2234 xmlFreeCatalogEntry(entry);
2239 } else if (type == SGML_CATA_CATALOG) {
2241 xmlCatalogEntryPtr entry;
2243 entry = xmlNewCatalogEntry(type, sysid, NULL, NULL,
2244 XML_CATA_PREFER_NONE);
2245 res = xmlHashAddEntry(catal->sgml, sysid, entry);
2247 xmlFreeCatalogEntry(entry);
2252 filename = xmlBuildURI(sysid, base);
2253 if (filename != NULL) {
2254 xmlExpandCatalog(catal, (const char *)filename);
2260 * drop anything else we won't handle it
2275 /************************************************************************
2277 * SGML Catalog handling *
2279 ************************************************************************/
2282 * xmlCatalogGetSGMLPublic:
2283 * @catal: an SGML catalog hash
2284 * @pubId: the public ID string
2286 * Try to lookup the system ID associated to a public ID
2288 * Returns the system ID if found or NULL otherwise.
2290 static const xmlChar *
2291 xmlCatalogGetSGMLPublic(xmlHashTablePtr catal, const xmlChar *pubID) {
2292 xmlCatalogEntryPtr entry;
2297 entry = (xmlCatalogEntryPtr) xmlHashLookup(catal, pubID);
2300 if (entry->type == SGML_CATA_PUBLIC)
2306 * xmlCatalogGetSGMLSystem:
2307 * @catal: an SGML catalog hash
2308 * @sysId: the public ID string
2310 * Try to lookup the catalog local reference for a system ID
2312 * Returns the system ID if found or NULL otherwise.
2314 static const xmlChar *
2315 xmlCatalogGetSGMLSystem(xmlHashTablePtr catal, const xmlChar *sysID) {
2316 xmlCatalogEntryPtr entry;
2321 entry = (xmlCatalogEntryPtr) xmlHashLookup(catal, sysID);
2324 if (entry->type == SGML_CATA_SYSTEM)
2330 * xmlCatalogSGMLResolve:
2331 * @catal: the SGML catalog
2332 * @pubId: the public ID string
2333 * @sysId: the system ID string
2335 * Do a complete resolution lookup of an External Identifier
2337 * Returns the URI of the resource or NULL if not found
2339 static const xmlChar *
2340 xmlCatalogSGMLResolve(xmlCatalogPtr catal, const xmlChar *pubID,
2341 const xmlChar *sysID) {
2342 const xmlChar *ret = NULL;
2344 if (catal->sgml == NULL)
2348 ret = xmlCatalogGetSGMLPublic(catal->sgml, pubID);
2352 ret = xmlCatalogGetSGMLSystem(catal->sgml, sysID);
2356 /************************************************************************
2358 * Specific Public interfaces *
2360 ************************************************************************/
2363 * xmlLoadSGMLSuperCatalog:
2364 * @filename: a file path
2366 * Load an SGML super catalog. It won't expand CATALOG or DELEGATE
2367 * references. This is only needed for manipulating SGML Super Catalogs
2368 * like adding and removing CATALOG or DELEGATE entries.
2370 * Returns the catalog parsed or NULL in case of error
2373 xmlLoadSGMLSuperCatalog(const char *filename)
2376 xmlCatalogPtr catal;
2379 content = xmlLoadFileContent(filename);
2380 if (content == NULL)
2383 catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE, xmlCatalogDefaultPrefer);
2384 if (catal == NULL) {
2389 ret = xmlParseSGMLCatalog(catal, content, filename, 1);
2392 xmlFreeCatalog(catal);
2400 * @filename: a file path
2402 * Load the catalog and build the associated data structures.
2403 * This can be either an XML Catalog or an SGML Catalog
2404 * It will recurse in SGML CATALOG entries. On the other hand XML
2405 * Catalogs are not handled recursively.
2407 * Returns the catalog parsed or NULL in case of error
2410 xmlLoadACatalog(const char *filename)
2414 xmlCatalogPtr catal;
2417 content = xmlLoadFileContent(filename);
2418 if (content == NULL)
2424 while ((*first != 0) && (*first != '-') && (*first != '<') &&
2425 (!(((*first >= 'A') && (*first <= 'Z')) ||
2426 ((*first >= 'a') && (*first <= 'z')))))
2429 if (*first != '<') {
2430 catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE, xmlCatalogDefaultPrefer);
2431 if (catal == NULL) {
2435 ret = xmlParseSGMLCatalog(catal, content, filename, 0);
2437 xmlFreeCatalog(catal);
2442 catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE, xmlCatalogDefaultPrefer);
2443 if (catal == NULL) {
2447 catal->xml = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
2448 NULL, BAD_CAST filename, xmlCatalogDefaultPrefer);
2457 * @filename: a file path
2459 * Load the catalog and expand the existing catal structure.
2460 * This can be either an XML Catalog or an SGML Catalog
2462 * Returns 0 in case of success, -1 in case of error
2465 xmlExpandCatalog(xmlCatalogPtr catal, const char *filename)
2469 if ((catal == NULL) || (filename == NULL))
2473 if (catal->type == XML_SGML_CATALOG_TYPE) {
2476 content = xmlLoadFileContent(filename);
2477 if (content == NULL)
2480 ret = xmlParseSGMLCatalog(catal, content, filename, 0);
2487 xmlCatalogEntryPtr tmp, cur;
2488 tmp = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
2489 NULL, BAD_CAST filename, xmlCatalogDefaultPrefer);
2495 while (cur->next != NULL) cur = cur->next;
2503 * xmlACatalogResolveSystem:
2505 * @sysID: the public ID string
2507 * Try to lookup the catalog resource for a system ID
2509 * Returns the system ID if found or NULL otherwise, the value returned
2510 * must be freed by the caller.
2513 xmlACatalogResolveSystem(xmlCatalogPtr catal, const xmlChar *sysID) {
2514 xmlChar *ret = NULL;
2516 if ((sysID == NULL) || (catal == NULL))
2519 if (xmlDebugCatalogs)
2520 xmlGenericError(xmlGenericErrorContext,
2521 "Resolve sysID %s\n", sysID);
2523 if (catal->type == XML_XML_CATALOG_TYPE) {
2524 ret = xmlCatalogListXMLResolve(catal->xml, NULL, sysID);
2525 if (ret == XML_CATAL_BREAK)
2528 const xmlChar *sgml;
2530 sgml = xmlCatalogGetSGMLSystem(catal->sgml, sysID);
2532 ret = xmlStrdup(sgml);
2538 * xmlACatalogResolvePublic:
2540 * @pubID: the public ID string
2542 * Try to lookup the system ID associated to a public ID in that catalog
2544 * Returns the system ID if found or NULL otherwise, the value returned
2545 * must be freed by the caller.
2548 xmlACatalogResolvePublic(xmlCatalogPtr catal, const xmlChar *pubID) {
2549 xmlChar *ret = NULL;
2551 if ((pubID == NULL) || (catal == NULL))
2554 if (xmlDebugCatalogs)
2555 xmlGenericError(xmlGenericErrorContext,
2556 "Resolve pubID %s\n", pubID);
2558 if (catal->type == XML_XML_CATALOG_TYPE) {
2559 ret = xmlCatalogListXMLResolve(catal->xml, pubID, NULL);
2560 if (ret == XML_CATAL_BREAK)
2563 const xmlChar *sgml;
2565 sgml = xmlCatalogGetSGMLPublic(catal->sgml, pubID);
2567 ret = xmlStrdup(sgml);
2573 * xmlACatalogResolve:
2575 * @pubID: the public ID string
2576 * @sysID: the system ID string
2578 * Do a complete resolution lookup of an External Identifier
2580 * Returns the URI of the resource or NULL if not found, it must be freed
2584 xmlACatalogResolve(xmlCatalogPtr catal, const xmlChar * pubID,
2585 const xmlChar * sysID)
2587 xmlChar *ret = NULL;
2589 if (((pubID == NULL) && (sysID == NULL)) || (catal == NULL))
2592 if (xmlDebugCatalogs) {
2593 if (pubID != NULL) {
2594 xmlGenericError(xmlGenericErrorContext,
2595 "Resolve: pubID %s\n", pubID);
2597 xmlGenericError(xmlGenericErrorContext,
2598 "Resolve: sysID %s\n", sysID);
2602 if (catal->type == XML_XML_CATALOG_TYPE) {
2603 ret = xmlCatalogListXMLResolve(catal->xml, pubID, sysID);
2604 if (ret == XML_CATAL_BREAK)
2607 const xmlChar *sgml;
2609 sgml = xmlCatalogSGMLResolve(catal, pubID, sysID);
2611 ret = xmlStrdup(sgml);
2617 * xmlACatalogResolveURI:
2621 * Do a complete resolution lookup of an URI
2623 * Returns the URI of the resource or NULL if not found, it must be freed
2627 xmlACatalogResolveURI(xmlCatalogPtr catal, const xmlChar *URI) {
2628 xmlChar *ret = NULL;
2630 if ((URI == NULL) || (catal == NULL))
2633 if (xmlDebugCatalogs)
2634 xmlGenericError(xmlGenericErrorContext,
2635 "Resolve URI %s\n", URI);
2637 if (catal->type == XML_XML_CATALOG_TYPE) {
2638 ret = xmlCatalogListXMLResolveURI(catal->xml, URI);
2639 if (ret == XML_CATAL_BREAK)
2642 const xmlChar *sgml;
2644 sgml = xmlCatalogSGMLResolve(catal, NULL, URI);
2646 sgml = xmlStrdup(sgml);
2656 * Free up all the memory associated with catalogs
2659 xmlACatalogDump(xmlCatalogPtr catal, FILE *out) {
2660 if ((out == NULL) || (catal == NULL))
2663 if (catal->type == XML_XML_CATALOG_TYPE) {
2664 xmlDumpXMLCatalog(out, catal->xml);
2666 xmlHashScan(catal->sgml,
2667 (xmlHashScanner) xmlCatalogDumpEntry, out);
2674 * @type: the type of record to add to the catalog
2675 * @orig: the system, public or prefix to match
2676 * @replace: the replacement value for the match
2678 * Add an entry in the catalog, it may overwrite existing but
2679 * different entries.
2681 * Returns 0 if successful, -1 otherwise
2684 xmlACatalogAdd(xmlCatalogPtr catal, const xmlChar * type,
2685 const xmlChar * orig, const xmlChar * replace)
2692 if (catal->type == XML_XML_CATALOG_TYPE) {
2693 res = xmlAddXMLCatalog(catal->xml, type, orig, replace);
2695 xmlCatalogEntryType cattype;
2697 cattype = xmlGetSGMLCatalogEntryType(type);
2698 if (cattype != XML_CATA_NONE) {
2699 xmlCatalogEntryPtr entry;
2701 entry = xmlNewCatalogEntry(cattype, orig, replace, NULL,
2702 XML_CATA_PREFER_NONE);
2703 if (catal->sgml == NULL)
2704 catal->sgml = xmlHashCreate(10);
2705 res = xmlHashAddEntry(catal->sgml, orig, entry);
2712 * xmlACatalogRemove:
2714 * @value: the value to remove
2716 * Remove an entry from the catalog
2718 * Returns the number of entries removed if successful, -1 otherwise
2721 xmlACatalogRemove(xmlCatalogPtr catal, const xmlChar *value) {
2724 if ((catal == NULL) || (value == NULL))
2727 if (catal->type == XML_XML_CATALOG_TYPE) {
2728 res = xmlDelXMLCatalog(catal->xml, value);
2730 res = xmlHashRemoveEntry(catal->sgml, value,
2731 (xmlHashDeallocator) xmlFreeCatalogEntry);
2740 * @sgml: should this create an SGML catalog
2742 * create a new Catalog.
2744 * Returns the xmlCatalogPtr or NULL in case of error
2747 xmlNewCatalog(int sgml) {
2748 xmlCatalogPtr catal = NULL;
2751 catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE,
2752 xmlCatalogDefaultPrefer);
2753 if ((catal != NULL) && (catal->sgml == NULL))
2754 catal->sgml = xmlHashCreate(10);
2756 catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE,
2757 xmlCatalogDefaultPrefer);
2762 * xmlCatalogIsEmpty:
2763 * @catal: should this create an SGML catalog
2765 * Check is a catalog is empty
2767 * Returns 1 if the catalog is empty, 0 if not, amd -1 in case of error.
2770 xmlCatalogIsEmpty(xmlCatalogPtr catal) {
2774 if (catal->type == XML_XML_CATALOG_TYPE) {
2775 if (catal->xml == NULL)
2777 if ((catal->xml->type != XML_CATA_CATALOG) &&
2778 (catal->xml->type != XML_CATA_BROKEN_CATALOG))
2780 if (catal->xml->children == NULL)
2786 if (catal->sgml == NULL)
2788 res = xmlHashSize(catal->sgml);
2797 /************************************************************************
2799 * Public interfaces manipulating the global shared default catalog *
2801 ************************************************************************/
2804 * xmlInitializeCatalogData:
2806 * Do the catalog initialization only of global data, doesn't try to load
2807 * any catalog actually.
2808 * this function is not thread safe, catalog initialization should
2809 * preferably be done once at startup
2812 xmlInitializeCatalogData(void) {
2813 if (xmlCatalogInitialized != 0)
2816 if (getenv("XML_DEBUG_CATALOG"))
2817 xmlDebugCatalogs = 1;
2818 xmlCatalogMutex = xmlNewRMutex();
2820 xmlCatalogInitialized = 1;
2823 * xmlInitializeCatalog:
2825 * Do the catalog initialization.
2826 * this function is not thread safe, catalog initialization should
2827 * preferably be done once at startup
2830 xmlInitializeCatalog(void) {
2831 if (xmlCatalogInitialized != 0)
2834 xmlInitializeCatalogData();
2835 xmlRMutexLock(xmlCatalogMutex);
2837 if (getenv("XML_DEBUG_CATALOG"))
2838 xmlDebugCatalogs = 1;
2840 if (xmlDefaultCatalog == NULL) {
2841 const char *catalogs;
2843 const char *cur, *paths;
2844 xmlCatalogPtr catal;
2845 xmlCatalogEntryPtr *nextent;
2847 catalogs = (const char *) getenv("XML_CATALOG_FILES");
2848 if (catalogs == NULL)
2849 catalogs = XML_XML_DEFAULT_CATALOG;
2851 catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE,
2852 xmlCatalogDefaultPrefer);
2853 if (catal != NULL) {
2854 /* the XML_CATALOG_FILES envvar is allowed to contain a
2855 space-separated list of entries. */
2857 nextent = &catal->xml;
2858 while (*cur != '\0') {
2859 while (IS_BLANK(*cur))
2863 while ((*cur != 0) && (!IS_BLANK(*cur)))
2865 path = (char *) xmlStrndup((const xmlChar *)paths, cur - paths);
2867 *nextent = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
2868 NULL, BAD_CAST path, xmlCatalogDefaultPrefer);
2869 if (*nextent != NULL)
2870 nextent = &((*nextent)->next);
2875 xmlDefaultCatalog = catal;
2879 xmlRMutexUnlock(xmlCatalogMutex);
2885 * @filename: a file path
2887 * Load the catalog and makes its definitions effective for the default
2888 * external entity loader. It will recurse in SGML CATALOG entries.
2889 * this function is not thread safe, catalog initialization should
2890 * preferably be done once at startup
2892 * Returns 0 in case of success -1 in case of error
2895 xmlLoadCatalog(const char *filename)
2898 xmlCatalogPtr catal;
2900 if (!xmlCatalogInitialized)
2901 xmlInitializeCatalogData();
2903 xmlRMutexLock(xmlCatalogMutex);
2905 if (xmlDefaultCatalog == NULL) {
2906 catal = xmlLoadACatalog(filename);
2910 xmlDefaultCatalog = catal;
2911 xmlRMutexUnlock(xmlCatalogMutex);
2915 ret = xmlExpandCatalog(xmlDefaultCatalog, filename);
2916 xmlRMutexUnlock(xmlCatalogMutex);
2922 * @pathss: a list of directories separated by a colon or a space.
2924 * Load the catalogs and makes their definitions effective for the default
2925 * external entity loader.
2926 * this function is not thread safe, catalog initialization should
2927 * preferably be done once at startup
2930 xmlLoadCatalogs(const char *pathss) {
2939 while ((cur != NULL) && (*cur != 0)) {
2940 while (IS_BLANK(*cur)) cur++;
2943 while ((*cur != 0) && (*cur != ':') && (!IS_BLANK(*cur)))
2945 path = xmlStrndup((const xmlChar *)paths, cur - paths);
2947 xmlLoadCatalog((const char *) path);
2957 * xmlCatalogCleanup:
2959 * Free up all the memory associated with catalogs
2962 xmlCatalogCleanup(void) {
2963 if (xmlCatalogInitialized == 0)
2966 xmlRMutexLock(xmlCatalogMutex);
2967 if (xmlDebugCatalogs)
2968 xmlGenericError(xmlGenericErrorContext,
2969 "Catalogs cleanup\n");
2970 if (xmlCatalogXMLFiles != NULL)
2971 xmlHashFree(xmlCatalogXMLFiles,
2972 (xmlHashDeallocator)xmlFreeCatalogHashEntryList);
2973 xmlCatalogXMLFiles = NULL;
2974 if (xmlDefaultCatalog != NULL)
2975 xmlFreeCatalog(xmlDefaultCatalog);
2976 xmlDefaultCatalog = NULL;
2977 xmlDebugCatalogs = 0;
2978 xmlCatalogInitialized = 0;
2979 xmlRMutexUnlock(xmlCatalogMutex);
2980 xmlFreeRMutex(xmlCatalogMutex);
2984 * xmlCatalogResolveSystem:
2985 * @sysID: the public ID string
2987 * Try to lookup the catalog resource for a system ID
2989 * Returns the system ID if found or NULL otherwise, the value returned
2990 * must be freed by the caller.
2993 xmlCatalogResolveSystem(const xmlChar *sysID) {
2996 if (!xmlCatalogInitialized)
2997 xmlInitializeCatalog();
2999 ret = xmlACatalogResolveSystem(xmlDefaultCatalog, sysID);
3004 * xmlCatalogResolvePublic:
3005 * @pubID: the public ID string
3007 * Try to lookup the system ID associated to a public ID
3009 * Returns the system ID if found or NULL otherwise, the value returned
3010 * must be freed by the caller.
3013 xmlCatalogResolvePublic(const xmlChar *pubID) {
3016 if (!xmlCatalogInitialized)
3017 xmlInitializeCatalog();
3019 ret = xmlACatalogResolvePublic(xmlDefaultCatalog, pubID);
3024 * xmlCatalogResolve:
3025 * @pubID: the public ID string
3026 * @sysID: the system ID string
3028 * Do a complete resolution lookup of an External Identifier
3030 * Returns the URI of the resource or NULL if not found, it must be freed
3034 xmlCatalogResolve(const xmlChar *pubID, const xmlChar *sysID) {
3037 if (!xmlCatalogInitialized)
3038 xmlInitializeCatalog();
3040 ret = xmlACatalogResolve(xmlDefaultCatalog, pubID, sysID);
3045 * xmlCatalogResolveURI:
3048 * Do a complete resolution lookup of an URI
3050 * Returns the URI of the resource or NULL if not found, it must be freed
3054 xmlCatalogResolveURI(const xmlChar *URI) {
3057 if (!xmlCatalogInitialized)
3058 xmlInitializeCatalog();
3060 ret = xmlACatalogResolveURI(xmlDefaultCatalog, URI);
3068 * Free up all the memory associated with catalogs
3071 xmlCatalogDump(FILE *out) {
3075 if (!xmlCatalogInitialized)
3076 xmlInitializeCatalog();
3078 xmlACatalogDump(xmlDefaultCatalog, out);
3083 * @type: the type of record to add to the catalog
3084 * @orig: the system, public or prefix to match
3085 * @replace: the replacement value for the match
3087 * Add an entry in the catalog, it may overwrite existing but
3088 * different entries.
3089 * If called before any other catalog routine, allows to override the
3090 * default shared catalog put in place by xmlInitializeCatalog();
3092 * Returns 0 if successful, -1 otherwise
3095 xmlCatalogAdd(const xmlChar *type, const xmlChar *orig, const xmlChar *replace) {
3098 if (!xmlCatalogInitialized)
3099 xmlInitializeCatalogData();
3101 xmlRMutexLock(xmlCatalogMutex);
3103 * Specific case where one want to override the default catalog
3104 * put in place by xmlInitializeCatalog();
3106 if ((xmlDefaultCatalog == NULL) &&
3107 (xmlStrEqual(type, BAD_CAST "catalog"))) {
3108 xmlDefaultCatalog = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE,
3109 xmlCatalogDefaultPrefer);
3110 xmlDefaultCatalog->xml = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
3111 orig, NULL, xmlCatalogDefaultPrefer);
3113 xmlRMutexUnlock(xmlCatalogMutex);
3117 res = xmlACatalogAdd(xmlDefaultCatalog, type, orig, replace);
3118 xmlRMutexUnlock(xmlCatalogMutex);
3124 * @value: the value to remove
3126 * Remove an entry from the catalog
3128 * Returns the number of entries removed if successful, -1 otherwise
3131 xmlCatalogRemove(const xmlChar *value) {
3134 if (!xmlCatalogInitialized)
3135 xmlInitializeCatalog();
3137 xmlRMutexLock(xmlCatalogMutex);
3138 res = xmlACatalogRemove(xmlDefaultCatalog, value);
3139 xmlRMutexUnlock(xmlCatalogMutex);
3144 * xmlCatalogConvert:
3146 * Convert all the SGML catalog entries as XML ones
3148 * Returns the number of entries converted if successful, -1 otherwise
3151 xmlCatalogConvert(void) {
3154 if (!xmlCatalogInitialized)
3155 xmlInitializeCatalog();
3157 xmlRMutexLock(xmlCatalogMutex);
3158 res = xmlConvertSGMLCatalog(xmlDefaultCatalog);
3159 xmlRMutexUnlock(xmlCatalogMutex);
3163 /************************************************************************
3165 * Public interface manipulating the common preferences *
3167 ************************************************************************/
3170 * xmlCatalogGetDefaults:
3172 * Used to get the user preference w.r.t. to what catalogs should
3175 * Returns the current xmlCatalogAllow value
3178 xmlCatalogGetDefaults(void) {
3179 return(xmlCatalogDefaultAllow);
3183 * xmlCatalogSetDefaults:
3184 * @allow: what catalogs should be accepted
3186 * Used to set the user preference w.r.t. to what catalogs should
3190 xmlCatalogSetDefaults(xmlCatalogAllow allow) {
3191 if (xmlDebugCatalogs) {
3193 case XML_CATA_ALLOW_NONE:
3194 xmlGenericError(xmlGenericErrorContext,
3195 "Disabling catalog usage\n");
3197 case XML_CATA_ALLOW_GLOBAL:
3198 xmlGenericError(xmlGenericErrorContext,
3199 "Allowing only global catalogs\n");
3201 case XML_CATA_ALLOW_DOCUMENT:
3202 xmlGenericError(xmlGenericErrorContext,
3203 "Allowing only catalogs from the document\n");
3205 case XML_CATA_ALLOW_ALL:
3206 xmlGenericError(xmlGenericErrorContext,
3207 "Allowing all catalogs\n");
3211 xmlCatalogDefaultAllow = allow;
3215 * xmlCatalogSetDefaultPrefer:
3216 * @prefer: the default preference for delegation
3218 * Allows to set the preference between public and system for deletion
3219 * in XML Catalog resolution. C.f. section 4.1.1 of the spec
3220 * Values accepted are XML_CATA_PREFER_PUBLIC or XML_CATA_PREFER_SYSTEM
3222 * Returns the previous value of the default preference for delegation
3225 xmlCatalogSetDefaultPrefer(xmlCatalogPrefer prefer) {
3226 xmlCatalogPrefer ret = xmlCatalogDefaultPrefer;
3228 if (prefer == XML_CATA_PREFER_NONE)
3231 if (xmlDebugCatalogs) {
3233 case XML_CATA_PREFER_PUBLIC:
3234 xmlGenericError(xmlGenericErrorContext,
3235 "Setting catalog preference to PUBLIC\n");
3237 case XML_CATA_PREFER_SYSTEM:
3238 xmlGenericError(xmlGenericErrorContext,
3239 "Setting catalog preference to SYSTEM\n");
3241 case XML_CATA_PREFER_NONE:
3245 xmlCatalogDefaultPrefer = prefer;
3250 * xmlCatalogSetDebug:
3251 * @level: the debug level of catalogs required
3253 * Used to set the debug level for catalog operation, 0 disable
3254 * debugging, 1 enable it
3256 * Returns the previous value of the catalog debugging level
3259 xmlCatalogSetDebug(int level) {
3260 int ret = xmlDebugCatalogs;
3263 xmlDebugCatalogs = 0;
3265 xmlDebugCatalogs = level;
3269 /************************************************************************
3271 * Minimal interfaces used for per-document catalogs by the parser *
3273 ************************************************************************/
3276 * xmlCatalogFreeLocal:
3277 * @catalogs: a document's list of catalogs
3279 * Free up the memory associated to the catalog list
3282 xmlCatalogFreeLocal(void *catalogs) {
3283 xmlCatalogEntryPtr catal;
3285 if (!xmlCatalogInitialized)
3286 xmlInitializeCatalog();
3288 catal = (xmlCatalogEntryPtr) catalogs;
3290 xmlFreeCatalogEntryList(catal);
3295 * xmlCatalogAddLocal:
3296 * @catalogs: a document's list of catalogs
3297 * @URL: the URL to a new local catalog
3299 * Add the new entry to the catalog list
3301 * Returns the updated list
3304 xmlCatalogAddLocal(void *catalogs, const xmlChar *URL) {
3305 xmlCatalogEntryPtr catal, add;
3307 if (!xmlCatalogInitialized)
3308 xmlInitializeCatalog();
3313 if (xmlDebugCatalogs)
3314 xmlGenericError(xmlGenericErrorContext,
3315 "Adding document catalog %s\n", URL);
3317 add = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL, URL, NULL,
3318 xmlCatalogDefaultPrefer);
3322 catal = (xmlCatalogEntryPtr) catalogs;
3324 return((void *) add);
3326 while (catal->next != NULL)
3327 catal = catal->next;
3333 * xmlCatalogLocalResolve:
3334 * @catalogs: a document's list of catalogs
3335 * @pubID: the public ID string
3336 * @sysID: the system ID string
3338 * Do a complete resolution lookup of an External Identifier using a
3339 * document's private catalog list
3341 * Returns the URI of the resource or NULL if not found, it must be freed
3345 xmlCatalogLocalResolve(void *catalogs, const xmlChar *pubID,
3346 const xmlChar *sysID) {
3347 xmlCatalogEntryPtr catal;
3350 if (!xmlCatalogInitialized)
3351 xmlInitializeCatalog();
3353 if ((pubID == NULL) && (sysID == NULL))
3356 if (xmlDebugCatalogs) {
3357 if (pubID != NULL) {
3358 xmlGenericError(xmlGenericErrorContext,
3359 "Local resolve: pubID %s\n", pubID);
3361 xmlGenericError(xmlGenericErrorContext,
3362 "Local resolve: sysID %s\n", sysID);
3366 catal = (xmlCatalogEntryPtr) catalogs;
3369 ret = xmlCatalogListXMLResolve(catal, pubID, sysID);
3370 if ((ret != NULL) && (ret != XML_CATAL_BREAK))
3376 * xmlCatalogLocalResolveURI:
3377 * @catalogs: a document's list of catalogs
3380 * Do a complete resolution lookup of an URI using a
3381 * document's private catalog list
3383 * Returns the URI of the resource or NULL if not found, it must be freed
3387 xmlCatalogLocalResolveURI(void *catalogs, const xmlChar *URI) {
3388 xmlCatalogEntryPtr catal;
3391 if (!xmlCatalogInitialized)
3392 xmlInitializeCatalog();
3397 if (xmlDebugCatalogs)
3398 xmlGenericError(xmlGenericErrorContext,
3399 "Resolve URI %s\n", URI);
3401 catal = (xmlCatalogEntryPtr) catalogs;
3404 ret = xmlCatalogListXMLResolveURI(catal, URI);
3405 if ((ret != NULL) && (ret != XML_CATAL_BREAK))
3410 /************************************************************************
3412 * Deprecated interfaces *
3414 ************************************************************************/
3416 * xmlCatalogGetSystem:
3417 * @sysID: the system ID string
3419 * Try to lookup the system ID associated to a public ID
3420 * DEPRECATED, use xmlCatalogResolveSystem()
3422 * Returns the system ID if found or NULL otherwise.
3425 xmlCatalogGetSystem(const xmlChar *sysID) {
3427 static xmlChar result[1000];
3430 if (!xmlCatalogInitialized)
3431 xmlInitializeCatalog();
3434 xmlGenericError(xmlGenericErrorContext,
3435 "Use of deprecated xmlCatalogGetSystem() call\n");
3443 * Check first the XML catalogs
3445 if (xmlDefaultCatalog != NULL) {
3446 ret = xmlCatalogListXMLResolve(xmlDefaultCatalog->xml, NULL, sysID);
3447 if ((ret != NULL) && (ret != XML_CATAL_BREAK)) {
3448 snprintf((char *) result, sizeof(result) - 1, "%s", (char *) ret);
3449 result[sizeof(result) - 1] = 0;
3454 if (xmlDefaultCatalog != NULL)
3455 return(xmlCatalogGetSGMLSystem(xmlDefaultCatalog->sgml, sysID));
3460 * xmlCatalogGetPublic:
3461 * @pubID: the public ID string
3463 * Try to lookup the system ID associated to a public ID
3464 * DEPRECATED, use xmlCatalogResolvePublic()
3466 * Returns the system ID if found or NULL otherwise.
3469 xmlCatalogGetPublic(const xmlChar *pubID) {
3471 static xmlChar result[1000];
3474 if (!xmlCatalogInitialized)
3475 xmlInitializeCatalog();
3478 xmlGenericError(xmlGenericErrorContext,
3479 "Use of deprecated xmlCatalogGetPublic() call\n");
3487 * Check first the XML catalogs
3489 if (xmlDefaultCatalog != NULL) {
3490 ret = xmlCatalogListXMLResolve(xmlDefaultCatalog->xml, pubID, NULL);
3491 if ((ret != NULL) && (ret != XML_CATAL_BREAK)) {
3492 snprintf((char *) result, sizeof(result) - 1, "%s", (char *) ret);
3493 result[sizeof(result) - 1] = 0;
3498 if (xmlDefaultCatalog != NULL)
3499 return(xmlCatalogGetSGMLPublic(xmlDefaultCatalog->sgml, pubID));
3503 #endif /* LIBXML_CATALOG_ENABLED */