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