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