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