xref: /reactos/sdk/lib/3rdparty/libxml2/catalog.c (revision 911153da)
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_TYPES_H
22 #include <sys/types.h>
23 #endif
24 #ifdef HAVE_SYS_STAT_H
25 #include <sys/stat.h>
26 #endif
27 #ifdef HAVE_UNISTD_H
28 #include <unistd.h>
29 #endif
30 #ifdef HAVE_FCNTL_H
31 #include <fcntl.h>
32 #endif
33 #include <libxml/xmlmemory.h>
34 #include <libxml/hash.h>
35 #include <libxml/uri.h>
36 #include <libxml/parserInternals.h>
37 #include <libxml/catalog.h>
38 #include <libxml/xmlerror.h>
39 #include <libxml/threads.h>
40 #include <libxml/globals.h>
41 
42 #include "buf.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 #ifdef LIBXML_SAX1_ENABLED
895 	if (xmlDefaultSAXHandler.error != NULL) {
896 	    xmlDefaultSAXHandler.error(NULL, "out of memory\n");
897 	}
898 #endif
899 	return(NULL);
900     }
901 
902     buf = xmlParserInputBufferCreateFilename(filename, XML_CHAR_ENCODING_NONE);
903     if (buf == NULL) {
904 	xmlFreeParserCtxt(ctxt);
905 	return(NULL);
906     }
907 
908     inputStream = xmlNewInputStream(ctxt);
909     if (inputStream == NULL) {
910 	xmlFreeParserInputBuffer(buf);
911 	xmlFreeParserCtxt(ctxt);
912 	return(NULL);
913     }
914 
915     inputStream->filename = (char *) xmlCanonicPath((const xmlChar *)filename);
916     inputStream->buf = buf;
917     xmlBufResetInput(buf->buffer, inputStream);
918 
919     inputPush(ctxt, inputStream);
920     if (ctxt->directory == NULL)
921         directory = xmlParserGetDirectory(filename);
922     if ((ctxt->directory == NULL) && (directory != NULL))
923         ctxt->directory = directory;
924     ctxt->valid = 0;
925     ctxt->validate = 0;
926     ctxt->loadsubset = 0;
927     ctxt->pedantic = 0;
928     ctxt->dictNames = 1;
929 
930     xmlParseDocument(ctxt);
931 
932     if (ctxt->wellFormed)
933 	ret = ctxt->myDoc;
934     else {
935         ret = NULL;
936         xmlFreeDoc(ctxt->myDoc);
937         ctxt->myDoc = NULL;
938     }
939     xmlFreeParserCtxt(ctxt);
940 
941     return(ret);
942 }
943 
944 /**
945  * xmlLoadFileContent:
946  * @filename:  a file path
947  *
948  * Load a file content into memory.
949  *
950  * Returns a pointer to the 0 terminated string or NULL in case of error
951  */
952 static xmlChar *
xmlLoadFileContent(const char * filename)953 xmlLoadFileContent(const char *filename)
954 {
955 #ifdef HAVE_STAT
956     int fd;
957 #else
958     FILE *fd;
959 #endif
960     int len;
961     long size;
962 
963 #ifdef HAVE_STAT
964     struct stat info;
965 #endif
966     xmlChar *content;
967 
968     if (filename == NULL)
969         return (NULL);
970 
971 #ifdef HAVE_STAT
972     if (stat(filename, &info) < 0)
973         return (NULL);
974 #endif
975 
976 #ifdef HAVE_STAT
977     if ((fd = open(filename, O_RDONLY)) < 0)
978 #else
979     if ((fd = fopen(filename, "rb")) == NULL)
980 #endif
981     {
982         return (NULL);
983     }
984 #ifdef HAVE_STAT
985     size = info.st_size;
986 #else
987     if (fseek(fd, 0, SEEK_END) || (size = ftell(fd)) == EOF || fseek(fd, 0, SEEK_SET)) {        /* File operations denied? ok, just close and return failure */
988         fclose(fd);
989         return (NULL);
990     }
991 #endif
992     content = (xmlChar*)xmlMallocAtomic(size + 10);
993     if (content == NULL) {
994         xmlCatalogErrMemory("allocating catalog data");
995 #ifdef HAVE_STAT
996 	close(fd);
997 #else
998 	fclose(fd);
999 #endif
1000         return (NULL);
1001     }
1002 #ifdef HAVE_STAT
1003     len = read(fd, content, size);
1004     close(fd);
1005 #else
1006     len = fread(content, 1, size, fd);
1007     fclose(fd);
1008 #endif
1009     if (len < 0) {
1010         xmlFree(content);
1011         return (NULL);
1012     }
1013     content[len] = 0;
1014 
1015     return(content);
1016 }
1017 
1018 /**
1019  * xmlCatalogNormalizePublic:
1020  * @pubID:  the public ID string
1021  *
1022  *  Normalizes the Public Identifier
1023  *
1024  * Implements 6.2. Public Identifier Normalization
1025  * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
1026  *
1027  * Returns the new string or NULL, the string must be deallocated
1028  *         by the caller.
1029  */
1030 static xmlChar *
xmlCatalogNormalizePublic(const xmlChar * pubID)1031 xmlCatalogNormalizePublic(const xmlChar *pubID)
1032 {
1033     int ok = 1;
1034     int white;
1035     const xmlChar *p;
1036     xmlChar *ret;
1037     xmlChar *q;
1038 
1039     if (pubID == NULL)
1040         return(NULL);
1041 
1042     white = 1;
1043     for (p = pubID;*p != 0 && ok;p++) {
1044         if (!xmlIsBlank_ch(*p))
1045             white = 0;
1046         else if (*p == 0x20 && !white)
1047             white = 1;
1048         else
1049             ok = 0;
1050     }
1051     if (ok && !white)	/* is normalized */
1052         return(NULL);
1053 
1054     ret = xmlStrdup(pubID);
1055     q = ret;
1056     white = 0;
1057     for (p = pubID;*p != 0;p++) {
1058         if (xmlIsBlank_ch(*p)) {
1059             if (q != ret)
1060                 white = 1;
1061         } else {
1062             if (white) {
1063                 *(q++) = 0x20;
1064                 white = 0;
1065             }
1066             *(q++) = *p;
1067         }
1068     }
1069     *q = 0;
1070     return(ret);
1071 }
1072 
1073 /************************************************************************
1074  *									*
1075  *			The XML Catalog parser				*
1076  *									*
1077  ************************************************************************/
1078 
1079 static xmlCatalogEntryPtr
1080 xmlParseXMLCatalogFile(xmlCatalogPrefer prefer, const xmlChar *filename);
1081 static void
1082 xmlParseXMLCatalogNodeList(xmlNodePtr cur, xmlCatalogPrefer prefer,
1083 	                   xmlCatalogEntryPtr parent, xmlCatalogEntryPtr cgroup);
1084 static xmlChar *
1085 xmlCatalogListXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,
1086 	              const xmlChar *sysID);
1087 static xmlChar *
1088 xmlCatalogListXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI);
1089 
1090 
1091 /**
1092  * xmlGetXMLCatalogEntryType:
1093  * @name:  the name
1094  *
1095  * lookup the internal type associated to an XML catalog entry name
1096  *
1097  * Returns the type associated with that name
1098  */
1099 static xmlCatalogEntryType
xmlGetXMLCatalogEntryType(const xmlChar * name)1100 xmlGetXMLCatalogEntryType(const xmlChar *name) {
1101     xmlCatalogEntryType type = XML_CATA_NONE;
1102     if (xmlStrEqual(name, (const xmlChar *) "system"))
1103 	type = XML_CATA_SYSTEM;
1104     else if (xmlStrEqual(name, (const xmlChar *) "public"))
1105 	type = XML_CATA_PUBLIC;
1106     else if (xmlStrEqual(name, (const xmlChar *) "rewriteSystem"))
1107 	type = XML_CATA_REWRITE_SYSTEM;
1108     else if (xmlStrEqual(name, (const xmlChar *) "delegatePublic"))
1109 	type = XML_CATA_DELEGATE_PUBLIC;
1110     else if (xmlStrEqual(name, (const xmlChar *) "delegateSystem"))
1111 	type = XML_CATA_DELEGATE_SYSTEM;
1112     else if (xmlStrEqual(name, (const xmlChar *) "uri"))
1113 	type = XML_CATA_URI;
1114     else if (xmlStrEqual(name, (const xmlChar *) "rewriteURI"))
1115 	type = XML_CATA_REWRITE_URI;
1116     else if (xmlStrEqual(name, (const xmlChar *) "delegateURI"))
1117 	type = XML_CATA_DELEGATE_URI;
1118     else if (xmlStrEqual(name, (const xmlChar *) "nextCatalog"))
1119 	type = XML_CATA_NEXT_CATALOG;
1120     else if (xmlStrEqual(name, (const xmlChar *) "catalog"))
1121 	type = XML_CATA_CATALOG;
1122     return(type);
1123 }
1124 
1125 /**
1126  * xmlParseXMLCatalogOneNode:
1127  * @cur:  the XML node
1128  * @type:  the type of Catalog entry
1129  * @name:  the name of the node
1130  * @attrName:  the attribute holding the value
1131  * @uriAttrName:  the attribute holding the URI-Reference
1132  * @prefer:  the PUBLIC vs. SYSTEM current preference value
1133  * @cgroup:  the group which includes this node
1134  *
1135  * Finishes the examination of an XML tree node of a catalog and build
1136  * a Catalog entry from it.
1137  *
1138  * Returns the new Catalog entry node or NULL in case of error.
1139  */
1140 static xmlCatalogEntryPtr
xmlParseXMLCatalogOneNode(xmlNodePtr cur,xmlCatalogEntryType type,const xmlChar * name,const xmlChar * attrName,const xmlChar * uriAttrName,xmlCatalogPrefer prefer,xmlCatalogEntryPtr cgroup)1141 xmlParseXMLCatalogOneNode(xmlNodePtr cur, xmlCatalogEntryType type,
1142 			  const xmlChar *name, const xmlChar *attrName,
1143 			  const xmlChar *uriAttrName, xmlCatalogPrefer prefer,
1144 			  xmlCatalogEntryPtr cgroup) {
1145     int ok = 1;
1146     xmlChar *uriValue;
1147     xmlChar *nameValue = NULL;
1148     xmlChar *base = NULL;
1149     xmlChar *URL = NULL;
1150     xmlCatalogEntryPtr ret = NULL;
1151 
1152     if (attrName != NULL) {
1153 	nameValue = xmlGetProp(cur, attrName);
1154 	if (nameValue == NULL) {
1155 	    xmlCatalogErr(ret, cur, XML_CATALOG_MISSING_ATTR,
1156 			  "%s entry lacks '%s'\n", name, attrName, NULL);
1157 	    ok = 0;
1158 	}
1159     }
1160     uriValue = xmlGetProp(cur, uriAttrName);
1161     if (uriValue == NULL) {
1162 	xmlCatalogErr(ret, cur, XML_CATALOG_MISSING_ATTR,
1163 		"%s entry lacks '%s'\n", name, uriAttrName, NULL);
1164 	ok = 0;
1165     }
1166     if (!ok) {
1167 	if (nameValue != NULL)
1168 	    xmlFree(nameValue);
1169 	if (uriValue != NULL)
1170 	    xmlFree(uriValue);
1171 	return(NULL);
1172     }
1173 
1174     base = xmlNodeGetBase(cur->doc, cur);
1175     URL = xmlBuildURI(uriValue, base);
1176     if (URL != NULL) {
1177 	if (xmlDebugCatalogs > 1) {
1178 	    if (nameValue != NULL)
1179 		xmlGenericError(xmlGenericErrorContext,
1180 			"Found %s: '%s' '%s'\n", name, nameValue, URL);
1181 	    else
1182 		xmlGenericError(xmlGenericErrorContext,
1183 			"Found %s: '%s'\n", name, URL);
1184 	}
1185 	ret = xmlNewCatalogEntry(type, nameValue, uriValue, URL, prefer, cgroup);
1186     } else {
1187 	xmlCatalogErr(ret, cur, XML_CATALOG_ENTRY_BROKEN,
1188 		"%s entry '%s' broken ?: %s\n", name, uriAttrName, uriValue);
1189     }
1190     if (nameValue != NULL)
1191 	xmlFree(nameValue);
1192     if (uriValue != NULL)
1193 	xmlFree(uriValue);
1194     if (base != NULL)
1195 	xmlFree(base);
1196     if (URL != NULL)
1197 	xmlFree(URL);
1198     return(ret);
1199 }
1200 
1201 /**
1202  * xmlParseXMLCatalogNode:
1203  * @cur:  the XML node
1204  * @prefer:  the PUBLIC vs. SYSTEM current preference value
1205  * @parent:  the parent Catalog entry
1206  * @cgroup:  the group which includes this node
1207  *
1208  * Examines an XML tree node of a catalog and build
1209  * a Catalog entry from it adding it to its parent. The examination can
1210  * be recursive.
1211  */
1212 static void
xmlParseXMLCatalogNode(xmlNodePtr cur,xmlCatalogPrefer prefer,xmlCatalogEntryPtr parent,xmlCatalogEntryPtr cgroup)1213 xmlParseXMLCatalogNode(xmlNodePtr cur, xmlCatalogPrefer prefer,
1214 	               xmlCatalogEntryPtr parent, xmlCatalogEntryPtr cgroup)
1215 {
1216     xmlChar *base = NULL;
1217     xmlCatalogEntryPtr entry = NULL;
1218 
1219     if (cur == NULL)
1220         return;
1221     if (xmlStrEqual(cur->name, BAD_CAST "group")) {
1222         xmlChar *prop;
1223 	xmlCatalogPrefer pref = XML_CATA_PREFER_NONE;
1224 
1225         prop = xmlGetProp(cur, BAD_CAST "prefer");
1226         if (prop != NULL) {
1227             if (xmlStrEqual(prop, BAD_CAST "system")) {
1228                 prefer = XML_CATA_PREFER_SYSTEM;
1229             } else if (xmlStrEqual(prop, BAD_CAST "public")) {
1230                 prefer = XML_CATA_PREFER_PUBLIC;
1231             } else {
1232 		xmlCatalogErr(parent, cur, XML_CATALOG_PREFER_VALUE,
1233                               "Invalid value for prefer: '%s'\n",
1234 			      prop, NULL, NULL);
1235             }
1236             xmlFree(prop);
1237 	    pref = prefer;
1238         }
1239 	prop = xmlGetProp(cur, BAD_CAST "id");
1240 	base = xmlGetNsProp(cur, BAD_CAST "base", XML_XML_NAMESPACE);
1241 	entry = xmlNewCatalogEntry(XML_CATA_GROUP, prop, base, NULL, pref, cgroup);
1242 	xmlFree(prop);
1243     } else if (xmlStrEqual(cur->name, BAD_CAST "public")) {
1244 	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_PUBLIC,
1245 		BAD_CAST "public", BAD_CAST "publicId", BAD_CAST "uri", prefer, cgroup);
1246     } else if (xmlStrEqual(cur->name, BAD_CAST "system")) {
1247 	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_SYSTEM,
1248 		BAD_CAST "system", BAD_CAST "systemId", BAD_CAST "uri", prefer, cgroup);
1249     } else if (xmlStrEqual(cur->name, BAD_CAST "rewriteSystem")) {
1250 	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_REWRITE_SYSTEM,
1251 		BAD_CAST "rewriteSystem", BAD_CAST "systemIdStartString",
1252 		BAD_CAST "rewritePrefix", prefer, cgroup);
1253     } else if (xmlStrEqual(cur->name, BAD_CAST "delegatePublic")) {
1254 	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_PUBLIC,
1255 		BAD_CAST "delegatePublic", BAD_CAST "publicIdStartString",
1256 		BAD_CAST "catalog", prefer, cgroup);
1257     } else if (xmlStrEqual(cur->name, BAD_CAST "delegateSystem")) {
1258 	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_SYSTEM,
1259 		BAD_CAST "delegateSystem", BAD_CAST "systemIdStartString",
1260 		BAD_CAST "catalog", prefer, cgroup);
1261     } else if (xmlStrEqual(cur->name, BAD_CAST "uri")) {
1262 	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_URI,
1263 		BAD_CAST "uri", BAD_CAST "name",
1264 		BAD_CAST "uri", prefer, cgroup);
1265     } else if (xmlStrEqual(cur->name, BAD_CAST "rewriteURI")) {
1266 	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_REWRITE_URI,
1267 		BAD_CAST "rewriteURI", BAD_CAST "uriStartString",
1268 		BAD_CAST "rewritePrefix", prefer, cgroup);
1269     } else if (xmlStrEqual(cur->name, BAD_CAST "delegateURI")) {
1270 	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_URI,
1271 		BAD_CAST "delegateURI", BAD_CAST "uriStartString",
1272 		BAD_CAST "catalog", prefer, cgroup);
1273     } else if (xmlStrEqual(cur->name, BAD_CAST "nextCatalog")) {
1274 	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_NEXT_CATALOG,
1275 		BAD_CAST "nextCatalog", NULL,
1276 		BAD_CAST "catalog", prefer, cgroup);
1277     }
1278     if (entry != NULL) {
1279         if (parent != NULL) {
1280 	    entry->parent = parent;
1281 	    if (parent->children == NULL)
1282 		parent->children = entry;
1283 	    else {
1284 		xmlCatalogEntryPtr prev;
1285 
1286 		prev = parent->children;
1287 		while (prev->next != NULL)
1288 		    prev = prev->next;
1289 		prev->next = entry;
1290 	    }
1291 	}
1292 	if (entry->type == XML_CATA_GROUP) {
1293 	    /*
1294 	     * Recurse to propagate prefer to the subtree
1295 	     * (xml:base handling is automated)
1296 	     */
1297             xmlParseXMLCatalogNodeList(cur->children, prefer, parent, entry);
1298 	}
1299     }
1300     if (base != NULL)
1301 	xmlFree(base);
1302 }
1303 
1304 /**
1305  * xmlParseXMLCatalogNodeList:
1306  * @cur:  the XML node list of siblings
1307  * @prefer:  the PUBLIC vs. SYSTEM current preference value
1308  * @parent:  the parent Catalog entry
1309  * @cgroup:  the group which includes this list
1310  *
1311  * Examines a list of XML sibling nodes of a catalog and build
1312  * a list of Catalog entry from it adding it to the parent.
1313  * The examination will recurse to examine node subtrees.
1314  */
1315 static void
xmlParseXMLCatalogNodeList(xmlNodePtr cur,xmlCatalogPrefer prefer,xmlCatalogEntryPtr parent,xmlCatalogEntryPtr cgroup)1316 xmlParseXMLCatalogNodeList(xmlNodePtr cur, xmlCatalogPrefer prefer,
1317 	                   xmlCatalogEntryPtr parent, xmlCatalogEntryPtr cgroup) {
1318     while (cur != NULL) {
1319 	if ((cur->ns != NULL) && (cur->ns->href != NULL) &&
1320 	    (xmlStrEqual(cur->ns->href, XML_CATALOGS_NAMESPACE))) {
1321 	    xmlParseXMLCatalogNode(cur, prefer, parent, cgroup);
1322 	}
1323 	cur = cur->next;
1324     }
1325     /* TODO: sort the list according to REWRITE lengths and prefer value */
1326 }
1327 
1328 /**
1329  * xmlParseXMLCatalogFile:
1330  * @prefer:  the PUBLIC vs. SYSTEM current preference value
1331  * @filename:  the filename for the catalog
1332  *
1333  * Parses the catalog file to extract the XML tree and then analyze the
1334  * tree to build a list of Catalog entries corresponding to this catalog
1335  *
1336  * Returns the resulting Catalog entries list
1337  */
1338 static xmlCatalogEntryPtr
xmlParseXMLCatalogFile(xmlCatalogPrefer prefer,const xmlChar * filename)1339 xmlParseXMLCatalogFile(xmlCatalogPrefer prefer, const xmlChar *filename) {
1340     xmlDocPtr doc;
1341     xmlNodePtr cur;
1342     xmlChar *prop;
1343     xmlCatalogEntryPtr parent = NULL;
1344 
1345     if (filename == NULL)
1346         return(NULL);
1347 
1348     doc = xmlParseCatalogFile((const char *) filename);
1349     if (doc == NULL) {
1350 	if (xmlDebugCatalogs)
1351 	    xmlGenericError(xmlGenericErrorContext,
1352 		    "Failed to parse catalog %s\n", filename);
1353 	return(NULL);
1354     }
1355 
1356     if (xmlDebugCatalogs)
1357 	xmlGenericError(xmlGenericErrorContext,
1358 		"%d Parsing catalog %s\n", xmlGetThreadId(), filename);
1359 
1360     cur = xmlDocGetRootElement(doc);
1361     if ((cur != NULL) && (xmlStrEqual(cur->name, BAD_CAST "catalog")) &&
1362 	(cur->ns != NULL) && (cur->ns->href != NULL) &&
1363 	(xmlStrEqual(cur->ns->href, XML_CATALOGS_NAMESPACE))) {
1364 
1365 	parent = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
1366 				    (const xmlChar *)filename, NULL, prefer, NULL);
1367         if (parent == NULL) {
1368 	    xmlFreeDoc(doc);
1369 	    return(NULL);
1370 	}
1371 
1372 	prop = xmlGetProp(cur, BAD_CAST "prefer");
1373 	if (prop != NULL) {
1374 	    if (xmlStrEqual(prop, BAD_CAST "system")) {
1375 		prefer = XML_CATA_PREFER_SYSTEM;
1376 	    } else if (xmlStrEqual(prop, BAD_CAST "public")) {
1377 		prefer = XML_CATA_PREFER_PUBLIC;
1378 	    } else {
1379 		xmlCatalogErr(NULL, cur, XML_CATALOG_PREFER_VALUE,
1380 			      "Invalid value for prefer: '%s'\n",
1381 			      prop, NULL, NULL);
1382 	    }
1383 	    xmlFree(prop);
1384 	}
1385 	cur = cur->children;
1386 	xmlParseXMLCatalogNodeList(cur, prefer, parent, NULL);
1387     } else {
1388 	xmlCatalogErr(NULL, (xmlNodePtr) doc, XML_CATALOG_NOT_CATALOG,
1389 		      "File %s is not an XML Catalog\n",
1390 		      filename, NULL, NULL);
1391 	xmlFreeDoc(doc);
1392 	return(NULL);
1393     }
1394     xmlFreeDoc(doc);
1395     return(parent);
1396 }
1397 
1398 /**
1399  * xmlFetchXMLCatalogFile:
1400  * @catal:  an existing but incomplete catalog entry
1401  *
1402  * Fetch and parse the subcatalog referenced by an entry
1403  *
1404  * Returns 0 in case of success, -1 otherwise
1405  */
1406 static int
xmlFetchXMLCatalogFile(xmlCatalogEntryPtr catal)1407 xmlFetchXMLCatalogFile(xmlCatalogEntryPtr catal) {
1408     xmlCatalogEntryPtr doc;
1409 
1410     if (catal == NULL)
1411 	return(-1);
1412     if (catal->URL == NULL)
1413 	return(-1);
1414 
1415     /*
1416      * lock the whole catalog for modification
1417      */
1418     xmlRMutexLock(xmlCatalogMutex);
1419     if (catal->children != NULL) {
1420 	/* Okay someone else did it in the meantime */
1421 	xmlRMutexUnlock(xmlCatalogMutex);
1422 	return(0);
1423     }
1424 
1425     if (xmlCatalogXMLFiles != NULL) {
1426 	doc = (xmlCatalogEntryPtr)
1427 	    xmlHashLookup(xmlCatalogXMLFiles, catal->URL);
1428 	if (doc != NULL) {
1429 	    if (xmlDebugCatalogs)
1430 		xmlGenericError(xmlGenericErrorContext,
1431 		    "Found %s in file hash\n", catal->URL);
1432 
1433 	    if (catal->type == XML_CATA_CATALOG)
1434 		catal->children = doc->children;
1435 	    else
1436 		catal->children = doc;
1437 	    catal->dealloc = 0;
1438 	    xmlRMutexUnlock(xmlCatalogMutex);
1439 	    return(0);
1440 	}
1441 	if (xmlDebugCatalogs)
1442 	    xmlGenericError(xmlGenericErrorContext,
1443 		"%s not found in file hash\n", catal->URL);
1444     }
1445 
1446     /*
1447      * Fetch and parse. Note that xmlParseXMLCatalogFile does not
1448      * use the existing catalog, there is no recursion allowed at
1449      * that level.
1450      */
1451     doc = xmlParseXMLCatalogFile(catal->prefer, catal->URL);
1452     if (doc == NULL) {
1453 	catal->type = XML_CATA_BROKEN_CATALOG;
1454 	xmlRMutexUnlock(xmlCatalogMutex);
1455 	return(-1);
1456     }
1457 
1458     if (catal->type == XML_CATA_CATALOG)
1459 	catal->children = doc->children;
1460     else
1461 	catal->children = doc;
1462 
1463     doc->dealloc = 1;
1464 
1465     if (xmlCatalogXMLFiles == NULL)
1466 	xmlCatalogXMLFiles = xmlHashCreate(10);
1467     if (xmlCatalogXMLFiles != NULL) {
1468 	if (xmlDebugCatalogs)
1469 	    xmlGenericError(xmlGenericErrorContext,
1470 		"%s added to file hash\n", catal->URL);
1471 	xmlHashAddEntry(xmlCatalogXMLFiles, catal->URL, doc);
1472     }
1473     xmlRMutexUnlock(xmlCatalogMutex);
1474     return(0);
1475 }
1476 
1477 /************************************************************************
1478  *									*
1479  *			XML Catalog handling				*
1480  *									*
1481  ************************************************************************/
1482 
1483 /**
1484  * xmlAddXMLCatalog:
1485  * @catal:  top of an XML catalog
1486  * @type:  the type of record to add to the catalog
1487  * @orig:  the system, public or prefix to match (or NULL)
1488  * @replace:  the replacement value for the match
1489  *
1490  * Add an entry in the XML catalog, it may overwrite existing but
1491  * different entries.
1492  *
1493  * Returns 0 if successful, -1 otherwise
1494  */
1495 static int
xmlAddXMLCatalog(xmlCatalogEntryPtr catal,const xmlChar * type,const xmlChar * orig,const xmlChar * replace)1496 xmlAddXMLCatalog(xmlCatalogEntryPtr catal, const xmlChar *type,
1497 	      const xmlChar *orig, const xmlChar *replace) {
1498     xmlCatalogEntryPtr cur;
1499     xmlCatalogEntryType typ;
1500     int doregister = 0;
1501 
1502     if ((catal == NULL) ||
1503 	((catal->type != XML_CATA_CATALOG) &&
1504 	 (catal->type != XML_CATA_BROKEN_CATALOG)))
1505 	return(-1);
1506     if (catal->children == NULL) {
1507 	xmlFetchXMLCatalogFile(catal);
1508     }
1509     if (catal->children == NULL)
1510 	doregister = 1;
1511 
1512     typ = xmlGetXMLCatalogEntryType(type);
1513     if (typ == XML_CATA_NONE) {
1514 	if (xmlDebugCatalogs)
1515 	    xmlGenericError(xmlGenericErrorContext,
1516 		    "Failed to add unknown element %s to catalog\n", type);
1517 	return(-1);
1518     }
1519 
1520     cur = catal->children;
1521     /*
1522      * Might be a simple "update in place"
1523      */
1524     if (cur != NULL) {
1525 	while (cur != NULL) {
1526 	    if ((orig != NULL) && (cur->type == typ) &&
1527 		(xmlStrEqual(orig, cur->name))) {
1528 		if (xmlDebugCatalogs)
1529 		    xmlGenericError(xmlGenericErrorContext,
1530 			    "Updating element %s to catalog\n", type);
1531 		if (cur->value != NULL)
1532 		    xmlFree(cur->value);
1533 		if (cur->URL != NULL)
1534 		    xmlFree(cur->URL);
1535 		cur->value = xmlStrdup(replace);
1536 		cur->URL = xmlStrdup(replace);
1537 		return(0);
1538 	    }
1539 	    if (cur->next == NULL)
1540 		break;
1541 	    cur = cur->next;
1542 	}
1543     }
1544     if (xmlDebugCatalogs)
1545 	xmlGenericError(xmlGenericErrorContext,
1546 		"Adding element %s to catalog\n", type);
1547     if (cur == NULL)
1548 	catal->children = xmlNewCatalogEntry(typ, orig, replace,
1549 		                             NULL, catal->prefer, NULL);
1550     else
1551 	cur->next = xmlNewCatalogEntry(typ, orig, replace,
1552 		                       NULL, catal->prefer, NULL);
1553     if (doregister) {
1554         catal->type = XML_CATA_CATALOG;
1555 	cur = (xmlCatalogEntryPtr)xmlHashLookup(xmlCatalogXMLFiles, catal->URL);
1556 	if (cur != NULL)
1557 	    cur->children = catal->children;
1558     }
1559 
1560     return(0);
1561 }
1562 
1563 /**
1564  * xmlDelXMLCatalog:
1565  * @catal:  top of an XML catalog
1566  * @value:  the value to remove from the catalog
1567  *
1568  * Remove entries in the XML catalog where the value or the URI
1569  * is equal to @value
1570  *
1571  * Returns the number of entries removed if successful, -1 otherwise
1572  */
1573 static int
xmlDelXMLCatalog(xmlCatalogEntryPtr catal,const xmlChar * value)1574 xmlDelXMLCatalog(xmlCatalogEntryPtr catal, const xmlChar *value) {
1575     xmlCatalogEntryPtr cur;
1576     int ret = 0;
1577 
1578     if ((catal == NULL) ||
1579 	((catal->type != XML_CATA_CATALOG) &&
1580 	 (catal->type != XML_CATA_BROKEN_CATALOG)))
1581 	return(-1);
1582     if (value == NULL)
1583 	return(-1);
1584     if (catal->children == NULL) {
1585 	xmlFetchXMLCatalogFile(catal);
1586     }
1587 
1588     /*
1589      * Scan the children
1590      */
1591     cur = catal->children;
1592     while (cur != NULL) {
1593 	if (((cur->name != NULL) && (xmlStrEqual(value, cur->name))) ||
1594 	    (xmlStrEqual(value, cur->value))) {
1595 	    if (xmlDebugCatalogs) {
1596 		if (cur->name != NULL)
1597 		    xmlGenericError(xmlGenericErrorContext,
1598 			    "Removing element %s from catalog\n", cur->name);
1599 		else
1600 		    xmlGenericError(xmlGenericErrorContext,
1601 			    "Removing element %s from catalog\n", cur->value);
1602 	    }
1603 	    cur->type = XML_CATA_REMOVED;
1604 	}
1605 	cur = cur->next;
1606     }
1607     return(ret);
1608 }
1609 
1610 /**
1611  * xmlCatalogXMLResolve:
1612  * @catal:  a catalog list
1613  * @pubID:  the public ID string
1614  * @sysID:  the system ID string
1615  *
1616  * Do a complete resolution lookup of an External Identifier for a
1617  * list of catalog entries.
1618  *
1619  * Implements (or tries to) 7.1. External Identifier Resolution
1620  * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
1621  *
1622  * Returns the URI of the resource or NULL if not found
1623  */
1624 static xmlChar *
xmlCatalogXMLResolve(xmlCatalogEntryPtr catal,const xmlChar * pubID,const xmlChar * sysID)1625 xmlCatalogXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,
1626 	              const xmlChar *sysID) {
1627     xmlChar *ret = NULL;
1628     xmlCatalogEntryPtr cur;
1629     int haveDelegate = 0;
1630     int haveNext = 0;
1631 
1632     /*
1633      * protection against loops
1634      */
1635     if (catal->depth > MAX_CATAL_DEPTH) {
1636 	xmlCatalogErr(catal, NULL, XML_CATALOG_RECURSION,
1637 		      "Detected recursion in catalog %s\n",
1638 		      catal->name, NULL, NULL);
1639 	return(NULL);
1640     }
1641     catal->depth++;
1642 
1643     /*
1644      * First tries steps 2/ 3/ 4/ if a system ID is provided.
1645      */
1646     if (sysID != NULL) {
1647 	xmlCatalogEntryPtr rewrite = NULL;
1648 	int lenrewrite = 0, len;
1649 	cur = catal;
1650 	haveDelegate = 0;
1651 	while (cur != NULL) {
1652 	    switch (cur->type) {
1653 		case XML_CATA_SYSTEM:
1654 		    if (xmlStrEqual(sysID, cur->name)) {
1655 			if (xmlDebugCatalogs)
1656 			    xmlGenericError(xmlGenericErrorContext,
1657 				    "Found system match %s, using %s\n",
1658 				            cur->name, cur->URL);
1659 			catal->depth--;
1660 			return(xmlStrdup(cur->URL));
1661 		    }
1662 		    break;
1663 		case XML_CATA_REWRITE_SYSTEM:
1664 		    len = xmlStrlen(cur->name);
1665 		    if ((len > lenrewrite) &&
1666 			(!xmlStrncmp(sysID, cur->name, len))) {
1667 			lenrewrite = len;
1668 			rewrite = cur;
1669 		    }
1670 		    break;
1671 		case XML_CATA_DELEGATE_SYSTEM:
1672 		    if (!xmlStrncmp(sysID, cur->name, xmlStrlen(cur->name)))
1673 			haveDelegate++;
1674 		    break;
1675 		case XML_CATA_NEXT_CATALOG:
1676 		    haveNext++;
1677 		    break;
1678 		default:
1679 		    break;
1680 	    }
1681 	    cur = cur->next;
1682 	}
1683 	if (rewrite != NULL) {
1684 	    if (xmlDebugCatalogs)
1685 		xmlGenericError(xmlGenericErrorContext,
1686 			"Using rewriting rule %s\n", rewrite->name);
1687 	    ret = xmlStrdup(rewrite->URL);
1688 	    if (ret != NULL)
1689 		ret = xmlStrcat(ret, &sysID[lenrewrite]);
1690 	    catal->depth--;
1691 	    return(ret);
1692 	}
1693 	if (haveDelegate) {
1694 	    const xmlChar *delegates[MAX_DELEGATE];
1695 	    int nbList = 0, i;
1696 
1697 	    /*
1698 	     * Assume the entries have been sorted by decreasing substring
1699 	     * matches when the list was produced.
1700 	     */
1701 	    cur = catal;
1702 	    while (cur != NULL) {
1703 		if ((cur->type == XML_CATA_DELEGATE_SYSTEM) &&
1704 		    (!xmlStrncmp(sysID, cur->name, xmlStrlen(cur->name)))) {
1705 		    for (i = 0;i < nbList;i++)
1706 			if (xmlStrEqual(cur->URL, delegates[i]))
1707 			    break;
1708 		    if (i < nbList) {
1709 			cur = cur->next;
1710 			continue;
1711 		    }
1712 		    if (nbList < MAX_DELEGATE)
1713 			delegates[nbList++] = cur->URL;
1714 
1715 		    if (cur->children == NULL) {
1716 			xmlFetchXMLCatalogFile(cur);
1717 		    }
1718 		    if (cur->children != NULL) {
1719 			if (xmlDebugCatalogs)
1720 			    xmlGenericError(xmlGenericErrorContext,
1721 				    "Trying system delegate %s\n", cur->URL);
1722 			ret = xmlCatalogListXMLResolve(
1723 				cur->children, NULL, sysID);
1724 			if (ret != NULL) {
1725 			    catal->depth--;
1726 			    return(ret);
1727 			}
1728 		    }
1729 		}
1730 		cur = cur->next;
1731 	    }
1732 	    /*
1733 	     * Apply the cut algorithm explained in 4/
1734 	     */
1735 	    catal->depth--;
1736 	    return(XML_CATAL_BREAK);
1737 	}
1738     }
1739     /*
1740      * Then tries 5/ 6/ if a public ID is provided
1741      */
1742     if (pubID != NULL) {
1743 	cur = catal;
1744 	haveDelegate = 0;
1745 	while (cur != NULL) {
1746 	    switch (cur->type) {
1747 		case XML_CATA_PUBLIC:
1748 		    if (xmlStrEqual(pubID, cur->name)) {
1749 			if (xmlDebugCatalogs)
1750 			    xmlGenericError(xmlGenericErrorContext,
1751 				    "Found public match %s\n", cur->name);
1752 			catal->depth--;
1753 			return(xmlStrdup(cur->URL));
1754 		    }
1755 		    break;
1756 		case XML_CATA_DELEGATE_PUBLIC:
1757 		    if (!xmlStrncmp(pubID, cur->name, xmlStrlen(cur->name)) &&
1758 			(cur->prefer == XML_CATA_PREFER_PUBLIC))
1759 			haveDelegate++;
1760 		    break;
1761 		case XML_CATA_NEXT_CATALOG:
1762 		    if (sysID == NULL)
1763 			haveNext++;
1764 		    break;
1765 		default:
1766 		    break;
1767 	    }
1768 	    cur = cur->next;
1769 	}
1770 	if (haveDelegate) {
1771 	    const xmlChar *delegates[MAX_DELEGATE];
1772 	    int nbList = 0, i;
1773 
1774 	    /*
1775 	     * Assume the entries have been sorted by decreasing substring
1776 	     * matches when the list was produced.
1777 	     */
1778 	    cur = catal;
1779 	    while (cur != NULL) {
1780 		if ((cur->type == XML_CATA_DELEGATE_PUBLIC) &&
1781 		    (cur->prefer == XML_CATA_PREFER_PUBLIC) &&
1782 		    (!xmlStrncmp(pubID, cur->name, xmlStrlen(cur->name)))) {
1783 
1784 		    for (i = 0;i < nbList;i++)
1785 			if (xmlStrEqual(cur->URL, delegates[i]))
1786 			    break;
1787 		    if (i < nbList) {
1788 			cur = cur->next;
1789 			continue;
1790 		    }
1791 		    if (nbList < MAX_DELEGATE)
1792 			delegates[nbList++] = cur->URL;
1793 
1794 		    if (cur->children == NULL) {
1795 			xmlFetchXMLCatalogFile(cur);
1796 		    }
1797 		    if (cur->children != NULL) {
1798 			if (xmlDebugCatalogs)
1799 			    xmlGenericError(xmlGenericErrorContext,
1800 				    "Trying public delegate %s\n", cur->URL);
1801 			ret = xmlCatalogListXMLResolve(
1802 				cur->children, pubID, NULL);
1803 			if (ret != NULL) {
1804 			    catal->depth--;
1805 			    return(ret);
1806 			}
1807 		    }
1808 		}
1809 		cur = cur->next;
1810 	    }
1811 	    /*
1812 	     * Apply the cut algorithm explained in 4/
1813 	     */
1814 	    catal->depth--;
1815 	    return(XML_CATAL_BREAK);
1816 	}
1817     }
1818     if (haveNext) {
1819 	cur = catal;
1820 	while (cur != NULL) {
1821 	    if (cur->type == XML_CATA_NEXT_CATALOG) {
1822 		if (cur->children == NULL) {
1823 		    xmlFetchXMLCatalogFile(cur);
1824 		}
1825 		if (cur->children != NULL) {
1826 		    ret = xmlCatalogListXMLResolve(cur->children, pubID, sysID);
1827 		    if (ret != NULL) {
1828 			catal->depth--;
1829 			return(ret);
1830 		    } else if (catal->depth > MAX_CATAL_DEPTH) {
1831 		        return(NULL);
1832 		    }
1833 		}
1834 	    }
1835 	    cur = cur->next;
1836 	}
1837     }
1838 
1839     catal->depth--;
1840     return(NULL);
1841 }
1842 
1843 /**
1844  * xmlCatalogXMLResolveURI:
1845  * @catal:  a catalog list
1846  * @URI:  the URI
1847  * @sysID:  the system ID string
1848  *
1849  * Do a complete resolution lookup of an External Identifier for a
1850  * list of catalog entries.
1851  *
1852  * Implements (or tries to) 7.2.2. URI Resolution
1853  * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
1854  *
1855  * Returns the URI of the resource or NULL if not found
1856  */
1857 static xmlChar *
xmlCatalogXMLResolveURI(xmlCatalogEntryPtr catal,const xmlChar * URI)1858 xmlCatalogXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI) {
1859     xmlChar *ret = NULL;
1860     xmlCatalogEntryPtr cur;
1861     int haveDelegate = 0;
1862     int haveNext = 0;
1863     xmlCatalogEntryPtr rewrite = NULL;
1864     int lenrewrite = 0, len;
1865 
1866     if (catal == NULL)
1867 	return(NULL);
1868 
1869     if (URI == NULL)
1870 	return(NULL);
1871 
1872     if (catal->depth > MAX_CATAL_DEPTH) {
1873 	xmlCatalogErr(catal, NULL, XML_CATALOG_RECURSION,
1874 		      "Detected recursion in catalog %s\n",
1875 		      catal->name, NULL, NULL);
1876 	return(NULL);
1877     }
1878 
1879     /*
1880      * First tries steps 2/ 3/ 4/ if a system ID is provided.
1881      */
1882     cur = catal;
1883     haveDelegate = 0;
1884     while (cur != NULL) {
1885 	switch (cur->type) {
1886 	    case XML_CATA_URI:
1887 		if (xmlStrEqual(URI, cur->name)) {
1888 		    if (xmlDebugCatalogs)
1889 			xmlGenericError(xmlGenericErrorContext,
1890 				"Found URI match %s\n", cur->name);
1891 		    return(xmlStrdup(cur->URL));
1892 		}
1893 		break;
1894 	    case XML_CATA_REWRITE_URI:
1895 		len = xmlStrlen(cur->name);
1896 		if ((len > lenrewrite) &&
1897 		    (!xmlStrncmp(URI, cur->name, len))) {
1898 		    lenrewrite = len;
1899 		    rewrite = cur;
1900 		}
1901 		break;
1902 	    case XML_CATA_DELEGATE_URI:
1903 		if (!xmlStrncmp(URI, cur->name, xmlStrlen(cur->name)))
1904 		    haveDelegate++;
1905 		break;
1906 	    case XML_CATA_NEXT_CATALOG:
1907 		haveNext++;
1908 		break;
1909 	    default:
1910 		break;
1911 	}
1912 	cur = cur->next;
1913     }
1914     if (rewrite != NULL) {
1915 	if (xmlDebugCatalogs)
1916 	    xmlGenericError(xmlGenericErrorContext,
1917 		    "Using rewriting rule %s\n", rewrite->name);
1918 	ret = xmlStrdup(rewrite->URL);
1919 	if (ret != NULL)
1920 	    ret = xmlStrcat(ret, &URI[lenrewrite]);
1921 	return(ret);
1922     }
1923     if (haveDelegate) {
1924 	const xmlChar *delegates[MAX_DELEGATE];
1925 	int nbList = 0, i;
1926 
1927 	/*
1928 	 * Assume the entries have been sorted by decreasing substring
1929 	 * matches when the list was produced.
1930 	 */
1931 	cur = catal;
1932 	while (cur != NULL) {
1933 	    if (((cur->type == XML_CATA_DELEGATE_SYSTEM) ||
1934 	         (cur->type == XML_CATA_DELEGATE_URI)) &&
1935 		(!xmlStrncmp(URI, cur->name, xmlStrlen(cur->name)))) {
1936 		for (i = 0;i < nbList;i++)
1937 		    if (xmlStrEqual(cur->URL, delegates[i]))
1938 			break;
1939 		if (i < nbList) {
1940 		    cur = cur->next;
1941 		    continue;
1942 		}
1943 		if (nbList < MAX_DELEGATE)
1944 		    delegates[nbList++] = cur->URL;
1945 
1946 		if (cur->children == NULL) {
1947 		    xmlFetchXMLCatalogFile(cur);
1948 		}
1949 		if (cur->children != NULL) {
1950 		    if (xmlDebugCatalogs)
1951 			xmlGenericError(xmlGenericErrorContext,
1952 				"Trying URI delegate %s\n", cur->URL);
1953 		    ret = xmlCatalogListXMLResolveURI(
1954 			    cur->children, URI);
1955 		    if (ret != NULL)
1956 			return(ret);
1957 		}
1958 	    }
1959 	    cur = cur->next;
1960 	}
1961 	/*
1962 	 * Apply the cut algorithm explained in 4/
1963 	 */
1964 	return(XML_CATAL_BREAK);
1965     }
1966     if (haveNext) {
1967 	cur = catal;
1968 	while (cur != NULL) {
1969 	    if (cur->type == XML_CATA_NEXT_CATALOG) {
1970 		if (cur->children == NULL) {
1971 		    xmlFetchXMLCatalogFile(cur);
1972 		}
1973 		if (cur->children != NULL) {
1974 		    ret = xmlCatalogListXMLResolveURI(cur->children, URI);
1975 		    if (ret != NULL)
1976 			return(ret);
1977 		}
1978 	    }
1979 	    cur = cur->next;
1980 	}
1981     }
1982 
1983     return(NULL);
1984 }
1985 
1986 /**
1987  * xmlCatalogListXMLResolve:
1988  * @catal:  a catalog list
1989  * @pubID:  the public ID string
1990  * @sysID:  the system ID string
1991  *
1992  * Do a complete resolution lookup of an External Identifier for a
1993  * list of catalogs
1994  *
1995  * Implements (or tries to) 7.1. External Identifier Resolution
1996  * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
1997  *
1998  * Returns the URI of the resource or NULL if not found
1999  */
2000 static xmlChar *
xmlCatalogListXMLResolve(xmlCatalogEntryPtr catal,const xmlChar * pubID,const xmlChar * sysID)2001 xmlCatalogListXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,
2002 	              const xmlChar *sysID) {
2003     xmlChar *ret = NULL;
2004     xmlChar *urnID = NULL;
2005     xmlChar *normid;
2006 
2007     if (catal == NULL)
2008         return(NULL);
2009     if ((pubID == NULL) && (sysID == NULL))
2010 	return(NULL);
2011 
2012     normid = xmlCatalogNormalizePublic(pubID);
2013     if (normid != NULL)
2014         pubID = (*normid != 0 ? normid : NULL);
2015 
2016     if (!xmlStrncmp(pubID, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) {
2017 	urnID = xmlCatalogUnWrapURN(pubID);
2018 	if (xmlDebugCatalogs) {
2019 	    if (urnID == NULL)
2020 		xmlGenericError(xmlGenericErrorContext,
2021 			"Public URN ID %s expanded to NULL\n", pubID);
2022 	    else
2023 		xmlGenericError(xmlGenericErrorContext,
2024 			"Public URN ID expanded to %s\n", urnID);
2025 	}
2026 	ret = xmlCatalogListXMLResolve(catal, urnID, sysID);
2027 	if (urnID != NULL)
2028 	    xmlFree(urnID);
2029 	if (normid != NULL)
2030 	    xmlFree(normid);
2031 	return(ret);
2032     }
2033     if (!xmlStrncmp(sysID, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) {
2034 	urnID = xmlCatalogUnWrapURN(sysID);
2035 	if (xmlDebugCatalogs) {
2036 	    if (urnID == NULL)
2037 		xmlGenericError(xmlGenericErrorContext,
2038 			"System URN ID %s expanded to NULL\n", sysID);
2039 	    else
2040 		xmlGenericError(xmlGenericErrorContext,
2041 			"System URN ID expanded to %s\n", urnID);
2042 	}
2043 	if (pubID == NULL)
2044 	    ret = xmlCatalogListXMLResolve(catal, urnID, NULL);
2045 	else if (xmlStrEqual(pubID, urnID))
2046 	    ret = xmlCatalogListXMLResolve(catal, pubID, NULL);
2047 	else {
2048 	    ret = xmlCatalogListXMLResolve(catal, pubID, urnID);
2049 	}
2050 	if (urnID != NULL)
2051 	    xmlFree(urnID);
2052 	if (normid != NULL)
2053 	    xmlFree(normid);
2054 	return(ret);
2055     }
2056     while (catal != NULL) {
2057 	if (catal->type == XML_CATA_CATALOG) {
2058 	    if (catal->children == NULL) {
2059 		xmlFetchXMLCatalogFile(catal);
2060 	    }
2061 	    if (catal->children != NULL) {
2062 		ret = xmlCatalogXMLResolve(catal->children, pubID, sysID);
2063 		if (ret != NULL) {
2064 		    break;
2065                 } else if (catal->children->depth > MAX_CATAL_DEPTH) {
2066 	            ret = NULL;
2067 		    break;
2068 	        }
2069 	    }
2070 	}
2071 	catal = catal->next;
2072     }
2073     if (normid != NULL)
2074 	xmlFree(normid);
2075     return(ret);
2076 }
2077 
2078 /**
2079  * xmlCatalogListXMLResolveURI:
2080  * @catal:  a catalog list
2081  * @URI:  the URI
2082  *
2083  * Do a complete resolution lookup of an URI for a list of catalogs
2084  *
2085  * Implements (or tries to) 7.2. URI Resolution
2086  * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
2087  *
2088  * Returns the URI of the resource or NULL if not found
2089  */
2090 static xmlChar *
xmlCatalogListXMLResolveURI(xmlCatalogEntryPtr catal,const xmlChar * URI)2091 xmlCatalogListXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI) {
2092     xmlChar *ret = NULL;
2093     xmlChar *urnID = NULL;
2094 
2095     if (catal == NULL)
2096         return(NULL);
2097     if (URI == NULL)
2098 	return(NULL);
2099 
2100     if (!xmlStrncmp(URI, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) {
2101 	urnID = xmlCatalogUnWrapURN(URI);
2102 	if (xmlDebugCatalogs) {
2103 	    if (urnID == NULL)
2104 		xmlGenericError(xmlGenericErrorContext,
2105 			"URN ID %s expanded to NULL\n", URI);
2106 	    else
2107 		xmlGenericError(xmlGenericErrorContext,
2108 			"URN ID expanded to %s\n", urnID);
2109 	}
2110 	ret = xmlCatalogListXMLResolve(catal, urnID, NULL);
2111 	if (urnID != NULL)
2112 	    xmlFree(urnID);
2113 	return(ret);
2114     }
2115     while (catal != NULL) {
2116 	if (catal->type == XML_CATA_CATALOG) {
2117 	    if (catal->children == NULL) {
2118 		xmlFetchXMLCatalogFile(catal);
2119 	    }
2120 	    if (catal->children != NULL) {
2121 		ret = xmlCatalogXMLResolveURI(catal->children, URI);
2122 		if (ret != NULL)
2123 		    return(ret);
2124 	    }
2125 	}
2126 	catal = catal->next;
2127     }
2128     return(ret);
2129 }
2130 
2131 /************************************************************************
2132  *									*
2133  *			The SGML Catalog parser				*
2134  *									*
2135  ************************************************************************/
2136 
2137 
2138 #define RAW *cur
2139 #define NEXT cur++;
2140 #define SKIP(x) cur += x;
2141 
2142 #define SKIP_BLANKS while (IS_BLANK_CH(*cur)) NEXT;
2143 
2144 /**
2145  * xmlParseSGMLCatalogComment:
2146  * @cur:  the current character
2147  *
2148  * Skip a comment in an SGML catalog
2149  *
2150  * Returns new current character
2151  */
2152 static const xmlChar *
xmlParseSGMLCatalogComment(const xmlChar * cur)2153 xmlParseSGMLCatalogComment(const xmlChar *cur) {
2154     if ((cur[0] != '-') || (cur[1] != '-'))
2155 	return(cur);
2156     SKIP(2);
2157     while ((cur[0] != 0) && ((cur[0] != '-') || ((cur[1] != '-'))))
2158 	NEXT;
2159     if (cur[0] == 0) {
2160 	return(NULL);
2161     }
2162     return(cur + 2);
2163 }
2164 
2165 /**
2166  * xmlParseSGMLCatalogPubid:
2167  * @cur:  the current character
2168  * @id:  the return location
2169  *
2170  * Parse an SGML catalog ID
2171  *
2172  * Returns new current character and store the value in @id
2173  */
2174 static const xmlChar *
xmlParseSGMLCatalogPubid(const xmlChar * cur,xmlChar ** id)2175 xmlParseSGMLCatalogPubid(const xmlChar *cur, xmlChar **id) {
2176     xmlChar *buf = NULL, *tmp;
2177     int len = 0;
2178     int size = 50;
2179     xmlChar stop;
2180     int count = 0;
2181 
2182     *id = NULL;
2183 
2184     if (RAW == '"') {
2185         NEXT;
2186 	stop = '"';
2187     } else if (RAW == '\'') {
2188         NEXT;
2189 	stop = '\'';
2190     } else {
2191 	stop = ' ';
2192     }
2193     buf = (xmlChar *) xmlMallocAtomic(size * sizeof(xmlChar));
2194     if (buf == NULL) {
2195         xmlCatalogErrMemory("allocating public ID");
2196 	return(NULL);
2197     }
2198     while (IS_PUBIDCHAR_CH(*cur) || (*cur == '?')) {
2199 	if ((*cur == stop) && (stop != ' '))
2200 	    break;
2201 	if ((stop == ' ') && (IS_BLANK_CH(*cur)))
2202 	    break;
2203 	if (len + 1 >= size) {
2204 	    size *= 2;
2205 	    tmp = (xmlChar *) xmlRealloc(buf, size * sizeof(xmlChar));
2206 	    if (tmp == NULL) {
2207 		xmlCatalogErrMemory("allocating public ID");
2208 		xmlFree(buf);
2209 		return(NULL);
2210 	    }
2211 	    buf = tmp;
2212 	}
2213 	buf[len++] = *cur;
2214 	count++;
2215 	NEXT;
2216     }
2217     buf[len] = 0;
2218     if (stop == ' ') {
2219 	if (!IS_BLANK_CH(*cur)) {
2220 	    xmlFree(buf);
2221 	    return(NULL);
2222 	}
2223     } else {
2224 	if (*cur != stop) {
2225 	    xmlFree(buf);
2226 	    return(NULL);
2227 	}
2228 	NEXT;
2229     }
2230     *id = buf;
2231     return(cur);
2232 }
2233 
2234 /**
2235  * xmlParseSGMLCatalogName:
2236  * @cur:  the current character
2237  * @name:  the return location
2238  *
2239  * Parse an SGML catalog name
2240  *
2241  * Returns new current character and store the value in @name
2242  */
2243 static const xmlChar *
xmlParseSGMLCatalogName(const xmlChar * cur,xmlChar ** name)2244 xmlParseSGMLCatalogName(const xmlChar *cur, xmlChar **name) {
2245     xmlChar buf[XML_MAX_NAMELEN + 5];
2246     int len = 0;
2247     int c;
2248 
2249     *name = NULL;
2250 
2251     /*
2252      * Handler for more complex cases
2253      */
2254     c = *cur;
2255     if ((!IS_LETTER(c) && (c != '_') && (c != ':'))) {
2256 	return(NULL);
2257     }
2258 
2259     while (((IS_LETTER(c)) || (IS_DIGIT(c)) ||
2260             (c == '.') || (c == '-') ||
2261 	    (c == '_') || (c == ':'))) {
2262 	buf[len++] = c;
2263 	cur++;
2264 	c = *cur;
2265 	if (len >= XML_MAX_NAMELEN)
2266 	    return(NULL);
2267     }
2268     *name = xmlStrndup(buf, len);
2269     return(cur);
2270 }
2271 
2272 /**
2273  * xmlGetSGMLCatalogEntryType:
2274  * @name:  the entry name
2275  *
2276  * Get the Catalog entry type for a given SGML Catalog name
2277  *
2278  * Returns Catalog entry type
2279  */
2280 static xmlCatalogEntryType
xmlGetSGMLCatalogEntryType(const xmlChar * name)2281 xmlGetSGMLCatalogEntryType(const xmlChar *name) {
2282     xmlCatalogEntryType type = XML_CATA_NONE;
2283     if (xmlStrEqual(name, (const xmlChar *) "SYSTEM"))
2284 	type = SGML_CATA_SYSTEM;
2285     else if (xmlStrEqual(name, (const xmlChar *) "PUBLIC"))
2286 	type = SGML_CATA_PUBLIC;
2287     else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE"))
2288 	type = SGML_CATA_DELEGATE;
2289     else if (xmlStrEqual(name, (const xmlChar *) "ENTITY"))
2290 	type = SGML_CATA_ENTITY;
2291     else if (xmlStrEqual(name, (const xmlChar *) "DOCTYPE"))
2292 	type = SGML_CATA_DOCTYPE;
2293     else if (xmlStrEqual(name, (const xmlChar *) "LINKTYPE"))
2294 	type = SGML_CATA_LINKTYPE;
2295     else if (xmlStrEqual(name, (const xmlChar *) "NOTATION"))
2296 	type = SGML_CATA_NOTATION;
2297     else if (xmlStrEqual(name, (const xmlChar *) "SGMLDECL"))
2298 	type = SGML_CATA_SGMLDECL;
2299     else if (xmlStrEqual(name, (const xmlChar *) "DOCUMENT"))
2300 	type = SGML_CATA_DOCUMENT;
2301     else if (xmlStrEqual(name, (const xmlChar *) "CATALOG"))
2302 	type = SGML_CATA_CATALOG;
2303     else if (xmlStrEqual(name, (const xmlChar *) "BASE"))
2304 	type = SGML_CATA_BASE;
2305     return(type);
2306 }
2307 
2308 /**
2309  * xmlParseSGMLCatalog:
2310  * @catal:  the SGML Catalog
2311  * @value:  the content of the SGML Catalog serialization
2312  * @file:  the filepath for the catalog
2313  * @super:  should this be handled as a Super Catalog in which case
2314  *          parsing is not recursive
2315  *
2316  * Parse an SGML catalog content and fill up the @catal hash table with
2317  * the new entries found.
2318  *
2319  * Returns 0 in case of success, -1 in case of error.
2320  */
2321 static int
xmlParseSGMLCatalog(xmlCatalogPtr catal,const xmlChar * value,const char * file,int super)2322 xmlParseSGMLCatalog(xmlCatalogPtr catal, const xmlChar *value,
2323 	            const char *file, int super) {
2324     const xmlChar *cur = value;
2325     xmlChar *base = NULL;
2326     int res;
2327 
2328     if ((cur == NULL) || (file == NULL))
2329         return(-1);
2330     base = xmlStrdup((const xmlChar *) file);
2331 
2332     while ((cur != NULL) && (cur[0] != 0)) {
2333 	SKIP_BLANKS;
2334 	if (cur[0] == 0)
2335 	    break;
2336 	if ((cur[0] == '-') && (cur[1] == '-')) {
2337 	    cur = xmlParseSGMLCatalogComment(cur);
2338 	    if (cur == NULL) {
2339 		/* error */
2340 		break;
2341 	    }
2342 	} else {
2343 	    xmlChar *sysid = NULL;
2344 	    xmlChar *name = NULL;
2345 	    xmlCatalogEntryType type = XML_CATA_NONE;
2346 
2347 	    cur = xmlParseSGMLCatalogName(cur, &name);
2348 	    if (cur == NULL || name == NULL) {
2349 		/* error */
2350 		break;
2351 	    }
2352 	    if (!IS_BLANK_CH(*cur)) {
2353 		/* error */
2354 		xmlFree(name);
2355 		break;
2356 	    }
2357 	    SKIP_BLANKS;
2358 	    if (xmlStrEqual(name, (const xmlChar *) "SYSTEM"))
2359                 type = SGML_CATA_SYSTEM;
2360 	    else if (xmlStrEqual(name, (const xmlChar *) "PUBLIC"))
2361                 type = SGML_CATA_PUBLIC;
2362 	    else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE"))
2363                 type = SGML_CATA_DELEGATE;
2364 	    else if (xmlStrEqual(name, (const xmlChar *) "ENTITY"))
2365                 type = SGML_CATA_ENTITY;
2366 	    else if (xmlStrEqual(name, (const xmlChar *) "DOCTYPE"))
2367                 type = SGML_CATA_DOCTYPE;
2368 	    else if (xmlStrEqual(name, (const xmlChar *) "LINKTYPE"))
2369                 type = SGML_CATA_LINKTYPE;
2370 	    else if (xmlStrEqual(name, (const xmlChar *) "NOTATION"))
2371                 type = SGML_CATA_NOTATION;
2372 	    else if (xmlStrEqual(name, (const xmlChar *) "SGMLDECL"))
2373                 type = SGML_CATA_SGMLDECL;
2374 	    else if (xmlStrEqual(name, (const xmlChar *) "DOCUMENT"))
2375                 type = SGML_CATA_DOCUMENT;
2376 	    else if (xmlStrEqual(name, (const xmlChar *) "CATALOG"))
2377                 type = SGML_CATA_CATALOG;
2378 	    else if (xmlStrEqual(name, (const xmlChar *) "BASE"))
2379                 type = SGML_CATA_BASE;
2380 	    else if (xmlStrEqual(name, (const xmlChar *) "OVERRIDE")) {
2381 		xmlFree(name);
2382 		cur = xmlParseSGMLCatalogName(cur, &name);
2383 		if (name == NULL) {
2384 		    /* error */
2385 		    break;
2386 		}
2387 		xmlFree(name);
2388 		continue;
2389 	    }
2390 	    xmlFree(name);
2391 	    name = NULL;
2392 
2393 	    switch(type) {
2394 		case SGML_CATA_ENTITY:
2395 		    if (*cur == '%')
2396 			type = SGML_CATA_PENTITY;
2397                     /* Falls through. */
2398 		case SGML_CATA_PENTITY:
2399 		case SGML_CATA_DOCTYPE:
2400 		case SGML_CATA_LINKTYPE:
2401 		case SGML_CATA_NOTATION:
2402 		    cur = xmlParseSGMLCatalogName(cur, &name);
2403 		    if (cur == NULL) {
2404 			/* error */
2405 			break;
2406 		    }
2407 		    if (!IS_BLANK_CH(*cur)) {
2408 			/* error */
2409 			break;
2410 		    }
2411 		    SKIP_BLANKS;
2412 		    cur = xmlParseSGMLCatalogPubid(cur, &sysid);
2413 		    if (cur == NULL) {
2414 			/* error */
2415 			break;
2416 		    }
2417 		    break;
2418 		case SGML_CATA_PUBLIC:
2419 		case SGML_CATA_SYSTEM:
2420 		case SGML_CATA_DELEGATE:
2421 		    cur = xmlParseSGMLCatalogPubid(cur, &name);
2422 		    if (cur == NULL) {
2423 			/* error */
2424 			break;
2425 		    }
2426 		    if (type != SGML_CATA_SYSTEM) {
2427 		        xmlChar *normid;
2428 
2429 		        normid = xmlCatalogNormalizePublic(name);
2430 		        if (normid != NULL) {
2431 		            if (name != NULL)
2432 		                xmlFree(name);
2433 		            if (*normid != 0)
2434 		                name = normid;
2435 		            else {
2436 		                xmlFree(normid);
2437 		                name = NULL;
2438 		            }
2439 		        }
2440 		    }
2441 		    if (!IS_BLANK_CH(*cur)) {
2442 			/* error */
2443 			break;
2444 		    }
2445 		    SKIP_BLANKS;
2446 		    cur = xmlParseSGMLCatalogPubid(cur, &sysid);
2447 		    if (cur == NULL) {
2448 			/* error */
2449 			break;
2450 		    }
2451 		    break;
2452 		case SGML_CATA_BASE:
2453 		case SGML_CATA_CATALOG:
2454 		case SGML_CATA_DOCUMENT:
2455 		case SGML_CATA_SGMLDECL:
2456 		    cur = xmlParseSGMLCatalogPubid(cur, &sysid);
2457 		    if (cur == NULL) {
2458 			/* error */
2459 			break;
2460 		    }
2461 		    break;
2462 		default:
2463 		    break;
2464 	    }
2465 	    if (cur == NULL) {
2466 		if (name != NULL)
2467 		    xmlFree(name);
2468 		if (sysid != NULL)
2469 		    xmlFree(sysid);
2470 		break;
2471 	    } else if (type == SGML_CATA_BASE) {
2472 		if (base != NULL)
2473 		    xmlFree(base);
2474 		base = xmlStrdup(sysid);
2475 	    } else if ((type == SGML_CATA_PUBLIC) ||
2476 		       (type == SGML_CATA_SYSTEM)) {
2477 		xmlChar *filename;
2478 
2479 		filename = xmlBuildURI(sysid, base);
2480 		if (filename != NULL) {
2481 		    xmlCatalogEntryPtr entry;
2482 
2483 		    entry = xmlNewCatalogEntry(type, name, filename,
2484 			                       NULL, XML_CATA_PREFER_NONE, NULL);
2485 		    res = xmlHashAddEntry(catal->sgml, name, entry);
2486 		    if (res < 0) {
2487 			xmlFreeCatalogEntry(entry, NULL);
2488 		    }
2489 		    xmlFree(filename);
2490 		}
2491 
2492 	    } else if (type == SGML_CATA_CATALOG) {
2493 		if (super) {
2494 		    xmlCatalogEntryPtr entry;
2495 
2496 		    entry = xmlNewCatalogEntry(type, sysid, NULL, NULL,
2497 			                       XML_CATA_PREFER_NONE, NULL);
2498 		    res = xmlHashAddEntry(catal->sgml, sysid, entry);
2499 		    if (res < 0) {
2500 			xmlFreeCatalogEntry(entry, NULL);
2501 		    }
2502 		} else {
2503 		    xmlChar *filename;
2504 
2505 		    filename = xmlBuildURI(sysid, base);
2506 		    if (filename != NULL) {
2507 			xmlExpandCatalog(catal, (const char *)filename);
2508 			xmlFree(filename);
2509 		    }
2510 		}
2511 	    }
2512 	    /*
2513 	     * drop anything else we won't handle it
2514 	     */
2515 	    if (name != NULL)
2516 		xmlFree(name);
2517 	    if (sysid != NULL)
2518 		xmlFree(sysid);
2519 	}
2520     }
2521     if (base != NULL)
2522 	xmlFree(base);
2523     if (cur == NULL)
2524 	return(-1);
2525     return(0);
2526 }
2527 
2528 /************************************************************************
2529  *									*
2530  *			SGML Catalog handling				*
2531  *									*
2532  ************************************************************************/
2533 
2534 /**
2535  * xmlCatalogGetSGMLPublic:
2536  * @catal:  an SGML catalog hash
2537  * @pubID:  the public ID string
2538  *
2539  * Try to lookup the catalog local reference associated to a public ID
2540  *
2541  * Returns the local resource if found or NULL otherwise.
2542  */
2543 static const xmlChar *
xmlCatalogGetSGMLPublic(xmlHashTablePtr catal,const xmlChar * pubID)2544 xmlCatalogGetSGMLPublic(xmlHashTablePtr catal, const xmlChar *pubID) {
2545     xmlCatalogEntryPtr entry;
2546     xmlChar *normid;
2547 
2548     if (catal == NULL)
2549 	return(NULL);
2550 
2551     normid = xmlCatalogNormalizePublic(pubID);
2552     if (normid != NULL)
2553         pubID = (*normid != 0 ? normid : NULL);
2554 
2555     entry = (xmlCatalogEntryPtr) xmlHashLookup(catal, pubID);
2556     if (entry == NULL) {
2557 	if (normid != NULL)
2558 	    xmlFree(normid);
2559 	return(NULL);
2560     }
2561     if (entry->type == SGML_CATA_PUBLIC) {
2562 	if (normid != NULL)
2563 	    xmlFree(normid);
2564 	return(entry->URL);
2565     }
2566     if (normid != NULL)
2567         xmlFree(normid);
2568     return(NULL);
2569 }
2570 
2571 /**
2572  * xmlCatalogGetSGMLSystem:
2573  * @catal:  an SGML catalog hash
2574  * @sysID:  the system ID string
2575  *
2576  * Try to lookup the catalog local reference for a system ID
2577  *
2578  * Returns the local resource if found or NULL otherwise.
2579  */
2580 static const xmlChar *
xmlCatalogGetSGMLSystem(xmlHashTablePtr catal,const xmlChar * sysID)2581 xmlCatalogGetSGMLSystem(xmlHashTablePtr catal, const xmlChar *sysID) {
2582     xmlCatalogEntryPtr entry;
2583 
2584     if (catal == NULL)
2585 	return(NULL);
2586 
2587     entry = (xmlCatalogEntryPtr) xmlHashLookup(catal, sysID);
2588     if (entry == NULL)
2589 	return(NULL);
2590     if (entry->type == SGML_CATA_SYSTEM)
2591 	return(entry->URL);
2592     return(NULL);
2593 }
2594 
2595 /**
2596  * xmlCatalogSGMLResolve:
2597  * @catal:  the SGML catalog
2598  * @pubID:  the public ID string
2599  * @sysID:  the system ID string
2600  *
2601  * Do a complete resolution lookup of an External Identifier
2602  *
2603  * Returns the URI of the resource or NULL if not found
2604  */
2605 static const xmlChar *
xmlCatalogSGMLResolve(xmlCatalogPtr catal,const xmlChar * pubID,const xmlChar * sysID)2606 xmlCatalogSGMLResolve(xmlCatalogPtr catal, const xmlChar *pubID,
2607 	              const xmlChar *sysID) {
2608     const xmlChar *ret = NULL;
2609 
2610     if (catal->sgml == NULL)
2611 	return(NULL);
2612 
2613     if (pubID != NULL)
2614 	ret = xmlCatalogGetSGMLPublic(catal->sgml, pubID);
2615     if (ret != NULL)
2616 	return(ret);
2617     if (sysID != NULL)
2618 	ret = xmlCatalogGetSGMLSystem(catal->sgml, sysID);
2619     if (ret != NULL)
2620 	return(ret);
2621     return(NULL);
2622 }
2623 
2624 /************************************************************************
2625  *									*
2626  *			Specific Public interfaces			*
2627  *									*
2628  ************************************************************************/
2629 
2630 /**
2631  * xmlLoadSGMLSuperCatalog:
2632  * @filename:  a file path
2633  *
2634  * Load an SGML super catalog. It won't expand CATALOG or DELEGATE
2635  * references. This is only needed for manipulating SGML Super Catalogs
2636  * like adding and removing CATALOG or DELEGATE entries.
2637  *
2638  * Returns the catalog parsed or NULL in case of error
2639  */
2640 xmlCatalogPtr
xmlLoadSGMLSuperCatalog(const char * filename)2641 xmlLoadSGMLSuperCatalog(const char *filename)
2642 {
2643     xmlChar *content;
2644     xmlCatalogPtr catal;
2645     int ret;
2646 
2647     content = xmlLoadFileContent(filename);
2648     if (content == NULL)
2649         return(NULL);
2650 
2651     catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE, xmlCatalogDefaultPrefer);
2652     if (catal == NULL) {
2653 	xmlFree(content);
2654 	return(NULL);
2655     }
2656 
2657     ret = xmlParseSGMLCatalog(catal, content, filename, 1);
2658     xmlFree(content);
2659     if (ret < 0) {
2660 	xmlFreeCatalog(catal);
2661 	return(NULL);
2662     }
2663     return (catal);
2664 }
2665 
2666 /**
2667  * xmlLoadACatalog:
2668  * @filename:  a file path
2669  *
2670  * Load the catalog and build the associated data structures.
2671  * This can be either an XML Catalog or an SGML Catalog
2672  * It will recurse in SGML CATALOG entries. On the other hand XML
2673  * Catalogs are not handled recursively.
2674  *
2675  * Returns the catalog parsed or NULL in case of error
2676  */
2677 xmlCatalogPtr
xmlLoadACatalog(const char * filename)2678 xmlLoadACatalog(const char *filename)
2679 {
2680     xmlChar *content;
2681     xmlChar *first;
2682     xmlCatalogPtr catal;
2683     int ret;
2684 
2685     content = xmlLoadFileContent(filename);
2686     if (content == NULL)
2687         return(NULL);
2688 
2689 
2690     first = content;
2691 
2692     while ((*first != 0) && (*first != '-') && (*first != '<') &&
2693 	   (!(((*first >= 'A') && (*first <= 'Z')) ||
2694 	      ((*first >= 'a') && (*first <= 'z')))))
2695 	first++;
2696 
2697     if (*first != '<') {
2698 	catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE, xmlCatalogDefaultPrefer);
2699 	if (catal == NULL) {
2700 	    xmlFree(content);
2701 	    return(NULL);
2702 	}
2703         ret = xmlParseSGMLCatalog(catal, content, filename, 0);
2704 	if (ret < 0) {
2705 	    xmlFreeCatalog(catal);
2706 	    xmlFree(content);
2707 	    return(NULL);
2708 	}
2709     } else {
2710 	catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE, xmlCatalogDefaultPrefer);
2711 	if (catal == NULL) {
2712 	    xmlFree(content);
2713 	    return(NULL);
2714 	}
2715         catal->xml = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
2716 		       NULL, BAD_CAST filename, xmlCatalogDefaultPrefer, NULL);
2717     }
2718     xmlFree(content);
2719     return (catal);
2720 }
2721 
2722 /**
2723  * xmlExpandCatalog:
2724  * @catal:  a catalog
2725  * @filename:  a file path
2726  *
2727  * Load the catalog and expand the existing catal structure.
2728  * This can be either an XML Catalog or an SGML Catalog
2729  *
2730  * Returns 0 in case of success, -1 in case of error
2731  */
2732 static int
xmlExpandCatalog(xmlCatalogPtr catal,const char * filename)2733 xmlExpandCatalog(xmlCatalogPtr catal, const char *filename)
2734 {
2735     int ret;
2736 
2737     if ((catal == NULL) || (filename == NULL))
2738 	return(-1);
2739 
2740 
2741     if (catal->type == XML_SGML_CATALOG_TYPE) {
2742 	xmlChar *content;
2743 
2744 	content = xmlLoadFileContent(filename);
2745 	if (content == NULL)
2746 	    return(-1);
2747 
2748         ret = xmlParseSGMLCatalog(catal, content, filename, 0);
2749 	if (ret < 0) {
2750 	    xmlFree(content);
2751 	    return(-1);
2752 	}
2753 	xmlFree(content);
2754     } else {
2755 	xmlCatalogEntryPtr tmp, cur;
2756 	tmp = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
2757 		       NULL, BAD_CAST filename, xmlCatalogDefaultPrefer, NULL);
2758 
2759 	cur = catal->xml;
2760 	if (cur == NULL) {
2761 	    catal->xml = tmp;
2762 	} else {
2763 	    while (cur->next != NULL) cur = cur->next;
2764 	    cur->next = tmp;
2765 	}
2766     }
2767     return (0);
2768 }
2769 
2770 /**
2771  * xmlACatalogResolveSystem:
2772  * @catal:  a Catalog
2773  * @sysID:  the system ID string
2774  *
2775  * Try to lookup the catalog resource for a system ID
2776  *
2777  * Returns the resource if found or NULL otherwise, the value returned
2778  *      must be freed by the caller.
2779  */
2780 xmlChar *
xmlACatalogResolveSystem(xmlCatalogPtr catal,const xmlChar * sysID)2781 xmlACatalogResolveSystem(xmlCatalogPtr catal, const xmlChar *sysID) {
2782     xmlChar *ret = NULL;
2783 
2784     if ((sysID == NULL) || (catal == NULL))
2785 	return(NULL);
2786 
2787     if (xmlDebugCatalogs)
2788 	xmlGenericError(xmlGenericErrorContext,
2789 		"Resolve sysID %s\n", sysID);
2790 
2791     if (catal->type == XML_XML_CATALOG_TYPE) {
2792 	ret = xmlCatalogListXMLResolve(catal->xml, NULL, sysID);
2793 	if (ret == XML_CATAL_BREAK)
2794 	    ret = NULL;
2795     } else {
2796 	const xmlChar *sgml;
2797 
2798 	sgml = xmlCatalogGetSGMLSystem(catal->sgml, sysID);
2799 	if (sgml != NULL)
2800 	    ret = xmlStrdup(sgml);
2801     }
2802     return(ret);
2803 }
2804 
2805 /**
2806  * xmlACatalogResolvePublic:
2807  * @catal:  a Catalog
2808  * @pubID:  the public ID string
2809  *
2810  * Try to lookup the catalog local reference associated to a public ID in that catalog
2811  *
2812  * Returns the local resource if found or NULL otherwise, the value returned
2813  *      must be freed by the caller.
2814  */
2815 xmlChar *
xmlACatalogResolvePublic(xmlCatalogPtr catal,const xmlChar * pubID)2816 xmlACatalogResolvePublic(xmlCatalogPtr catal, const xmlChar *pubID) {
2817     xmlChar *ret = NULL;
2818 
2819     if ((pubID == NULL) || (catal == NULL))
2820 	return(NULL);
2821 
2822     if (xmlDebugCatalogs)
2823 	xmlGenericError(xmlGenericErrorContext,
2824 		"Resolve pubID %s\n", pubID);
2825 
2826     if (catal->type == XML_XML_CATALOG_TYPE) {
2827 	ret = xmlCatalogListXMLResolve(catal->xml, pubID, NULL);
2828 	if (ret == XML_CATAL_BREAK)
2829 	    ret = NULL;
2830     } else {
2831 	const xmlChar *sgml;
2832 
2833 	sgml = xmlCatalogGetSGMLPublic(catal->sgml, pubID);
2834 	if (sgml != NULL)
2835 	    ret = xmlStrdup(sgml);
2836     }
2837     return(ret);
2838 }
2839 
2840 /**
2841  * xmlACatalogResolve:
2842  * @catal:  a Catalog
2843  * @pubID:  the public ID string
2844  * @sysID:  the system ID string
2845  *
2846  * Do a complete resolution lookup of an External Identifier
2847  *
2848  * Returns the URI of the resource or NULL if not found, it must be freed
2849  *      by the caller.
2850  */
2851 xmlChar *
xmlACatalogResolve(xmlCatalogPtr catal,const xmlChar * pubID,const xmlChar * sysID)2852 xmlACatalogResolve(xmlCatalogPtr catal, const xmlChar * pubID,
2853                    const xmlChar * sysID)
2854 {
2855     xmlChar *ret = NULL;
2856 
2857     if (((pubID == NULL) && (sysID == NULL)) || (catal == NULL))
2858         return (NULL);
2859 
2860     if (xmlDebugCatalogs) {
2861          if ((pubID != NULL) && (sysID != NULL)) {
2862              xmlGenericError(xmlGenericErrorContext,
2863                              "Resolve: pubID %s sysID %s\n", pubID, sysID);
2864          } else if (pubID != NULL) {
2865              xmlGenericError(xmlGenericErrorContext,
2866                              "Resolve: pubID %s\n", pubID);
2867          } else {
2868              xmlGenericError(xmlGenericErrorContext,
2869                              "Resolve: sysID %s\n", sysID);
2870          }
2871     }
2872 
2873     if (catal->type == XML_XML_CATALOG_TYPE) {
2874         ret = xmlCatalogListXMLResolve(catal->xml, pubID, sysID);
2875 	if (ret == XML_CATAL_BREAK)
2876 	    ret = NULL;
2877     } else {
2878         const xmlChar *sgml;
2879 
2880         sgml = xmlCatalogSGMLResolve(catal, pubID, sysID);
2881         if (sgml != NULL)
2882             ret = xmlStrdup(sgml);
2883     }
2884     return (ret);
2885 }
2886 
2887 /**
2888  * xmlACatalogResolveURI:
2889  * @catal:  a Catalog
2890  * @URI:  the URI
2891  *
2892  * Do a complete resolution lookup of an URI
2893  *
2894  * Returns the URI of the resource or NULL if not found, it must be freed
2895  *      by the caller.
2896  */
2897 xmlChar *
xmlACatalogResolveURI(xmlCatalogPtr catal,const xmlChar * URI)2898 xmlACatalogResolveURI(xmlCatalogPtr catal, const xmlChar *URI) {
2899     xmlChar *ret = NULL;
2900 
2901     if ((URI == NULL) || (catal == NULL))
2902 	return(NULL);
2903 
2904     if (xmlDebugCatalogs)
2905 	xmlGenericError(xmlGenericErrorContext,
2906 		"Resolve URI %s\n", URI);
2907 
2908     if (catal->type == XML_XML_CATALOG_TYPE) {
2909 	ret = xmlCatalogListXMLResolveURI(catal->xml, URI);
2910 	if (ret == XML_CATAL_BREAK)
2911 	    ret = NULL;
2912     } else {
2913 	const xmlChar *sgml;
2914 
2915 	sgml = xmlCatalogSGMLResolve(catal, NULL, URI);
2916 	if (sgml != NULL)
2917             ret = xmlStrdup(sgml);
2918     }
2919     return(ret);
2920 }
2921 
2922 #ifdef LIBXML_OUTPUT_ENABLED
2923 /**
2924  * xmlACatalogDump:
2925  * @catal:  a Catalog
2926  * @out:  the file.
2927  *
2928  * Dump the given catalog to the given file.
2929  */
2930 void
xmlACatalogDump(xmlCatalogPtr catal,FILE * out)2931 xmlACatalogDump(xmlCatalogPtr catal, FILE *out) {
2932     if ((out == NULL) || (catal == NULL))
2933 	return;
2934 
2935     if (catal->type == XML_XML_CATALOG_TYPE) {
2936 	xmlDumpXMLCatalog(out, catal->xml);
2937     } else {
2938 	xmlHashScan(catal->sgml, xmlCatalogDumpEntry, out);
2939     }
2940 }
2941 #endif /* LIBXML_OUTPUT_ENABLED */
2942 
2943 /**
2944  * xmlACatalogAdd:
2945  * @catal:  a Catalog
2946  * @type:  the type of record to add to the catalog
2947  * @orig:  the system, public or prefix to match
2948  * @replace:  the replacement value for the match
2949  *
2950  * Add an entry in the catalog, it may overwrite existing but
2951  * different entries.
2952  *
2953  * Returns 0 if successful, -1 otherwise
2954  */
2955 int
xmlACatalogAdd(xmlCatalogPtr catal,const xmlChar * type,const xmlChar * orig,const xmlChar * replace)2956 xmlACatalogAdd(xmlCatalogPtr catal, const xmlChar * type,
2957               const xmlChar * orig, const xmlChar * replace)
2958 {
2959     int res = -1;
2960 
2961     if (catal == NULL)
2962 	return(-1);
2963 
2964     if (catal->type == XML_XML_CATALOG_TYPE) {
2965         res = xmlAddXMLCatalog(catal->xml, type, orig, replace);
2966     } else {
2967         xmlCatalogEntryType cattype;
2968 
2969         cattype = xmlGetSGMLCatalogEntryType(type);
2970         if (cattype != XML_CATA_NONE) {
2971             xmlCatalogEntryPtr entry;
2972 
2973             entry = xmlNewCatalogEntry(cattype, orig, replace, NULL,
2974                                        XML_CATA_PREFER_NONE, NULL);
2975 	    if (catal->sgml == NULL)
2976 		catal->sgml = xmlHashCreate(10);
2977             res = xmlHashAddEntry(catal->sgml, orig, entry);
2978         }
2979     }
2980     return (res);
2981 }
2982 
2983 /**
2984  * xmlACatalogRemove:
2985  * @catal:  a Catalog
2986  * @value:  the value to remove
2987  *
2988  * Remove an entry from the catalog
2989  *
2990  * Returns the number of entries removed if successful, -1 otherwise
2991  */
2992 int
xmlACatalogRemove(xmlCatalogPtr catal,const xmlChar * value)2993 xmlACatalogRemove(xmlCatalogPtr catal, const xmlChar *value) {
2994     int res = -1;
2995 
2996     if ((catal == NULL) || (value == NULL))
2997 	return(-1);
2998 
2999     if (catal->type == XML_XML_CATALOG_TYPE) {
3000 	res = xmlDelXMLCatalog(catal->xml, value);
3001     } else {
3002 	res = xmlHashRemoveEntry(catal->sgml, value, xmlFreeCatalogEntry);
3003 	if (res == 0)
3004 	    res = 1;
3005     }
3006     return(res);
3007 }
3008 
3009 /**
3010  * xmlNewCatalog:
3011  * @sgml:  should this create an SGML catalog
3012  *
3013  * create a new Catalog.
3014  *
3015  * Returns the xmlCatalogPtr or NULL in case of error
3016  */
3017 xmlCatalogPtr
xmlNewCatalog(int sgml)3018 xmlNewCatalog(int sgml) {
3019     xmlCatalogPtr catal = NULL;
3020 
3021     if (sgml) {
3022 	catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE,
3023 		                    xmlCatalogDefaultPrefer);
3024         if ((catal != NULL) && (catal->sgml == NULL))
3025 	    catal->sgml = xmlHashCreate(10);
3026     } else
3027 	catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE,
3028 		                    xmlCatalogDefaultPrefer);
3029     return(catal);
3030 }
3031 
3032 /**
3033  * xmlCatalogIsEmpty:
3034  * @catal:  should this create an SGML catalog
3035  *
3036  * Check is a catalog is empty
3037  *
3038  * Returns 1 if the catalog is empty, 0 if not, amd -1 in case of error.
3039  */
3040 int
xmlCatalogIsEmpty(xmlCatalogPtr catal)3041 xmlCatalogIsEmpty(xmlCatalogPtr catal) {
3042     if (catal == NULL)
3043 	return(-1);
3044 
3045     if (catal->type == XML_XML_CATALOG_TYPE) {
3046 	if (catal->xml == NULL)
3047 	    return(1);
3048 	if ((catal->xml->type != XML_CATA_CATALOG) &&
3049 	    (catal->xml->type != XML_CATA_BROKEN_CATALOG))
3050 	    return(-1);
3051 	if (catal->xml->children == NULL)
3052 	    return(1);
3053         return(0);
3054     } else {
3055 	int res;
3056 
3057 	if (catal->sgml == NULL)
3058 	    return(1);
3059 	res = xmlHashSize(catal->sgml);
3060 	if (res == 0)
3061 	    return(1);
3062 	if (res < 0)
3063 	    return(-1);
3064     }
3065     return(0);
3066 }
3067 
3068 /************************************************************************
3069  *									*
3070  *   Public interfaces manipulating the global shared default catalog	*
3071  *									*
3072  ************************************************************************/
3073 
3074 /**
3075  * xmlInitializeCatalogData:
3076  *
3077  * Do the catalog initialization only of global data, doesn't try to load
3078  * any catalog actually.
3079  * this function is not thread safe, catalog initialization should
3080  * preferably be done once at startup
3081  */
3082 static void
xmlInitializeCatalogData(void)3083 xmlInitializeCatalogData(void) {
3084     if (xmlCatalogInitialized != 0)
3085 	return;
3086 
3087     if (getenv("XML_DEBUG_CATALOG"))
3088 	xmlDebugCatalogs = 1;
3089     xmlCatalogMutex = xmlNewRMutex();
3090 
3091     xmlCatalogInitialized = 1;
3092 }
3093 /**
3094  * xmlInitializeCatalog:
3095  *
3096  * Do the catalog initialization.
3097  * this function is not thread safe, catalog initialization should
3098  * preferably be done once at startup
3099  */
3100 void
xmlInitializeCatalog(void)3101 xmlInitializeCatalog(void) {
3102     if (xmlCatalogInitialized != 0)
3103 	return;
3104 
3105     xmlInitializeCatalogData();
3106     xmlRMutexLock(xmlCatalogMutex);
3107 
3108     if (getenv("XML_DEBUG_CATALOG"))
3109 	xmlDebugCatalogs = 1;
3110 
3111     if (xmlDefaultCatalog == NULL) {
3112 	const char *catalogs;
3113 	char *path;
3114 	const char *cur, *paths;
3115 	xmlCatalogPtr catal;
3116 	xmlCatalogEntryPtr *nextent;
3117 
3118 	catalogs = (const char *) getenv("XML_CATALOG_FILES");
3119 	if (catalogs == NULL)
3120 #if defined(_WIN32) && defined(_MSC_VER)
3121     {
3122 		void* hmodule;
3123 		hmodule = GetModuleHandleA("libxml2.dll");
3124 		if (hmodule == NULL)
3125 			hmodule = GetModuleHandleA(NULL);
3126 		if (hmodule != NULL) {
3127 			char buf[256];
3128 			unsigned long len = GetModuleFileNameA(hmodule, buf, 255);
3129 			if (len != 0) {
3130 				char* p = &(buf[len]);
3131 				while (*p != '\\' && p > buf)
3132 					p--;
3133 				if (p != buf) {
3134 					xmlChar* uri;
3135 					strncpy(p, "\\..\\etc\\catalog", 255 - (p - buf));
3136 					uri = xmlCanonicPath((const xmlChar*)buf);
3137 					if (uri != NULL) {
3138 						strncpy(XML_XML_DEFAULT_CATALOG, uri, 255);
3139 						xmlFree(uri);
3140 					}
3141 				}
3142 			}
3143 		}
3144 		catalogs = XML_XML_DEFAULT_CATALOG;
3145     }
3146 #else
3147 	    catalogs = XML_XML_DEFAULT_CATALOG;
3148 #endif
3149 
3150 	catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE,
3151 		xmlCatalogDefaultPrefer);
3152 	if (catal != NULL) {
3153 	    /* the XML_CATALOG_FILES envvar is allowed to contain a
3154 	       space-separated list of entries. */
3155 	    cur = catalogs;
3156 	    nextent = &catal->xml;
3157 	    while (*cur != '\0') {
3158 		while (xmlIsBlank_ch(*cur))
3159 		    cur++;
3160 		if (*cur != 0) {
3161 		    paths = cur;
3162 		    while ((*cur != 0) && (!xmlIsBlank_ch(*cur)))
3163 			cur++;
3164 		    path = (char *) xmlStrndup((const xmlChar *)paths, cur - paths);
3165 		    if (path != NULL) {
3166 			*nextent = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
3167 				NULL, BAD_CAST path, xmlCatalogDefaultPrefer, NULL);
3168 			if (*nextent != NULL)
3169 			    nextent = &((*nextent)->next);
3170 			xmlFree(path);
3171 		    }
3172 		}
3173 	    }
3174 	    xmlDefaultCatalog = catal;
3175 	}
3176     }
3177 
3178     xmlRMutexUnlock(xmlCatalogMutex);
3179 }
3180 
3181 
3182 /**
3183  * xmlLoadCatalog:
3184  * @filename:  a file path
3185  *
3186  * Load the catalog and makes its definitions effective for the default
3187  * external entity loader. It will recurse in SGML CATALOG entries.
3188  * this function is not thread safe, catalog initialization should
3189  * preferably be done once at startup
3190  *
3191  * Returns 0 in case of success -1 in case of error
3192  */
3193 int
xmlLoadCatalog(const char * filename)3194 xmlLoadCatalog(const char *filename)
3195 {
3196     int ret;
3197     xmlCatalogPtr catal;
3198 
3199     if (!xmlCatalogInitialized)
3200 	xmlInitializeCatalogData();
3201 
3202     xmlRMutexLock(xmlCatalogMutex);
3203 
3204     if (xmlDefaultCatalog == NULL) {
3205 	catal = xmlLoadACatalog(filename);
3206 	if (catal == NULL) {
3207 	    xmlRMutexUnlock(xmlCatalogMutex);
3208 	    return(-1);
3209 	}
3210 
3211 	xmlDefaultCatalog = catal;
3212 	xmlRMutexUnlock(xmlCatalogMutex);
3213 	return(0);
3214     }
3215 
3216     ret = xmlExpandCatalog(xmlDefaultCatalog, filename);
3217     xmlRMutexUnlock(xmlCatalogMutex);
3218     return(ret);
3219 }
3220 
3221 /**
3222  * xmlLoadCatalogs:
3223  * @pathss:  a list of directories separated by a colon or a space.
3224  *
3225  * Load the catalogs and makes their definitions effective for the default
3226  * external entity loader.
3227  * this function is not thread safe, catalog initialization should
3228  * preferably be done once at startup
3229  */
3230 void
xmlLoadCatalogs(const char * pathss)3231 xmlLoadCatalogs(const char *pathss) {
3232     const char *cur;
3233     const char *paths;
3234     xmlChar *path;
3235 #ifdef _WIN32
3236     int i, iLen;
3237 #endif
3238 
3239     if (pathss == NULL)
3240 	return;
3241 
3242     cur = pathss;
3243     while (*cur != 0) {
3244 	while (xmlIsBlank_ch(*cur)) cur++;
3245 	if (*cur != 0) {
3246 	    paths = cur;
3247 	    while ((*cur != 0) && (*cur != PATH_SEPARATOR) && (!xmlIsBlank_ch(*cur)))
3248 		cur++;
3249 	    path = xmlStrndup((const xmlChar *)paths, cur - paths);
3250 	    if (path != NULL) {
3251 #ifdef _WIN32
3252         iLen = strlen((const char*)path);
3253         for(i = 0; i < iLen; i++) {
3254             if(path[i] == '\\') {
3255                 path[i] = '/';
3256             }
3257         }
3258 #endif
3259 		xmlLoadCatalog((const char *) path);
3260 		xmlFree(path);
3261 	    }
3262 	}
3263 	while (*cur == PATH_SEPARATOR)
3264 	    cur++;
3265     }
3266 }
3267 
3268 /**
3269  * xmlCatalogCleanup:
3270  *
3271  * Free up all the memory associated with catalogs
3272  */
3273 void
xmlCatalogCleanup(void)3274 xmlCatalogCleanup(void) {
3275     if (xmlCatalogInitialized == 0)
3276         return;
3277 
3278     xmlRMutexLock(xmlCatalogMutex);
3279     if (xmlDebugCatalogs)
3280 	xmlGenericError(xmlGenericErrorContext,
3281 		"Catalogs cleanup\n");
3282     if (xmlCatalogXMLFiles != NULL)
3283 	xmlHashFree(xmlCatalogXMLFiles, xmlFreeCatalogHashEntryList);
3284     xmlCatalogXMLFiles = NULL;
3285     if (xmlDefaultCatalog != NULL)
3286 	xmlFreeCatalog(xmlDefaultCatalog);
3287     xmlDefaultCatalog = NULL;
3288     xmlDebugCatalogs = 0;
3289     xmlCatalogInitialized = 0;
3290     xmlRMutexUnlock(xmlCatalogMutex);
3291     xmlFreeRMutex(xmlCatalogMutex);
3292 }
3293 
3294 /**
3295  * xmlCatalogResolveSystem:
3296  * @sysID:  the system ID string
3297  *
3298  * Try to lookup the catalog resource for a system ID
3299  *
3300  * Returns the resource if found or NULL otherwise, the value returned
3301  *      must be freed by the caller.
3302  */
3303 xmlChar *
xmlCatalogResolveSystem(const xmlChar * sysID)3304 xmlCatalogResolveSystem(const xmlChar *sysID) {
3305     xmlChar *ret;
3306 
3307     if (!xmlCatalogInitialized)
3308 	xmlInitializeCatalog();
3309 
3310     ret = xmlACatalogResolveSystem(xmlDefaultCatalog, sysID);
3311     return(ret);
3312 }
3313 
3314 /**
3315  * xmlCatalogResolvePublic:
3316  * @pubID:  the public ID string
3317  *
3318  * Try to lookup the catalog reference associated to a public ID
3319  *
3320  * Returns the resource if found or NULL otherwise, the value returned
3321  *      must be freed by the caller.
3322  */
3323 xmlChar *
xmlCatalogResolvePublic(const xmlChar * pubID)3324 xmlCatalogResolvePublic(const xmlChar *pubID) {
3325     xmlChar *ret;
3326 
3327     if (!xmlCatalogInitialized)
3328 	xmlInitializeCatalog();
3329 
3330     ret = xmlACatalogResolvePublic(xmlDefaultCatalog, pubID);
3331     return(ret);
3332 }
3333 
3334 /**
3335  * xmlCatalogResolve:
3336  * @pubID:  the public ID string
3337  * @sysID:  the system ID string
3338  *
3339  * Do a complete resolution lookup of an External Identifier
3340  *
3341  * Returns the URI of the resource or NULL if not found, it must be freed
3342  *      by the caller.
3343  */
3344 xmlChar *
xmlCatalogResolve(const xmlChar * pubID,const xmlChar * sysID)3345 xmlCatalogResolve(const xmlChar *pubID, const xmlChar *sysID) {
3346     xmlChar *ret;
3347 
3348     if (!xmlCatalogInitialized)
3349 	xmlInitializeCatalog();
3350 
3351     ret = xmlACatalogResolve(xmlDefaultCatalog, pubID, sysID);
3352     return(ret);
3353 }
3354 
3355 /**
3356  * xmlCatalogResolveURI:
3357  * @URI:  the URI
3358  *
3359  * Do a complete resolution lookup of an URI
3360  *
3361  * Returns the URI of the resource or NULL if not found, it must be freed
3362  *      by the caller.
3363  */
3364 xmlChar *
xmlCatalogResolveURI(const xmlChar * URI)3365 xmlCatalogResolveURI(const xmlChar *URI) {
3366     xmlChar *ret;
3367 
3368     if (!xmlCatalogInitialized)
3369 	xmlInitializeCatalog();
3370 
3371     ret = xmlACatalogResolveURI(xmlDefaultCatalog, URI);
3372     return(ret);
3373 }
3374 
3375 #ifdef LIBXML_OUTPUT_ENABLED
3376 /**
3377  * xmlCatalogDump:
3378  * @out:  the file.
3379  *
3380  * Dump all the global catalog content to the given file.
3381  */
3382 void
xmlCatalogDump(FILE * out)3383 xmlCatalogDump(FILE *out) {
3384     if (out == NULL)
3385 	return;
3386 
3387     if (!xmlCatalogInitialized)
3388 	xmlInitializeCatalog();
3389 
3390     xmlACatalogDump(xmlDefaultCatalog, out);
3391 }
3392 #endif /* LIBXML_OUTPUT_ENABLED */
3393 
3394 /**
3395  * xmlCatalogAdd:
3396  * @type:  the type of record to add to the catalog
3397  * @orig:  the system, public or prefix to match
3398  * @replace:  the replacement value for the match
3399  *
3400  * Add an entry in the catalog, it may overwrite existing but
3401  * different entries.
3402  * If called before any other catalog routine, allows to override the
3403  * default shared catalog put in place by xmlInitializeCatalog();
3404  *
3405  * Returns 0 if successful, -1 otherwise
3406  */
3407 int
xmlCatalogAdd(const xmlChar * type,const xmlChar * orig,const xmlChar * replace)3408 xmlCatalogAdd(const xmlChar *type, const xmlChar *orig, const xmlChar *replace) {
3409     int res = -1;
3410 
3411     if (!xmlCatalogInitialized)
3412 	xmlInitializeCatalogData();
3413 
3414     xmlRMutexLock(xmlCatalogMutex);
3415     /*
3416      * Specific case where one want to override the default catalog
3417      * put in place by xmlInitializeCatalog();
3418      */
3419     if ((xmlDefaultCatalog == NULL) &&
3420 	(xmlStrEqual(type, BAD_CAST "catalog"))) {
3421 	xmlDefaultCatalog = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE,
3422 		                          xmlCatalogDefaultPrefer);
3423 	if (xmlDefaultCatalog != NULL) {
3424 	   xmlDefaultCatalog->xml = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
3425 				    orig, NULL,  xmlCatalogDefaultPrefer, NULL);
3426 	}
3427 	xmlRMutexUnlock(xmlCatalogMutex);
3428 	return(0);
3429     }
3430 
3431     res = xmlACatalogAdd(xmlDefaultCatalog, type, orig, replace);
3432     xmlRMutexUnlock(xmlCatalogMutex);
3433     return(res);
3434 }
3435 
3436 /**
3437  * xmlCatalogRemove:
3438  * @value:  the value to remove
3439  *
3440  * Remove an entry from the catalog
3441  *
3442  * Returns the number of entries removed if successful, -1 otherwise
3443  */
3444 int
xmlCatalogRemove(const xmlChar * value)3445 xmlCatalogRemove(const xmlChar *value) {
3446     int res;
3447 
3448     if (!xmlCatalogInitialized)
3449 	xmlInitializeCatalog();
3450 
3451     xmlRMutexLock(xmlCatalogMutex);
3452     res = xmlACatalogRemove(xmlDefaultCatalog, value);
3453     xmlRMutexUnlock(xmlCatalogMutex);
3454     return(res);
3455 }
3456 
3457 /**
3458  * xmlCatalogConvert:
3459  *
3460  * Convert all the SGML catalog entries as XML ones
3461  *
3462  * Returns the number of entries converted if successful, -1 otherwise
3463  */
3464 int
xmlCatalogConvert(void)3465 xmlCatalogConvert(void) {
3466     int res = -1;
3467 
3468     if (!xmlCatalogInitialized)
3469 	xmlInitializeCatalog();
3470 
3471     xmlRMutexLock(xmlCatalogMutex);
3472     res = xmlConvertSGMLCatalog(xmlDefaultCatalog);
3473     xmlRMutexUnlock(xmlCatalogMutex);
3474     return(res);
3475 }
3476 
3477 /************************************************************************
3478  *									*
3479  *	Public interface manipulating the common preferences		*
3480  *									*
3481  ************************************************************************/
3482 
3483 /**
3484  * xmlCatalogGetDefaults:
3485  *
3486  * Used to get the user preference w.r.t. to what catalogs should
3487  * be accepted
3488  *
3489  * Returns the current xmlCatalogAllow value
3490  */
3491 xmlCatalogAllow
xmlCatalogGetDefaults(void)3492 xmlCatalogGetDefaults(void) {
3493     return(xmlCatalogDefaultAllow);
3494 }
3495 
3496 /**
3497  * xmlCatalogSetDefaults:
3498  * @allow:  what catalogs should be accepted
3499  *
3500  * Used to set the user preference w.r.t. to what catalogs should
3501  * be accepted
3502  */
3503 void
xmlCatalogSetDefaults(xmlCatalogAllow allow)3504 xmlCatalogSetDefaults(xmlCatalogAllow allow) {
3505     if (xmlDebugCatalogs) {
3506 	switch (allow) {
3507 	    case XML_CATA_ALLOW_NONE:
3508 		xmlGenericError(xmlGenericErrorContext,
3509 			"Disabling catalog usage\n");
3510 		break;
3511 	    case XML_CATA_ALLOW_GLOBAL:
3512 		xmlGenericError(xmlGenericErrorContext,
3513 			"Allowing only global catalogs\n");
3514 		break;
3515 	    case XML_CATA_ALLOW_DOCUMENT:
3516 		xmlGenericError(xmlGenericErrorContext,
3517 			"Allowing only catalogs from the document\n");
3518 		break;
3519 	    case XML_CATA_ALLOW_ALL:
3520 		xmlGenericError(xmlGenericErrorContext,
3521 			"Allowing all catalogs\n");
3522 		break;
3523 	}
3524     }
3525     xmlCatalogDefaultAllow = allow;
3526 }
3527 
3528 /**
3529  * xmlCatalogSetDefaultPrefer:
3530  * @prefer:  the default preference for delegation
3531  *
3532  * Allows to set the preference between public and system for deletion
3533  * in XML Catalog resolution. C.f. section 4.1.1 of the spec
3534  * Values accepted are XML_CATA_PREFER_PUBLIC or XML_CATA_PREFER_SYSTEM
3535  *
3536  * Returns the previous value of the default preference for delegation
3537  */
3538 xmlCatalogPrefer
xmlCatalogSetDefaultPrefer(xmlCatalogPrefer prefer)3539 xmlCatalogSetDefaultPrefer(xmlCatalogPrefer prefer) {
3540     xmlCatalogPrefer ret = xmlCatalogDefaultPrefer;
3541 
3542     if (prefer == XML_CATA_PREFER_NONE)
3543 	return(ret);
3544 
3545     if (xmlDebugCatalogs) {
3546 	switch (prefer) {
3547 	    case XML_CATA_PREFER_PUBLIC:
3548 		xmlGenericError(xmlGenericErrorContext,
3549 			"Setting catalog preference to PUBLIC\n");
3550 		break;
3551 	    case XML_CATA_PREFER_SYSTEM:
3552 		xmlGenericError(xmlGenericErrorContext,
3553 			"Setting catalog preference to SYSTEM\n");
3554 		break;
3555 	    default:
3556 		return(ret);
3557 	}
3558     }
3559     xmlCatalogDefaultPrefer = prefer;
3560     return(ret);
3561 }
3562 
3563 /**
3564  * xmlCatalogSetDebug:
3565  * @level:  the debug level of catalogs required
3566  *
3567  * Used to set the debug level for catalog operation, 0 disable
3568  * debugging, 1 enable it
3569  *
3570  * Returns the previous value of the catalog debugging level
3571  */
3572 int
xmlCatalogSetDebug(int level)3573 xmlCatalogSetDebug(int level) {
3574     int ret = xmlDebugCatalogs;
3575 
3576     if (level <= 0)
3577         xmlDebugCatalogs = 0;
3578     else
3579 	xmlDebugCatalogs = level;
3580     return(ret);
3581 }
3582 
3583 /************************************************************************
3584  *									*
3585  *   Minimal interfaces used for per-document catalogs by the parser	*
3586  *									*
3587  ************************************************************************/
3588 
3589 /**
3590  * xmlCatalogFreeLocal:
3591  * @catalogs:  a document's list of catalogs
3592  *
3593  * Free up the memory associated to the catalog list
3594  */
3595 void
xmlCatalogFreeLocal(void * catalogs)3596 xmlCatalogFreeLocal(void *catalogs) {
3597     xmlCatalogEntryPtr catal;
3598 
3599     if (!xmlCatalogInitialized)
3600 	xmlInitializeCatalog();
3601 
3602     catal = (xmlCatalogEntryPtr) catalogs;
3603     if (catal != NULL)
3604 	xmlFreeCatalogEntryList(catal);
3605 }
3606 
3607 
3608 /**
3609  * xmlCatalogAddLocal:
3610  * @catalogs:  a document's list of catalogs
3611  * @URL:  the URL to a new local catalog
3612  *
3613  * Add the new entry to the catalog list
3614  *
3615  * Returns the updated list
3616  */
3617 void *
xmlCatalogAddLocal(void * catalogs,const xmlChar * URL)3618 xmlCatalogAddLocal(void *catalogs, const xmlChar *URL) {
3619     xmlCatalogEntryPtr catal, add;
3620 
3621     if (!xmlCatalogInitialized)
3622 	xmlInitializeCatalog();
3623 
3624     if (URL == NULL)
3625 	return(catalogs);
3626 
3627     if (xmlDebugCatalogs)
3628 	xmlGenericError(xmlGenericErrorContext,
3629 		"Adding document catalog %s\n", URL);
3630 
3631     add = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL, URL, NULL,
3632 	                     xmlCatalogDefaultPrefer, NULL);
3633     if (add == NULL)
3634 	return(catalogs);
3635 
3636     catal = (xmlCatalogEntryPtr) catalogs;
3637     if (catal == NULL)
3638 	return((void *) add);
3639 
3640     while (catal->next != NULL)
3641 	catal = catal->next;
3642     catal->next = add;
3643     return(catalogs);
3644 }
3645 
3646 /**
3647  * xmlCatalogLocalResolve:
3648  * @catalogs:  a document's list of catalogs
3649  * @pubID:  the public ID string
3650  * @sysID:  the system ID string
3651  *
3652  * Do a complete resolution lookup of an External Identifier using a
3653  * document's private catalog list
3654  *
3655  * Returns the URI of the resource or NULL if not found, it must be freed
3656  *      by the caller.
3657  */
3658 xmlChar *
xmlCatalogLocalResolve(void * catalogs,const xmlChar * pubID,const xmlChar * sysID)3659 xmlCatalogLocalResolve(void *catalogs, const xmlChar *pubID,
3660 	               const xmlChar *sysID) {
3661     xmlCatalogEntryPtr catal;
3662     xmlChar *ret;
3663 
3664     if (!xmlCatalogInitialized)
3665 	xmlInitializeCatalog();
3666 
3667     if ((pubID == NULL) && (sysID == NULL))
3668 	return(NULL);
3669 
3670     if (xmlDebugCatalogs) {
3671         if ((pubID != NULL) && (sysID != NULL)) {
3672             xmlGenericError(xmlGenericErrorContext,
3673                             "Local Resolve: pubID %s sysID %s\n", pubID, sysID);
3674         } else if (pubID != NULL) {
3675             xmlGenericError(xmlGenericErrorContext,
3676                             "Local Resolve: pubID %s\n", pubID);
3677         } else {
3678             xmlGenericError(xmlGenericErrorContext,
3679                             "Local Resolve: sysID %s\n", sysID);
3680         }
3681     }
3682 
3683     catal = (xmlCatalogEntryPtr) catalogs;
3684     if (catal == NULL)
3685 	return(NULL);
3686     ret = xmlCatalogListXMLResolve(catal, pubID, sysID);
3687     if ((ret != NULL) && (ret != XML_CATAL_BREAK))
3688 	return(ret);
3689     return(NULL);
3690 }
3691 
3692 /**
3693  * xmlCatalogLocalResolveURI:
3694  * @catalogs:  a document's list of catalogs
3695  * @URI:  the URI
3696  *
3697  * Do a complete resolution lookup of an URI using a
3698  * document's private catalog list
3699  *
3700  * Returns the URI of the resource or NULL if not found, it must be freed
3701  *      by the caller.
3702  */
3703 xmlChar *
xmlCatalogLocalResolveURI(void * catalogs,const xmlChar * URI)3704 xmlCatalogLocalResolveURI(void *catalogs, const xmlChar *URI) {
3705     xmlCatalogEntryPtr catal;
3706     xmlChar *ret;
3707 
3708     if (!xmlCatalogInitialized)
3709 	xmlInitializeCatalog();
3710 
3711     if (URI == NULL)
3712 	return(NULL);
3713 
3714     if (xmlDebugCatalogs)
3715 	xmlGenericError(xmlGenericErrorContext,
3716 		"Resolve URI %s\n", URI);
3717 
3718     catal = (xmlCatalogEntryPtr) catalogs;
3719     if (catal == NULL)
3720 	return(NULL);
3721     ret = xmlCatalogListXMLResolveURI(catal, URI);
3722     if ((ret != NULL) && (ret != XML_CATAL_BREAK))
3723 	return(ret);
3724     return(NULL);
3725 }
3726 
3727 /************************************************************************
3728  *									*
3729  *			Deprecated interfaces				*
3730  *									*
3731  ************************************************************************/
3732 /**
3733  * xmlCatalogGetSystem:
3734  * @sysID:  the system ID string
3735  *
3736  * Try to lookup the catalog reference associated to a system ID
3737  * DEPRECATED, use xmlCatalogResolveSystem()
3738  *
3739  * Returns the resource if found or NULL otherwise.
3740  */
3741 const xmlChar *
xmlCatalogGetSystem(const xmlChar * sysID)3742 xmlCatalogGetSystem(const xmlChar *sysID) {
3743     xmlChar *ret;
3744     static xmlChar result[1000];
3745     static int msg = 0;
3746 
3747     if (!xmlCatalogInitialized)
3748 	xmlInitializeCatalog();
3749 
3750     if (msg == 0) {
3751 	xmlGenericError(xmlGenericErrorContext,
3752 		"Use of deprecated xmlCatalogGetSystem() call\n");
3753 	msg++;
3754     }
3755 
3756     if (sysID == NULL)
3757 	return(NULL);
3758 
3759     /*
3760      * Check first the XML catalogs
3761      */
3762     if (xmlDefaultCatalog != NULL) {
3763 	ret = xmlCatalogListXMLResolve(xmlDefaultCatalog->xml, NULL, sysID);
3764 	if ((ret != NULL) && (ret != XML_CATAL_BREAK)) {
3765 	    snprintf((char *) result, sizeof(result) - 1, "%s", (char *) ret);
3766 	    result[sizeof(result) - 1] = 0;
3767 	    return(result);
3768 	}
3769     }
3770 
3771     if (xmlDefaultCatalog != NULL)
3772 	return(xmlCatalogGetSGMLSystem(xmlDefaultCatalog->sgml, sysID));
3773     return(NULL);
3774 }
3775 
3776 /**
3777  * xmlCatalogGetPublic:
3778  * @pubID:  the public ID string
3779  *
3780  * Try to lookup the catalog reference associated to a public ID
3781  * DEPRECATED, use xmlCatalogResolvePublic()
3782  *
3783  * Returns the resource if found or NULL otherwise.
3784  */
3785 const xmlChar *
xmlCatalogGetPublic(const xmlChar * pubID)3786 xmlCatalogGetPublic(const xmlChar *pubID) {
3787     xmlChar *ret;
3788     static xmlChar result[1000];
3789     static int msg = 0;
3790 
3791     if (!xmlCatalogInitialized)
3792 	xmlInitializeCatalog();
3793 
3794     if (msg == 0) {
3795 	xmlGenericError(xmlGenericErrorContext,
3796 		"Use of deprecated xmlCatalogGetPublic() call\n");
3797 	msg++;
3798     }
3799 
3800     if (pubID == NULL)
3801 	return(NULL);
3802 
3803     /*
3804      * Check first the XML catalogs
3805      */
3806     if (xmlDefaultCatalog != NULL) {
3807 	ret = xmlCatalogListXMLResolve(xmlDefaultCatalog->xml, pubID, NULL);
3808 	if ((ret != NULL) && (ret != XML_CATAL_BREAK)) {
3809 	    snprintf((char *) result, sizeof(result) - 1, "%s", (char *) ret);
3810 	    result[sizeof(result) - 1] = 0;
3811 	    return(result);
3812 	}
3813     }
3814 
3815     if (xmlDefaultCatalog != NULL)
3816 	return(xmlCatalogGetSGMLPublic(xmlDefaultCatalog->sgml, pubID));
3817     return(NULL);
3818 }
3819 
3820 #endif /* LIBXML_CATALOG_ENABLED */
3821