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