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