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