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