| /** | |
| * catalog.c: set of generic Catalog related routines | |
| * | |
| * Reference: SGML Open Technical Resolution TR9401:1997. | |
| * http://www.jclark.com/sp/catalog.htm | |
| * | |
| * XML Catalogs Working Draft 06 August 2001 | |
| * http://www.oasis-open.org/committees/entity/spec-2001-08-06.html | |
| * | |
| * See Copyright for the status of this software. | |
| * | |
| * Daniel.Veillard@imag.fr | |
| */ | |
| #define IN_LIBXML | |
| #include "libxml.h" | |
| #ifdef LIBXML_CATALOG_ENABLED | |
| #ifdef HAVE_SYS_TYPES_H | |
| #include <sys/types.h> | |
| #endif | |
| #ifdef HAVE_SYS_STAT_H | |
| #include <sys/stat.h> | |
| #endif | |
| #ifdef HAVE_UNISTD_H | |
| #include <unistd.h> | |
| #endif | |
| #ifdef HAVE_FCNTL_H | |
| #include <fcntl.h> | |
| #endif | |
| #ifdef HAVE_STDLIB_H | |
| #include <stdlib.h> | |
| #endif | |
| #include <string.h> | |
| #include <libxml/xmlmemory.h> | |
| #include <libxml/hash.h> | |
| #include <libxml/uri.h> | |
| #include <libxml/parserInternals.h> | |
| #include <libxml/catalog.h> | |
| #include <libxml/xmlerror.h> | |
| #include <libxml/threads.h> | |
| #include <libxml/globals.h> | |
| #include "buf.h" | |
| #define MAX_DELEGATE 50 | |
| #define MAX_CATAL_DEPTH 50 | |
| #ifdef _WIN32 | |
| # define PATH_SEPARATOR ';' | |
| #else | |
| # define PATH_SEPARATOR ':' | |
| #endif | |
| /** | |
| * TODO: | |
| * | |
| * macro to flag unimplemented blocks | |
| * XML_CATALOG_PREFER user env to select between system/public prefered | |
| * option. C.f. Richard Tobin <richard@cogsci.ed.ac.uk> | |
| *> Just FYI, I am using an environment variable XML_CATALOG_PREFER with | |
| *> values "system" and "public". I have made the default be "system" to | |
| *> match yours. | |
| */ | |
| #define TODO \ | |
| xmlGenericError(xmlGenericErrorContext, \ | |
| "Unimplemented block at %s:%d\n", \ | |
| __FILE__, __LINE__); | |
| #define XML_URN_PUBID "urn:publicid:" | |
| #define XML_CATAL_BREAK ((xmlChar *) -1) | |
| #ifndef XML_XML_DEFAULT_CATALOG | |
| #define XML_XML_DEFAULT_CATALOG "file:///etc/xml/catalog" | |
| #endif | |
| #ifndef XML_SGML_DEFAULT_CATALOG | |
| #define XML_SGML_DEFAULT_CATALOG "file:///etc/sgml/catalog" | |
| #endif | |
| #if defined(_WIN32) && defined(_MSC_VER) | |
| #undef XML_XML_DEFAULT_CATALOG | |
| static char XML_XML_DEFAULT_CATALOG[256] = "file:///etc/xml/catalog"; | |
| #if defined(_WIN32_WCE) | |
| /* Windows CE don't have a A variant */ | |
| #define GetModuleHandleA GetModuleHandle | |
| #define GetModuleFileNameA GetModuleFileName | |
| #else | |
| #if !defined(_WINDOWS_) | |
| void* __stdcall GetModuleHandleA(const char*); | |
| unsigned long __stdcall GetModuleFileNameA(void*, char*, unsigned long); | |
| #endif | |
| #endif | |
| #endif | |
| static xmlChar *xmlCatalogNormalizePublic(const xmlChar *pubID); | |
| static int xmlExpandCatalog(xmlCatalogPtr catal, const char *filename); | |
| /************************************************************************ | |
| * * | |
| * Types, all private * | |
| * * | |
| ************************************************************************/ | |
| typedef enum { | |
| XML_CATA_REMOVED = -1, | |
| XML_CATA_NONE = 0, | |
| XML_CATA_CATALOG, | |
| XML_CATA_BROKEN_CATALOG, | |
| XML_CATA_NEXT_CATALOG, | |
| XML_CATA_GROUP, | |
| XML_CATA_PUBLIC, | |
| XML_CATA_SYSTEM, | |
| XML_CATA_REWRITE_SYSTEM, | |
| XML_CATA_DELEGATE_PUBLIC, | |
| XML_CATA_DELEGATE_SYSTEM, | |
| XML_CATA_URI, | |
| XML_CATA_REWRITE_URI, | |
| XML_CATA_DELEGATE_URI, | |
| SGML_CATA_SYSTEM, | |
| SGML_CATA_PUBLIC, | |
| SGML_CATA_ENTITY, | |
| SGML_CATA_PENTITY, | |
| SGML_CATA_DOCTYPE, | |
| SGML_CATA_LINKTYPE, | |
| SGML_CATA_NOTATION, | |
| SGML_CATA_DELEGATE, | |
| SGML_CATA_BASE, | |
| SGML_CATA_CATALOG, | |
| SGML_CATA_DOCUMENT, | |
| SGML_CATA_SGMLDECL | |
| } xmlCatalogEntryType; | |
| typedef struct _xmlCatalogEntry xmlCatalogEntry; | |
| typedef xmlCatalogEntry *xmlCatalogEntryPtr; | |
| struct _xmlCatalogEntry { | |
| struct _xmlCatalogEntry *next; | |
| struct _xmlCatalogEntry *parent; | |
| struct _xmlCatalogEntry *children; | |
| xmlCatalogEntryType type; | |
| xmlChar *name; | |
| xmlChar *value; | |
| xmlChar *URL; /* The expanded URL using the base */ | |
| xmlCatalogPrefer prefer; | |
| int dealloc; | |
| int depth; | |
| struct _xmlCatalogEntry *group; | |
| }; | |
| typedef enum { | |
| XML_XML_CATALOG_TYPE = 1, | |
| XML_SGML_CATALOG_TYPE | |
| } xmlCatalogType; | |
| #define XML_MAX_SGML_CATA_DEPTH 10 | |
| struct _xmlCatalog { | |
| xmlCatalogType type; /* either XML or SGML */ | |
| /* | |
| * SGML Catalogs are stored as a simple hash table of catalog entries | |
| * Catalog stack to check against overflows when building the | |
| * SGML catalog | |
| */ | |
| char *catalTab[XML_MAX_SGML_CATA_DEPTH]; /* stack of catals */ | |
| int catalNr; /* Number of current catal streams */ | |
| int catalMax; /* Max number of catal streams */ | |
| xmlHashTablePtr sgml; | |
| /* | |
| * XML Catalogs are stored as a tree of Catalog entries | |
| */ | |
| xmlCatalogPrefer prefer; | |
| xmlCatalogEntryPtr xml; | |
| }; | |
| /************************************************************************ | |
| * * | |
| * Global variables * | |
| * * | |
| ************************************************************************/ | |
| /* | |
| * Those are preferences | |
| */ | |
| static int xmlDebugCatalogs = 0; /* used for debugging */ | |
| static xmlCatalogAllow xmlCatalogDefaultAllow = XML_CATA_ALLOW_ALL; | |
| static xmlCatalogPrefer xmlCatalogDefaultPrefer = XML_CATA_PREFER_PUBLIC; | |
| /* | |
| * Hash table containing all the trees of XML catalogs parsed by | |
| * the application. | |
| */ | |
| static xmlHashTablePtr xmlCatalogXMLFiles = NULL; | |
| /* | |
| * The default catalog in use by the application | |
| */ | |
| static xmlCatalogPtr xmlDefaultCatalog = NULL; | |
| /* | |
| * A mutex for modifying the shared global catalog(s) | |
| * xmlDefaultCatalog tree. | |
| * It also protects xmlCatalogXMLFiles | |
| * The core of this readers/writer scheme is in xmlFetchXMLCatalogFile() | |
| */ | |
| static xmlRMutexPtr xmlCatalogMutex = NULL; | |
| /* | |
| * Whether the catalog support was initialized. | |
| */ | |
| static int xmlCatalogInitialized = 0; | |
| /************************************************************************ | |
| * * | |
| * Catalog error handlers * | |
| * * | |
| ************************************************************************/ | |
| /** | |
| * xmlCatalogErrMemory: | |
| * @extra: extra informations | |
| * | |
| * Handle an out of memory condition | |
| */ | |
| static void | |
| xmlCatalogErrMemory(const char *extra) | |
| { | |
| __xmlRaiseError(NULL, NULL, NULL, NULL, NULL, XML_FROM_CATALOG, | |
| XML_ERR_NO_MEMORY, XML_ERR_ERROR, NULL, 0, | |
| extra, NULL, NULL, 0, 0, | |
| "Memory allocation failed : %s\n", extra); | |
| } | |
| /** | |
| * xmlCatalogErr: | |
| * @catal: the Catalog entry | |
| * @node: the context node | |
| * @msg: the error message | |
| * @extra: extra informations | |
| * | |
| * Handle a catalog error | |
| */ | |
| static void LIBXML_ATTR_FORMAT(4,0) | |
| xmlCatalogErr(xmlCatalogEntryPtr catal, xmlNodePtr node, int error, | |
| const char *msg, const xmlChar *str1, const xmlChar *str2, | |
| const xmlChar *str3) | |
| { | |
| __xmlRaiseError(NULL, NULL, NULL, catal, node, XML_FROM_CATALOG, | |
| error, XML_ERR_ERROR, NULL, 0, | |
| (const char *) str1, (const char *) str2, | |
| (const char *) str3, 0, 0, | |
| msg, str1, str2, str3); | |
| } | |
| /************************************************************************ | |
| * * | |
| * Allocation and Freeing * | |
| * * | |
| ************************************************************************/ | |
| /** | |
| * xmlNewCatalogEntry: | |
| * @type: type of entry | |
| * @name: name of the entry | |
| * @value: value of the entry | |
| * @prefer: the PUBLIC vs. SYSTEM current preference value | |
| * @group: for members of a group, the group entry | |
| * | |
| * create a new Catalog entry, this type is shared both by XML and | |
| * SGML catalogs, but the acceptable types values differs. | |
| * | |
| * Returns the xmlCatalogEntryPtr or NULL in case of error | |
| */ | |
| static xmlCatalogEntryPtr | |
| xmlNewCatalogEntry(xmlCatalogEntryType type, const xmlChar *name, | |
| const xmlChar *value, const xmlChar *URL, xmlCatalogPrefer prefer, | |
| xmlCatalogEntryPtr group) { | |
| xmlCatalogEntryPtr ret; | |
| xmlChar *normid = NULL; | |
| ret = (xmlCatalogEntryPtr) xmlMalloc(sizeof(xmlCatalogEntry)); | |
| if (ret == NULL) { | |
| xmlCatalogErrMemory("allocating catalog entry"); | |
| return(NULL); | |
| } | |
| ret->next = NULL; | |
| ret->parent = NULL; | |
| ret->children = NULL; | |
| ret->type = type; | |
| if (type == XML_CATA_PUBLIC || type == XML_CATA_DELEGATE_PUBLIC) { | |
| normid = xmlCatalogNormalizePublic(name); | |
| if (normid != NULL) | |
| name = (*normid != 0 ? normid : NULL); | |
| } | |
| if (name != NULL) | |
| ret->name = xmlStrdup(name); | |
| else | |
| ret->name = NULL; | |
| if (normid != NULL) | |
| xmlFree(normid); | |
| if (value != NULL) | |
| ret->value = xmlStrdup(value); | |
| else | |
| ret->value = NULL; | |
| if (URL == NULL) | |
| URL = value; | |
| if (URL != NULL) | |
| ret->URL = xmlStrdup(URL); | |
| else | |
| ret->URL = NULL; | |
| ret->prefer = prefer; | |
| ret->dealloc = 0; | |
| ret->depth = 0; | |
| ret->group = group; | |
| return(ret); | |
| } | |
| static void | |
| xmlFreeCatalogEntryList(xmlCatalogEntryPtr ret); | |
| /** | |
| * xmlFreeCatalogEntry: | |
| * @ret: a Catalog entry | |
| * | |
| * Free the memory allocated to a Catalog entry | |
| */ | |
| static void | |
| xmlFreeCatalogEntry(xmlCatalogEntryPtr ret) { | |
| if (ret == NULL) | |
| return; | |
| /* | |
| * Entries stored in the file hash must be deallocated | |
| * only by the file hash cleaner ! | |
| */ | |
| if (ret->dealloc == 1) | |
| return; | |
| if (xmlDebugCatalogs) { | |
| if (ret->name != NULL) | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Free catalog entry %s\n", ret->name); | |
| else if (ret->value != NULL) | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Free catalog entry %s\n", ret->value); | |
| else | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Free catalog entry\n"); | |
| } | |
| if (ret->name != NULL) | |
| xmlFree(ret->name); | |
| if (ret->value != NULL) | |
| xmlFree(ret->value); | |
| if (ret->URL != NULL) | |
| xmlFree(ret->URL); | |
| xmlFree(ret); | |
| } | |
| /** | |
| * xmlFreeCatalogEntryList: | |
| * @ret: a Catalog entry list | |
| * | |
| * Free the memory allocated to a full chained list of Catalog entries | |
| */ | |
| static void | |
| xmlFreeCatalogEntryList(xmlCatalogEntryPtr ret) { | |
| xmlCatalogEntryPtr next; | |
| while (ret != NULL) { | |
| next = ret->next; | |
| xmlFreeCatalogEntry(ret); | |
| ret = next; | |
| } | |
| } | |
| /** | |
| * xmlFreeCatalogHashEntryList: | |
| * @ret: a Catalog entry list | |
| * | |
| * Free the memory allocated to list of Catalog entries from the | |
| * catalog file hash. | |
| */ | |
| static void | |
| xmlFreeCatalogHashEntryList(xmlCatalogEntryPtr catal) { | |
| xmlCatalogEntryPtr children, next; | |
| if (catal == NULL) | |
| return; | |
| children = catal->children; | |
| while (children != NULL) { | |
| next = children->next; | |
| children->dealloc = 0; | |
| children->children = NULL; | |
| xmlFreeCatalogEntry(children); | |
| children = next; | |
| } | |
| catal->dealloc = 0; | |
| xmlFreeCatalogEntry(catal); | |
| } | |
| /** | |
| * xmlCreateNewCatalog: | |
| * @type: type of catalog | |
| * @prefer: the PUBLIC vs. SYSTEM current preference value | |
| * | |
| * create a new Catalog, this type is shared both by XML and | |
| * SGML catalogs, but the acceptable types values differs. | |
| * | |
| * Returns the xmlCatalogPtr or NULL in case of error | |
| */ | |
| static xmlCatalogPtr | |
| xmlCreateNewCatalog(xmlCatalogType type, xmlCatalogPrefer prefer) { | |
| xmlCatalogPtr ret; | |
| ret = (xmlCatalogPtr) xmlMalloc(sizeof(xmlCatalog)); | |
| if (ret == NULL) { | |
| xmlCatalogErrMemory("allocating catalog"); | |
| return(NULL); | |
| } | |
| memset(ret, 0, sizeof(xmlCatalog)); | |
| ret->type = type; | |
| ret->catalNr = 0; | |
| ret->catalMax = XML_MAX_SGML_CATA_DEPTH; | |
| ret->prefer = prefer; | |
| if (ret->type == XML_SGML_CATALOG_TYPE) | |
| ret->sgml = xmlHashCreate(10); | |
| return(ret); | |
| } | |
| /** | |
| * xmlFreeCatalog: | |
| * @catal: a Catalog | |
| * | |
| * Free the memory allocated to a Catalog | |
| */ | |
| void | |
| xmlFreeCatalog(xmlCatalogPtr catal) { | |
| if (catal == NULL) | |
| return; | |
| if (catal->xml != NULL) | |
| xmlFreeCatalogEntryList(catal->xml); | |
| if (catal->sgml != NULL) | |
| xmlHashFree(catal->sgml, | |
| (xmlHashDeallocator) xmlFreeCatalogEntry); | |
| xmlFree(catal); | |
| } | |
| /************************************************************************ | |
| * * | |
| * Serializing Catalogs * | |
| * * | |
| ************************************************************************/ | |
| #ifdef LIBXML_OUTPUT_ENABLED | |
| /** | |
| * xmlCatalogDumpEntry: | |
| * @entry: the catalog entry | |
| * @out: the file. | |
| * | |
| * Serialize an SGML Catalog entry | |
| */ | |
| static void | |
| xmlCatalogDumpEntry(xmlCatalogEntryPtr entry, FILE *out) { | |
| if ((entry == NULL) || (out == NULL)) | |
| return; | |
| switch (entry->type) { | |
| case SGML_CATA_ENTITY: | |
| fprintf(out, "ENTITY "); break; | |
| case SGML_CATA_PENTITY: | |
| fprintf(out, "ENTITY %%"); break; | |
| case SGML_CATA_DOCTYPE: | |
| fprintf(out, "DOCTYPE "); break; | |
| case SGML_CATA_LINKTYPE: | |
| fprintf(out, "LINKTYPE "); break; | |
| case SGML_CATA_NOTATION: | |
| fprintf(out, "NOTATION "); break; | |
| case SGML_CATA_PUBLIC: | |
| fprintf(out, "PUBLIC "); break; | |
| case SGML_CATA_SYSTEM: | |
| fprintf(out, "SYSTEM "); break; | |
| case SGML_CATA_DELEGATE: | |
| fprintf(out, "DELEGATE "); break; | |
| case SGML_CATA_BASE: | |
| fprintf(out, "BASE "); break; | |
| case SGML_CATA_CATALOG: | |
| fprintf(out, "CATALOG "); break; | |
| case SGML_CATA_DOCUMENT: | |
| fprintf(out, "DOCUMENT "); break; | |
| case SGML_CATA_SGMLDECL: | |
| fprintf(out, "SGMLDECL "); break; | |
| default: | |
| return; | |
| } | |
| switch (entry->type) { | |
| case SGML_CATA_ENTITY: | |
| case SGML_CATA_PENTITY: | |
| case SGML_CATA_DOCTYPE: | |
| case SGML_CATA_LINKTYPE: | |
| case SGML_CATA_NOTATION: | |
| fprintf(out, "%s", (const char *) entry->name); break; | |
| case SGML_CATA_PUBLIC: | |
| case SGML_CATA_SYSTEM: | |
| case SGML_CATA_SGMLDECL: | |
| case SGML_CATA_DOCUMENT: | |
| case SGML_CATA_CATALOG: | |
| case SGML_CATA_BASE: | |
| case SGML_CATA_DELEGATE: | |
| fprintf(out, "\"%s\"", entry->name); break; | |
| default: | |
| break; | |
| } | |
| switch (entry->type) { | |
| case SGML_CATA_ENTITY: | |
| case SGML_CATA_PENTITY: | |
| case SGML_CATA_DOCTYPE: | |
| case SGML_CATA_LINKTYPE: | |
| case SGML_CATA_NOTATION: | |
| case SGML_CATA_PUBLIC: | |
| case SGML_CATA_SYSTEM: | |
| case SGML_CATA_DELEGATE: | |
| fprintf(out, " \"%s\"", entry->value); break; | |
| default: | |
| break; | |
| } | |
| fprintf(out, "\n"); | |
| } | |
| /** | |
| * xmlDumpXMLCatalogNode: | |
| * @catal: top catalog entry | |
| * @catalog: pointer to the xml tree | |
| * @doc: the containing document | |
| * @ns: the current namespace | |
| * @cgroup: group node for group members | |
| * | |
| * Serializes a Catalog entry, called by xmlDumpXMLCatalog and recursively | |
| * for group entries | |
| */ | |
| static void xmlDumpXMLCatalogNode(xmlCatalogEntryPtr catal, xmlNodePtr catalog, | |
| xmlDocPtr doc, xmlNsPtr ns, xmlCatalogEntryPtr cgroup) { | |
| xmlNodePtr node; | |
| xmlCatalogEntryPtr cur; | |
| /* | |
| * add all the catalog entries | |
| */ | |
| cur = catal; | |
| while (cur != NULL) { | |
| if (cur->group == cgroup) { | |
| switch (cur->type) { | |
| case XML_CATA_REMOVED: | |
| break; | |
| case XML_CATA_BROKEN_CATALOG: | |
| case XML_CATA_CATALOG: | |
| if (cur == catal) { | |
| cur = cur->children; | |
| continue; | |
| } | |
| break; | |
| case XML_CATA_NEXT_CATALOG: | |
| node = xmlNewDocNode(doc, ns, BAD_CAST "nextCatalog", NULL); | |
| xmlSetProp(node, BAD_CAST "catalog", cur->value); | |
| xmlAddChild(catalog, node); | |
| break; | |
| case XML_CATA_NONE: | |
| break; | |
| case XML_CATA_GROUP: | |
| node = xmlNewDocNode(doc, ns, BAD_CAST "group", NULL); | |
| xmlSetProp(node, BAD_CAST "id", cur->name); | |
| if (cur->value != NULL) { | |
| xmlNsPtr xns; | |
| xns = xmlSearchNsByHref(doc, node, XML_XML_NAMESPACE); | |
| if (xns != NULL) | |
| xmlSetNsProp(node, xns, BAD_CAST "base", | |
| cur->value); | |
| } | |
| switch (cur->prefer) { | |
| case XML_CATA_PREFER_NONE: | |
| break; | |
| case XML_CATA_PREFER_PUBLIC: | |
| xmlSetProp(node, BAD_CAST "prefer", BAD_CAST "public"); | |
| break; | |
| case XML_CATA_PREFER_SYSTEM: | |
| xmlSetProp(node, BAD_CAST "prefer", BAD_CAST "system"); | |
| break; | |
| } | |
| xmlDumpXMLCatalogNode(cur->next, node, doc, ns, cur); | |
| xmlAddChild(catalog, node); | |
| break; | |
| case XML_CATA_PUBLIC: | |
| node = xmlNewDocNode(doc, ns, BAD_CAST "public", NULL); | |
| xmlSetProp(node, BAD_CAST "publicId", cur->name); | |
| xmlSetProp(node, BAD_CAST "uri", cur->value); | |
| xmlAddChild(catalog, node); | |
| break; | |
| case XML_CATA_SYSTEM: | |
| node = xmlNewDocNode(doc, ns, BAD_CAST "system", NULL); | |
| xmlSetProp(node, BAD_CAST "systemId", cur->name); | |
| xmlSetProp(node, BAD_CAST "uri", cur->value); | |
| xmlAddChild(catalog, node); | |
| break; | |
| case XML_CATA_REWRITE_SYSTEM: | |
| node = xmlNewDocNode(doc, ns, BAD_CAST "rewriteSystem", NULL); | |
| xmlSetProp(node, BAD_CAST "systemIdStartString", cur->name); | |
| xmlSetProp(node, BAD_CAST "rewritePrefix", cur->value); | |
| xmlAddChild(catalog, node); | |
| break; | |
| case XML_CATA_DELEGATE_PUBLIC: | |
| node = xmlNewDocNode(doc, ns, BAD_CAST "delegatePublic", NULL); | |
| xmlSetProp(node, BAD_CAST "publicIdStartString", cur->name); | |
| xmlSetProp(node, BAD_CAST "catalog", cur->value); | |
| xmlAddChild(catalog, node); | |
| break; | |
| case XML_CATA_DELEGATE_SYSTEM: | |
| node = xmlNewDocNode(doc, ns, BAD_CAST "delegateSystem", NULL); | |
| xmlSetProp(node, BAD_CAST "systemIdStartString", cur->name); | |
| xmlSetProp(node, BAD_CAST "catalog", cur->value); | |
| xmlAddChild(catalog, node); | |
| break; | |
| case XML_CATA_URI: | |
| node = xmlNewDocNode(doc, ns, BAD_CAST "uri", NULL); | |
| xmlSetProp(node, BAD_CAST "name", cur->name); | |
| xmlSetProp(node, BAD_CAST "uri", cur->value); | |
| xmlAddChild(catalog, node); | |
| break; | |
| case XML_CATA_REWRITE_URI: | |
| node = xmlNewDocNode(doc, ns, BAD_CAST "rewriteURI", NULL); | |
| xmlSetProp(node, BAD_CAST "uriStartString", cur->name); | |
| xmlSetProp(node, BAD_CAST "rewritePrefix", cur->value); | |
| xmlAddChild(catalog, node); | |
| break; | |
| case XML_CATA_DELEGATE_URI: | |
| node = xmlNewDocNode(doc, ns, BAD_CAST "delegateURI", NULL); | |
| xmlSetProp(node, BAD_CAST "uriStartString", cur->name); | |
| xmlSetProp(node, BAD_CAST "catalog", cur->value); | |
| xmlAddChild(catalog, node); | |
| break; | |
| case SGML_CATA_SYSTEM: | |
| case SGML_CATA_PUBLIC: | |
| case SGML_CATA_ENTITY: | |
| case SGML_CATA_PENTITY: | |
| case SGML_CATA_DOCTYPE: | |
| case SGML_CATA_LINKTYPE: | |
| case SGML_CATA_NOTATION: | |
| case SGML_CATA_DELEGATE: | |
| case SGML_CATA_BASE: | |
| case SGML_CATA_CATALOG: | |
| case SGML_CATA_DOCUMENT: | |
| case SGML_CATA_SGMLDECL: | |
| break; | |
| } | |
| } | |
| cur = cur->next; | |
| } | |
| } | |
| static int | |
| xmlDumpXMLCatalog(FILE *out, xmlCatalogEntryPtr catal) { | |
| int ret; | |
| xmlDocPtr doc; | |
| xmlNsPtr ns; | |
| xmlDtdPtr dtd; | |
| xmlNodePtr catalog; | |
| xmlOutputBufferPtr buf; | |
| /* | |
| * Rebuild a catalog | |
| */ | |
| doc = xmlNewDoc(NULL); | |
| if (doc == NULL) | |
| return(-1); | |
| dtd = xmlNewDtd(doc, BAD_CAST "catalog", | |
| BAD_CAST "-//OASIS//DTD Entity Resolution XML Catalog V1.0//EN", | |
| BAD_CAST "http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd"); | |
| xmlAddChild((xmlNodePtr) doc, (xmlNodePtr) dtd); | |
| ns = xmlNewNs(NULL, XML_CATALOGS_NAMESPACE, NULL); | |
| if (ns == NULL) { | |
| xmlFreeDoc(doc); | |
| return(-1); | |
| } | |
| catalog = xmlNewDocNode(doc, ns, BAD_CAST "catalog", NULL); | |
| if (catalog == NULL) { | |
| xmlFreeNs(ns); | |
| xmlFreeDoc(doc); | |
| return(-1); | |
| } | |
| catalog->nsDef = ns; | |
| xmlAddChild((xmlNodePtr) doc, catalog); | |
| xmlDumpXMLCatalogNode(catal, catalog, doc, ns, NULL); | |
| /* | |
| * reserialize it | |
| */ | |
| buf = xmlOutputBufferCreateFile(out, NULL); | |
| if (buf == NULL) { | |
| xmlFreeDoc(doc); | |
| return(-1); | |
| } | |
| ret = xmlSaveFormatFileTo(buf, doc, NULL, 1); | |
| /* | |
| * Free it | |
| */ | |
| xmlFreeDoc(doc); | |
| return(ret); | |
| } | |
| #endif /* LIBXML_OUTPUT_ENABLED */ | |
| /************************************************************************ | |
| * * | |
| * Converting SGML Catalogs to XML * | |
| * * | |
| ************************************************************************/ | |
| /** | |
| * xmlCatalogConvertEntry: | |
| * @entry: the entry | |
| * @catal: pointer to the catalog being converted | |
| * | |
| * Convert one entry from the catalog | |
| */ | |
| static void | |
| xmlCatalogConvertEntry(xmlCatalogEntryPtr entry, xmlCatalogPtr catal) { | |
| if ((entry == NULL) || (catal == NULL) || (catal->sgml == NULL) || | |
| (catal->xml == NULL)) | |
| return; | |
| switch (entry->type) { | |
| case SGML_CATA_ENTITY: | |
| entry->type = XML_CATA_PUBLIC; | |
| break; | |
| case SGML_CATA_PENTITY: | |
| entry->type = XML_CATA_PUBLIC; | |
| break; | |
| case SGML_CATA_DOCTYPE: | |
| entry->type = XML_CATA_PUBLIC; | |
| break; | |
| case SGML_CATA_LINKTYPE: | |
| entry->type = XML_CATA_PUBLIC; | |
| break; | |
| case SGML_CATA_NOTATION: | |
| entry->type = XML_CATA_PUBLIC; | |
| break; | |
| case SGML_CATA_PUBLIC: | |
| entry->type = XML_CATA_PUBLIC; | |
| break; | |
| case SGML_CATA_SYSTEM: | |
| entry->type = XML_CATA_SYSTEM; | |
| break; | |
| case SGML_CATA_DELEGATE: | |
| entry->type = XML_CATA_DELEGATE_PUBLIC; | |
| break; | |
| case SGML_CATA_CATALOG: | |
| entry->type = XML_CATA_CATALOG; | |
| break; | |
| default: | |
| xmlHashRemoveEntry(catal->sgml, entry->name, | |
| (xmlHashDeallocator) xmlFreeCatalogEntry); | |
| return; | |
| } | |
| /* | |
| * Conversion successful, remove from the SGML catalog | |
| * and add it to the default XML one | |
| */ | |
| xmlHashRemoveEntry(catal->sgml, entry->name, NULL); | |
| entry->parent = catal->xml; | |
| entry->next = NULL; | |
| if (catal->xml->children == NULL) | |
| catal->xml->children = entry; | |
| else { | |
| xmlCatalogEntryPtr prev; | |
| prev = catal->xml->children; | |
| while (prev->next != NULL) | |
| prev = prev->next; | |
| prev->next = entry; | |
| } | |
| } | |
| /** | |
| * xmlConvertSGMLCatalog: | |
| * @catal: the catalog | |
| * | |
| * Convert all the SGML catalog entries as XML ones | |
| * | |
| * Returns the number of entries converted if successful, -1 otherwise | |
| */ | |
| int | |
| xmlConvertSGMLCatalog(xmlCatalogPtr catal) { | |
| if ((catal == NULL) || (catal->type != XML_SGML_CATALOG_TYPE)) | |
| return(-1); | |
| if (xmlDebugCatalogs) { | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Converting SGML catalog to XML\n"); | |
| } | |
| xmlHashScan(catal->sgml, | |
| (xmlHashScanner) xmlCatalogConvertEntry, | |
| &catal); | |
| return(0); | |
| } | |
| /************************************************************************ | |
| * * | |
| * Helper function * | |
| * * | |
| ************************************************************************/ | |
| /** | |
| * xmlCatalogUnWrapURN: | |
| * @urn: an "urn:publicid:" to unwrap | |
| * | |
| * Expand the URN into the equivalent Public Identifier | |
| * | |
| * Returns the new identifier or NULL, the string must be deallocated | |
| * by the caller. | |
| */ | |
| static xmlChar * | |
| xmlCatalogUnWrapURN(const xmlChar *urn) { | |
| xmlChar result[2000]; | |
| unsigned int i = 0; | |
| if (xmlStrncmp(urn, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) | |
| return(NULL); | |
| urn += sizeof(XML_URN_PUBID) - 1; | |
| while (*urn != 0) { | |
| if (i > sizeof(result) - 4) | |
| break; | |
| if (*urn == '+') { | |
| result[i++] = ' '; | |
| urn++; | |
| } else if (*urn == ':') { | |
| result[i++] = '/'; | |
| result[i++] = '/'; | |
| urn++; | |
| } else if (*urn == ';') { | |
| result[i++] = ':'; | |
| result[i++] = ':'; | |
| urn++; | |
| } else if (*urn == '%') { | |
| if ((urn[1] == '2') && (urn[2] == 'B')) | |
| result[i++] = '+'; | |
| else if ((urn[1] == '3') && (urn[2] == 'A')) | |
| result[i++] = ':'; | |
| else if ((urn[1] == '2') && (urn[2] == 'F')) | |
| result[i++] = '/'; | |
| else if ((urn[1] == '3') && (urn[2] == 'B')) | |
| result[i++] = ';'; | |
| else if ((urn[1] == '2') && (urn[2] == '7')) | |
| result[i++] = '\''; | |
| else if ((urn[1] == '3') && (urn[2] == 'F')) | |
| result[i++] = '?'; | |
| else if ((urn[1] == '2') && (urn[2] == '3')) | |
| result[i++] = '#'; | |
| else if ((urn[1] == '2') && (urn[2] == '5')) | |
| result[i++] = '%'; | |
| else { | |
| result[i++] = *urn; | |
| urn++; | |
| continue; | |
| } | |
| urn += 3; | |
| } else { | |
| result[i++] = *urn; | |
| urn++; | |
| } | |
| } | |
| result[i] = 0; | |
| return(xmlStrdup(result)); | |
| } | |
| /** | |
| * xmlParseCatalogFile: | |
| * @filename: the filename | |
| * | |
| * parse an XML file and build a tree. It's like xmlParseFile() | |
| * except it bypass all catalog lookups. | |
| * | |
| * Returns the resulting document tree or NULL in case of error | |
| */ | |
| xmlDocPtr | |
| xmlParseCatalogFile(const char *filename) { | |
| xmlDocPtr ret; | |
| xmlParserCtxtPtr ctxt; | |
| char *directory = NULL; | |
| xmlParserInputPtr inputStream; | |
| xmlParserInputBufferPtr buf; | |
| ctxt = xmlNewParserCtxt(); | |
| if (ctxt == NULL) { | |
| #ifdef LIBXML_SAX1_ENABLED | |
| if (xmlDefaultSAXHandler.error != NULL) { | |
| xmlDefaultSAXHandler.error(NULL, "out of memory\n"); | |
| } | |
| #endif | |
| return(NULL); | |
| } | |
| buf = xmlParserInputBufferCreateFilename(filename, XML_CHAR_ENCODING_NONE); | |
| if (buf == NULL) { | |
| xmlFreeParserCtxt(ctxt); | |
| return(NULL); | |
| } | |
| inputStream = xmlNewInputStream(ctxt); | |
| if (inputStream == NULL) { | |
| xmlFreeParserCtxt(ctxt); | |
| return(NULL); | |
| } | |
| inputStream->filename = (char *) xmlCanonicPath((const xmlChar *)filename); | |
| inputStream->buf = buf; | |
| xmlBufResetInput(buf->buffer, inputStream); | |
| inputPush(ctxt, inputStream); | |
| if ((ctxt->directory == NULL) && (directory == NULL)) | |
| directory = xmlParserGetDirectory(filename); | |
| if ((ctxt->directory == NULL) && (directory != NULL)) | |
| ctxt->directory = directory; | |
| ctxt->valid = 0; | |
| ctxt->validate = 0; | |
| ctxt->loadsubset = 0; | |
| ctxt->pedantic = 0; | |
| ctxt->dictNames = 1; | |
| xmlParseDocument(ctxt); | |
| if (ctxt->wellFormed) | |
| ret = ctxt->myDoc; | |
| else { | |
| ret = NULL; | |
| xmlFreeDoc(ctxt->myDoc); | |
| ctxt->myDoc = NULL; | |
| } | |
| xmlFreeParserCtxt(ctxt); | |
| return(ret); | |
| } | |
| /** | |
| * xmlLoadFileContent: | |
| * @filename: a file path | |
| * | |
| * Load a file content into memory. | |
| * | |
| * Returns a pointer to the 0 terminated string or NULL in case of error | |
| */ | |
| static xmlChar * | |
| xmlLoadFileContent(const char *filename) | |
| { | |
| #ifdef HAVE_STAT | |
| int fd; | |
| #else | |
| FILE *fd; | |
| #endif | |
| int len; | |
| long size; | |
| #ifdef HAVE_STAT | |
| struct stat info; | |
| #endif | |
| xmlChar *content; | |
| if (filename == NULL) | |
| return (NULL); | |
| #ifdef HAVE_STAT | |
| if (stat(filename, &info) < 0) | |
| return (NULL); | |
| #endif | |
| #ifdef HAVE_STAT | |
| if ((fd = open(filename, O_RDONLY)) < 0) | |
| #else | |
| if ((fd = fopen(filename, "rb")) == NULL) | |
| #endif | |
| { | |
| return (NULL); | |
| } | |
| #ifdef HAVE_STAT | |
| size = info.st_size; | |
| #else | |
| if (fseek(fd, 0, SEEK_END) || (size = ftell(fd)) == EOF || fseek(fd, 0, SEEK_SET)) { /* File operations denied? ok, just close and return failure */ | |
| fclose(fd); | |
| return (NULL); | |
| } | |
| #endif | |
| content = (xmlChar*)xmlMallocAtomic(size + 10); | |
| if (content == NULL) { | |
| xmlCatalogErrMemory("allocating catalog data"); | |
| #ifdef HAVE_STAT | |
| close(fd); | |
| #else | |
| fclose(fd); | |
| #endif | |
| return (NULL); | |
| } | |
| #ifdef HAVE_STAT | |
| len = read(fd, content, size); | |
| close(fd); | |
| #else | |
| len = fread(content, 1, size, fd); | |
| fclose(fd); | |
| #endif | |
| if (len < 0) { | |
| xmlFree(content); | |
| return (NULL); | |
| } | |
| content[len] = 0; | |
| return(content); | |
| } | |
| /** | |
| * xmlCatalogNormalizePublic: | |
| * @pubID: the public ID string | |
| * | |
| * Normalizes the Public Identifier | |
| * | |
| * Implements 6.2. Public Identifier Normalization | |
| * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html | |
| * | |
| * Returns the new string or NULL, the string must be deallocated | |
| * by the caller. | |
| */ | |
| static xmlChar * | |
| xmlCatalogNormalizePublic(const xmlChar *pubID) | |
| { | |
| int ok = 1; | |
| int white; | |
| const xmlChar *p; | |
| xmlChar *ret; | |
| xmlChar *q; | |
| if (pubID == NULL) | |
| return(NULL); | |
| white = 1; | |
| for (p = pubID;*p != 0 && ok;p++) { | |
| if (!xmlIsBlank_ch(*p)) | |
| white = 0; | |
| else if (*p == 0x20 && !white) | |
| white = 1; | |
| else | |
| ok = 0; | |
| } | |
| if (ok && !white) /* is normalized */ | |
| return(NULL); | |
| ret = xmlStrdup(pubID); | |
| q = ret; | |
| white = 0; | |
| for (p = pubID;*p != 0;p++) { | |
| if (xmlIsBlank_ch(*p)) { | |
| if (q != ret) | |
| white = 1; | |
| } else { | |
| if (white) { | |
| *(q++) = 0x20; | |
| white = 0; | |
| } | |
| *(q++) = *p; | |
| } | |
| } | |
| *q = 0; | |
| return(ret); | |
| } | |
| /************************************************************************ | |
| * * | |
| * The XML Catalog parser * | |
| * * | |
| ************************************************************************/ | |
| static xmlCatalogEntryPtr | |
| xmlParseXMLCatalogFile(xmlCatalogPrefer prefer, const xmlChar *filename); | |
| static void | |
| xmlParseXMLCatalogNodeList(xmlNodePtr cur, xmlCatalogPrefer prefer, | |
| xmlCatalogEntryPtr parent, xmlCatalogEntryPtr cgroup); | |
| static xmlChar * | |
| xmlCatalogListXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID, | |
| const xmlChar *sysID); | |
| static xmlChar * | |
| xmlCatalogListXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI); | |
| /** | |
| * xmlGetXMLCatalogEntryType: | |
| * @name: the name | |
| * | |
| * lookup the internal type associated to an XML catalog entry name | |
| * | |
| * Returns the type associated with that name | |
| */ | |
| static xmlCatalogEntryType | |
| xmlGetXMLCatalogEntryType(const xmlChar *name) { | |
| xmlCatalogEntryType type = XML_CATA_NONE; | |
| if (xmlStrEqual(name, (const xmlChar *) "system")) | |
| type = XML_CATA_SYSTEM; | |
| else if (xmlStrEqual(name, (const xmlChar *) "public")) | |
| type = XML_CATA_PUBLIC; | |
| else if (xmlStrEqual(name, (const xmlChar *) "rewriteSystem")) | |
| type = XML_CATA_REWRITE_SYSTEM; | |
| else if (xmlStrEqual(name, (const xmlChar *) "delegatePublic")) | |
| type = XML_CATA_DELEGATE_PUBLIC; | |
| else if (xmlStrEqual(name, (const xmlChar *) "delegateSystem")) | |
| type = XML_CATA_DELEGATE_SYSTEM; | |
| else if (xmlStrEqual(name, (const xmlChar *) "uri")) | |
| type = XML_CATA_URI; | |
| else if (xmlStrEqual(name, (const xmlChar *) "rewriteURI")) | |
| type = XML_CATA_REWRITE_URI; | |
| else if (xmlStrEqual(name, (const xmlChar *) "delegateURI")) | |
| type = XML_CATA_DELEGATE_URI; | |
| else if (xmlStrEqual(name, (const xmlChar *) "nextCatalog")) | |
| type = XML_CATA_NEXT_CATALOG; | |
| else if (xmlStrEqual(name, (const xmlChar *) "catalog")) | |
| type = XML_CATA_CATALOG; | |
| return(type); | |
| } | |
| /** | |
| * xmlParseXMLCatalogOneNode: | |
| * @cur: the XML node | |
| * @type: the type of Catalog entry | |
| * @name: the name of the node | |
| * @attrName: the attribute holding the value | |
| * @uriAttrName: the attribute holding the URI-Reference | |
| * @prefer: the PUBLIC vs. SYSTEM current preference value | |
| * @cgroup: the group which includes this node | |
| * | |
| * Finishes the examination of an XML tree node of a catalog and build | |
| * a Catalog entry from it. | |
| * | |
| * Returns the new Catalog entry node or NULL in case of error. | |
| */ | |
| static xmlCatalogEntryPtr | |
| xmlParseXMLCatalogOneNode(xmlNodePtr cur, xmlCatalogEntryType type, | |
| const xmlChar *name, const xmlChar *attrName, | |
| const xmlChar *uriAttrName, xmlCatalogPrefer prefer, | |
| xmlCatalogEntryPtr cgroup) { | |
| int ok = 1; | |
| xmlChar *uriValue; | |
| xmlChar *nameValue = NULL; | |
| xmlChar *base = NULL; | |
| xmlChar *URL = NULL; | |
| xmlCatalogEntryPtr ret = NULL; | |
| if (attrName != NULL) { | |
| nameValue = xmlGetProp(cur, attrName); | |
| if (nameValue == NULL) { | |
| xmlCatalogErr(ret, cur, XML_CATALOG_MISSING_ATTR, | |
| "%s entry lacks '%s'\n", name, attrName, NULL); | |
| ok = 0; | |
| } | |
| } | |
| uriValue = xmlGetProp(cur, uriAttrName); | |
| if (uriValue == NULL) { | |
| xmlCatalogErr(ret, cur, XML_CATALOG_MISSING_ATTR, | |
| "%s entry lacks '%s'\n", name, uriAttrName, NULL); | |
| ok = 0; | |
| } | |
| if (!ok) { | |
| if (nameValue != NULL) | |
| xmlFree(nameValue); | |
| if (uriValue != NULL) | |
| xmlFree(uriValue); | |
| return(NULL); | |
| } | |
| base = xmlNodeGetBase(cur->doc, cur); | |
| URL = xmlBuildURI(uriValue, base); | |
| if (URL != NULL) { | |
| if (xmlDebugCatalogs > 1) { | |
| if (nameValue != NULL) | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Found %s: '%s' '%s'\n", name, nameValue, URL); | |
| else | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Found %s: '%s'\n", name, URL); | |
| } | |
| ret = xmlNewCatalogEntry(type, nameValue, uriValue, URL, prefer, cgroup); | |
| } else { | |
| xmlCatalogErr(ret, cur, XML_CATALOG_ENTRY_BROKEN, | |
| "%s entry '%s' broken ?: %s\n", name, uriAttrName, uriValue); | |
| } | |
| if (nameValue != NULL) | |
| xmlFree(nameValue); | |
| if (uriValue != NULL) | |
| xmlFree(uriValue); | |
| if (base != NULL) | |
| xmlFree(base); | |
| if (URL != NULL) | |
| xmlFree(URL); | |
| return(ret); | |
| } | |
| /** | |
| * xmlParseXMLCatalogNode: | |
| * @cur: the XML node | |
| * @prefer: the PUBLIC vs. SYSTEM current preference value | |
| * @parent: the parent Catalog entry | |
| * @cgroup: the group which includes this node | |
| * | |
| * Examines an XML tree node of a catalog and build | |
| * a Catalog entry from it adding it to its parent. The examination can | |
| * be recursive. | |
| */ | |
| static void | |
| xmlParseXMLCatalogNode(xmlNodePtr cur, xmlCatalogPrefer prefer, | |
| xmlCatalogEntryPtr parent, xmlCatalogEntryPtr cgroup) | |
| { | |
| xmlChar *base = NULL; | |
| xmlCatalogEntryPtr entry = NULL; | |
| if (cur == NULL) | |
| return; | |
| if (xmlStrEqual(cur->name, BAD_CAST "group")) { | |
| xmlChar *prop; | |
| xmlCatalogPrefer pref = XML_CATA_PREFER_NONE; | |
| prop = xmlGetProp(cur, BAD_CAST "prefer"); | |
| if (prop != NULL) { | |
| if (xmlStrEqual(prop, BAD_CAST "system")) { | |
| prefer = XML_CATA_PREFER_SYSTEM; | |
| } else if (xmlStrEqual(prop, BAD_CAST "public")) { | |
| prefer = XML_CATA_PREFER_PUBLIC; | |
| } else { | |
| xmlCatalogErr(parent, cur, XML_CATALOG_PREFER_VALUE, | |
| "Invalid value for prefer: '%s'\n", | |
| prop, NULL, NULL); | |
| } | |
| xmlFree(prop); | |
| pref = prefer; | |
| } | |
| prop = xmlGetProp(cur, BAD_CAST "id"); | |
| base = xmlGetNsProp(cur, BAD_CAST "base", XML_XML_NAMESPACE); | |
| entry = xmlNewCatalogEntry(XML_CATA_GROUP, prop, base, NULL, pref, cgroup); | |
| xmlFree(prop); | |
| } else if (xmlStrEqual(cur->name, BAD_CAST "public")) { | |
| entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_PUBLIC, | |
| BAD_CAST "public", BAD_CAST "publicId", BAD_CAST "uri", prefer, cgroup); | |
| } else if (xmlStrEqual(cur->name, BAD_CAST "system")) { | |
| entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_SYSTEM, | |
| BAD_CAST "system", BAD_CAST "systemId", BAD_CAST "uri", prefer, cgroup); | |
| } else if (xmlStrEqual(cur->name, BAD_CAST "rewriteSystem")) { | |
| entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_REWRITE_SYSTEM, | |
| BAD_CAST "rewriteSystem", BAD_CAST "systemIdStartString", | |
| BAD_CAST "rewritePrefix", prefer, cgroup); | |
| } else if (xmlStrEqual(cur->name, BAD_CAST "delegatePublic")) { | |
| entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_PUBLIC, | |
| BAD_CAST "delegatePublic", BAD_CAST "publicIdStartString", | |
| BAD_CAST "catalog", prefer, cgroup); | |
| } else if (xmlStrEqual(cur->name, BAD_CAST "delegateSystem")) { | |
| entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_SYSTEM, | |
| BAD_CAST "delegateSystem", BAD_CAST "systemIdStartString", | |
| BAD_CAST "catalog", prefer, cgroup); | |
| } else if (xmlStrEqual(cur->name, BAD_CAST "uri")) { | |
| entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_URI, | |
| BAD_CAST "uri", BAD_CAST "name", | |
| BAD_CAST "uri", prefer, cgroup); | |
| } else if (xmlStrEqual(cur->name, BAD_CAST "rewriteURI")) { | |
| entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_REWRITE_URI, | |
| BAD_CAST "rewriteURI", BAD_CAST "uriStartString", | |
| BAD_CAST "rewritePrefix", prefer, cgroup); | |
| } else if (xmlStrEqual(cur->name, BAD_CAST "delegateURI")) { | |
| entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_URI, | |
| BAD_CAST "delegateURI", BAD_CAST "uriStartString", | |
| BAD_CAST "catalog", prefer, cgroup); | |
| } else if (xmlStrEqual(cur->name, BAD_CAST "nextCatalog")) { | |
| entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_NEXT_CATALOG, | |
| BAD_CAST "nextCatalog", NULL, | |
| BAD_CAST "catalog", prefer, cgroup); | |
| } | |
| if (entry != NULL) { | |
| if (parent != NULL) { | |
| entry->parent = parent; | |
| if (parent->children == NULL) | |
| parent->children = entry; | |
| else { | |
| xmlCatalogEntryPtr prev; | |
| prev = parent->children; | |
| while (prev->next != NULL) | |
| prev = prev->next; | |
| prev->next = entry; | |
| } | |
| } | |
| if (entry->type == XML_CATA_GROUP) { | |
| /* | |
| * Recurse to propagate prefer to the subtree | |
| * (xml:base handling is automated) | |
| */ | |
| xmlParseXMLCatalogNodeList(cur->children, prefer, parent, entry); | |
| } | |
| } | |
| if (base != NULL) | |
| xmlFree(base); | |
| } | |
| /** | |
| * xmlParseXMLCatalogNodeList: | |
| * @cur: the XML node list of siblings | |
| * @prefer: the PUBLIC vs. SYSTEM current preference value | |
| * @parent: the parent Catalog entry | |
| * @cgroup: the group which includes this list | |
| * | |
| * Examines a list of XML sibling nodes of a catalog and build | |
| * a list of Catalog entry from it adding it to the parent. | |
| * The examination will recurse to examine node subtrees. | |
| */ | |
| static void | |
| xmlParseXMLCatalogNodeList(xmlNodePtr cur, xmlCatalogPrefer prefer, | |
| xmlCatalogEntryPtr parent, xmlCatalogEntryPtr cgroup) { | |
| while (cur != NULL) { | |
| if ((cur->ns != NULL) && (cur->ns->href != NULL) && | |
| (xmlStrEqual(cur->ns->href, XML_CATALOGS_NAMESPACE))) { | |
| xmlParseXMLCatalogNode(cur, prefer, parent, cgroup); | |
| } | |
| cur = cur->next; | |
| } | |
| /* TODO: sort the list according to REWRITE lengths and prefer value */ | |
| } | |
| /** | |
| * xmlParseXMLCatalogFile: | |
| * @prefer: the PUBLIC vs. SYSTEM current preference value | |
| * @filename: the filename for the catalog | |
| * | |
| * Parses the catalog file to extract the XML tree and then analyze the | |
| * tree to build a list of Catalog entries corresponding to this catalog | |
| * | |
| * Returns the resulting Catalog entries list | |
| */ | |
| static xmlCatalogEntryPtr | |
| xmlParseXMLCatalogFile(xmlCatalogPrefer prefer, const xmlChar *filename) { | |
| xmlDocPtr doc; | |
| xmlNodePtr cur; | |
| xmlChar *prop; | |
| xmlCatalogEntryPtr parent = NULL; | |
| if (filename == NULL) | |
| return(NULL); | |
| doc = xmlParseCatalogFile((const char *) filename); | |
| if (doc == NULL) { | |
| if (xmlDebugCatalogs) | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Failed to parse catalog %s\n", filename); | |
| return(NULL); | |
| } | |
| if (xmlDebugCatalogs) | |
| xmlGenericError(xmlGenericErrorContext, | |
| "%d Parsing catalog %s\n", xmlGetThreadId(), filename); | |
| cur = xmlDocGetRootElement(doc); | |
| if ((cur != NULL) && (xmlStrEqual(cur->name, BAD_CAST "catalog")) && | |
| (cur->ns != NULL) && (cur->ns->href != NULL) && | |
| (xmlStrEqual(cur->ns->href, XML_CATALOGS_NAMESPACE))) { | |
| parent = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL, | |
| (const xmlChar *)filename, NULL, prefer, NULL); | |
| if (parent == NULL) { | |
| xmlFreeDoc(doc); | |
| return(NULL); | |
| } | |
| prop = xmlGetProp(cur, BAD_CAST "prefer"); | |
| if (prop != NULL) { | |
| if (xmlStrEqual(prop, BAD_CAST "system")) { | |
| prefer = XML_CATA_PREFER_SYSTEM; | |
| } else if (xmlStrEqual(prop, BAD_CAST "public")) { | |
| prefer = XML_CATA_PREFER_PUBLIC; | |
| } else { | |
| xmlCatalogErr(NULL, cur, XML_CATALOG_PREFER_VALUE, | |
| "Invalid value for prefer: '%s'\n", | |
| prop, NULL, NULL); | |
| } | |
| xmlFree(prop); | |
| } | |
| cur = cur->children; | |
| xmlParseXMLCatalogNodeList(cur, prefer, parent, NULL); | |
| } else { | |
| xmlCatalogErr(NULL, (xmlNodePtr) doc, XML_CATALOG_NOT_CATALOG, | |
| "File %s is not an XML Catalog\n", | |
| filename, NULL, NULL); | |
| xmlFreeDoc(doc); | |
| return(NULL); | |
| } | |
| xmlFreeDoc(doc); | |
| return(parent); | |
| } | |
| /** | |
| * xmlFetchXMLCatalogFile: | |
| * @catal: an existing but incomplete catalog entry | |
| * | |
| * Fetch and parse the subcatalog referenced by an entry | |
| * | |
| * Returns 0 in case of success, -1 otherwise | |
| */ | |
| static int | |
| xmlFetchXMLCatalogFile(xmlCatalogEntryPtr catal) { | |
| xmlCatalogEntryPtr doc; | |
| if (catal == NULL) | |
| return(-1); | |
| if (catal->URL == NULL) | |
| return(-1); | |
| /* | |
| * lock the whole catalog for modification | |
| */ | |
| xmlRMutexLock(xmlCatalogMutex); | |
| if (catal->children != NULL) { | |
| /* Okay someone else did it in the meantime */ | |
| xmlRMutexUnlock(xmlCatalogMutex); | |
| return(0); | |
| } | |
| if (xmlCatalogXMLFiles != NULL) { | |
| doc = (xmlCatalogEntryPtr) | |
| xmlHashLookup(xmlCatalogXMLFiles, catal->URL); | |
| if (doc != NULL) { | |
| if (xmlDebugCatalogs) | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Found %s in file hash\n", catal->URL); | |
| if (catal->type == XML_CATA_CATALOG) | |
| catal->children = doc->children; | |
| else | |
| catal->children = doc; | |
| catal->dealloc = 0; | |
| xmlRMutexUnlock(xmlCatalogMutex); | |
| return(0); | |
| } | |
| if (xmlDebugCatalogs) | |
| xmlGenericError(xmlGenericErrorContext, | |
| "%s not found in file hash\n", catal->URL); | |
| } | |
| /* | |
| * Fetch and parse. Note that xmlParseXMLCatalogFile does not | |
| * use the existing catalog, there is no recursion allowed at | |
| * that level. | |
| */ | |
| doc = xmlParseXMLCatalogFile(catal->prefer, catal->URL); | |
| if (doc == NULL) { | |
| catal->type = XML_CATA_BROKEN_CATALOG; | |
| xmlRMutexUnlock(xmlCatalogMutex); | |
| return(-1); | |
| } | |
| if (catal->type == XML_CATA_CATALOG) | |
| catal->children = doc->children; | |
| else | |
| catal->children = doc; | |
| doc->dealloc = 1; | |
| if (xmlCatalogXMLFiles == NULL) | |
| xmlCatalogXMLFiles = xmlHashCreate(10); | |
| if (xmlCatalogXMLFiles != NULL) { | |
| if (xmlDebugCatalogs) | |
| xmlGenericError(xmlGenericErrorContext, | |
| "%s added to file hash\n", catal->URL); | |
| xmlHashAddEntry(xmlCatalogXMLFiles, catal->URL, doc); | |
| } | |
| xmlRMutexUnlock(xmlCatalogMutex); | |
| return(0); | |
| } | |
| /************************************************************************ | |
| * * | |
| * XML Catalog handling * | |
| * * | |
| ************************************************************************/ | |
| /** | |
| * xmlAddXMLCatalog: | |
| * @catal: top of an XML catalog | |
| * @type: the type of record to add to the catalog | |
| * @orig: the system, public or prefix to match (or NULL) | |
| * @replace: the replacement value for the match | |
| * | |
| * Add an entry in the XML catalog, it may overwrite existing but | |
| * different entries. | |
| * | |
| * Returns 0 if successful, -1 otherwise | |
| */ | |
| static int | |
| xmlAddXMLCatalog(xmlCatalogEntryPtr catal, const xmlChar *type, | |
| const xmlChar *orig, const xmlChar *replace) { | |
| xmlCatalogEntryPtr cur; | |
| xmlCatalogEntryType typ; | |
| int doregister = 0; | |
| if ((catal == NULL) || | |
| ((catal->type != XML_CATA_CATALOG) && | |
| (catal->type != XML_CATA_BROKEN_CATALOG))) | |
| return(-1); | |
| if (catal->children == NULL) { | |
| xmlFetchXMLCatalogFile(catal); | |
| } | |
| if (catal->children == NULL) | |
| doregister = 1; | |
| typ = xmlGetXMLCatalogEntryType(type); | |
| if (typ == XML_CATA_NONE) { | |
| if (xmlDebugCatalogs) | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Failed to add unknown element %s to catalog\n", type); | |
| return(-1); | |
| } | |
| cur = catal->children; | |
| /* | |
| * Might be a simple "update in place" | |
| */ | |
| if (cur != NULL) { | |
| while (cur != NULL) { | |
| if ((orig != NULL) && (cur->type == typ) && | |
| (xmlStrEqual(orig, cur->name))) { | |
| if (xmlDebugCatalogs) | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Updating element %s to catalog\n", type); | |
| if (cur->value != NULL) | |
| xmlFree(cur->value); | |
| if (cur->URL != NULL) | |
| xmlFree(cur->URL); | |
| cur->value = xmlStrdup(replace); | |
| cur->URL = xmlStrdup(replace); | |
| return(0); | |
| } | |
| if (cur->next == NULL) | |
| break; | |
| cur = cur->next; | |
| } | |
| } | |
| if (xmlDebugCatalogs) | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Adding element %s to catalog\n", type); | |
| if (cur == NULL) | |
| catal->children = xmlNewCatalogEntry(typ, orig, replace, | |
| NULL, catal->prefer, NULL); | |
| else | |
| cur->next = xmlNewCatalogEntry(typ, orig, replace, | |
| NULL, catal->prefer, NULL); | |
| if (doregister) { | |
| catal->type = XML_CATA_CATALOG; | |
| cur = (xmlCatalogEntryPtr)xmlHashLookup(xmlCatalogXMLFiles, catal->URL); | |
| if (cur != NULL) | |
| cur->children = catal->children; | |
| } | |
| return(0); | |
| } | |
| /** | |
| * xmlDelXMLCatalog: | |
| * @catal: top of an XML catalog | |
| * @value: the value to remove from the catalog | |
| * | |
| * Remove entries in the XML catalog where the value or the URI | |
| * is equal to @value | |
| * | |
| * Returns the number of entries removed if successful, -1 otherwise | |
| */ | |
| static int | |
| xmlDelXMLCatalog(xmlCatalogEntryPtr catal, const xmlChar *value) { | |
| xmlCatalogEntryPtr cur; | |
| int ret = 0; | |
| if ((catal == NULL) || | |
| ((catal->type != XML_CATA_CATALOG) && | |
| (catal->type != XML_CATA_BROKEN_CATALOG))) | |
| return(-1); | |
| if (value == NULL) | |
| return(-1); | |
| if (catal->children == NULL) { | |
| xmlFetchXMLCatalogFile(catal); | |
| } | |
| /* | |
| * Scan the children | |
| */ | |
| cur = catal->children; | |
| while (cur != NULL) { | |
| if (((cur->name != NULL) && (xmlStrEqual(value, cur->name))) || | |
| (xmlStrEqual(value, cur->value))) { | |
| if (xmlDebugCatalogs) { | |
| if (cur->name != NULL) | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Removing element %s from catalog\n", cur->name); | |
| else | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Removing element %s from catalog\n", cur->value); | |
| } | |
| cur->type = XML_CATA_REMOVED; | |
| } | |
| cur = cur->next; | |
| } | |
| return(ret); | |
| } | |
| /** | |
| * xmlCatalogXMLResolve: | |
| * @catal: a catalog list | |
| * @pubID: the public ID string | |
| * @sysID: the system ID string | |
| * | |
| * Do a complete resolution lookup of an External Identifier for a | |
| * list of catalog entries. | |
| * | |
| * Implements (or tries to) 7.1. External Identifier Resolution | |
| * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html | |
| * | |
| * Returns the URI of the resource or NULL if not found | |
| */ | |
| static xmlChar * | |
| xmlCatalogXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID, | |
| const xmlChar *sysID) { | |
| xmlChar *ret = NULL; | |
| xmlCatalogEntryPtr cur; | |
| int haveDelegate = 0; | |
| int haveNext = 0; | |
| /* | |
| * protection against loops | |
| */ | |
| if (catal->depth > MAX_CATAL_DEPTH) { | |
| xmlCatalogErr(catal, NULL, XML_CATALOG_RECURSION, | |
| "Detected recursion in catalog %s\n", | |
| catal->name, NULL, NULL); | |
| return(NULL); | |
| } | |
| catal->depth++; | |
| /* | |
| * First tries steps 2/ 3/ 4/ if a system ID is provided. | |
| */ | |
| if (sysID != NULL) { | |
| xmlCatalogEntryPtr rewrite = NULL; | |
| int lenrewrite = 0, len; | |
| cur = catal; | |
| haveDelegate = 0; | |
| while (cur != NULL) { | |
| switch (cur->type) { | |
| case XML_CATA_SYSTEM: | |
| if (xmlStrEqual(sysID, cur->name)) { | |
| if (xmlDebugCatalogs) | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Found system match %s, using %s\n", | |
| cur->name, cur->URL); | |
| catal->depth--; | |
| return(xmlStrdup(cur->URL)); | |
| } | |
| break; | |
| case XML_CATA_REWRITE_SYSTEM: | |
| len = xmlStrlen(cur->name); | |
| if ((len > lenrewrite) && | |
| (!xmlStrncmp(sysID, cur->name, len))) { | |
| lenrewrite = len; | |
| rewrite = cur; | |
| } | |
| break; | |
| case XML_CATA_DELEGATE_SYSTEM: | |
| if (!xmlStrncmp(sysID, cur->name, xmlStrlen(cur->name))) | |
| haveDelegate++; | |
| break; | |
| case XML_CATA_NEXT_CATALOG: | |
| haveNext++; | |
| break; | |
| default: | |
| break; | |
| } | |
| cur = cur->next; | |
| } | |
| if (rewrite != NULL) { | |
| if (xmlDebugCatalogs) | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Using rewriting rule %s\n", rewrite->name); | |
| ret = xmlStrdup(rewrite->URL); | |
| if (ret != NULL) | |
| ret = xmlStrcat(ret, &sysID[lenrewrite]); | |
| catal->depth--; | |
| return(ret); | |
| } | |
| if (haveDelegate) { | |
| const xmlChar *delegates[MAX_DELEGATE]; | |
| int nbList = 0, i; | |
| /* | |
| * Assume the entries have been sorted by decreasing substring | |
| * matches when the list was produced. | |
| */ | |
| cur = catal; | |
| while (cur != NULL) { | |
| if ((cur->type == XML_CATA_DELEGATE_SYSTEM) && | |
| (!xmlStrncmp(sysID, cur->name, xmlStrlen(cur->name)))) { | |
| for (i = 0;i < nbList;i++) | |
| if (xmlStrEqual(cur->URL, delegates[i])) | |
| break; | |
| if (i < nbList) { | |
| cur = cur->next; | |
| continue; | |
| } | |
| if (nbList < MAX_DELEGATE) | |
| delegates[nbList++] = cur->URL; | |
| if (cur->children == NULL) { | |
| xmlFetchXMLCatalogFile(cur); | |
| } | |
| if (cur->children != NULL) { | |
| if (xmlDebugCatalogs) | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Trying system delegate %s\n", cur->URL); | |
| ret = xmlCatalogListXMLResolve( | |
| cur->children, NULL, sysID); | |
| if (ret != NULL) { | |
| catal->depth--; | |
| return(ret); | |
| } | |
| } | |
| } | |
| cur = cur->next; | |
| } | |
| /* | |
| * Apply the cut algorithm explained in 4/ | |
| */ | |
| catal->depth--; | |
| return(XML_CATAL_BREAK); | |
| } | |
| } | |
| /* | |
| * Then tries 5/ 6/ if a public ID is provided | |
| */ | |
| if (pubID != NULL) { | |
| cur = catal; | |
| haveDelegate = 0; | |
| while (cur != NULL) { | |
| switch (cur->type) { | |
| case XML_CATA_PUBLIC: | |
| if (xmlStrEqual(pubID, cur->name)) { | |
| if (xmlDebugCatalogs) | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Found public match %s\n", cur->name); | |
| catal->depth--; | |
| return(xmlStrdup(cur->URL)); | |
| } | |
| break; | |
| case XML_CATA_DELEGATE_PUBLIC: | |
| if (!xmlStrncmp(pubID, cur->name, xmlStrlen(cur->name)) && | |
| (cur->prefer == XML_CATA_PREFER_PUBLIC)) | |
| haveDelegate++; | |
| break; | |
| case XML_CATA_NEXT_CATALOG: | |
| if (sysID == NULL) | |
| haveNext++; | |
| break; | |
| default: | |
| break; | |
| } | |
| cur = cur->next; | |
| } | |
| if (haveDelegate) { | |
| const xmlChar *delegates[MAX_DELEGATE]; | |
| int nbList = 0, i; | |
| /* | |
| * Assume the entries have been sorted by decreasing substring | |
| * matches when the list was produced. | |
| */ | |
| cur = catal; | |
| while (cur != NULL) { | |
| if ((cur->type == XML_CATA_DELEGATE_PUBLIC) && | |
| (cur->prefer == XML_CATA_PREFER_PUBLIC) && | |
| (!xmlStrncmp(pubID, cur->name, xmlStrlen(cur->name)))) { | |
| for (i = 0;i < nbList;i++) | |
| if (xmlStrEqual(cur->URL, delegates[i])) | |
| break; | |
| if (i < nbList) { | |
| cur = cur->next; | |
| continue; | |
| } | |
| if (nbList < MAX_DELEGATE) | |
| delegates[nbList++] = cur->URL; | |
| if (cur->children == NULL) { | |
| xmlFetchXMLCatalogFile(cur); | |
| } | |
| if (cur->children != NULL) { | |
| if (xmlDebugCatalogs) | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Trying public delegate %s\n", cur->URL); | |
| ret = xmlCatalogListXMLResolve( | |
| cur->children, pubID, NULL); | |
| if (ret != NULL) { | |
| catal->depth--; | |
| return(ret); | |
| } | |
| } | |
| } | |
| cur = cur->next; | |
| } | |
| /* | |
| * Apply the cut algorithm explained in 4/ | |
| */ | |
| catal->depth--; | |
| return(XML_CATAL_BREAK); | |
| } | |
| } | |
| if (haveNext) { | |
| cur = catal; | |
| while (cur != NULL) { | |
| if (cur->type == XML_CATA_NEXT_CATALOG) { | |
| if (cur->children == NULL) { | |
| xmlFetchXMLCatalogFile(cur); | |
| } | |
| if (cur->children != NULL) { | |
| ret = xmlCatalogListXMLResolve(cur->children, pubID, sysID); | |
| if (ret != NULL) { | |
| catal->depth--; | |
| return(ret); | |
| } else if (catal->depth > MAX_CATAL_DEPTH) { | |
| return(NULL); | |
| } | |
| } | |
| } | |
| cur = cur->next; | |
| } | |
| } | |
| catal->depth--; | |
| return(NULL); | |
| } | |
| /** | |
| * xmlCatalogXMLResolveURI: | |
| * @catal: a catalog list | |
| * @URI: the URI | |
| * @sysID: the system ID string | |
| * | |
| * Do a complete resolution lookup of an External Identifier for a | |
| * list of catalog entries. | |
| * | |
| * Implements (or tries to) 7.2.2. URI Resolution | |
| * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html | |
| * | |
| * Returns the URI of the resource or NULL if not found | |
| */ | |
| static xmlChar * | |
| xmlCatalogXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI) { | |
| xmlChar *ret = NULL; | |
| xmlCatalogEntryPtr cur; | |
| int haveDelegate = 0; | |
| int haveNext = 0; | |
| xmlCatalogEntryPtr rewrite = NULL; | |
| int lenrewrite = 0, len; | |
| if (catal == NULL) | |
| return(NULL); | |
| if (URI == NULL) | |
| return(NULL); | |
| if (catal->depth > MAX_CATAL_DEPTH) { | |
| xmlCatalogErr(catal, NULL, XML_CATALOG_RECURSION, | |
| "Detected recursion in catalog %s\n", | |
| catal->name, NULL, NULL); | |
| return(NULL); | |
| } | |
| /* | |
| * First tries steps 2/ 3/ 4/ if a system ID is provided. | |
| */ | |
| cur = catal; | |
| haveDelegate = 0; | |
| while (cur != NULL) { | |
| switch (cur->type) { | |
| case XML_CATA_URI: | |
| if (xmlStrEqual(URI, cur->name)) { | |
| if (xmlDebugCatalogs) | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Found URI match %s\n", cur->name); | |
| return(xmlStrdup(cur->URL)); | |
| } | |
| break; | |
| case XML_CATA_REWRITE_URI: | |
| len = xmlStrlen(cur->name); | |
| if ((len > lenrewrite) && | |
| (!xmlStrncmp(URI, cur->name, len))) { | |
| lenrewrite = len; | |
| rewrite = cur; | |
| } | |
| break; | |
| case XML_CATA_DELEGATE_URI: | |
| if (!xmlStrncmp(URI, cur->name, xmlStrlen(cur->name))) | |
| haveDelegate++; | |
| break; | |
| case XML_CATA_NEXT_CATALOG: | |
| haveNext++; | |
| break; | |
| default: | |
| break; | |
| } | |
| cur = cur->next; | |
| } | |
| if (rewrite != NULL) { | |
| if (xmlDebugCatalogs) | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Using rewriting rule %s\n", rewrite->name); | |
| ret = xmlStrdup(rewrite->URL); | |
| if (ret != NULL) | |
| ret = xmlStrcat(ret, &URI[lenrewrite]); | |
| return(ret); | |
| } | |
| if (haveDelegate) { | |
| const xmlChar *delegates[MAX_DELEGATE]; | |
| int nbList = 0, i; | |
| /* | |
| * Assume the entries have been sorted by decreasing substring | |
| * matches when the list was produced. | |
| */ | |
| cur = catal; | |
| while (cur != NULL) { | |
| if (((cur->type == XML_CATA_DELEGATE_SYSTEM) || | |
| (cur->type == XML_CATA_DELEGATE_URI)) && | |
| (!xmlStrncmp(URI, cur->name, xmlStrlen(cur->name)))) { | |
| for (i = 0;i < nbList;i++) | |
| if (xmlStrEqual(cur->URL, delegates[i])) | |
| break; | |
| if (i < nbList) { | |
| cur = cur->next; | |
| continue; | |
| } | |
| if (nbList < MAX_DELEGATE) | |
| delegates[nbList++] = cur->URL; | |
| if (cur->children == NULL) { | |
| xmlFetchXMLCatalogFile(cur); | |
| } | |
| if (cur->children != NULL) { | |
| if (xmlDebugCatalogs) | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Trying URI delegate %s\n", cur->URL); | |
| ret = xmlCatalogListXMLResolveURI( | |
| cur->children, URI); | |
| if (ret != NULL) | |
| return(ret); | |
| } | |
| } | |
| cur = cur->next; | |
| } | |
| /* | |
| * Apply the cut algorithm explained in 4/ | |
| */ | |
| return(XML_CATAL_BREAK); | |
| } | |
| if (haveNext) { | |
| cur = catal; | |
| while (cur != NULL) { | |
| if (cur->type == XML_CATA_NEXT_CATALOG) { | |
| if (cur->children == NULL) { | |
| xmlFetchXMLCatalogFile(cur); | |
| } | |
| if (cur->children != NULL) { | |
| ret = xmlCatalogListXMLResolveURI(cur->children, URI); | |
| if (ret != NULL) | |
| return(ret); | |
| } | |
| } | |
| cur = cur->next; | |
| } | |
| } | |
| return(NULL); | |
| } | |
| /** | |
| * xmlCatalogListXMLResolve: | |
| * @catal: a catalog list | |
| * @pubID: the public ID string | |
| * @sysID: the system ID string | |
| * | |
| * Do a complete resolution lookup of an External Identifier for a | |
| * list of catalogs | |
| * | |
| * Implements (or tries to) 7.1. External Identifier Resolution | |
| * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html | |
| * | |
| * Returns the URI of the resource or NULL if not found | |
| */ | |
| static xmlChar * | |
| xmlCatalogListXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID, | |
| const xmlChar *sysID) { | |
| xmlChar *ret = NULL; | |
| xmlChar *urnID = NULL; | |
| xmlChar *normid; | |
| if (catal == NULL) | |
| return(NULL); | |
| if ((pubID == NULL) && (sysID == NULL)) | |
| return(NULL); | |
| normid = xmlCatalogNormalizePublic(pubID); | |
| if (normid != NULL) | |
| pubID = (*normid != 0 ? normid : NULL); | |
| if (!xmlStrncmp(pubID, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) { | |
| urnID = xmlCatalogUnWrapURN(pubID); | |
| if (xmlDebugCatalogs) { | |
| if (urnID == NULL) | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Public URN ID %s expanded to NULL\n", pubID); | |
| else | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Public URN ID expanded to %s\n", urnID); | |
| } | |
| ret = xmlCatalogListXMLResolve(catal, urnID, sysID); | |
| if (urnID != NULL) | |
| xmlFree(urnID); | |
| if (normid != NULL) | |
| xmlFree(normid); | |
| return(ret); | |
| } | |
| if (!xmlStrncmp(sysID, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) { | |
| urnID = xmlCatalogUnWrapURN(sysID); | |
| if (xmlDebugCatalogs) { | |
| if (urnID == NULL) | |
| xmlGenericError(xmlGenericErrorContext, | |
| "System URN ID %s expanded to NULL\n", sysID); | |
| else | |
| xmlGenericError(xmlGenericErrorContext, | |
| "System URN ID expanded to %s\n", urnID); | |
| } | |
| if (pubID == NULL) | |
| ret = xmlCatalogListXMLResolve(catal, urnID, NULL); | |
| else if (xmlStrEqual(pubID, urnID)) | |
| ret = xmlCatalogListXMLResolve(catal, pubID, NULL); | |
| else { | |
| ret = xmlCatalogListXMLResolve(catal, pubID, urnID); | |
| } | |
| if (urnID != NULL) | |
| xmlFree(urnID); | |
| if (normid != NULL) | |
| xmlFree(normid); | |
| return(ret); | |
| } | |
| while (catal != NULL) { | |
| if (catal->type == XML_CATA_CATALOG) { | |
| if (catal->children == NULL) { | |
| xmlFetchXMLCatalogFile(catal); | |
| } | |
| if (catal->children != NULL) { | |
| ret = xmlCatalogXMLResolve(catal->children, pubID, sysID); | |
| if (ret != NULL) { | |
| break; | |
| } else if ((catal->children != NULL) && | |
| (catal->children->depth > MAX_CATAL_DEPTH)) { | |
| ret = NULL; | |
| break; | |
| } | |
| } | |
| } | |
| catal = catal->next; | |
| } | |
| if (normid != NULL) | |
| xmlFree(normid); | |
| return(ret); | |
| } | |
| /** | |
| * xmlCatalogListXMLResolveURI: | |
| * @catal: a catalog list | |
| * @URI: the URI | |
| * | |
| * Do a complete resolution lookup of an URI for a list of catalogs | |
| * | |
| * Implements (or tries to) 7.2. URI Resolution | |
| * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html | |
| * | |
| * Returns the URI of the resource or NULL if not found | |
| */ | |
| static xmlChar * | |
| xmlCatalogListXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI) { | |
| xmlChar *ret = NULL; | |
| xmlChar *urnID = NULL; | |
| if (catal == NULL) | |
| return(NULL); | |
| if (URI == NULL) | |
| return(NULL); | |
| if (!xmlStrncmp(URI, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) { | |
| urnID = xmlCatalogUnWrapURN(URI); | |
| if (xmlDebugCatalogs) { | |
| if (urnID == NULL) | |
| xmlGenericError(xmlGenericErrorContext, | |
| "URN ID %s expanded to NULL\n", URI); | |
| else | |
| xmlGenericError(xmlGenericErrorContext, | |
| "URN ID expanded to %s\n", urnID); | |
| } | |
| ret = xmlCatalogListXMLResolve(catal, urnID, NULL); | |
| if (urnID != NULL) | |
| xmlFree(urnID); | |
| return(ret); | |
| } | |
| while (catal != NULL) { | |
| if (catal->type == XML_CATA_CATALOG) { | |
| if (catal->children == NULL) { | |
| xmlFetchXMLCatalogFile(catal); | |
| } | |
| if (catal->children != NULL) { | |
| ret = xmlCatalogXMLResolveURI(catal->children, URI); | |
| if (ret != NULL) | |
| return(ret); | |
| } | |
| } | |
| catal = catal->next; | |
| } | |
| return(ret); | |
| } | |
| /************************************************************************ | |
| * * | |
| * The SGML Catalog parser * | |
| * * | |
| ************************************************************************/ | |
| #define RAW *cur | |
| #define NEXT cur++; | |
| #define SKIP(x) cur += x; | |
| #define SKIP_BLANKS while (IS_BLANK_CH(*cur)) NEXT; | |
| /** | |
| * xmlParseSGMLCatalogComment: | |
| * @cur: the current character | |
| * | |
| * Skip a comment in an SGML catalog | |
| * | |
| * Returns new current character | |
| */ | |
| static const xmlChar * | |
| xmlParseSGMLCatalogComment(const xmlChar *cur) { | |
| if ((cur[0] != '-') || (cur[1] != '-')) | |
| return(cur); | |
| SKIP(2); | |
| while ((cur[0] != 0) && ((cur[0] != '-') || ((cur[1] != '-')))) | |
| NEXT; | |
| if (cur[0] == 0) { | |
| return(NULL); | |
| } | |
| return(cur + 2); | |
| } | |
| /** | |
| * xmlParseSGMLCatalogPubid: | |
| * @cur: the current character | |
| * @id: the return location | |
| * | |
| * Parse an SGML catalog ID | |
| * | |
| * Returns new current character and store the value in @id | |
| */ | |
| static const xmlChar * | |
| xmlParseSGMLCatalogPubid(const xmlChar *cur, xmlChar **id) { | |
| xmlChar *buf = NULL, *tmp; | |
| int len = 0; | |
| int size = 50; | |
| xmlChar stop; | |
| int count = 0; | |
| *id = NULL; | |
| if (RAW == '"') { | |
| NEXT; | |
| stop = '"'; | |
| } else if (RAW == '\'') { | |
| NEXT; | |
| stop = '\''; | |
| } else { | |
| stop = ' '; | |
| } | |
| buf = (xmlChar *) xmlMallocAtomic(size * sizeof(xmlChar)); | |
| if (buf == NULL) { | |
| xmlCatalogErrMemory("allocating public ID"); | |
| return(NULL); | |
| } | |
| while (IS_PUBIDCHAR_CH(*cur) || (*cur == '?')) { | |
| if ((*cur == stop) && (stop != ' ')) | |
| break; | |
| if ((stop == ' ') && (IS_BLANK_CH(*cur))) | |
| break; | |
| if (len + 1 >= size) { | |
| size *= 2; | |
| tmp = (xmlChar *) xmlRealloc(buf, size * sizeof(xmlChar)); | |
| if (tmp == NULL) { | |
| xmlCatalogErrMemory("allocating public ID"); | |
| xmlFree(buf); | |
| return(NULL); | |
| } | |
| buf = tmp; | |
| } | |
| buf[len++] = *cur; | |
| count++; | |
| NEXT; | |
| } | |
| buf[len] = 0; | |
| if (stop == ' ') { | |
| if (!IS_BLANK_CH(*cur)) { | |
| xmlFree(buf); | |
| return(NULL); | |
| } | |
| } else { | |
| if (*cur != stop) { | |
| xmlFree(buf); | |
| return(NULL); | |
| } | |
| NEXT; | |
| } | |
| *id = buf; | |
| return(cur); | |
| } | |
| /** | |
| * xmlParseSGMLCatalogName: | |
| * @cur: the current character | |
| * @name: the return location | |
| * | |
| * Parse an SGML catalog name | |
| * | |
| * Returns new current character and store the value in @name | |
| */ | |
| static const xmlChar * | |
| xmlParseSGMLCatalogName(const xmlChar *cur, xmlChar **name) { | |
| xmlChar buf[XML_MAX_NAMELEN + 5]; | |
| int len = 0; | |
| int c; | |
| *name = NULL; | |
| /* | |
| * Handler for more complex cases | |
| */ | |
| c = *cur; | |
| if ((!IS_LETTER(c) && (c != '_') && (c != ':'))) { | |
| return(NULL); | |
| } | |
| while (((IS_LETTER(c)) || (IS_DIGIT(c)) || | |
| (c == '.') || (c == '-') || | |
| (c == '_') || (c == ':'))) { | |
| buf[len++] = c; | |
| cur++; | |
| c = *cur; | |
| if (len >= XML_MAX_NAMELEN) | |
| return(NULL); | |
| } | |
| *name = xmlStrndup(buf, len); | |
| return(cur); | |
| } | |
| /** | |
| * xmlGetSGMLCatalogEntryType: | |
| * @name: the entry name | |
| * | |
| * Get the Catalog entry type for a given SGML Catalog name | |
| * | |
| * Returns Catalog entry type | |
| */ | |
| static xmlCatalogEntryType | |
| xmlGetSGMLCatalogEntryType(const xmlChar *name) { | |
| xmlCatalogEntryType type = XML_CATA_NONE; | |
| if (xmlStrEqual(name, (const xmlChar *) "SYSTEM")) | |
| type = SGML_CATA_SYSTEM; | |
| else if (xmlStrEqual(name, (const xmlChar *) "PUBLIC")) | |
| type = SGML_CATA_PUBLIC; | |
| else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE")) | |
| type = SGML_CATA_DELEGATE; | |
| else if (xmlStrEqual(name, (const xmlChar *) "ENTITY")) | |
| type = SGML_CATA_ENTITY; | |
| else if (xmlStrEqual(name, (const xmlChar *) "DOCTYPE")) | |
| type = SGML_CATA_DOCTYPE; | |
| else if (xmlStrEqual(name, (const xmlChar *) "LINKTYPE")) | |
| type = SGML_CATA_LINKTYPE; | |
| else if (xmlStrEqual(name, (const xmlChar *) "NOTATION")) | |
| type = SGML_CATA_NOTATION; | |
| else if (xmlStrEqual(name, (const xmlChar *) "SGMLDECL")) | |
| type = SGML_CATA_SGMLDECL; | |
| else if (xmlStrEqual(name, (const xmlChar *) "DOCUMENT")) | |
| type = SGML_CATA_DOCUMENT; | |
| else if (xmlStrEqual(name, (const xmlChar *) "CATALOG")) | |
| type = SGML_CATA_CATALOG; | |
| else if (xmlStrEqual(name, (const xmlChar *) "BASE")) | |
| type = SGML_CATA_BASE; | |
| return(type); | |
| } | |
| /** | |
| * xmlParseSGMLCatalog: | |
| * @catal: the SGML Catalog | |
| * @value: the content of the SGML Catalog serialization | |
| * @file: the filepath for the catalog | |
| * @super: should this be handled as a Super Catalog in which case | |
| * parsing is not recursive | |
| * | |
| * Parse an SGML catalog content and fill up the @catal hash table with | |
| * the new entries found. | |
| * | |
| * Returns 0 in case of success, -1 in case of error. | |
| */ | |
| static int | |
| xmlParseSGMLCatalog(xmlCatalogPtr catal, const xmlChar *value, | |
| const char *file, int super) { | |
| const xmlChar *cur = value; | |
| xmlChar *base = NULL; | |
| int res; | |
| if ((cur == NULL) || (file == NULL)) | |
| return(-1); | |
| base = xmlStrdup((const xmlChar *) file); | |
| while ((cur != NULL) && (cur[0] != 0)) { | |
| SKIP_BLANKS; | |
| if (cur[0] == 0) | |
| break; | |
| if ((cur[0] == '-') && (cur[1] == '-')) { | |
| cur = xmlParseSGMLCatalogComment(cur); | |
| if (cur == NULL) { | |
| /* error */ | |
| break; | |
| } | |
| } else { | |
| xmlChar *sysid = NULL; | |
| xmlChar *name = NULL; | |
| xmlCatalogEntryType type = XML_CATA_NONE; | |
| cur = xmlParseSGMLCatalogName(cur, &name); | |
| if (name == NULL) { | |
| /* error */ | |
| break; | |
| } | |
| if (!IS_BLANK_CH(*cur)) { | |
| /* error */ | |
| break; | |
| } | |
| SKIP_BLANKS; | |
| if (xmlStrEqual(name, (const xmlChar *) "SYSTEM")) | |
| type = SGML_CATA_SYSTEM; | |
| else if (xmlStrEqual(name, (const xmlChar *) "PUBLIC")) | |
| type = SGML_CATA_PUBLIC; | |
| else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE")) | |
| type = SGML_CATA_DELEGATE; | |
| else if (xmlStrEqual(name, (const xmlChar *) "ENTITY")) | |
| type = SGML_CATA_ENTITY; | |
| else if (xmlStrEqual(name, (const xmlChar *) "DOCTYPE")) | |
| type = SGML_CATA_DOCTYPE; | |
| else if (xmlStrEqual(name, (const xmlChar *) "LINKTYPE")) | |
| type = SGML_CATA_LINKTYPE; | |
| else if (xmlStrEqual(name, (const xmlChar *) "NOTATION")) | |
| type = SGML_CATA_NOTATION; | |
| else if (xmlStrEqual(name, (const xmlChar *) "SGMLDECL")) | |
| type = SGML_CATA_SGMLDECL; | |
| else if (xmlStrEqual(name, (const xmlChar *) "DOCUMENT")) | |
| type = SGML_CATA_DOCUMENT; | |
| else if (xmlStrEqual(name, (const xmlChar *) "CATALOG")) | |
| type = SGML_CATA_CATALOG; | |
| else if (xmlStrEqual(name, (const xmlChar *) "BASE")) | |
| type = SGML_CATA_BASE; | |
| else if (xmlStrEqual(name, (const xmlChar *) "OVERRIDE")) { | |
| xmlFree(name); | |
| cur = xmlParseSGMLCatalogName(cur, &name); | |
| if (name == NULL) { | |
| /* error */ | |
| break; | |
| } | |
| xmlFree(name); | |
| continue; | |
| } | |
| xmlFree(name); | |
| name = NULL; | |
| switch(type) { | |
| case SGML_CATA_ENTITY: | |
| if (*cur == '%') | |
| type = SGML_CATA_PENTITY; | |
| case SGML_CATA_PENTITY: | |
| case SGML_CATA_DOCTYPE: | |
| case SGML_CATA_LINKTYPE: | |
| case SGML_CATA_NOTATION: | |
| cur = xmlParseSGMLCatalogName(cur, &name); | |
| if (cur == NULL) { | |
| /* error */ | |
| break; | |
| } | |
| if (!IS_BLANK_CH(*cur)) { | |
| /* error */ | |
| break; | |
| } | |
| SKIP_BLANKS; | |
| cur = xmlParseSGMLCatalogPubid(cur, &sysid); | |
| if (cur == NULL) { | |
| /* error */ | |
| break; | |
| } | |
| break; | |
| case SGML_CATA_PUBLIC: | |
| case SGML_CATA_SYSTEM: | |
| case SGML_CATA_DELEGATE: | |
| cur = xmlParseSGMLCatalogPubid(cur, &name); | |
| if (cur == NULL) { | |
| /* error */ | |
| break; | |
| } | |
| if (type != SGML_CATA_SYSTEM) { | |
| xmlChar *normid; | |
| normid = xmlCatalogNormalizePublic(name); | |
| if (normid != NULL) { | |
| if (name != NULL) | |
| xmlFree(name); | |
| if (*normid != 0) | |
| name = normid; | |
| else { | |
| xmlFree(normid); | |
| name = NULL; | |
| } | |
| } | |
| } | |
| if (!IS_BLANK_CH(*cur)) { | |
| /* error */ | |
| break; | |
| } | |
| SKIP_BLANKS; | |
| cur = xmlParseSGMLCatalogPubid(cur, &sysid); | |
| if (cur == NULL) { | |
| /* error */ | |
| break; | |
| } | |
| break; | |
| case SGML_CATA_BASE: | |
| case SGML_CATA_CATALOG: | |
| case SGML_CATA_DOCUMENT: | |
| case SGML_CATA_SGMLDECL: | |
| cur = xmlParseSGMLCatalogPubid(cur, &sysid); | |
| if (cur == NULL) { | |
| /* error */ | |
| break; | |
| } | |
| break; | |
| default: | |
| break; | |
| } | |
| if (cur == NULL) { | |
| if (name != NULL) | |
| xmlFree(name); | |
| if (sysid != NULL) | |
| xmlFree(sysid); | |
| break; | |
| } else if (type == SGML_CATA_BASE) { | |
| if (base != NULL) | |
| xmlFree(base); | |
| base = xmlStrdup(sysid); | |
| } else if ((type == SGML_CATA_PUBLIC) || | |
| (type == SGML_CATA_SYSTEM)) { | |
| xmlChar *filename; | |
| filename = xmlBuildURI(sysid, base); | |
| if (filename != NULL) { | |
| xmlCatalogEntryPtr entry; | |
| entry = xmlNewCatalogEntry(type, name, filename, | |
| NULL, XML_CATA_PREFER_NONE, NULL); | |
| res = xmlHashAddEntry(catal->sgml, name, entry); | |
| if (res < 0) { | |
| xmlFreeCatalogEntry(entry); | |
| } | |
| xmlFree(filename); | |
| } | |
| } else if (type == SGML_CATA_CATALOG) { | |
| if (super) { | |
| xmlCatalogEntryPtr entry; | |
| entry = xmlNewCatalogEntry(type, sysid, NULL, NULL, | |
| XML_CATA_PREFER_NONE, NULL); | |
| res = xmlHashAddEntry(catal->sgml, sysid, entry); | |
| if (res < 0) { | |
| xmlFreeCatalogEntry(entry); | |
| } | |
| } else { | |
| xmlChar *filename; | |
| filename = xmlBuildURI(sysid, base); | |
| if (filename != NULL) { | |
| xmlExpandCatalog(catal, (const char *)filename); | |
| xmlFree(filename); | |
| } | |
| } | |
| } | |
| /* | |
| * drop anything else we won't handle it | |
| */ | |
| if (name != NULL) | |
| xmlFree(name); | |
| if (sysid != NULL) | |
| xmlFree(sysid); | |
| } | |
| } | |
| if (base != NULL) | |
| xmlFree(base); | |
| if (cur == NULL) | |
| return(-1); | |
| return(0); | |
| } | |
| /************************************************************************ | |
| * * | |
| * SGML Catalog handling * | |
| * * | |
| ************************************************************************/ | |
| /** | |
| * xmlCatalogGetSGMLPublic: | |
| * @catal: an SGML catalog hash | |
| * @pubID: the public ID string | |
| * | |
| * Try to lookup the catalog local reference associated to a public ID | |
| * | |
| * Returns the local resource if found or NULL otherwise. | |
| */ | |
| static const xmlChar * | |
| xmlCatalogGetSGMLPublic(xmlHashTablePtr catal, const xmlChar *pubID) { | |
| xmlCatalogEntryPtr entry; | |
| xmlChar *normid; | |
| if (catal == NULL) | |
| return(NULL); | |
| normid = xmlCatalogNormalizePublic(pubID); | |
| if (normid != NULL) | |
| pubID = (*normid != 0 ? normid : NULL); | |
| entry = (xmlCatalogEntryPtr) xmlHashLookup(catal, pubID); | |
| if (entry == NULL) { | |
| if (normid != NULL) | |
| xmlFree(normid); | |
| return(NULL); | |
| } | |
| if (entry->type == SGML_CATA_PUBLIC) { | |
| if (normid != NULL) | |
| xmlFree(normid); | |
| return(entry->URL); | |
| } | |
| if (normid != NULL) | |
| xmlFree(normid); | |
| return(NULL); | |
| } | |
| /** | |
| * xmlCatalogGetSGMLSystem: | |
| * @catal: an SGML catalog hash | |
| * @sysID: the system ID string | |
| * | |
| * Try to lookup the catalog local reference for a system ID | |
| * | |
| * Returns the local resource if found or NULL otherwise. | |
| */ | |
| static const xmlChar * | |
| xmlCatalogGetSGMLSystem(xmlHashTablePtr catal, const xmlChar *sysID) { | |
| xmlCatalogEntryPtr entry; | |
| if (catal == NULL) | |
| return(NULL); | |
| entry = (xmlCatalogEntryPtr) xmlHashLookup(catal, sysID); | |
| if (entry == NULL) | |
| return(NULL); | |
| if (entry->type == SGML_CATA_SYSTEM) | |
| return(entry->URL); | |
| return(NULL); | |
| } | |
| /** | |
| * xmlCatalogSGMLResolve: | |
| * @catal: the SGML catalog | |
| * @pubID: the public ID string | |
| * @sysID: the system ID string | |
| * | |
| * Do a complete resolution lookup of an External Identifier | |
| * | |
| * Returns the URI of the resource or NULL if not found | |
| */ | |
| static const xmlChar * | |
| xmlCatalogSGMLResolve(xmlCatalogPtr catal, const xmlChar *pubID, | |
| const xmlChar *sysID) { | |
| const xmlChar *ret = NULL; | |
| if (catal->sgml == NULL) | |
| return(NULL); | |
| if (pubID != NULL) | |
| ret = xmlCatalogGetSGMLPublic(catal->sgml, pubID); | |
| if (ret != NULL) | |
| return(ret); | |
| if (sysID != NULL) | |
| ret = xmlCatalogGetSGMLSystem(catal->sgml, sysID); | |
| if (ret != NULL) | |
| return(ret); | |
| return(NULL); | |
| } | |
| /************************************************************************ | |
| * * | |
| * Specific Public interfaces * | |
| * * | |
| ************************************************************************/ | |
| /** | |
| * xmlLoadSGMLSuperCatalog: | |
| * @filename: a file path | |
| * | |
| * Load an SGML super catalog. It won't expand CATALOG or DELEGATE | |
| * references. This is only needed for manipulating SGML Super Catalogs | |
| * like adding and removing CATALOG or DELEGATE entries. | |
| * | |
| * Returns the catalog parsed or NULL in case of error | |
| */ | |
| xmlCatalogPtr | |
| xmlLoadSGMLSuperCatalog(const char *filename) | |
| { | |
| xmlChar *content; | |
| xmlCatalogPtr catal; | |
| int ret; | |
| content = xmlLoadFileContent(filename); | |
| if (content == NULL) | |
| return(NULL); | |
| catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE, xmlCatalogDefaultPrefer); | |
| if (catal == NULL) { | |
| xmlFree(content); | |
| return(NULL); | |
| } | |
| ret = xmlParseSGMLCatalog(catal, content, filename, 1); | |
| xmlFree(content); | |
| if (ret < 0) { | |
| xmlFreeCatalog(catal); | |
| return(NULL); | |
| } | |
| return (catal); | |
| } | |
| /** | |
| * xmlLoadACatalog: | |
| * @filename: a file path | |
| * | |
| * Load the catalog and build the associated data structures. | |
| * This can be either an XML Catalog or an SGML Catalog | |
| * It will recurse in SGML CATALOG entries. On the other hand XML | |
| * Catalogs are not handled recursively. | |
| * | |
| * Returns the catalog parsed or NULL in case of error | |
| */ | |
| xmlCatalogPtr | |
| xmlLoadACatalog(const char *filename) | |
| { | |
| xmlChar *content; | |
| xmlChar *first; | |
| xmlCatalogPtr catal; | |
| int ret; | |
| content = xmlLoadFileContent(filename); | |
| if (content == NULL) | |
| return(NULL); | |
| first = content; | |
| while ((*first != 0) && (*first != '-') && (*first != '<') && | |
| (!(((*first >= 'A') && (*first <= 'Z')) || | |
| ((*first >= 'a') && (*first <= 'z'))))) | |
| first++; | |
| if (*first != '<') { | |
| catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE, xmlCatalogDefaultPrefer); | |
| if (catal == NULL) { | |
| xmlFree(content); | |
| return(NULL); | |
| } | |
| ret = xmlParseSGMLCatalog(catal, content, filename, 0); | |
| if (ret < 0) { | |
| xmlFreeCatalog(catal); | |
| xmlFree(content); | |
| return(NULL); | |
| } | |
| } else { | |
| catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE, xmlCatalogDefaultPrefer); | |
| if (catal == NULL) { | |
| xmlFree(content); | |
| return(NULL); | |
| } | |
| catal->xml = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL, | |
| NULL, BAD_CAST filename, xmlCatalogDefaultPrefer, NULL); | |
| } | |
| xmlFree(content); | |
| return (catal); | |
| } | |
| /** | |
| * xmlExpandCatalog: | |
| * @catal: a catalog | |
| * @filename: a file path | |
| * | |
| * Load the catalog and expand the existing catal structure. | |
| * This can be either an XML Catalog or an SGML Catalog | |
| * | |
| * Returns 0 in case of success, -1 in case of error | |
| */ | |
| static int | |
| xmlExpandCatalog(xmlCatalogPtr catal, const char *filename) | |
| { | |
| int ret; | |
| if ((catal == NULL) || (filename == NULL)) | |
| return(-1); | |
| if (catal->type == XML_SGML_CATALOG_TYPE) { | |
| xmlChar *content; | |
| content = xmlLoadFileContent(filename); | |
| if (content == NULL) | |
| return(-1); | |
| ret = xmlParseSGMLCatalog(catal, content, filename, 0); | |
| if (ret < 0) { | |
| xmlFree(content); | |
| return(-1); | |
| } | |
| xmlFree(content); | |
| } else { | |
| xmlCatalogEntryPtr tmp, cur; | |
| tmp = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL, | |
| NULL, BAD_CAST filename, xmlCatalogDefaultPrefer, NULL); | |
| cur = catal->xml; | |
| if (cur == NULL) { | |
| catal->xml = tmp; | |
| } else { | |
| while (cur->next != NULL) cur = cur->next; | |
| cur->next = tmp; | |
| } | |
| } | |
| return (0); | |
| } | |
| /** | |
| * xmlACatalogResolveSystem: | |
| * @catal: a Catalog | |
| * @sysID: the system ID string | |
| * | |
| * Try to lookup the catalog resource for a system ID | |
| * | |
| * Returns the resource if found or NULL otherwise, the value returned | |
| * must be freed by the caller. | |
| */ | |
| xmlChar * | |
| xmlACatalogResolveSystem(xmlCatalogPtr catal, const xmlChar *sysID) { | |
| xmlChar *ret = NULL; | |
| if ((sysID == NULL) || (catal == NULL)) | |
| return(NULL); | |
| if (xmlDebugCatalogs) | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Resolve sysID %s\n", sysID); | |
| if (catal->type == XML_XML_CATALOG_TYPE) { | |
| ret = xmlCatalogListXMLResolve(catal->xml, NULL, sysID); | |
| if (ret == XML_CATAL_BREAK) | |
| ret = NULL; | |
| } else { | |
| const xmlChar *sgml; | |
| sgml = xmlCatalogGetSGMLSystem(catal->sgml, sysID); | |
| if (sgml != NULL) | |
| ret = xmlStrdup(sgml); | |
| } | |
| return(ret); | |
| } | |
| /** | |
| * xmlACatalogResolvePublic: | |
| * @catal: a Catalog | |
| * @pubID: the public ID string | |
| * | |
| * Try to lookup the catalog local reference associated to a public ID in that catalog | |
| * | |
| * Returns the local resource if found or NULL otherwise, the value returned | |
| * must be freed by the caller. | |
| */ | |
| xmlChar * | |
| xmlACatalogResolvePublic(xmlCatalogPtr catal, const xmlChar *pubID) { | |
| xmlChar *ret = NULL; | |
| if ((pubID == NULL) || (catal == NULL)) | |
| return(NULL); | |
| if (xmlDebugCatalogs) | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Resolve pubID %s\n", pubID); | |
| if (catal->type == XML_XML_CATALOG_TYPE) { | |
| ret = xmlCatalogListXMLResolve(catal->xml, pubID, NULL); | |
| if (ret == XML_CATAL_BREAK) | |
| ret = NULL; | |
| } else { | |
| const xmlChar *sgml; | |
| sgml = xmlCatalogGetSGMLPublic(catal->sgml, pubID); | |
| if (sgml != NULL) | |
| ret = xmlStrdup(sgml); | |
| } | |
| return(ret); | |
| } | |
| /** | |
| * xmlACatalogResolve: | |
| * @catal: a Catalog | |
| * @pubID: the public ID string | |
| * @sysID: the system ID string | |
| * | |
| * Do a complete resolution lookup of an External Identifier | |
| * | |
| * Returns the URI of the resource or NULL if not found, it must be freed | |
| * by the caller. | |
| */ | |
| xmlChar * | |
| xmlACatalogResolve(xmlCatalogPtr catal, const xmlChar * pubID, | |
| const xmlChar * sysID) | |
| { | |
| xmlChar *ret = NULL; | |
| if (((pubID == NULL) && (sysID == NULL)) || (catal == NULL)) | |
| return (NULL); | |
| if (xmlDebugCatalogs) { | |
| if ((pubID != NULL) && (sysID != NULL)) { | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Resolve: pubID %s sysID %s\n", pubID, sysID); | |
| } else if (pubID != NULL) { | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Resolve: pubID %s\n", pubID); | |
| } else { | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Resolve: sysID %s\n", sysID); | |
| } | |
| } | |
| if (catal->type == XML_XML_CATALOG_TYPE) { | |
| ret = xmlCatalogListXMLResolve(catal->xml, pubID, sysID); | |
| if (ret == XML_CATAL_BREAK) | |
| ret = NULL; | |
| } else { | |
| const xmlChar *sgml; | |
| sgml = xmlCatalogSGMLResolve(catal, pubID, sysID); | |
| if (sgml != NULL) | |
| ret = xmlStrdup(sgml); | |
| } | |
| return (ret); | |
| } | |
| /** | |
| * xmlACatalogResolveURI: | |
| * @catal: a Catalog | |
| * @URI: the URI | |
| * | |
| * Do a complete resolution lookup of an URI | |
| * | |
| * Returns the URI of the resource or NULL if not found, it must be freed | |
| * by the caller. | |
| */ | |
| xmlChar * | |
| xmlACatalogResolveURI(xmlCatalogPtr catal, const xmlChar *URI) { | |
| xmlChar *ret = NULL; | |
| if ((URI == NULL) || (catal == NULL)) | |
| return(NULL); | |
| if (xmlDebugCatalogs) | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Resolve URI %s\n", URI); | |
| if (catal->type == XML_XML_CATALOG_TYPE) { | |
| ret = xmlCatalogListXMLResolveURI(catal->xml, URI); | |
| if (ret == XML_CATAL_BREAK) | |
| ret = NULL; | |
| } else { | |
| const xmlChar *sgml; | |
| sgml = xmlCatalogSGMLResolve(catal, NULL, URI); | |
| if (sgml != NULL) | |
| ret = xmlStrdup(sgml); | |
| } | |
| return(ret); | |
| } | |
| #ifdef LIBXML_OUTPUT_ENABLED | |
| /** | |
| * xmlACatalogDump: | |
| * @catal: a Catalog | |
| * @out: the file. | |
| * | |
| * Dump the given catalog to the given file. | |
| */ | |
| void | |
| xmlACatalogDump(xmlCatalogPtr catal, FILE *out) { | |
| if ((out == NULL) || (catal == NULL)) | |
| return; | |
| if (catal->type == XML_XML_CATALOG_TYPE) { | |
| xmlDumpXMLCatalog(out, catal->xml); | |
| } else { | |
| xmlHashScan(catal->sgml, | |
| (xmlHashScanner) xmlCatalogDumpEntry, out); | |
| } | |
| } | |
| #endif /* LIBXML_OUTPUT_ENABLED */ | |
| /** | |
| * xmlACatalogAdd: | |
| * @catal: a Catalog | |
| * @type: the type of record to add to the catalog | |
| * @orig: the system, public or prefix to match | |
| * @replace: the replacement value for the match | |
| * | |
| * Add an entry in the catalog, it may overwrite existing but | |
| * different entries. | |
| * | |
| * Returns 0 if successful, -1 otherwise | |
| */ | |
| int | |
| xmlACatalogAdd(xmlCatalogPtr catal, const xmlChar * type, | |
| const xmlChar * orig, const xmlChar * replace) | |
| { | |
| int res = -1; | |
| if (catal == NULL) | |
| return(-1); | |
| if (catal->type == XML_XML_CATALOG_TYPE) { | |
| res = xmlAddXMLCatalog(catal->xml, type, orig, replace); | |
| } else { | |
| xmlCatalogEntryType cattype; | |
| cattype = xmlGetSGMLCatalogEntryType(type); | |
| if (cattype != XML_CATA_NONE) { | |
| xmlCatalogEntryPtr entry; | |
| entry = xmlNewCatalogEntry(cattype, orig, replace, NULL, | |
| XML_CATA_PREFER_NONE, NULL); | |
| if (catal->sgml == NULL) | |
| catal->sgml = xmlHashCreate(10); | |
| res = xmlHashAddEntry(catal->sgml, orig, entry); | |
| } | |
| } | |
| return (res); | |
| } | |
| /** | |
| * xmlACatalogRemove: | |
| * @catal: a Catalog | |
| * @value: the value to remove | |
| * | |
| * Remove an entry from the catalog | |
| * | |
| * Returns the number of entries removed if successful, -1 otherwise | |
| */ | |
| int | |
| xmlACatalogRemove(xmlCatalogPtr catal, const xmlChar *value) { | |
| int res = -1; | |
| if ((catal == NULL) || (value == NULL)) | |
| return(-1); | |
| if (catal->type == XML_XML_CATALOG_TYPE) { | |
| res = xmlDelXMLCatalog(catal->xml, value); | |
| } else { | |
| res = xmlHashRemoveEntry(catal->sgml, value, | |
| (xmlHashDeallocator) xmlFreeCatalogEntry); | |
| if (res == 0) | |
| res = 1; | |
| } | |
| return(res); | |
| } | |
| /** | |
| * xmlNewCatalog: | |
| * @sgml: should this create an SGML catalog | |
| * | |
| * create a new Catalog. | |
| * | |
| * Returns the xmlCatalogPtr or NULL in case of error | |
| */ | |
| xmlCatalogPtr | |
| xmlNewCatalog(int sgml) { | |
| xmlCatalogPtr catal = NULL; | |
| if (sgml) { | |
| catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE, | |
| xmlCatalogDefaultPrefer); | |
| if ((catal != NULL) && (catal->sgml == NULL)) | |
| catal->sgml = xmlHashCreate(10); | |
| } else | |
| catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE, | |
| xmlCatalogDefaultPrefer); | |
| return(catal); | |
| } | |
| /** | |
| * xmlCatalogIsEmpty: | |
| * @catal: should this create an SGML catalog | |
| * | |
| * Check is a catalog is empty | |
| * | |
| * Returns 1 if the catalog is empty, 0 if not, amd -1 in case of error. | |
| */ | |
| int | |
| xmlCatalogIsEmpty(xmlCatalogPtr catal) { | |
| if (catal == NULL) | |
| return(-1); | |
| if (catal->type == XML_XML_CATALOG_TYPE) { | |
| if (catal->xml == NULL) | |
| return(1); | |
| if ((catal->xml->type != XML_CATA_CATALOG) && | |
| (catal->xml->type != XML_CATA_BROKEN_CATALOG)) | |
| return(-1); | |
| if (catal->xml->children == NULL) | |
| return(1); | |
| return(0); | |
| } else { | |
| int res; | |
| if (catal->sgml == NULL) | |
| return(1); | |
| res = xmlHashSize(catal->sgml); | |
| if (res == 0) | |
| return(1); | |
| if (res < 0) | |
| return(-1); | |
| } | |
| return(0); | |
| } | |
| /************************************************************************ | |
| * * | |
| * Public interfaces manipulating the global shared default catalog * | |
| * * | |
| ************************************************************************/ | |
| /** | |
| * xmlInitializeCatalogData: | |
| * | |
| * Do the catalog initialization only of global data, doesn't try to load | |
| * any catalog actually. | |
| * this function is not thread safe, catalog initialization should | |
| * preferably be done once at startup | |
| */ | |
| static void | |
| xmlInitializeCatalogData(void) { | |
| if (xmlCatalogInitialized != 0) | |
| return; | |
| if (getenv("XML_DEBUG_CATALOG")) | |
| xmlDebugCatalogs = 1; | |
| xmlCatalogMutex = xmlNewRMutex(); | |
| xmlCatalogInitialized = 1; | |
| } | |
| /** | |
| * xmlInitializeCatalog: | |
| * | |
| * Do the catalog initialization. | |
| * this function is not thread safe, catalog initialization should | |
| * preferably be done once at startup | |
| */ | |
| void | |
| xmlInitializeCatalog(void) { | |
| if (xmlCatalogInitialized != 0) | |
| return; | |
| xmlInitializeCatalogData(); | |
| xmlRMutexLock(xmlCatalogMutex); | |
| if (getenv("XML_DEBUG_CATALOG")) | |
| xmlDebugCatalogs = 1; | |
| if (xmlDefaultCatalog == NULL) { | |
| const char *catalogs; | |
| char *path; | |
| const char *cur, *paths; | |
| xmlCatalogPtr catal; | |
| xmlCatalogEntryPtr *nextent; | |
| catalogs = (const char *) getenv("XML_CATALOG_FILES"); | |
| if (catalogs == NULL) | |
| #if defined(_WIN32) && defined(_MSC_VER) | |
| { | |
| void* hmodule; | |
| hmodule = GetModuleHandleA("libxml2.dll"); | |
| if (hmodule == NULL) | |
| hmodule = GetModuleHandleA(NULL); | |
| if (hmodule != NULL) { | |
| char buf[256]; | |
| unsigned long len = GetModuleFileNameA(hmodule, buf, 255); | |
| if (len != 0) { | |
| char* p = &(buf[len]); | |
| while (*p != '\\' && p > buf) | |
| p--; | |
| if (p != buf) { | |
| xmlChar* uri; | |
| strncpy(p, "\\..\\etc\\catalog", 255 - (p - buf)); | |
| uri = xmlCanonicPath((const xmlChar*)buf); | |
| if (uri != NULL) { | |
| strncpy(XML_XML_DEFAULT_CATALOG, uri, 255); | |
| xmlFree(uri); | |
| } | |
| } | |
| } | |
| } | |
| catalogs = XML_XML_DEFAULT_CATALOG; | |
| } | |
| #else | |
| catalogs = XML_XML_DEFAULT_CATALOG; | |
| #endif | |
| catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE, | |
| xmlCatalogDefaultPrefer); | |
| if (catal != NULL) { | |
| /* the XML_CATALOG_FILES envvar is allowed to contain a | |
| space-separated list of entries. */ | |
| cur = catalogs; | |
| nextent = &catal->xml; | |
| while (*cur != '\0') { | |
| while (xmlIsBlank_ch(*cur)) | |
| cur++; | |
| if (*cur != 0) { | |
| paths = cur; | |
| while ((*cur != 0) && (!xmlIsBlank_ch(*cur))) | |
| cur++; | |
| path = (char *) xmlStrndup((const xmlChar *)paths, cur - paths); | |
| if (path != NULL) { | |
| *nextent = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL, | |
| NULL, BAD_CAST path, xmlCatalogDefaultPrefer, NULL); | |
| if (*nextent != NULL) | |
| nextent = &((*nextent)->next); | |
| xmlFree(path); | |
| } | |
| } | |
| } | |
| xmlDefaultCatalog = catal; | |
| } | |
| } | |
| xmlRMutexUnlock(xmlCatalogMutex); | |
| } | |
| /** | |
| * xmlLoadCatalog: | |
| * @filename: a file path | |
| * | |
| * Load the catalog and makes its definitions effective for the default | |
| * external entity loader. It will recurse in SGML CATALOG entries. | |
| * this function is not thread safe, catalog initialization should | |
| * preferably be done once at startup | |
| * | |
| * Returns 0 in case of success -1 in case of error | |
| */ | |
| int | |
| xmlLoadCatalog(const char *filename) | |
| { | |
| int ret; | |
| xmlCatalogPtr catal; | |
| if (!xmlCatalogInitialized) | |
| xmlInitializeCatalogData(); | |
| xmlRMutexLock(xmlCatalogMutex); | |
| if (xmlDefaultCatalog == NULL) { | |
| catal = xmlLoadACatalog(filename); | |
| if (catal == NULL) { | |
| xmlRMutexUnlock(xmlCatalogMutex); | |
| return(-1); | |
| } | |
| xmlDefaultCatalog = catal; | |
| xmlRMutexUnlock(xmlCatalogMutex); | |
| return(0); | |
| } | |
| ret = xmlExpandCatalog(xmlDefaultCatalog, filename); | |
| xmlRMutexUnlock(xmlCatalogMutex); | |
| return(ret); | |
| } | |
| /** | |
| * xmlLoadCatalogs: | |
| * @pathss: a list of directories separated by a colon or a space. | |
| * | |
| * Load the catalogs and makes their definitions effective for the default | |
| * external entity loader. | |
| * this function is not thread safe, catalog initialization should | |
| * preferably be done once at startup | |
| */ | |
| void | |
| xmlLoadCatalogs(const char *pathss) { | |
| const char *cur; | |
| const char *paths; | |
| xmlChar *path; | |
| #ifdef _WIN32 | |
| int i, iLen; | |
| #endif | |
| if (pathss == NULL) | |
| return; | |
| cur = pathss; | |
| while (*cur != 0) { | |
| while (xmlIsBlank_ch(*cur)) cur++; | |
| if (*cur != 0) { | |
| paths = cur; | |
| while ((*cur != 0) && (*cur != PATH_SEPARATOR) && (!xmlIsBlank_ch(*cur))) | |
| cur++; | |
| path = xmlStrndup((const xmlChar *)paths, cur - paths); | |
| #ifdef _WIN32 | |
| iLen = strlen((const char*)path); | |
| for(i = 0; i < iLen; i++) { | |
| if(path[i] == '\\') { | |
| path[i] = '/'; | |
| } | |
| } | |
| #endif | |
| if (path != NULL) { | |
| xmlLoadCatalog((const char *) path); | |
| xmlFree(path); | |
| } | |
| } | |
| while (*cur == PATH_SEPARATOR) | |
| cur++; | |
| } | |
| } | |
| /** | |
| * xmlCatalogCleanup: | |
| * | |
| * Free up all the memory associated with catalogs | |
| */ | |
| void | |
| xmlCatalogCleanup(void) { | |
| if (xmlCatalogInitialized == 0) | |
| return; | |
| xmlRMutexLock(xmlCatalogMutex); | |
| if (xmlDebugCatalogs) | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Catalogs cleanup\n"); | |
| if (xmlCatalogXMLFiles != NULL) | |
| xmlHashFree(xmlCatalogXMLFiles, | |
| (xmlHashDeallocator)xmlFreeCatalogHashEntryList); | |
| xmlCatalogXMLFiles = NULL; | |
| if (xmlDefaultCatalog != NULL) | |
| xmlFreeCatalog(xmlDefaultCatalog); | |
| xmlDefaultCatalog = NULL; | |
| xmlDebugCatalogs = 0; | |
| xmlCatalogInitialized = 0; | |
| xmlRMutexUnlock(xmlCatalogMutex); | |
| xmlFreeRMutex(xmlCatalogMutex); | |
| } | |
| /** | |
| * xmlCatalogResolveSystem: | |
| * @sysID: the system ID string | |
| * | |
| * Try to lookup the catalog resource for a system ID | |
| * | |
| * Returns the resource if found or NULL otherwise, the value returned | |
| * must be freed by the caller. | |
| */ | |
| xmlChar * | |
| xmlCatalogResolveSystem(const xmlChar *sysID) { | |
| xmlChar *ret; | |
| if (!xmlCatalogInitialized) | |
| xmlInitializeCatalog(); | |
| ret = xmlACatalogResolveSystem(xmlDefaultCatalog, sysID); | |
| return(ret); | |
| } | |
| /** | |
| * xmlCatalogResolvePublic: | |
| * @pubID: the public ID string | |
| * | |
| * Try to lookup the catalog reference associated to a public ID | |
| * | |
| * Returns the resource if found or NULL otherwise, the value returned | |
| * must be freed by the caller. | |
| */ | |
| xmlChar * | |
| xmlCatalogResolvePublic(const xmlChar *pubID) { | |
| xmlChar *ret; | |
| if (!xmlCatalogInitialized) | |
| xmlInitializeCatalog(); | |
| ret = xmlACatalogResolvePublic(xmlDefaultCatalog, pubID); | |
| return(ret); | |
| } | |
| /** | |
| * xmlCatalogResolve: | |
| * @pubID: the public ID string | |
| * @sysID: the system ID string | |
| * | |
| * Do a complete resolution lookup of an External Identifier | |
| * | |
| * Returns the URI of the resource or NULL if not found, it must be freed | |
| * by the caller. | |
| */ | |
| xmlChar * | |
| xmlCatalogResolve(const xmlChar *pubID, const xmlChar *sysID) { | |
| xmlChar *ret; | |
| if (!xmlCatalogInitialized) | |
| xmlInitializeCatalog(); | |
| ret = xmlACatalogResolve(xmlDefaultCatalog, pubID, sysID); | |
| return(ret); | |
| } | |
| /** | |
| * xmlCatalogResolveURI: | |
| * @URI: the URI | |
| * | |
| * Do a complete resolution lookup of an URI | |
| * | |
| * Returns the URI of the resource or NULL if not found, it must be freed | |
| * by the caller. | |
| */ | |
| xmlChar * | |
| xmlCatalogResolveURI(const xmlChar *URI) { | |
| xmlChar *ret; | |
| if (!xmlCatalogInitialized) | |
| xmlInitializeCatalog(); | |
| ret = xmlACatalogResolveURI(xmlDefaultCatalog, URI); | |
| return(ret); | |
| } | |
| #ifdef LIBXML_OUTPUT_ENABLED | |
| /** | |
| * xmlCatalogDump: | |
| * @out: the file. | |
| * | |
| * Dump all the global catalog content to the given file. | |
| */ | |
| void | |
| xmlCatalogDump(FILE *out) { | |
| if (out == NULL) | |
| return; | |
| if (!xmlCatalogInitialized) | |
| xmlInitializeCatalog(); | |
| xmlACatalogDump(xmlDefaultCatalog, out); | |
| } | |
| #endif /* LIBXML_OUTPUT_ENABLED */ | |
| /** | |
| * xmlCatalogAdd: | |
| * @type: the type of record to add to the catalog | |
| * @orig: the system, public or prefix to match | |
| * @replace: the replacement value for the match | |
| * | |
| * Add an entry in the catalog, it may overwrite existing but | |
| * different entries. | |
| * If called before any other catalog routine, allows to override the | |
| * default shared catalog put in place by xmlInitializeCatalog(); | |
| * | |
| * Returns 0 if successful, -1 otherwise | |
| */ | |
| int | |
| xmlCatalogAdd(const xmlChar *type, const xmlChar *orig, const xmlChar *replace) { | |
| int res = -1; | |
| if (!xmlCatalogInitialized) | |
| xmlInitializeCatalogData(); | |
| xmlRMutexLock(xmlCatalogMutex); | |
| /* | |
| * Specific case where one want to override the default catalog | |
| * put in place by xmlInitializeCatalog(); | |
| */ | |
| if ((xmlDefaultCatalog == NULL) && | |
| (xmlStrEqual(type, BAD_CAST "catalog"))) { | |
| xmlDefaultCatalog = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE, | |
| xmlCatalogDefaultPrefer); | |
| xmlDefaultCatalog->xml = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL, | |
| orig, NULL, xmlCatalogDefaultPrefer, NULL); | |
| xmlRMutexUnlock(xmlCatalogMutex); | |
| return(0); | |
| } | |
| res = xmlACatalogAdd(xmlDefaultCatalog, type, orig, replace); | |
| xmlRMutexUnlock(xmlCatalogMutex); | |
| return(res); | |
| } | |
| /** | |
| * xmlCatalogRemove: | |
| * @value: the value to remove | |
| * | |
| * Remove an entry from the catalog | |
| * | |
| * Returns the number of entries removed if successful, -1 otherwise | |
| */ | |
| int | |
| xmlCatalogRemove(const xmlChar *value) { | |
| int res; | |
| if (!xmlCatalogInitialized) | |
| xmlInitializeCatalog(); | |
| xmlRMutexLock(xmlCatalogMutex); | |
| res = xmlACatalogRemove(xmlDefaultCatalog, value); | |
| xmlRMutexUnlock(xmlCatalogMutex); | |
| return(res); | |
| } | |
| /** | |
| * xmlCatalogConvert: | |
| * | |
| * Convert all the SGML catalog entries as XML ones | |
| * | |
| * Returns the number of entries converted if successful, -1 otherwise | |
| */ | |
| int | |
| xmlCatalogConvert(void) { | |
| int res = -1; | |
| if (!xmlCatalogInitialized) | |
| xmlInitializeCatalog(); | |
| xmlRMutexLock(xmlCatalogMutex); | |
| res = xmlConvertSGMLCatalog(xmlDefaultCatalog); | |
| xmlRMutexUnlock(xmlCatalogMutex); | |
| return(res); | |
| } | |
| /************************************************************************ | |
| * * | |
| * Public interface manipulating the common preferences * | |
| * * | |
| ************************************************************************/ | |
| /** | |
| * xmlCatalogGetDefaults: | |
| * | |
| * Used to get the user preference w.r.t. to what catalogs should | |
| * be accepted | |
| * | |
| * Returns the current xmlCatalogAllow value | |
| */ | |
| xmlCatalogAllow | |
| xmlCatalogGetDefaults(void) { | |
| return(xmlCatalogDefaultAllow); | |
| } | |
| /** | |
| * xmlCatalogSetDefaults: | |
| * @allow: what catalogs should be accepted | |
| * | |
| * Used to set the user preference w.r.t. to what catalogs should | |
| * be accepted | |
| */ | |
| void | |
| xmlCatalogSetDefaults(xmlCatalogAllow allow) { | |
| if (xmlDebugCatalogs) { | |
| switch (allow) { | |
| case XML_CATA_ALLOW_NONE: | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Disabling catalog usage\n"); | |
| break; | |
| case XML_CATA_ALLOW_GLOBAL: | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Allowing only global catalogs\n"); | |
| break; | |
| case XML_CATA_ALLOW_DOCUMENT: | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Allowing only catalogs from the document\n"); | |
| break; | |
| case XML_CATA_ALLOW_ALL: | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Allowing all catalogs\n"); | |
| break; | |
| } | |
| } | |
| xmlCatalogDefaultAllow = allow; | |
| } | |
| /** | |
| * xmlCatalogSetDefaultPrefer: | |
| * @prefer: the default preference for delegation | |
| * | |
| * Allows to set the preference between public and system for deletion | |
| * in XML Catalog resolution. C.f. section 4.1.1 of the spec | |
| * Values accepted are XML_CATA_PREFER_PUBLIC or XML_CATA_PREFER_SYSTEM | |
| * | |
| * Returns the previous value of the default preference for delegation | |
| */ | |
| xmlCatalogPrefer | |
| xmlCatalogSetDefaultPrefer(xmlCatalogPrefer prefer) { | |
| xmlCatalogPrefer ret = xmlCatalogDefaultPrefer; | |
| if (prefer == XML_CATA_PREFER_NONE) | |
| return(ret); | |
| if (xmlDebugCatalogs) { | |
| switch (prefer) { | |
| case XML_CATA_PREFER_PUBLIC: | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Setting catalog preference to PUBLIC\n"); | |
| break; | |
| case XML_CATA_PREFER_SYSTEM: | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Setting catalog preference to SYSTEM\n"); | |
| break; | |
| default: | |
| return(ret); | |
| } | |
| } | |
| xmlCatalogDefaultPrefer = prefer; | |
| return(ret); | |
| } | |
| /** | |
| * xmlCatalogSetDebug: | |
| * @level: the debug level of catalogs required | |
| * | |
| * Used to set the debug level for catalog operation, 0 disable | |
| * debugging, 1 enable it | |
| * | |
| * Returns the previous value of the catalog debugging level | |
| */ | |
| int | |
| xmlCatalogSetDebug(int level) { | |
| int ret = xmlDebugCatalogs; | |
| if (level <= 0) | |
| xmlDebugCatalogs = 0; | |
| else | |
| xmlDebugCatalogs = level; | |
| return(ret); | |
| } | |
| /************************************************************************ | |
| * * | |
| * Minimal interfaces used for per-document catalogs by the parser * | |
| * * | |
| ************************************************************************/ | |
| /** | |
| * xmlCatalogFreeLocal: | |
| * @catalogs: a document's list of catalogs | |
| * | |
| * Free up the memory associated to the catalog list | |
| */ | |
| void | |
| xmlCatalogFreeLocal(void *catalogs) { | |
| xmlCatalogEntryPtr catal; | |
| if (!xmlCatalogInitialized) | |
| xmlInitializeCatalog(); | |
| catal = (xmlCatalogEntryPtr) catalogs; | |
| if (catal != NULL) | |
| xmlFreeCatalogEntryList(catal); | |
| } | |
| /** | |
| * xmlCatalogAddLocal: | |
| * @catalogs: a document's list of catalogs | |
| * @URL: the URL to a new local catalog | |
| * | |
| * Add the new entry to the catalog list | |
| * | |
| * Returns the updated list | |
| */ | |
| void * | |
| xmlCatalogAddLocal(void *catalogs, const xmlChar *URL) { | |
| xmlCatalogEntryPtr catal, add; | |
| if (!xmlCatalogInitialized) | |
| xmlInitializeCatalog(); | |
| if (URL == NULL) | |
| return(catalogs); | |
| if (xmlDebugCatalogs) | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Adding document catalog %s\n", URL); | |
| add = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL, URL, NULL, | |
| xmlCatalogDefaultPrefer, NULL); | |
| if (add == NULL) | |
| return(catalogs); | |
| catal = (xmlCatalogEntryPtr) catalogs; | |
| if (catal == NULL) | |
| return((void *) add); | |
| while (catal->next != NULL) | |
| catal = catal->next; | |
| catal->next = add; | |
| return(catalogs); | |
| } | |
| /** | |
| * xmlCatalogLocalResolve: | |
| * @catalogs: a document's list of catalogs | |
| * @pubID: the public ID string | |
| * @sysID: the system ID string | |
| * | |
| * Do a complete resolution lookup of an External Identifier using a | |
| * document's private catalog list | |
| * | |
| * Returns the URI of the resource or NULL if not found, it must be freed | |
| * by the caller. | |
| */ | |
| xmlChar * | |
| xmlCatalogLocalResolve(void *catalogs, const xmlChar *pubID, | |
| const xmlChar *sysID) { | |
| xmlCatalogEntryPtr catal; | |
| xmlChar *ret; | |
| if (!xmlCatalogInitialized) | |
| xmlInitializeCatalog(); | |
| if ((pubID == NULL) && (sysID == NULL)) | |
| return(NULL); | |
| if (xmlDebugCatalogs) { | |
| if ((pubID != NULL) && (sysID != NULL)) { | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Local Resolve: pubID %s sysID %s\n", pubID, sysID); | |
| } else if (pubID != NULL) { | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Local Resolve: pubID %s\n", pubID); | |
| } else { | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Local Resolve: sysID %s\n", sysID); | |
| } | |
| } | |
| catal = (xmlCatalogEntryPtr) catalogs; | |
| if (catal == NULL) | |
| return(NULL); | |
| ret = xmlCatalogListXMLResolve(catal, pubID, sysID); | |
| if ((ret != NULL) && (ret != XML_CATAL_BREAK)) | |
| return(ret); | |
| return(NULL); | |
| } | |
| /** | |
| * xmlCatalogLocalResolveURI: | |
| * @catalogs: a document's list of catalogs | |
| * @URI: the URI | |
| * | |
| * Do a complete resolution lookup of an URI using a | |
| * document's private catalog list | |
| * | |
| * Returns the URI of the resource or NULL if not found, it must be freed | |
| * by the caller. | |
| */ | |
| xmlChar * | |
| xmlCatalogLocalResolveURI(void *catalogs, const xmlChar *URI) { | |
| xmlCatalogEntryPtr catal; | |
| xmlChar *ret; | |
| if (!xmlCatalogInitialized) | |
| xmlInitializeCatalog(); | |
| if (URI == NULL) | |
| return(NULL); | |
| if (xmlDebugCatalogs) | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Resolve URI %s\n", URI); | |
| catal = (xmlCatalogEntryPtr) catalogs; | |
| if (catal == NULL) | |
| return(NULL); | |
| ret = xmlCatalogListXMLResolveURI(catal, URI); | |
| if ((ret != NULL) && (ret != XML_CATAL_BREAK)) | |
| return(ret); | |
| return(NULL); | |
| } | |
| /************************************************************************ | |
| * * | |
| * Deprecated interfaces * | |
| * * | |
| ************************************************************************/ | |
| /** | |
| * xmlCatalogGetSystem: | |
| * @sysID: the system ID string | |
| * | |
| * Try to lookup the catalog reference associated to a system ID | |
| * DEPRECATED, use xmlCatalogResolveSystem() | |
| * | |
| * Returns the resource if found or NULL otherwise. | |
| */ | |
| const xmlChar * | |
| xmlCatalogGetSystem(const xmlChar *sysID) { | |
| xmlChar *ret; | |
| static xmlChar result[1000]; | |
| static int msg = 0; | |
| if (!xmlCatalogInitialized) | |
| xmlInitializeCatalog(); | |
| if (msg == 0) { | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Use of deprecated xmlCatalogGetSystem() call\n"); | |
| msg++; | |
| } | |
| if (sysID == NULL) | |
| return(NULL); | |
| /* | |
| * Check first the XML catalogs | |
| */ | |
| if (xmlDefaultCatalog != NULL) { | |
| ret = xmlCatalogListXMLResolve(xmlDefaultCatalog->xml, NULL, sysID); | |
| if ((ret != NULL) && (ret != XML_CATAL_BREAK)) { | |
| snprintf((char *) result, sizeof(result) - 1, "%s", (char *) ret); | |
| result[sizeof(result) - 1] = 0; | |
| return(result); | |
| } | |
| } | |
| if (xmlDefaultCatalog != NULL) | |
| return(xmlCatalogGetSGMLSystem(xmlDefaultCatalog->sgml, sysID)); | |
| return(NULL); | |
| } | |
| /** | |
| * xmlCatalogGetPublic: | |
| * @pubID: the public ID string | |
| * | |
| * Try to lookup the catalog reference associated to a public ID | |
| * DEPRECATED, use xmlCatalogResolvePublic() | |
| * | |
| * Returns the resource if found or NULL otherwise. | |
| */ | |
| const xmlChar * | |
| xmlCatalogGetPublic(const xmlChar *pubID) { | |
| xmlChar *ret; | |
| static xmlChar result[1000]; | |
| static int msg = 0; | |
| if (!xmlCatalogInitialized) | |
| xmlInitializeCatalog(); | |
| if (msg == 0) { | |
| xmlGenericError(xmlGenericErrorContext, | |
| "Use of deprecated xmlCatalogGetPublic() call\n"); | |
| msg++; | |
| } | |
| if (pubID == NULL) | |
| return(NULL); | |
| /* | |
| * Check first the XML catalogs | |
| */ | |
| if (xmlDefaultCatalog != NULL) { | |
| ret = xmlCatalogListXMLResolve(xmlDefaultCatalog->xml, pubID, NULL); | |
| if ((ret != NULL) && (ret != XML_CATAL_BREAK)) { | |
| snprintf((char *) result, sizeof(result) - 1, "%s", (char *) ret); | |
| result[sizeof(result) - 1] = 0; | |
| return(result); | |
| } | |
| } | |
| if (xmlDefaultCatalog != NULL) | |
| return(xmlCatalogGetSGMLPublic(xmlDefaultCatalog->sgml, pubID)); | |
| return(NULL); | |
| } | |
| #define bottom_catalog | |
| #include "elfgcchack.h" | |
| #endif /* LIBXML_CATALOG_ENABLED */ |