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