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