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