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