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