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