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