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