| /** |
| * 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> |
| |
| #define MAX_DELEGATE 50 |
| |
| /** |
| * 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 |
| |
| 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_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; |
| }; |
| |
| 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; |
| |
| |
| /************************************************************************ |
| * * |
| * 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 |
| * |
| * 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 ret; |
| |
| ret = (xmlCatalogEntryPtr) xmlMalloc(sizeof(xmlCatalogEntry)); |
| if (ret == NULL) { |
| xmlGenericError(xmlGenericErrorContext, |
| "malloc of %d byte failed\n", sizeof(xmlCatalogEntry)); |
| return(NULL); |
| } |
| ret->next = NULL; |
| ret->parent = NULL; |
| ret->children = NULL; |
| ret->type = type; |
| if (name != NULL) |
| ret->name = xmlStrdup(name); |
| else |
| ret->name = NULL; |
| 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; |
| 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) { |
| xmlGenericError(xmlGenericErrorContext, |
| "malloc of %d byte failed\n", sizeof(xmlCatalog)); |
| 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 entry |
| * |
| * 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 * |
| * * |
| ************************************************************************/ |
| |
| /** |
| * xmlCatalogDumpEntry: |
| * @entry: the |
| * @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"); |
| } |
| |
| static int |
| xmlDumpXMLCatalog(FILE *out, xmlCatalogEntryPtr catal) { |
| int ret; |
| xmlDocPtr doc; |
| xmlNsPtr ns; |
| xmlDtdPtr dtd; |
| xmlNodePtr node, catalog; |
| xmlOutputBufferPtr buf; |
| xmlCatalogEntryPtr cur; |
| |
| /* |
| * 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); |
| |
| /* |
| * add all the catalog entries |
| */ |
| cur = catal; |
| while (cur != NULL) { |
| 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_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; |
| } |
| |
| /* |
| * 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); |
| } |
| |
| /************************************************************************ |
| * * |
| * 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) - 3) |
| 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[1] == 'B')) |
| result[i++] = '+'; |
| else if ((urn[1] == '3') && (urn[1] == 'A')) |
| result[i++] = ':'; |
| else if ((urn[1] == '2') && (urn[1] == 'F')) |
| result[i++] = '/'; |
| else if ((urn[1] == '3') && (urn[1] == 'B')) |
| result[i++] = ';'; |
| else if ((urn[1] == '2') && (urn[1] == '7')) |
| result[i++] = '\''; |
| else if ((urn[1] == '3') && (urn[1] == 'F')) |
| result[i++] = '?'; |
| else if ((urn[1] == '2') && (urn[1] == '3')) |
| result[i++] = '#'; |
| else if ((urn[1] == '2') && (urn[1] == '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) { |
| if (xmlDefaultSAXHandler.error != NULL) { |
| xmlDefaultSAXHandler.error(NULL, "out of memory\n"); |
| } |
| 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 = xmlCanonicPath(filename); |
| inputStream->buf = buf; |
| inputStream->base = inputStream->buf->buffer->content; |
| inputStream->cur = inputStream->buf->buffer->content; |
| inputStream->end = |
| &inputStream->buf->buffer->content[inputStream->buf->buffer->use]; |
| |
| 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; |
| |
| 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 = xmlMallocAtomic(size + 10); |
| if (content == NULL) { |
| xmlGenericError(xmlGenericErrorContext, |
| "malloc of %d byte failed\n", size + 10); |
| return (NULL); |
| } |
| #ifdef HAVE_STAT |
| len = read(fd, content, size); |
| #else |
| len = fread(content, 1, size, fd); |
| #endif |
| if (len < 0) { |
| xmlFree(content); |
| return (NULL); |
| } |
| #ifdef HAVE_STAT |
| close(fd); |
| #else |
| fclose(fd); |
| #endif |
| content[len] = 0; |
| |
| return(content); |
| } |
| |
| /************************************************************************ |
| * * |
| * The XML Catalog parser * |
| * * |
| ************************************************************************/ |
| |
| static xmlCatalogEntryPtr |
| xmlParseXMLCatalogFile(xmlCatalogPrefer prefer, const xmlChar *filename); |
| static void |
| xmlParseXMLCatalogNodeList(xmlNodePtr cur, xmlCatalogPrefer prefer, |
| xmlCatalogEntryPtr parent); |
| 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 associate 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 |
| * |
| * 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) { |
| 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) { |
| xmlGenericError(xmlGenericErrorContext, |
| "%s entry lacks '%s'\n", name, attrName); |
| ok = 0; |
| } |
| } |
| uriValue = xmlGetProp(cur, uriAttrName); |
| if (uriValue == NULL) { |
| xmlGenericError(xmlGenericErrorContext, |
| "%s entry lacks '%s'\n", name, uriAttrName); |
| 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); |
| } else { |
| xmlGenericError(xmlGenericErrorContext, |
| "%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 |
| * |
| * 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) |
| { |
| xmlChar *uri = NULL; |
| xmlChar *URL = NULL; |
| xmlChar *base = NULL; |
| xmlCatalogEntryPtr entry = NULL; |
| |
| if (cur == NULL) |
| return; |
| if (xmlStrEqual(cur->name, BAD_CAST "group")) { |
| xmlChar *prop; |
| |
| 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 { |
| xmlGenericError(xmlGenericErrorContext, |
| "Invalid value for prefer: '%s'\n", prop); |
| } |
| xmlFree(prop); |
| } |
| /* |
| * Recurse to propagate prefer to the subtree |
| * (xml:base handling is automated) |
| */ |
| xmlParseXMLCatalogNodeList(cur->children, prefer, parent); |
| } else if (xmlStrEqual(cur->name, BAD_CAST "public")) { |
| entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_PUBLIC, |
| BAD_CAST "public", BAD_CAST "publicId", BAD_CAST "uri", prefer); |
| } else if (xmlStrEqual(cur->name, BAD_CAST "system")) { |
| entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_SYSTEM, |
| BAD_CAST "system", BAD_CAST "systemId", BAD_CAST "uri", prefer); |
| } 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); |
| } 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); |
| } 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); |
| } else if (xmlStrEqual(cur->name, BAD_CAST "uri")) { |
| entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_URI, |
| BAD_CAST "uri", BAD_CAST "name", |
| BAD_CAST "uri", prefer); |
| } 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); |
| } 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); |
| } else if (xmlStrEqual(cur->name, BAD_CAST "nextCatalog")) { |
| entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_NEXT_CATALOG, |
| BAD_CAST "nextCatalog", NULL, |
| BAD_CAST "catalog", prefer); |
| } |
| if ((entry != NULL) && (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 (base != NULL) |
| xmlFree(base); |
| if (uri != NULL) |
| xmlFree(uri); |
| if (URL != NULL) |
| xmlFree(URL); |
| } |
| |
| /** |
| * xmlParseXMLCatalogNodeList: |
| * @cur: the XML node list of siblings |
| * @prefer: the PUBLIC vs. SYSTEM current preference value |
| * @parent: the parent Catalog entry |
| * |
| * 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) { |
| while (cur != NULL) { |
| if ((cur->ns != NULL) && (cur->ns->href != NULL) && |
| (xmlStrEqual(cur->ns->href, XML_CATALOGS_NAMESPACE))) { |
| xmlParseXMLCatalogNode(cur, prefer, parent); |
| } |
| 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); |
| 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 { |
| xmlGenericError(xmlGenericErrorContext, |
| "Invalid value for prefer: '%s'\n", |
| prop); |
| } |
| xmlFree(prop); |
| } |
| cur = cur->children; |
| xmlParseXMLCatalogNodeList(cur, prefer, parent); |
| } else { |
| xmlGenericError(xmlGenericErrorContext, |
| "File %s is not an XML Catalog\n", filename); |
| 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); |
| if (catal->children != 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); |
| else |
| cur->next = xmlNewCatalogEntry(typ, orig, replace, |
| NULL, catal->prefer); |
| if (doregister) { |
| cur = 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; |
| |
| /* |
| * 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\n", cur->name); |
| 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]); |
| 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) |
| return(ret); |
| } |
| } |
| cur = cur->next; |
| } |
| /* |
| * Apply the cut algorithm explained in 4/ |
| */ |
| 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); |
| 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) |
| 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 = xmlCatalogListXMLResolve(cur->children, pubID, sysID); |
| if (ret != NULL) |
| return(ret); |
| } |
| } |
| cur = cur->next; |
| } |
| } |
| |
| 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); |
| |
| /* |
| * 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; |
| |
| if (catal == NULL) |
| return(NULL); |
| if ((pubID == NULL) && (sysID == NULL)) |
| return(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); |
| 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, 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 = xmlCatalogXMLResolve(catal->children, pubID, sysID); |
| if (ret != NULL) |
| return(ret); |
| } |
| } |
| catal = catal->next; |
| } |
| 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(*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; |
| 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) { |
| xmlGenericError(xmlGenericErrorContext, |
| "malloc of %d byte failed\n", size); |
| return(NULL); |
| } |
| while (xmlIsPubidChar(*cur) || (*cur == '?')) { |
| if ((*cur == stop) && (stop != ' ')) |
| break; |
| if ((stop == ' ') && (IS_BLANK(*cur))) |
| break; |
| if (len + 1 >= size) { |
| size *= 2; |
| buf = (xmlChar *) xmlRealloc(buf, size * sizeof(xmlChar)); |
| if (buf == NULL) { |
| xmlGenericError(xmlGenericErrorContext, |
| "realloc of %d byte failed\n", size); |
| return(NULL); |
| } |
| } |
| buf[len++] = *cur; |
| count++; |
| NEXT; |
| } |
| buf[len] = 0; |
| if (stop == ' ') { |
| if (!IS_BLANK(*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; |
| else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE")) |
| type = SGML_CATA_DELEGATE; |
| 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(*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 *) "DELEGATE")) |
| type = SGML_CATA_DELEGATE; |
| 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(*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 (!IS_BLANK(*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); |
| 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); |
| 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 system ID associated to a public ID |
| * |
| * Returns the system ID if found or NULL otherwise. |
| */ |
| static const xmlChar * |
| xmlCatalogGetSGMLPublic(xmlHashTablePtr catal, const xmlChar *pubID) { |
| xmlCatalogEntryPtr entry; |
| |
| if (catal == NULL) |
| return(NULL); |
| |
| entry = (xmlCatalogEntryPtr) xmlHashLookup(catal, pubID); |
| if (entry == NULL) |
| return(NULL); |
| if (entry->type == SGML_CATA_PUBLIC) |
| return(entry->URL); |
| return(NULL); |
| } |
| |
| /** |
| * xmlCatalogGetSGMLSystem: |
| * @catal: an SGML catalog hash |
| * @sysId: the public ID string |
| * |
| * Try to lookup the catalog local reference for a system ID |
| * |
| * Returns the system ID 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); |
| 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); |
| } |
| 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); |
| |
| 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 public ID string |
| * |
| * Try to lookup the catalog resource for a system ID |
| * |
| * Returns the system ID 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 system ID associated to a public ID in that catalog |
| * |
| * Returns the system ID 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) { |
| 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) |
| sgml = xmlStrdup(sgml); |
| } |
| return(ret); |
| } |
| |
| /** |
| * xmlACatalogDump: |
| * @catal: a Catalog |
| * @out: the file. |
| * |
| * Free up all the memory associated with catalogs |
| */ |
| 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); |
| } |
| } |
| |
| /** |
| * 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); |
| 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) |
| catalogs = XML_XML_DEFAULT_CATALOG; |
| |
| 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 (IS_BLANK(*cur)) |
| cur++; |
| if (*cur != 0) { |
| paths = cur; |
| while ((*cur != 0) && (!IS_BLANK(*cur))) |
| cur++; |
| path = (char *) xmlStrndup((const xmlChar *)paths, cur - paths); |
| if (path != NULL) { |
| *nextent = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL, |
| NULL, BAD_CAST path, xmlCatalogDefaultPrefer); |
| 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) |
| 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; |
| |
| if (pathss == NULL) |
| return; |
| |
| cur = pathss; |
| while ((cur != NULL) && (*cur != 0)) { |
| while (IS_BLANK(*cur)) cur++; |
| if (*cur != 0) { |
| paths = cur; |
| while ((*cur != 0) && (*cur != ':') && (!IS_BLANK(*cur))) |
| cur++; |
| path = xmlStrndup((const xmlChar *)paths, cur - paths); |
| if (path != NULL) { |
| xmlLoadCatalog((const char *) path); |
| xmlFree(path); |
| } |
| } |
| while (*cur == ':') |
| 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 public ID string |
| * |
| * Try to lookup the catalog resource for a system ID |
| * |
| * Returns the system ID 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 system ID associated to a public ID |
| * |
| * Returns the system ID 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); |
| } |
| |
| /** |
| * xmlCatalogDump: |
| * @out: the file. |
| * |
| * Free up all the memory associated with catalogs |
| */ |
| void |
| xmlCatalogDump(FILE *out) { |
| if (out == NULL) |
| return; |
| |
| if (!xmlCatalogInitialized) |
| xmlInitializeCatalog(); |
| |
| xmlACatalogDump(xmlDefaultCatalog, out); |
| } |
| |
| /** |
| * 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); |
| |
| 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; |
| case XML_CATA_PREFER_NONE: |
| break; |
| } |
| } |
| 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); |
| 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) { |
| 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 system ID associated to a public ID |
| * DEPRECATED, use xmlCatalogResolveSystem() |
| * |
| * Returns the system ID 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 system ID associated to a public ID |
| * DEPRECATED, use xmlCatalogResolvePublic() |
| * |
| * Returns the system ID 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); |
| } |
| |
| #endif /* LIBXML_CATALOG_ENABLED */ |