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