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