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