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