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