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