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