catalog.c
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 #pragma clang diagnostic push 247 #pragma clang diagnostic ignored "-Wformat-nonliteral" 248 __xmlRaiseError(NULL, NULL, NULL, catal, node, XML_FROM_CATALOG, 249 error, XML_ERR_ERROR, NULL, 0, 250 (const char *) str1, (const char *) str2, 251 (const char *) str3, 0, 0, 252 msg, str1, str2, str3); 253 #pragma clang diagnostic pop 254 } 255 256 257 /************************************************************************ 258 * * 259 * Allocation and Freeing * 260 * * 261 ************************************************************************/ 262 263 /** 264 * xmlNewCatalogEntry: 265 * @type: type of entry 266 * @name: name of the entry 267 * @value: value of the entry 268 * @prefer: the PUBLIC vs. SYSTEM current preference value 269 * @group: for members of a group, the group entry 270 * 271 * create a new Catalog entry, this type is shared both by XML and 272 * SGML catalogs, but the acceptable types values differs. 273 * 274 * Returns the xmlCatalogEntryPtr or NULL in case of error 275 */ 276 static xmlCatalogEntryPtr 277 xmlNewCatalogEntry(xmlCatalogEntryType type, const xmlChar *name, 278 const xmlChar *value, const xmlChar *URL, xmlCatalogPrefer prefer, 279 xmlCatalogEntryPtr group) { 280 xmlCatalogEntryPtr ret; 281 xmlChar *normid = NULL; 282 283 ret = (xmlCatalogEntryPtr) xmlMalloc(sizeof(xmlCatalogEntry)); 284 if (ret == NULL) { 285 xmlCatalogErrMemory("allocating catalog entry"); 286 return(NULL); 287 } 288 ret->next = NULL; 289 ret->parent = NULL; 290 ret->children = NULL; 291 ret->type = type; 292 if (type == XML_CATA_PUBLIC || type == XML_CATA_DELEGATE_PUBLIC) { 293 normid = xmlCatalogNormalizePublic(name); 294 if (normid != NULL) 295 name = (*normid != 0 ? normid : NULL); 296 } 297 if (name != NULL) 298 ret->name = xmlStrdup(name); 299 else 300 ret->name = NULL; 301 if (normid != NULL) 302 xmlFree(normid); 303 if (value != NULL) 304 ret->value = xmlStrdup(value); 305 else 306 ret->value = NULL; 307 if (URL == NULL) 308 URL = value; 309 if (URL != NULL) 310 ret->URL = xmlStrdup(URL); 311 else 312 ret->URL = NULL; 313 ret->prefer = prefer; 314 ret->dealloc = 0; 315 ret->depth = 0; 316 ret->group = group; 317 return(ret); 318 } 319 320 static void 321 xmlFreeCatalogEntryList(xmlCatalogEntryPtr ret); 322 323 /** 324 * xmlFreeCatalogEntry: 325 * @ret: a Catalog entry 326 * 327 * Free the memory allocated to a Catalog entry 328 */ 329 static void 330 xmlFreeCatalogEntry(xmlCatalogEntryPtr ret) { 331 if (ret == NULL) 332 return; 333 /* 334 * Entries stored in the file hash must be deallocated 335 * only by the file hash cleaner ! 336 */ 337 if (ret->dealloc == 1) 338 return; 339 340 if (xmlDebugCatalogs) { 341 if (ret->name != NULL) 342 xmlGenericError(xmlGenericErrorContext, 343 "Free catalog entry %s\n", ret->name); 344 else if (ret->value != NULL) 345 xmlGenericError(xmlGenericErrorContext, 346 "Free catalog entry %s\n", ret->value); 347 else 348 xmlGenericError(xmlGenericErrorContext, 349 "Free catalog entry\n"); 350 } 351 352 if (ret->name != NULL) 353 xmlFree(ret->name); 354 if (ret->value != NULL) 355 xmlFree(ret->value); 356 if (ret->URL != NULL) 357 xmlFree(ret->URL); 358 xmlFree(ret); 359 } 360 361 /** 362 * xmlFreeCatalogEntryList: 363 * @ret: a Catalog entry list 364 * 365 * Free the memory allocated to a full chained list of Catalog entries 366 */ 367 static void 368 xmlFreeCatalogEntryList(xmlCatalogEntryPtr ret) { 369 xmlCatalogEntryPtr next; 370 371 while (ret != NULL) { 372 next = ret->next; 373 xmlFreeCatalogEntry(ret); 374 ret = next; 375 } 376 } 377 378 /** 379 * xmlFreeCatalogHashEntryList: 380 * @ret: a Catalog entry list 381 * 382 * Free the memory allocated to list of Catalog entries from the 383 * catalog file hash. 384 */ 385 static void 386 xmlFreeCatalogHashEntryList(xmlCatalogEntryPtr catal) { 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); 398 children = next; 399 } 400 catal->dealloc = 0; 401 xmlFreeCatalogEntry(catal); 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, 447 (xmlHashDeallocator) xmlFreeCatalogEntry); 448 xmlFree(catal); 449 } 450 451 /************************************************************************ 452 * * 453 * Serializing Catalogs * 454 * * 455 ************************************************************************/ 456 457 #ifdef LIBXML_OUTPUT_ENABLED 458 /** 459 * xmlCatalogDumpEntry: 460 * @entry: the catalog entry 461 * @out: the file. 462 * 463 * Serialize an SGML Catalog entry 464 */ 465 static void 466 xmlCatalogDumpEntry(xmlCatalogEntryPtr entry, FILE *out) { 467 if ((entry == NULL) || (out == NULL)) 468 return; 469 switch (entry->type) { 470 case SGML_CATA_ENTITY: 471 fprintf(out, "ENTITY "); break; 472 case SGML_CATA_PENTITY: 473 fprintf(out, "ENTITY %%"); break; 474 case SGML_CATA_DOCTYPE: 475 fprintf(out, "DOCTYPE "); break; 476 case SGML_CATA_LINKTYPE: 477 fprintf(out, "LINKTYPE "); break; 478 case SGML_CATA_NOTATION: 479 fprintf(out, "NOTATION "); break; 480 case SGML_CATA_PUBLIC: 481 fprintf(out, "PUBLIC "); break; 482 case SGML_CATA_SYSTEM: 483 fprintf(out, "SYSTEM "); break; 484 case SGML_CATA_DELEGATE: 485 fprintf(out, "DELEGATE "); break; 486 case SGML_CATA_BASE: 487 fprintf(out, "BASE "); break; 488 case SGML_CATA_CATALOG: 489 fprintf(out, "CATALOG "); break; 490 case SGML_CATA_DOCUMENT: 491 fprintf(out, "DOCUMENT "); break; 492 case SGML_CATA_SGMLDECL: 493 fprintf(out, "SGMLDECL "); break; 494 default: 495 return; 496 } 497 switch (entry->type) { 498 case SGML_CATA_ENTITY: 499 case SGML_CATA_PENTITY: 500 case SGML_CATA_DOCTYPE: 501 case SGML_CATA_LINKTYPE: 502 case SGML_CATA_NOTATION: 503 fprintf(out, "%s", (const char *) entry->name); break; 504 case SGML_CATA_PUBLIC: 505 case SGML_CATA_SYSTEM: 506 case SGML_CATA_SGMLDECL: 507 case SGML_CATA_DOCUMENT: 508 case SGML_CATA_CATALOG: 509 case SGML_CATA_BASE: 510 case SGML_CATA_DELEGATE: 511 fprintf(out, "\"%s\"", entry->name); break; 512 default: 513 break; 514 } 515 switch (entry->type) { 516 case SGML_CATA_ENTITY: 517 case SGML_CATA_PENTITY: 518 case SGML_CATA_DOCTYPE: 519 case SGML_CATA_LINKTYPE: 520 case SGML_CATA_NOTATION: 521 case SGML_CATA_PUBLIC: 522 case SGML_CATA_SYSTEM: 523 case SGML_CATA_DELEGATE: 524 fprintf(out, " \"%s\"", entry->value); break; 525 default: 526 break; 527 } 528 fprintf(out, "\n"); 529 } 530 531 /** 532 * xmlDumpXMLCatalogNode: 533 * @catal: top catalog entry 534 * @catalog: pointer to the xml tree 535 * @doc: the containing document 536 * @ns: the current namespace 537 * @cgroup: group node for group members 538 * 539 * Serializes a Catalog entry, called by xmlDumpXMLCatalog and recursively 540 * for group entries 541 */ 542 static void xmlDumpXMLCatalogNode(xmlCatalogEntryPtr catal, xmlNodePtr catalog, 543 xmlDocPtr doc, xmlNsPtr ns, xmlCatalogEntryPtr cgroup) { 544 xmlNodePtr node; 545 xmlCatalogEntryPtr cur; 546 /* 547 * add all the catalog entries 548 */ 549 cur = catal; 550 while (cur != NULL) { 551 if (cur->group == cgroup) { 552 switch (cur->type) { 553 case XML_CATA_REMOVED: 554 break; 555 case XML_CATA_BROKEN_CATALOG: 556 case XML_CATA_CATALOG: 557 if (cur == catal) { 558 cur = cur->children; 559 continue; 560 } 561 break; 562 case XML_CATA_NEXT_CATALOG: 563 node = xmlNewDocNode(doc, ns, BAD_CAST "nextCatalog", NULL); 564 xmlSetProp(node, BAD_CAST "catalog", cur->value); 565 xmlAddChild(catalog, node); 566 break; 567 case XML_CATA_NONE: 568 break; 569 case XML_CATA_GROUP: 570 node = xmlNewDocNode(doc, ns, BAD_CAST "group", NULL); 571 xmlSetProp(node, BAD_CAST "id", cur->name); 572 if (cur->value != NULL) { 573 xmlNsPtr xns; 574 xns = xmlSearchNsByHref(doc, node, XML_XML_NAMESPACE); 575 if (xns != NULL) 576 xmlSetNsProp(node, xns, BAD_CAST "base", 577 cur->value); 578 } 579 switch (cur->prefer) { 580 case XML_CATA_PREFER_NONE: 581 break; 582 case XML_CATA_PREFER_PUBLIC: 583 xmlSetProp(node, BAD_CAST "prefer", BAD_CAST "public"); 584 break; 585 case XML_CATA_PREFER_SYSTEM: 586 xmlSetProp(node, BAD_CAST "prefer", BAD_CAST "system"); 587 break; 588 } 589 xmlDumpXMLCatalogNode(cur->next, node, doc, ns, cur); 590 xmlAddChild(catalog, node); 591 break; 592 case XML_CATA_PUBLIC: 593 node = xmlNewDocNode(doc, ns, BAD_CAST "public", NULL); 594 xmlSetProp(node, BAD_CAST "publicId", cur->name); 595 xmlSetProp(node, BAD_CAST "uri", cur->value); 596 xmlAddChild(catalog, node); 597 break; 598 case XML_CATA_SYSTEM: 599 node = xmlNewDocNode(doc, ns, BAD_CAST "system", NULL); 600 xmlSetProp(node, BAD_CAST "systemId", cur->name); 601 xmlSetProp(node, BAD_CAST "uri", cur->value); 602 xmlAddChild(catalog, node); 603 break; 604 case XML_CATA_REWRITE_SYSTEM: 605 node = xmlNewDocNode(doc, ns, BAD_CAST "rewriteSystem", NULL); 606 xmlSetProp(node, BAD_CAST "systemIdStartString", cur->name); 607 xmlSetProp(node, BAD_CAST "rewritePrefix", cur->value); 608 xmlAddChild(catalog, node); 609 break; 610 case XML_CATA_DELEGATE_PUBLIC: 611 node = xmlNewDocNode(doc, ns, BAD_CAST "delegatePublic", NULL); 612 xmlSetProp(node, BAD_CAST "publicIdStartString", cur->name); 613 xmlSetProp(node, BAD_CAST "catalog", cur->value); 614 xmlAddChild(catalog, node); 615 break; 616 case XML_CATA_DELEGATE_SYSTEM: 617 node = xmlNewDocNode(doc, ns, BAD_CAST "delegateSystem", NULL); 618 xmlSetProp(node, BAD_CAST "systemIdStartString", cur->name); 619 xmlSetProp(node, BAD_CAST "catalog", cur->value); 620 xmlAddChild(catalog, node); 621 break; 622 case XML_CATA_URI: 623 node = xmlNewDocNode(doc, ns, BAD_CAST "uri", NULL); 624 xmlSetProp(node, BAD_CAST "name", cur->name); 625 xmlSetProp(node, BAD_CAST "uri", cur->value); 626 xmlAddChild(catalog, node); 627 break; 628 case XML_CATA_REWRITE_URI: 629 node = xmlNewDocNode(doc, ns, BAD_CAST "rewriteURI", NULL); 630 xmlSetProp(node, BAD_CAST "uriStartString", cur->name); 631 xmlSetProp(node, BAD_CAST "rewritePrefix", cur->value); 632 xmlAddChild(catalog, node); 633 break; 634 case XML_CATA_DELEGATE_URI: 635 node = xmlNewDocNode(doc, ns, BAD_CAST "delegateURI", NULL); 636 xmlSetProp(node, BAD_CAST "uriStartString", cur->name); 637 xmlSetProp(node, BAD_CAST "catalog", cur->value); 638 xmlAddChild(catalog, node); 639 break; 640 case SGML_CATA_SYSTEM: 641 case SGML_CATA_PUBLIC: 642 case SGML_CATA_ENTITY: 643 case SGML_CATA_PENTITY: 644 case SGML_CATA_DOCTYPE: 645 case SGML_CATA_LINKTYPE: 646 case SGML_CATA_NOTATION: 647 case SGML_CATA_DELEGATE: 648 case SGML_CATA_BASE: 649 case SGML_CATA_CATALOG: 650 case SGML_CATA_DOCUMENT: 651 case SGML_CATA_SGMLDECL: 652 break; 653 } 654 } 655 cur = cur->next; 656 } 657 } 658 659 static int 660 xmlDumpXMLCatalog(FILE *out, xmlCatalogEntryPtr catal) { 661 int ret; 662 xmlDocPtr doc; 663 xmlNsPtr ns; 664 xmlDtdPtr dtd; 665 xmlNodePtr catalog; 666 xmlOutputBufferPtr buf; 667 668 /* 669 * Rebuild a catalog 670 */ 671 doc = xmlNewDoc(NULL); 672 if (doc == NULL) 673 return(-1); 674 dtd = xmlNewDtd(doc, BAD_CAST "catalog", 675 BAD_CAST "-//OASIS//DTD Entity Resolution XML Catalog V1.0//EN", 676 BAD_CAST "http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd"); 677 678 xmlAddChild((xmlNodePtr) doc, (xmlNodePtr) dtd); 679 680 ns = xmlNewNs(NULL, XML_CATALOGS_NAMESPACE, NULL); 681 if (ns == NULL) { 682 xmlFreeDoc(doc); 683 return(-1); 684 } 685 catalog = xmlNewDocNode(doc, ns, BAD_CAST "catalog", NULL); 686 if (catalog == NULL) { 687 xmlFreeNs(ns); 688 xmlFreeDoc(doc); 689 return(-1); 690 } 691 catalog->nsDef = ns; 692 xmlAddChild((xmlNodePtr) doc, catalog); 693 694 xmlDumpXMLCatalogNode(catal, catalog, doc, ns, NULL); 695 696 /* 697 * reserialize it 698 */ 699 buf = xmlOutputBufferCreateFile(out, NULL); 700 if (buf == NULL) { 701 xmlFreeDoc(doc); 702 return(-1); 703 } 704 ret = xmlSaveFormatFileTo(buf, doc, NULL, 1); 705 706 /* 707 * Free it 708 */ 709 xmlFreeDoc(doc); 710 711 return(ret); 712 } 713 #endif /* LIBXML_OUTPUT_ENABLED */ 714 715 /************************************************************************ 716 * * 717 * Converting SGML Catalogs to XML * 718 * * 719 ************************************************************************/ 720 721 /** 722 * xmlCatalogConvertEntry: 723 * @entry: the entry 724 * @catal: pointer to the catalog being converted 725 * 726 * Convert one entry from the catalog 727 */ 728 static void 729 xmlCatalogConvertEntry(xmlCatalogEntryPtr entry, xmlCatalogPtr catal) { 730 if ((entry == NULL) || (catal == NULL) || (catal->sgml == NULL) || 731 (catal->xml == NULL)) 732 return; 733 switch (entry->type) { 734 case SGML_CATA_ENTITY: 735 entry->type = XML_CATA_PUBLIC; 736 break; 737 case SGML_CATA_PENTITY: 738 entry->type = XML_CATA_PUBLIC; 739 break; 740 case SGML_CATA_DOCTYPE: 741 entry->type = XML_CATA_PUBLIC; 742 break; 743 case SGML_CATA_LINKTYPE: 744 entry->type = XML_CATA_PUBLIC; 745 break; 746 case SGML_CATA_NOTATION: 747 entry->type = XML_CATA_PUBLIC; 748 break; 749 case SGML_CATA_PUBLIC: 750 entry->type = XML_CATA_PUBLIC; 751 break; 752 case SGML_CATA_SYSTEM: 753 entry->type = XML_CATA_SYSTEM; 754 break; 755 case SGML_CATA_DELEGATE: 756 entry->type = XML_CATA_DELEGATE_PUBLIC; 757 break; 758 case SGML_CATA_CATALOG: 759 entry->type = XML_CATA_CATALOG; 760 break; 761 default: 762 xmlHashRemoveEntry(catal->sgml, entry->name, 763 (xmlHashDeallocator) xmlFreeCatalogEntry); 764 return; 765 } 766 /* 767 * Conversion successful, remove from the SGML catalog 768 * and add it to the default XML one 769 */ 770 xmlHashRemoveEntry(catal->sgml, entry->name, NULL); 771 entry->parent = catal->xml; 772 entry->next = NULL; 773 if (catal->xml->children == NULL) 774 catal->xml->children = entry; 775 else { 776 xmlCatalogEntryPtr prev; 777 778 prev = catal->xml->children; 779 while (prev->next != NULL) 780 prev = prev->next; 781 prev->next = entry; 782 } 783 } 784 785 /** 786 * xmlConvertSGMLCatalog: 787 * @catal: the catalog 788 * 789 * Convert all the SGML catalog entries as XML ones 790 * 791 * Returns the number of entries converted if successful, -1 otherwise 792 */ 793 int 794 xmlConvertSGMLCatalog(xmlCatalogPtr catal) { 795 796 if ((catal == NULL) || (catal->type != XML_SGML_CATALOG_TYPE)) 797 return(-1); 798 799 if (xmlDebugCatalogs) { 800 xmlGenericError(xmlGenericErrorContext, 801 "Converting SGML catalog to XML\n"); 802 } 803 xmlHashScan(catal->sgml, 804 (xmlHashScanner) xmlCatalogConvertEntry, 805 &catal); 806 return(0); 807 } 808 809 /************************************************************************ 810 * * 811 * Helper function * 812 * * 813 ************************************************************************/ 814 815 /** 816 * xmlCatalogUnWrapURN: 817 * @urn: an "urn:publicid:" to unwrap 818 * 819 * Expand the URN into the equivalent Public Identifier 820 * 821 * Returns the new identifier or NULL, the string must be deallocated 822 * by the caller. 823 */ 824 static xmlChar * 825 xmlCatalogUnWrapURN(const xmlChar *urn) { 826 xmlChar result[2000]; 827 unsigned int i = 0; 828 829 if (xmlStrncmp(urn, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) 830 return(NULL); 831 urn += sizeof(XML_URN_PUBID) - 1; 832 833 while (*urn != 0) { 834 if (i > sizeof(result) - 4) 835 break; 836 if (*urn == '+') { 837 result[i++] = ' '; 838 urn++; 839 } else if (*urn == ':') { 840 result[i++] = '/'; 841 result[i++] = '/'; 842 urn++; 843 } else if (*urn == ';') { 844 result[i++] = ':'; 845 result[i++] = ':'; 846 urn++; 847 } else if (*urn == '%') { 848 if ((urn[1] == '2') && (urn[2] == 'B')) 849 result[i++] = '+'; 850 else if ((urn[1] == '3') && (urn[2] == 'A')) 851 result[i++] = ':'; 852 else if ((urn[1] == '2') && (urn[2] == 'F')) 853 result[i++] = '/'; 854 else if ((urn[1] == '3') && (urn[2] == 'B')) 855 result[i++] = ';'; 856 else if ((urn[1] == '2') && (urn[2] == '7')) 857 result[i++] = '\''; 858 else if ((urn[1] == '3') && (urn[2] == 'F')) 859 result[i++] = '?'; 860 else if ((urn[1] == '2') && (urn[2] == '3')) 861 result[i++] = '#'; 862 else if ((urn[1] == '2') && (urn[2] == '5')) 863 result[i++] = '%'; 864 else { 865 result[i++] = *urn; 866 urn++; 867 continue; 868 } 869 urn += 3; 870 } else { 871 result[i++] = *urn; 872 urn++; 873 } 874 } 875 result[i] = 0; 876 877 return(xmlStrdup(result)); 878 } 879 880 /** 881 * xmlParseCatalogFile: 882 * @filename: the filename 883 * 884 * parse an XML file and build a tree. It's like xmlParseFile() 885 * except it bypass all catalog lookups. 886 * 887 * Returns the resulting document tree or NULL in case of error 888 */ 889 890 xmlDocPtr 891 xmlParseCatalogFile(const char *filename) { 892 xmlDocPtr ret; 893 xmlParserCtxtPtr ctxt; 894 char *directory = NULL; 895 xmlParserInputPtr inputStream; 896 xmlParserInputBufferPtr buf; 897 898 ctxt = xmlNewParserCtxt(); 899 if (ctxt == NULL) { 900 #ifdef LIBXML_SAX1_ENABLED 901 if (xmlDefaultSAXHandler.error != NULL) { 902 xmlDefaultSAXHandler.error(NULL, "out of memory\n"); 903 } 904 #endif 905 return(NULL); 906 } 907 908 buf = xmlParserInputBufferCreateFilename(filename, XML_CHAR_ENCODING_NONE); 909 if (buf == NULL) { 910 xmlFreeParserCtxt(ctxt); 911 return(NULL); 912 } 913 914 inputStream = xmlNewInputStream(ctxt); 915 if (inputStream == NULL) { 916 xmlFreeParserCtxt(ctxt); 917 return(NULL); 918 } 919 920 inputStream->filename = (char *) xmlCanonicPath((const xmlChar *)filename); 921 inputStream->buf = buf; 922 xmlBufResetInput(buf->buffer, inputStream); 923 924 inputPush(ctxt, inputStream); 925 if ((ctxt->directory == NULL) && (directory == NULL)) 926 directory = xmlParserGetDirectory(filename); 927 if ((ctxt->directory == NULL) && (directory != NULL)) 928 ctxt->directory = directory; 929 ctxt->valid = 0; 930 ctxt->validate = 0; 931 ctxt->loadsubset = 0; 932 ctxt->pedantic = 0; 933 ctxt->dictNames = 1; 934 935 xmlParseDocument(ctxt); 936 937 if (ctxt->wellFormed) 938 ret = ctxt->myDoc; 939 else { 940 ret = NULL; 941 xmlFreeDoc(ctxt->myDoc); 942 ctxt->myDoc = NULL; 943 } 944 xmlFreeParserCtxt(ctxt); 945 946 return(ret); 947 } 948 949 /** 950 * xmlLoadFileContent: 951 * @filename: a file path 952 * 953 * Load a file content into memory. 954 * 955 * Returns a pointer to the 0 terminated string or NULL in case of error 956 */ 957 static xmlChar * 958 xmlLoadFileContent(const char *filename) 959 { 960 #ifdef HAVE_STAT 961 int fd; 962 #else 963 FILE *fd; 964 #endif 965 int len; 966 long size; 967 968 #ifdef HAVE_STAT 969 struct stat info; 970 #endif 971 xmlChar *content; 972 973 if (filename == NULL) 974 return (NULL); 975 976 #ifdef HAVE_STAT 977 if (stat(filename, &info) < 0) 978 return (NULL); 979 #endif 980 981 #ifdef HAVE_STAT 982 if ((fd = open(filename, O_RDONLY)) < 0) 983 #else 984 if ((fd = fopen(filename, "rb")) == NULL) 985 #endif 986 { 987 return (NULL); 988 } 989 #ifdef HAVE_STAT 990 size = info.st_size; 991 #else 992 if (fseek(fd, 0, SEEK_END) || (size = ftell(fd)) == EOF || fseek(fd, 0, SEEK_SET)) { /* File operations denied? ok, just close and return failure */ 993 fclose(fd); 994 return (NULL); 995 } 996 #endif 997 content = (xmlChar*)xmlMallocAtomic(size + 10); 998 if (content == NULL) { 999 xmlCatalogErrMemory("allocating catalog data"); 1000 #ifdef HAVE_STAT 1001 close(fd); 1002 #else 1003 fclose(fd); 1004 #endif 1005 return (NULL); 1006 } 1007 #ifdef HAVE_STAT 1008 len = read(fd, content, size); 1009 close(fd); 1010 #else 1011 len = fread(content, 1, size, fd); 1012 fclose(fd); 1013 #endif 1014 if (len < 0) { 1015 xmlFree(content); 1016 return (NULL); 1017 } 1018 content[len] = 0; 1019 1020 return(content); 1021 } 1022 1023 /** 1024 * xmlCatalogNormalizePublic: 1025 * @pubID: the public ID string 1026 * 1027 * Normalizes the Public Identifier 1028 * 1029 * Implements 6.2. Public Identifier Normalization 1030 * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html 1031 * 1032 * Returns the new string or NULL, the string must be deallocated 1033 * by the caller. 1034 */ 1035 static xmlChar * 1036 xmlCatalogNormalizePublic(const xmlChar *pubID) 1037 { 1038 int ok = 1; 1039 int white; 1040 const xmlChar *p; 1041 xmlChar *ret; 1042 xmlChar *q; 1043 1044 if (pubID == NULL) 1045 return(NULL); 1046 1047 white = 1; 1048 for (p = pubID;*p != 0 && ok;p++) { 1049 if (!xmlIsBlank_ch(*p)) 1050 white = 0; 1051 else if (*p == 0x20 && !white) 1052 white = 1; 1053 else 1054 ok = 0; 1055 } 1056 if (ok && !white) /* is normalized */ 1057 return(NULL); 1058 1059 ret = xmlStrdup(pubID); 1060 q = ret; 1061 white = 0; 1062 for (p = pubID;*p != 0;p++) { 1063 if (xmlIsBlank_ch(*p)) { 1064 if (q != ret) 1065 white = 1; 1066 } else { 1067 if (white) { 1068 *(q++) = 0x20; 1069 white = 0; 1070 } 1071 *(q++) = *p; 1072 } 1073 } 1074 *q = 0; 1075 return(ret); 1076 } 1077 1078 /************************************************************************ 1079 * * 1080 * The XML Catalog parser * 1081 * * 1082 ************************************************************************/ 1083 1084 static xmlCatalogEntryPtr 1085 xmlParseXMLCatalogFile(xmlCatalogPrefer prefer, const xmlChar *filename); 1086 static void 1087 xmlParseXMLCatalogNodeList(xmlNodePtr cur, xmlCatalogPrefer prefer, 1088 xmlCatalogEntryPtr parent, xmlCatalogEntryPtr cgroup); 1089 static xmlChar * 1090 xmlCatalogListXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID, 1091 const xmlChar *sysID); 1092 static xmlChar * 1093 xmlCatalogListXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI); 1094 1095 1096 /** 1097 * xmlGetXMLCatalogEntryType: 1098 * @name: the name 1099 * 1100 * lookup the internal type associated to an XML catalog entry name 1101 * 1102 * Returns the type associated with that name 1103 */ 1104 static xmlCatalogEntryType 1105 xmlGetXMLCatalogEntryType(const xmlChar *name) { 1106 xmlCatalogEntryType type = XML_CATA_NONE; 1107 if (xmlStrEqual(name, (const xmlChar *) "system")) 1108 type = XML_CATA_SYSTEM; 1109 else if (xmlStrEqual(name, (const xmlChar *) "public")) 1110 type = XML_CATA_PUBLIC; 1111 else if (xmlStrEqual(name, (const xmlChar *) "rewriteSystem")) 1112 type = XML_CATA_REWRITE_SYSTEM; 1113 else if (xmlStrEqual(name, (const xmlChar *) "delegatePublic")) 1114 type = XML_CATA_DELEGATE_PUBLIC; 1115 else if (xmlStrEqual(name, (const xmlChar *) "delegateSystem")) 1116 type = XML_CATA_DELEGATE_SYSTEM; 1117 else if (xmlStrEqual(name, (const xmlChar *) "uri")) 1118 type = XML_CATA_URI; 1119 else if (xmlStrEqual(name, (const xmlChar *) "rewriteURI")) 1120 type = XML_CATA_REWRITE_URI; 1121 else if (xmlStrEqual(name, (const xmlChar *) "delegateURI")) 1122 type = XML_CATA_DELEGATE_URI; 1123 else if (xmlStrEqual(name, (const xmlChar *) "nextCatalog")) 1124 type = XML_CATA_NEXT_CATALOG; 1125 else if (xmlStrEqual(name, (const xmlChar *) "catalog")) 1126 type = XML_CATA_CATALOG; 1127 return(type); 1128 } 1129 1130 /** 1131 * xmlParseXMLCatalogOneNode: 1132 * @cur: the XML node 1133 * @type: the type of Catalog entry 1134 * @name: the name of the node 1135 * @attrName: the attribute holding the value 1136 * @uriAttrName: the attribute holding the URI-Reference 1137 * @prefer: the PUBLIC vs. SYSTEM current preference value 1138 * @cgroup: the group which includes this node 1139 * 1140 * Finishes the examination of an XML tree node of a catalog and build 1141 * a Catalog entry from it. 1142 * 1143 * Returns the new Catalog entry node or NULL in case of error. 1144 */ 1145 static xmlCatalogEntryPtr 1146 xmlParseXMLCatalogOneNode(xmlNodePtr cur, xmlCatalogEntryType type, 1147 const xmlChar *name, const xmlChar *attrName, 1148 const xmlChar *uriAttrName, xmlCatalogPrefer prefer, 1149 xmlCatalogEntryPtr cgroup) { 1150 int ok = 1; 1151 xmlChar *uriValue; 1152 xmlChar *nameValue = NULL; 1153 xmlChar *base = NULL; 1154 xmlChar *URL = NULL; 1155 xmlCatalogEntryPtr ret = NULL; 1156 1157 if (attrName != NULL) { 1158 nameValue = xmlGetProp(cur, attrName); 1159 if (nameValue == NULL) { 1160 xmlCatalogErr(ret, cur, XML_CATALOG_MISSING_ATTR, 1161 "%s entry lacks '%s'\n", name, attrName, NULL); 1162 ok = 0; 1163 } 1164 } 1165 uriValue = xmlGetProp(cur, uriAttrName); 1166 if (uriValue == NULL) { 1167 xmlCatalogErr(ret, cur, XML_CATALOG_MISSING_ATTR, 1168 "%s entry lacks '%s'\n", name, uriAttrName, NULL); 1169 ok = 0; 1170 } 1171 if (!ok) { 1172 if (nameValue != NULL) 1173 xmlFree(nameValue); 1174 if (uriValue != NULL) 1175 xmlFree(uriValue); 1176 return(NULL); 1177 } 1178 1179 base = xmlNodeGetBase(cur->doc, cur); 1180 URL = xmlBuildURI(uriValue, base); 1181 if (URL != NULL) { 1182 if (xmlDebugCatalogs > 1) { 1183 if (nameValue != NULL) 1184 xmlGenericError(xmlGenericErrorContext, 1185 "Found %s: '%s' '%s'\n", name, nameValue, URL); 1186 else 1187 xmlGenericError(xmlGenericErrorContext, 1188 "Found %s: '%s'\n", name, URL); 1189 } 1190 ret = xmlNewCatalogEntry(type, nameValue, uriValue, URL, prefer, cgroup); 1191 } else { 1192 xmlCatalogErr(ret, cur, XML_CATALOG_ENTRY_BROKEN, 1193 "%s entry '%s' broken ?: %s\n", name, uriAttrName, uriValue); 1194 } 1195 if (nameValue != NULL) 1196 xmlFree(nameValue); 1197 if (uriValue != NULL) 1198 xmlFree(uriValue); 1199 if (base != NULL) 1200 xmlFree(base); 1201 if (URL != NULL) 1202 xmlFree(URL); 1203 return(ret); 1204 } 1205 1206 /** 1207 * xmlParseXMLCatalogNode: 1208 * @cur: the XML node 1209 * @prefer: the PUBLIC vs. SYSTEM current preference value 1210 * @parent: the parent Catalog entry 1211 * @cgroup: the group which includes this node 1212 * 1213 * Examines an XML tree node of a catalog and build 1214 * a Catalog entry from it adding it to its parent. The examination can 1215 * be recursive. 1216 */ 1217 static void 1218 xmlParseXMLCatalogNode(xmlNodePtr cur, xmlCatalogPrefer prefer, 1219 xmlCatalogEntryPtr parent, xmlCatalogEntryPtr cgroup) 1220 { 1221 xmlChar *base = NULL; 1222 xmlCatalogEntryPtr entry = NULL; 1223 1224 if (cur == NULL) 1225 return; 1226 if (xmlStrEqual(cur->name, BAD_CAST "group")) { 1227 xmlChar *prop; 1228 xmlCatalogPrefer pref = XML_CATA_PREFER_NONE; 1229 1230 prop = xmlGetProp(cur, BAD_CAST "prefer"); 1231 if (prop != NULL) { 1232 if (xmlStrEqual(prop, BAD_CAST "system")) { 1233 prefer = XML_CATA_PREFER_SYSTEM; 1234 } else if (xmlStrEqual(prop, BAD_CAST "public")) { 1235 prefer = XML_CATA_PREFER_PUBLIC; 1236 } else { 1237 xmlCatalogErr(parent, cur, XML_CATALOG_PREFER_VALUE, 1238 "Invalid value for prefer: '%s'\n", 1239 prop, NULL, NULL); 1240 } 1241 xmlFree(prop); 1242 pref = prefer; 1243 } 1244 prop = xmlGetProp(cur, BAD_CAST "id"); 1245 base = xmlGetNsProp(cur, BAD_CAST "base", XML_XML_NAMESPACE); 1246 entry = xmlNewCatalogEntry(XML_CATA_GROUP, prop, base, NULL, pref, cgroup); 1247 xmlFree(prop); 1248 } else if (xmlStrEqual(cur->name, BAD_CAST "public")) { 1249 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_PUBLIC, 1250 BAD_CAST "public", BAD_CAST "publicId", BAD_CAST "uri", prefer, cgroup); 1251 } else if (xmlStrEqual(cur->name, BAD_CAST "system")) { 1252 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_SYSTEM, 1253 BAD_CAST "system", BAD_CAST "systemId", BAD_CAST "uri", prefer, cgroup); 1254 } else if (xmlStrEqual(cur->name, BAD_CAST "rewriteSystem")) { 1255 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_REWRITE_SYSTEM, 1256 BAD_CAST "rewriteSystem", BAD_CAST "systemIdStartString", 1257 BAD_CAST "rewritePrefix", prefer, cgroup); 1258 } else if (xmlStrEqual(cur->name, BAD_CAST "delegatePublic")) { 1259 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_PUBLIC, 1260 BAD_CAST "delegatePublic", BAD_CAST "publicIdStartString", 1261 BAD_CAST "catalog", prefer, cgroup); 1262 } else if (xmlStrEqual(cur->name, BAD_CAST "delegateSystem")) { 1263 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_SYSTEM, 1264 BAD_CAST "delegateSystem", BAD_CAST "systemIdStartString", 1265 BAD_CAST "catalog", prefer, cgroup); 1266 } else if (xmlStrEqual(cur->name, BAD_CAST "uri")) { 1267 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_URI, 1268 BAD_CAST "uri", BAD_CAST "name", 1269 BAD_CAST "uri", prefer, cgroup); 1270 } else if (xmlStrEqual(cur->name, BAD_CAST "rewriteURI")) { 1271 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_REWRITE_URI, 1272 BAD_CAST "rewriteURI", BAD_CAST "uriStartString", 1273 BAD_CAST "rewritePrefix", prefer, cgroup); 1274 } else if (xmlStrEqual(cur->name, BAD_CAST "delegateURI")) { 1275 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_URI, 1276 BAD_CAST "delegateURI", BAD_CAST "uriStartString", 1277 BAD_CAST "catalog", prefer, cgroup); 1278 } else if (xmlStrEqual(cur->name, BAD_CAST "nextCatalog")) { 1279 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_NEXT_CATALOG, 1280 BAD_CAST "nextCatalog", NULL, 1281 BAD_CAST "catalog", prefer, cgroup); 1282 } 1283 if (entry != NULL) { 1284 if (parent != NULL) { 1285 entry->parent = parent; 1286 if (parent->children == NULL) 1287 parent->children = entry; 1288 else { 1289 xmlCatalogEntryPtr prev; 1290 1291 prev = parent->children; 1292 while (prev->next != NULL) 1293 prev = prev->next; 1294 prev->next = entry; 1295 } 1296 } 1297 if (entry->type == XML_CATA_GROUP) { 1298 /* 1299 * Recurse to propagate prefer to the subtree 1300 * (xml:base handling is automated) 1301 */ 1302 xmlParseXMLCatalogNodeList(cur->children, prefer, parent, entry); 1303 } 1304 } 1305 if (base != NULL) 1306 xmlFree(base); 1307 } 1308 1309 /** 1310 * xmlParseXMLCatalogNodeList: 1311 * @cur: the XML node list of siblings 1312 * @prefer: the PUBLIC vs. SYSTEM current preference value 1313 * @parent: the parent Catalog entry 1314 * @cgroup: the group which includes this list 1315 * 1316 * Examines a list of XML sibling nodes of a catalog and build 1317 * a list of Catalog entry from it adding it to the parent. 1318 * The examination will recurse to examine node subtrees. 1319 */ 1320 static void 1321 xmlParseXMLCatalogNodeList(xmlNodePtr cur, xmlCatalogPrefer prefer, 1322 xmlCatalogEntryPtr parent, xmlCatalogEntryPtr cgroup) { 1323 while (cur != NULL) { 1324 if ((cur->ns != NULL) && (cur->ns->href != NULL) && 1325 (xmlStrEqual(cur->ns->href, XML_CATALOGS_NAMESPACE))) { 1326 xmlParseXMLCatalogNode(cur, prefer, parent, cgroup); 1327 } 1328 cur = cur->next; 1329 } 1330 /* TODO: sort the list according to REWRITE lengths and prefer value */ 1331 } 1332 1333 /** 1334 * xmlParseXMLCatalogFile: 1335 * @prefer: the PUBLIC vs. SYSTEM current preference value 1336 * @filename: the filename for the catalog 1337 * 1338 * Parses the catalog file to extract the XML tree and then analyze the 1339 * tree to build a list of Catalog entries corresponding to this catalog 1340 * 1341 * Returns the resulting Catalog entries list 1342 */ 1343 static xmlCatalogEntryPtr 1344 xmlParseXMLCatalogFile(xmlCatalogPrefer prefer, const xmlChar *filename) { 1345 xmlDocPtr doc; 1346 xmlNodePtr cur; 1347 xmlChar *prop; 1348 xmlCatalogEntryPtr parent = NULL; 1349 1350 if (filename == NULL) 1351 return(NULL); 1352 1353 doc = xmlParseCatalogFile((const char *) filename); 1354 if (doc == NULL) { 1355 if (xmlDebugCatalogs) 1356 xmlGenericError(xmlGenericErrorContext, 1357 "Failed to parse catalog %s\n", filename); 1358 return(NULL); 1359 } 1360 1361 if (xmlDebugCatalogs) 1362 xmlGenericError(xmlGenericErrorContext, 1363 "%d Parsing catalog %s\n", xmlGetThreadId(), filename); 1364 1365 cur = xmlDocGetRootElement(doc); 1366 if ((cur != NULL) && (xmlStrEqual(cur->name, BAD_CAST "catalog")) && 1367 (cur->ns != NULL) && (cur->ns->href != NULL) && 1368 (xmlStrEqual(cur->ns->href, XML_CATALOGS_NAMESPACE))) { 1369 1370 parent = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL, 1371 (const xmlChar *)filename, NULL, prefer, NULL); 1372 if (parent == NULL) { 1373 xmlFreeDoc(doc); 1374 return(NULL); 1375 } 1376 1377 prop = xmlGetProp(cur, BAD_CAST "prefer"); 1378 if (prop != NULL) { 1379 if (xmlStrEqual(prop, BAD_CAST "system")) { 1380 prefer = XML_CATA_PREFER_SYSTEM; 1381 } else if (xmlStrEqual(prop, BAD_CAST "public")) { 1382 prefer = XML_CATA_PREFER_PUBLIC; 1383 } else { 1384 xmlCatalogErr(NULL, cur, XML_CATALOG_PREFER_VALUE, 1385 "Invalid value for prefer: '%s'\n", 1386 prop, NULL, NULL); 1387 } 1388 xmlFree(prop); 1389 } 1390 cur = cur->children; 1391 xmlParseXMLCatalogNodeList(cur, prefer, parent, NULL); 1392 } else { 1393 xmlCatalogErr(NULL, (xmlNodePtr) doc, XML_CATALOG_NOT_CATALOG, 1394 "File %s is not an XML Catalog\n", 1395 filename, NULL, NULL); 1396 xmlFreeDoc(doc); 1397 return(NULL); 1398 } 1399 xmlFreeDoc(doc); 1400 return(parent); 1401 } 1402 1403 /** 1404 * xmlFetchXMLCatalogFile: 1405 * @catal: an existing but incomplete catalog entry 1406 * 1407 * Fetch and parse the subcatalog referenced by an entry 1408 * 1409 * Returns 0 in case of success, -1 otherwise 1410 */ 1411 static int 1412 xmlFetchXMLCatalogFile(xmlCatalogEntryPtr catal) { 1413 xmlCatalogEntryPtr doc; 1414 1415 if (catal == NULL) 1416 return(-1); 1417 if (catal->URL == NULL) 1418 return(-1); 1419 1420 /* 1421 * lock the whole catalog for modification 1422 */ 1423 xmlRMutexLock(xmlCatalogMutex); 1424 if (catal->children != NULL) { 1425 /* Okay someone else did it in the meantime */ 1426 xmlRMutexUnlock(xmlCatalogMutex); 1427 return(0); 1428 } 1429 1430 if (xmlCatalogXMLFiles != NULL) { 1431 doc = (xmlCatalogEntryPtr) 1432 xmlHashLookup(xmlCatalogXMLFiles, catal->URL); 1433 if (doc != NULL) { 1434 if (xmlDebugCatalogs) 1435 xmlGenericError(xmlGenericErrorContext, 1436 "Found %s in file hash\n", catal->URL); 1437 1438 if (catal->type == XML_CATA_CATALOG) 1439 catal->children = doc->children; 1440 else 1441 catal->children = doc; 1442 catal->dealloc = 0; 1443 xmlRMutexUnlock(xmlCatalogMutex); 1444 return(0); 1445 } 1446 if (xmlDebugCatalogs) 1447 xmlGenericError(xmlGenericErrorContext, 1448 "%s not found in file hash\n", catal->URL); 1449 } 1450 1451 /* 1452 * Fetch and parse. Note that xmlParseXMLCatalogFile does not 1453 * use the existing catalog, there is no recursion allowed at 1454 * that level. 1455 */ 1456 doc = xmlParseXMLCatalogFile(catal->prefer, catal->URL); 1457 if (doc == NULL) { 1458 catal->type = XML_CATA_BROKEN_CATALOG; 1459 xmlRMutexUnlock(xmlCatalogMutex); 1460 return(-1); 1461 } 1462 1463 if (catal->type == XML_CATA_CATALOG) 1464 catal->children = doc->children; 1465 else 1466 catal->children = doc; 1467 1468 doc->dealloc = 1; 1469 1470 if (xmlCatalogXMLFiles == NULL) 1471 xmlCatalogXMLFiles = xmlHashCreate(10); 1472 if (xmlCatalogXMLFiles != NULL) { 1473 if (xmlDebugCatalogs) 1474 xmlGenericError(xmlGenericErrorContext, 1475 "%s added to file hash\n", catal->URL); 1476 xmlHashAddEntry(xmlCatalogXMLFiles, catal->URL, doc); 1477 } 1478 xmlRMutexUnlock(xmlCatalogMutex); 1479 return(0); 1480 } 1481 1482 /************************************************************************ 1483 * * 1484 * XML Catalog handling * 1485 * * 1486 ************************************************************************/ 1487 1488 /** 1489 * xmlAddXMLCatalog: 1490 * @catal: top of an XML catalog 1491 * @type: the type of record to add to the catalog 1492 * @orig: the system, public or prefix to match (or NULL) 1493 * @replace: the replacement value for the match 1494 * 1495 * Add an entry in the XML catalog, it may overwrite existing but 1496 * different entries. 1497 * 1498 * Returns 0 if successful, -1 otherwise 1499 */ 1500 static int 1501 xmlAddXMLCatalog(xmlCatalogEntryPtr catal, const xmlChar *type, 1502 const xmlChar *orig, const xmlChar *replace) { 1503 xmlCatalogEntryPtr cur; 1504 xmlCatalogEntryType typ; 1505 int doregister = 0; 1506 1507 if ((catal == NULL) || 1508 ((catal->type != XML_CATA_CATALOG) && 1509 (catal->type != XML_CATA_BROKEN_CATALOG))) 1510 return(-1); 1511 if (catal->children == NULL) { 1512 xmlFetchXMLCatalogFile(catal); 1513 } 1514 if (catal->children == NULL) 1515 doregister = 1; 1516 1517 typ = xmlGetXMLCatalogEntryType(type); 1518 if (typ == XML_CATA_NONE) { 1519 if (xmlDebugCatalogs) 1520 xmlGenericError(xmlGenericErrorContext, 1521 "Failed to add unknown element %s to catalog\n", type); 1522 return(-1); 1523 } 1524 1525 cur = catal->children; 1526 /* 1527 * Might be a simple "update in place" 1528 */ 1529 if (cur != NULL) { 1530 while (cur != NULL) { 1531 if ((orig != NULL) && (cur->type == typ) && 1532 (xmlStrEqual(orig, cur->name))) { 1533 if (xmlDebugCatalogs) 1534 xmlGenericError(xmlGenericErrorContext, 1535 "Updating element %s to catalog\n", type); 1536 if (cur->value != NULL) 1537 xmlFree(cur->value); 1538 if (cur->URL != NULL) 1539 xmlFree(cur->URL); 1540 cur->value = xmlStrdup(replace); 1541 cur->URL = xmlStrdup(replace); 1542 return(0); 1543 } 1544 if (cur->next == NULL) 1545 break; 1546 cur = cur->next; 1547 } 1548 } 1549 if (xmlDebugCatalogs) 1550 xmlGenericError(xmlGenericErrorContext, 1551 "Adding element %s to catalog\n", type); 1552 if (cur == NULL) 1553 catal->children = xmlNewCatalogEntry(typ, orig, replace, 1554 NULL, catal->prefer, NULL); 1555 else 1556 cur->next = xmlNewCatalogEntry(typ, orig, replace, 1557 NULL, catal->prefer, NULL); 1558 if (doregister) { 1559 catal->type = XML_CATA_CATALOG; 1560 cur = (xmlCatalogEntryPtr)xmlHashLookup(xmlCatalogXMLFiles, catal->URL); 1561 if (cur != NULL) 1562 cur->children = catal->children; 1563 } 1564 1565 return(0); 1566 } 1567 1568 /** 1569 * xmlDelXMLCatalog: 1570 * @catal: top of an XML catalog 1571 * @value: the value to remove from the catalog 1572 * 1573 * Remove entries in the XML catalog where the value or the URI 1574 * is equal to @value 1575 * 1576 * Returns the number of entries removed if successful, -1 otherwise 1577 */ 1578 static int 1579 xmlDelXMLCatalog(xmlCatalogEntryPtr catal, const xmlChar *value) { 1580 xmlCatalogEntryPtr cur; 1581 int ret = 0; 1582 1583 if ((catal == NULL) || 1584 ((catal->type != XML_CATA_CATALOG) && 1585 (catal->type != XML_CATA_BROKEN_CATALOG))) 1586 return(-1); 1587 if (value == NULL) 1588 return(-1); 1589 if (catal->children == NULL) { 1590 xmlFetchXMLCatalogFile(catal); 1591 } 1592 1593 /* 1594 * Scan the children 1595 */ 1596 cur = catal->children; 1597 while (cur != NULL) { 1598 if (((cur->name != NULL) && (xmlStrEqual(value, cur->name))) || 1599 (xmlStrEqual(value, cur->value))) { 1600 if (xmlDebugCatalogs) { 1601 if (cur->name != NULL) 1602 xmlGenericError(xmlGenericErrorContext, 1603 "Removing element %s from catalog\n", cur->name); 1604 else 1605 xmlGenericError(xmlGenericErrorContext, 1606 "Removing element %s from catalog\n", cur->value); 1607 } 1608 cur->type = XML_CATA_REMOVED; 1609 } 1610 cur = cur->next; 1611 } 1612 return(ret); 1613 } 1614 1615 /** 1616 * xmlCatalogXMLResolve: 1617 * @catal: a catalog list 1618 * @pubID: the public ID string 1619 * @sysID: the system ID string 1620 * 1621 * Do a complete resolution lookup of an External Identifier for a 1622 * list of catalog entries. 1623 * 1624 * Implements (or tries to) 7.1. External Identifier Resolution 1625 * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html 1626 * 1627 * Returns the URI of the resource or NULL if not found 1628 */ 1629 static xmlChar * 1630 xmlCatalogXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID, 1631 const xmlChar *sysID) { 1632 xmlChar *ret = NULL; 1633 xmlCatalogEntryPtr cur; 1634 int haveDelegate = 0; 1635 int haveNext = 0; 1636 1637 /* 1638 * protection against loops 1639 */ 1640 if (catal->depth > MAX_CATAL_DEPTH) { 1641 xmlCatalogErr(catal, NULL, XML_CATALOG_RECURSION, 1642 "Detected recursion in catalog %s\n", 1643 catal->name, NULL, NULL); 1644 return(NULL); 1645 } 1646 catal->depth++; 1647 1648 /* 1649 * First tries steps 2/ 3/ 4/ if a system ID is provided. 1650 */ 1651 if (sysID != NULL) { 1652 xmlCatalogEntryPtr rewrite = NULL; 1653 int lenrewrite = 0, len; 1654 cur = catal; 1655 haveDelegate = 0; 1656 while (cur != NULL) { 1657 switch (cur->type) { 1658 case XML_CATA_SYSTEM: 1659 if (xmlStrEqual(sysID, cur->name)) { 1660 if (xmlDebugCatalogs) 1661 xmlGenericError(xmlGenericErrorContext, 1662 "Found system match %s, using %s\n", 1663 cur->name, cur->URL); 1664 catal->depth--; 1665 return(xmlStrdup(cur->URL)); 1666 } 1667 break; 1668 case XML_CATA_REWRITE_SYSTEM: 1669 len = xmlStrlen(cur->name); 1670 if ((len > lenrewrite) && 1671 (!xmlStrncmp(sysID, cur->name, len))) { 1672 lenrewrite = len; 1673 rewrite = cur; 1674 } 1675 break; 1676 case XML_CATA_DELEGATE_SYSTEM: 1677 if (!xmlStrncmp(sysID, cur->name, xmlStrlen(cur->name))) 1678 haveDelegate++; 1679 break; 1680 case XML_CATA_NEXT_CATALOG: 1681 haveNext++; 1682 break; 1683 default: 1684 break; 1685 } 1686 cur = cur->next; 1687 } 1688 if (rewrite != NULL) { 1689 if (xmlDebugCatalogs) 1690 xmlGenericError(xmlGenericErrorContext, 1691 "Using rewriting rule %s\n", rewrite->name); 1692 ret = xmlStrdup(rewrite->URL); 1693 if (ret != NULL) 1694 ret = xmlStrcat(ret, &sysID[lenrewrite]); 1695 catal->depth--; 1696 return(ret); 1697 } 1698 if (haveDelegate) { 1699 const xmlChar *delegates[MAX_DELEGATE]; 1700 int nbList = 0, i; 1701 1702 /* 1703 * Assume the entries have been sorted by decreasing substring 1704 * matches when the list was produced. 1705 */ 1706 cur = catal; 1707 while (cur != NULL) { 1708 if ((cur->type == XML_CATA_DELEGATE_SYSTEM) && 1709 (!xmlStrncmp(sysID, cur->name, xmlStrlen(cur->name)))) { 1710 for (i = 0;i < nbList;i++) 1711 if (xmlStrEqual(cur->URL, delegates[i])) 1712 break; 1713 if (i < nbList) { 1714 cur = cur->next; 1715 continue; 1716 } 1717 if (nbList < MAX_DELEGATE) 1718 delegates[nbList++] = cur->URL; 1719 1720 if (cur->children == NULL) { 1721 xmlFetchXMLCatalogFile(cur); 1722 } 1723 if (cur->children != NULL) { 1724 if (xmlDebugCatalogs) 1725 xmlGenericError(xmlGenericErrorContext, 1726 "Trying system delegate %s\n", cur->URL); 1727 ret = xmlCatalogListXMLResolve( 1728 cur->children, NULL, sysID); 1729 if (ret != NULL) { 1730 catal->depth--; 1731 return(ret); 1732 } 1733 } 1734 } 1735 cur = cur->next; 1736 } 1737 /* 1738 * Apply the cut algorithm explained in 4/ 1739 */ 1740 catal->depth--; 1741 return(XML_CATAL_BREAK); 1742 } 1743 } 1744 /* 1745 * Then tries 5/ 6/ if a public ID is provided 1746 */ 1747 if (pubID != NULL) { 1748 cur = catal; 1749 haveDelegate = 0; 1750 while (cur != NULL) { 1751 switch (cur->type) { 1752 case XML_CATA_PUBLIC: 1753 if (xmlStrEqual(pubID, cur->name)) { 1754 if (xmlDebugCatalogs) 1755 xmlGenericError(xmlGenericErrorContext, 1756 "Found public match %s\n", cur->name); 1757 catal->depth--; 1758 return(xmlStrdup(cur->URL)); 1759 } 1760 break; 1761 case XML_CATA_DELEGATE_PUBLIC: 1762 if (!xmlStrncmp(pubID, cur->name, xmlStrlen(cur->name)) && 1763 (cur->prefer == XML_CATA_PREFER_PUBLIC)) 1764 haveDelegate++; 1765 break; 1766 case XML_CATA_NEXT_CATALOG: 1767 if (sysID == NULL) 1768 haveNext++; 1769 break; 1770 default: 1771 break; 1772 } 1773 cur = cur->next; 1774 } 1775 if (haveDelegate) { 1776 const xmlChar *delegates[MAX_DELEGATE]; 1777 int nbList = 0, i; 1778 1779 /* 1780 * Assume the entries have been sorted by decreasing substring 1781 * matches when the list was produced. 1782 */ 1783 cur = catal; 1784 while (cur != NULL) { 1785 if ((cur->type == XML_CATA_DELEGATE_PUBLIC) && 1786 (cur->prefer == XML_CATA_PREFER_PUBLIC) && 1787 (!xmlStrncmp(pubID, cur->name, xmlStrlen(cur->name)))) { 1788 1789 for (i = 0;i < nbList;i++) 1790 if (xmlStrEqual(cur->URL, delegates[i])) 1791 break; 1792 if (i < nbList) { 1793 cur = cur->next; 1794 continue; 1795 } 1796 if (nbList < MAX_DELEGATE) 1797 delegates[nbList++] = cur->URL; 1798 1799 if (cur->children == NULL) { 1800 xmlFetchXMLCatalogFile(cur); 1801 } 1802 if (cur->children != NULL) { 1803 if (xmlDebugCatalogs) 1804 xmlGenericError(xmlGenericErrorContext, 1805 "Trying public delegate %s\n", cur->URL); 1806 ret = xmlCatalogListXMLResolve( 1807 cur->children, pubID, NULL); 1808 if (ret != NULL) { 1809 catal->depth--; 1810 return(ret); 1811 } 1812 } 1813 } 1814 cur = cur->next; 1815 } 1816 /* 1817 * Apply the cut algorithm explained in 4/ 1818 */ 1819 catal->depth--; 1820 return(XML_CATAL_BREAK); 1821 } 1822 } 1823 if (haveNext) { 1824 cur = catal; 1825 while (cur != NULL) { 1826 if (cur->type == XML_CATA_NEXT_CATALOG) { 1827 if (cur->children == NULL) { 1828 xmlFetchXMLCatalogFile(cur); 1829 } 1830 if (cur->children != NULL) { 1831 ret = xmlCatalogListXMLResolve(cur->children, pubID, sysID); 1832 if (ret != NULL) { 1833 catal->depth--; 1834 return(ret); 1835 } else if (catal->depth > MAX_CATAL_DEPTH) { 1836 return(NULL); 1837 } 1838 } 1839 } 1840 cur = cur->next; 1841 } 1842 } 1843 1844 catal->depth--; 1845 return(NULL); 1846 } 1847 1848 /** 1849 * xmlCatalogXMLResolveURI: 1850 * @catal: a catalog list 1851 * @URI: the URI 1852 * @sysID: the system ID string 1853 * 1854 * Do a complete resolution lookup of an External Identifier for a 1855 * list of catalog entries. 1856 * 1857 * Implements (or tries to) 7.2.2. URI Resolution 1858 * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html 1859 * 1860 * Returns the URI of the resource or NULL if not found 1861 */ 1862 static xmlChar * 1863 xmlCatalogXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI) { 1864 xmlChar *ret = NULL; 1865 xmlCatalogEntryPtr cur; 1866 int haveDelegate = 0; 1867 int haveNext = 0; 1868 xmlCatalogEntryPtr rewrite = NULL; 1869 int lenrewrite = 0, len; 1870 1871 if (catal == NULL) 1872 return(NULL); 1873 1874 if (URI == NULL) 1875 return(NULL); 1876 1877 if (catal->depth > MAX_CATAL_DEPTH) { 1878 xmlCatalogErr(catal, NULL, XML_CATALOG_RECURSION, 1879 "Detected recursion in catalog %s\n", 1880 catal->name, NULL, NULL); 1881 return(NULL); 1882 } 1883 1884 /* 1885 * First tries steps 2/ 3/ 4/ if a system ID is provided. 1886 */ 1887 cur = catal; 1888 haveDelegate = 0; 1889 while (cur != NULL) { 1890 switch (cur->type) { 1891 case XML_CATA_URI: 1892 if (xmlStrEqual(URI, cur->name)) { 1893 if (xmlDebugCatalogs) 1894 xmlGenericError(xmlGenericErrorContext, 1895 "Found URI match %s\n", cur->name); 1896 return(xmlStrdup(cur->URL)); 1897 } 1898 break; 1899 case XML_CATA_REWRITE_URI: 1900 len = xmlStrlen(cur->name); 1901 if ((len > lenrewrite) && 1902 (!xmlStrncmp(URI, cur->name, len))) { 1903 lenrewrite = len; 1904 rewrite = cur; 1905 } 1906 break; 1907 case XML_CATA_DELEGATE_URI: 1908 if (!xmlStrncmp(URI, cur->name, xmlStrlen(cur->name))) 1909 haveDelegate++; 1910 break; 1911 case XML_CATA_NEXT_CATALOG: 1912 haveNext++; 1913 break; 1914 default: 1915 break; 1916 } 1917 cur = cur->next; 1918 } 1919 if (rewrite != NULL) { 1920 if (xmlDebugCatalogs) 1921 xmlGenericError(xmlGenericErrorContext, 1922 "Using rewriting rule %s\n", rewrite->name); 1923 ret = xmlStrdup(rewrite->URL); 1924 if (ret != NULL) 1925 ret = xmlStrcat(ret, &URI[lenrewrite]); 1926 return(ret); 1927 } 1928 if (haveDelegate) { 1929 const xmlChar *delegates[MAX_DELEGATE]; 1930 int nbList = 0, i; 1931 1932 /* 1933 * Assume the entries have been sorted by decreasing substring 1934 * matches when the list was produced. 1935 */ 1936 cur = catal; 1937 while (cur != NULL) { 1938 if (((cur->type == XML_CATA_DELEGATE_SYSTEM) || 1939 (cur->type == XML_CATA_DELEGATE_URI)) && 1940 (!xmlStrncmp(URI, cur->name, xmlStrlen(cur->name)))) { 1941 for (i = 0;i < nbList;i++) 1942 if (xmlStrEqual(cur->URL, delegates[i])) 1943 break; 1944 if (i < nbList) { 1945 cur = cur->next; 1946 continue; 1947 } 1948 if (nbList < MAX_DELEGATE) 1949 delegates[nbList++] = cur->URL; 1950 1951 if (cur->children == NULL) { 1952 xmlFetchXMLCatalogFile(cur); 1953 } 1954 if (cur->children != NULL) { 1955 if (xmlDebugCatalogs) 1956 xmlGenericError(xmlGenericErrorContext, 1957 "Trying URI delegate %s\n", cur->URL); 1958 ret = xmlCatalogListXMLResolveURI( 1959 cur->children, URI); 1960 if (ret != NULL) 1961 return(ret); 1962 } 1963 } 1964 cur = cur->next; 1965 } 1966 /* 1967 * Apply the cut algorithm explained in 4/ 1968 */ 1969 return(XML_CATAL_BREAK); 1970 } 1971 if (haveNext) { 1972 cur = catal; 1973 while (cur != NULL) { 1974 if (cur->type == XML_CATA_NEXT_CATALOG) { 1975 if (cur->children == NULL) { 1976 xmlFetchXMLCatalogFile(cur); 1977 } 1978 if (cur->children != NULL) { 1979 ret = xmlCatalogListXMLResolveURI(cur->children, URI); 1980 if (ret != NULL) 1981 return(ret); 1982 } 1983 } 1984 cur = cur->next; 1985 } 1986 } 1987 1988 return(NULL); 1989 } 1990 1991 /** 1992 * xmlCatalogListXMLResolve: 1993 * @catal: a catalog list 1994 * @pubID: the public ID string 1995 * @sysID: the system ID string 1996 * 1997 * Do a complete resolution lookup of an External Identifier for a 1998 * list of catalogs 1999 * 2000 * Implements (or tries to) 7.1. External Identifier Resolution 2001 * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html 2002 * 2003 * Returns the URI of the resource or NULL if not found 2004 */ 2005 static xmlChar * 2006 xmlCatalogListXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID, 2007 const xmlChar *sysID) { 2008 xmlChar *ret = NULL; 2009 xmlChar *urnID = NULL; 2010 xmlChar *normid; 2011 2012 if (catal == NULL) 2013 return(NULL); 2014 if ((pubID == NULL) && (sysID == NULL)) 2015 return(NULL); 2016 2017 normid = xmlCatalogNormalizePublic(pubID); 2018 if (normid != NULL) 2019 pubID = (*normid != 0 ? normid : NULL); 2020 2021 if (!xmlStrncmp(pubID, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) { 2022 urnID = xmlCatalogUnWrapURN(pubID); 2023 if (xmlDebugCatalogs) { 2024 if (urnID == NULL) 2025 xmlGenericError(xmlGenericErrorContext, 2026 "Public URN ID %s expanded to NULL\n", pubID); 2027 else 2028 xmlGenericError(xmlGenericErrorContext, 2029 "Public URN ID expanded to %s\n", urnID); 2030 } 2031 ret = xmlCatalogListXMLResolve(catal, urnID, sysID); 2032 if (urnID != NULL) 2033 xmlFree(urnID); 2034 if (normid != NULL) 2035 xmlFree(normid); 2036 return(ret); 2037 } 2038 if (!xmlStrncmp(sysID, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) { 2039 urnID = xmlCatalogUnWrapURN(sysID); 2040 if (xmlDebugCatalogs) { 2041 if (urnID == NULL) 2042 xmlGenericError(xmlGenericErrorContext, 2043 "System URN ID %s expanded to NULL\n", sysID); 2044 else 2045 xmlGenericError(xmlGenericErrorContext, 2046 "System URN ID expanded to %s\n", urnID); 2047 } 2048 if (pubID == NULL) 2049 ret = xmlCatalogListXMLResolve(catal, urnID, NULL); 2050 else if (xmlStrEqual(pubID, urnID)) 2051 ret = xmlCatalogListXMLResolve(catal, pubID, NULL); 2052 else { 2053 ret = xmlCatalogListXMLResolve(catal, pubID, urnID); 2054 } 2055 if (urnID != NULL) 2056 xmlFree(urnID); 2057 if (normid != NULL) 2058 xmlFree(normid); 2059 return(ret); 2060 } 2061 while (catal != NULL) { 2062 if (catal->type == XML_CATA_CATALOG) { 2063 if (catal->children == NULL) { 2064 xmlFetchXMLCatalogFile(catal); 2065 } 2066 if (catal->children != NULL) { 2067 ret = xmlCatalogXMLResolve(catal->children, pubID, sysID); 2068 if (ret != NULL) { 2069 break; 2070 } else if ((catal->children != NULL) && 2071 (catal->children->depth > MAX_CATAL_DEPTH)) { 2072 ret = NULL; 2073 break; 2074 } 2075 } 2076 } 2077 catal = catal->next; 2078 } 2079 if (normid != NULL) 2080 xmlFree(normid); 2081 return(ret); 2082 } 2083 2084 /** 2085 * xmlCatalogListXMLResolveURI: 2086 * @catal: a catalog list 2087 * @URI: the URI 2088 * 2089 * Do a complete resolution lookup of an URI for a list of catalogs 2090 * 2091 * Implements (or tries to) 7.2. URI Resolution 2092 * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html 2093 * 2094 * Returns the URI of the resource or NULL if not found 2095 */ 2096 static xmlChar * 2097 xmlCatalogListXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI) { 2098 xmlChar *ret = NULL; 2099 xmlChar *urnID = NULL; 2100 2101 if (catal == NULL) 2102 return(NULL); 2103 if (URI == NULL) 2104 return(NULL); 2105 2106 if (!xmlStrncmp(URI, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) { 2107 urnID = xmlCatalogUnWrapURN(URI); 2108 if (xmlDebugCatalogs) { 2109 if (urnID == NULL) 2110 xmlGenericError(xmlGenericErrorContext, 2111 "URN ID %s expanded to NULL\n", URI); 2112 else 2113 xmlGenericError(xmlGenericErrorContext, 2114 "URN ID expanded to %s\n", urnID); 2115 } 2116 ret = xmlCatalogListXMLResolve(catal, urnID, NULL); 2117 if (urnID != NULL) 2118 xmlFree(urnID); 2119 return(ret); 2120 } 2121 while (catal != NULL) { 2122 if (catal->type == XML_CATA_CATALOG) { 2123 if (catal->children == NULL) { 2124 xmlFetchXMLCatalogFile(catal); 2125 } 2126 if (catal->children != NULL) { 2127 ret = xmlCatalogXMLResolveURI(catal->children, URI); 2128 if (ret != NULL) 2129 return(ret); 2130 } 2131 } 2132 catal = catal->next; 2133 } 2134 return(ret); 2135 } 2136 2137 /************************************************************************ 2138 * * 2139 * The SGML Catalog parser * 2140 * * 2141 ************************************************************************/ 2142 2143 2144 #define RAW *cur 2145 #define NEXT cur++; 2146 #define SKIP(x) cur += x; 2147 2148 #define SKIP_BLANKS while (IS_BLANK_CH(*cur)) NEXT; 2149 2150 /** 2151 * xmlParseSGMLCatalogComment: 2152 * @cur: the current character 2153 * 2154 * Skip a comment in an SGML catalog 2155 * 2156 * Returns new current character 2157 */ 2158 static const xmlChar * 2159 xmlParseSGMLCatalogComment(const xmlChar *cur) { 2160 if ((cur[0] != '-') || (cur[1] != '-')) 2161 return(cur); 2162 SKIP(2); 2163 while ((cur[0] != 0) && ((cur[0] != '-') || ((cur[1] != '-')))) 2164 NEXT; 2165 if (cur[0] == 0) { 2166 return(NULL); 2167 } 2168 return(cur + 2); 2169 } 2170 2171 /** 2172 * xmlParseSGMLCatalogPubid: 2173 * @cur: the current character 2174 * @id: the return location 2175 * 2176 * Parse an SGML catalog ID 2177 * 2178 * Returns new current character and store the value in @id 2179 */ 2180 static const xmlChar * 2181 xmlParseSGMLCatalogPubid(const xmlChar *cur, xmlChar **id) { 2182 xmlChar *buf = NULL, *tmp; 2183 int len = 0; 2184 int size = 50; 2185 xmlChar stop; 2186 int count = 0; 2187 2188 *id = NULL; 2189 2190 if (RAW == '"') { 2191 NEXT; 2192 stop = '"'; 2193 } else if (RAW == '\'') { 2194 NEXT; 2195 stop = '\''; 2196 } else { 2197 stop = ' '; 2198 } 2199 buf = (xmlChar *) xmlMallocAtomic(size * sizeof(xmlChar)); 2200 if (buf == NULL) { 2201 xmlCatalogErrMemory("allocating public ID"); 2202 return(NULL); 2203 } 2204 while (IS_PUBIDCHAR_CH(*cur) || (*cur == '?')) { 2205 if ((*cur == stop) && (stop != ' ')) 2206 break; 2207 if ((stop == ' ') && (IS_BLANK_CH(*cur))) 2208 break; 2209 if (len + 1 >= size) { 2210 size *= 2; 2211 tmp = (xmlChar *) xmlRealloc(buf, size * sizeof(xmlChar)); 2212 if (tmp == NULL) { 2213 xmlCatalogErrMemory("allocating public ID"); 2214 xmlFree(buf); 2215 return(NULL); 2216 } 2217 buf = tmp; 2218 } 2219 buf[len++] = *cur; 2220 count++; 2221 NEXT; 2222 } 2223 buf[len] = 0; 2224 if (stop == ' ') { 2225 if (!IS_BLANK_CH(*cur)) { 2226 xmlFree(buf); 2227 return(NULL); 2228 } 2229 } else { 2230 if (*cur != stop) { 2231 xmlFree(buf); 2232 return(NULL); 2233 } 2234 NEXT; 2235 } 2236 *id = buf; 2237 return(cur); 2238 } 2239 2240 /** 2241 * xmlParseSGMLCatalogName: 2242 * @cur: the current character 2243 * @name: the return location 2244 * 2245 * Parse an SGML catalog name 2246 * 2247 * Returns new current character and store the value in @name 2248 */ 2249 static const xmlChar * 2250 xmlParseSGMLCatalogName(const xmlChar *cur, xmlChar **name) { 2251 xmlChar buf[XML_MAX_NAMELEN + 5]; 2252 int len = 0; 2253 int c; 2254 2255 *name = NULL; 2256 2257 /* 2258 * Handler for more complex cases 2259 */ 2260 c = *cur; 2261 if ((!IS_LETTER(c) && (c != '_') && (c != ':'))) { 2262 return(NULL); 2263 } 2264 2265 while (((IS_LETTER(c)) || (IS_DIGIT(c)) || 2266 (c == '.') || (c == '-') || 2267 (c == '_') || (c == ':'))) { 2268 buf[len++] = c; 2269 cur++; 2270 c = *cur; 2271 if (len >= XML_MAX_NAMELEN) 2272 return(NULL); 2273 } 2274 *name = xmlStrndup(buf, len); 2275 return(cur); 2276 } 2277 2278 /** 2279 * xmlGetSGMLCatalogEntryType: 2280 * @name: the entry name 2281 * 2282 * Get the Catalog entry type for a given SGML Catalog name 2283 * 2284 * Returns Catalog entry type 2285 */ 2286 static xmlCatalogEntryType 2287 xmlGetSGMLCatalogEntryType(const xmlChar *name) { 2288 xmlCatalogEntryType type = XML_CATA_NONE; 2289 if (xmlStrEqual(name, (const xmlChar *) "SYSTEM")) 2290 type = SGML_CATA_SYSTEM; 2291 else if (xmlStrEqual(name, (const xmlChar *) "PUBLIC")) 2292 type = SGML_CATA_PUBLIC; 2293 else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE")) 2294 type = SGML_CATA_DELEGATE; 2295 else if (xmlStrEqual(name, (const xmlChar *) "ENTITY")) 2296 type = SGML_CATA_ENTITY; 2297 else if (xmlStrEqual(name, (const xmlChar *) "DOCTYPE")) 2298 type = SGML_CATA_DOCTYPE; 2299 else if (xmlStrEqual(name, (const xmlChar *) "LINKTYPE")) 2300 type = SGML_CATA_LINKTYPE; 2301 else if (xmlStrEqual(name, (const xmlChar *) "NOTATION")) 2302 type = SGML_CATA_NOTATION; 2303 else if (xmlStrEqual(name, (const xmlChar *) "SGMLDECL")) 2304 type = SGML_CATA_SGMLDECL; 2305 else if (xmlStrEqual(name, (const xmlChar *) "DOCUMENT")) 2306 type = SGML_CATA_DOCUMENT; 2307 else if (xmlStrEqual(name, (const xmlChar *) "CATALOG")) 2308 type = SGML_CATA_CATALOG; 2309 else if (xmlStrEqual(name, (const xmlChar *) "BASE")) 2310 type = SGML_CATA_BASE; 2311 return(type); 2312 } 2313 2314 /** 2315 * xmlParseSGMLCatalog: 2316 * @catal: the SGML Catalog 2317 * @value: the content of the SGML Catalog serialization 2318 * @file: the filepath for the catalog 2319 * @super: should this be handled as a Super Catalog in which case 2320 * parsing is not recursive 2321 * 2322 * Parse an SGML catalog content and fill up the @catal hash table with 2323 * the new entries found. 2324 * 2325 * Returns 0 in case of success, -1 in case of error. 2326 */ 2327 static int 2328 xmlParseSGMLCatalog(xmlCatalogPtr catal, const xmlChar *value, 2329 const char *file, int super) { 2330 const xmlChar *cur = value; 2331 xmlChar *base = NULL; 2332 int res; 2333 2334 if ((cur == NULL) || (file == NULL)) 2335 return(-1); 2336 base = xmlStrdup((const xmlChar *) file); 2337 2338 while ((cur != NULL) && (cur[0] != 0)) { 2339 SKIP_BLANKS; 2340 if (cur[0] == 0) 2341 break; 2342 if ((cur[0] == '-') && (cur[1] == '-')) { 2343 cur = xmlParseSGMLCatalogComment(cur); 2344 if (cur == NULL) { 2345 /* error */ 2346 break; 2347 } 2348 } else { 2349 xmlChar *sysid = NULL; 2350 xmlChar *name = NULL; 2351 xmlCatalogEntryType type = XML_CATA_NONE; 2352 2353 cur = xmlParseSGMLCatalogName(cur, &name); 2354 if (name == NULL) { 2355 /* error */ 2356 break; 2357 } 2358 if (!IS_BLANK_CH(*cur)) { 2359 /* error */ 2360 break; 2361 } 2362 SKIP_BLANKS; 2363 if (xmlStrEqual(name, (const xmlChar *) "SYSTEM")) 2364 type = SGML_CATA_SYSTEM; 2365 else if (xmlStrEqual(name, (const xmlChar *) "PUBLIC")) 2366 type = SGML_CATA_PUBLIC; 2367 else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE")) 2368 type = SGML_CATA_DELEGATE; 2369 else if (xmlStrEqual(name, (const xmlChar *) "ENTITY")) 2370 type = SGML_CATA_ENTITY; 2371 else if (xmlStrEqual(name, (const xmlChar *) "DOCTYPE")) 2372 type = SGML_CATA_DOCTYPE; 2373 else if (xmlStrEqual(name, (const xmlChar *) "LINKTYPE")) 2374 type = SGML_CATA_LINKTYPE; 2375 else if (xmlStrEqual(name, (const xmlChar *) "NOTATION")) 2376 type = SGML_CATA_NOTATION; 2377 else if (xmlStrEqual(name, (const xmlChar *) "SGMLDECL")) 2378 type = SGML_CATA_SGMLDECL; 2379 else if (xmlStrEqual(name, (const xmlChar *) "DOCUMENT")) 2380 type = SGML_CATA_DOCUMENT; 2381 else if (xmlStrEqual(name, (const xmlChar *) "CATALOG")) 2382 type = SGML_CATA_CATALOG; 2383 else if (xmlStrEqual(name, (const xmlChar *) "BASE")) 2384 type = SGML_CATA_BASE; 2385 else if (xmlStrEqual(name, (const xmlChar *) "OVERRIDE")) { 2386 xmlFree(name); 2387 cur = xmlParseSGMLCatalogName(cur, &name); 2388 if (name == NULL) { 2389 /* error */ 2390 break; 2391 } 2392 xmlFree(name); 2393 continue; 2394 } 2395 xmlFree(name); 2396 name = NULL; 2397 2398 switch(type) { 2399 case SGML_CATA_ENTITY: 2400 if (*cur == '%') 2401 type = SGML_CATA_PENTITY; 2402 case SGML_CATA_PENTITY: 2403 case SGML_CATA_DOCTYPE: 2404 case SGML_CATA_LINKTYPE: 2405 case SGML_CATA_NOTATION: 2406 cur = xmlParseSGMLCatalogName(cur, &name); 2407 if (cur == NULL) { 2408 /* error */ 2409 break; 2410 } 2411 if (!IS_BLANK_CH(*cur)) { 2412 /* error */ 2413 break; 2414 } 2415 SKIP_BLANKS; 2416 cur = xmlParseSGMLCatalogPubid(cur, &sysid); 2417 if (cur == NULL) { 2418 /* error */ 2419 break; 2420 } 2421 break; 2422 case SGML_CATA_PUBLIC: 2423 case SGML_CATA_SYSTEM: 2424 case SGML_CATA_DELEGATE: 2425 cur = xmlParseSGMLCatalogPubid(cur, &name); 2426 if (cur == NULL) { 2427 /* error */ 2428 break; 2429 } 2430 if (type != SGML_CATA_SYSTEM) { 2431 xmlChar *normid; 2432 2433 normid = xmlCatalogNormalizePublic(name); 2434 if (normid != NULL) { 2435 if (name != NULL) 2436 xmlFree(name); 2437 if (*normid != 0) 2438 name = normid; 2439 else { 2440 xmlFree(normid); 2441 name = NULL; 2442 } 2443 } 2444 } 2445 if (!IS_BLANK_CH(*cur)) { 2446 /* error */ 2447 break; 2448 } 2449 SKIP_BLANKS; 2450 cur = xmlParseSGMLCatalogPubid(cur, &sysid); 2451 if (cur == NULL) { 2452 /* error */ 2453 break; 2454 } 2455 break; 2456 case SGML_CATA_BASE: 2457 case SGML_CATA_CATALOG: 2458 case SGML_CATA_DOCUMENT: 2459 case SGML_CATA_SGMLDECL: 2460 cur = xmlParseSGMLCatalogPubid(cur, &sysid); 2461 if (cur == NULL) { 2462 /* error */ 2463 break; 2464 } 2465 break; 2466 default: 2467 break; 2468 } 2469 if (cur == NULL) { 2470 if (name != NULL) 2471 xmlFree(name); 2472 if (sysid != NULL) 2473 xmlFree(sysid); 2474 break; 2475 } else if (type == SGML_CATA_BASE) { 2476 if (base != NULL) 2477 xmlFree(base); 2478 base = xmlStrdup(sysid); 2479 } else if ((type == SGML_CATA_PUBLIC) || 2480 (type == SGML_CATA_SYSTEM)) { 2481 xmlChar *filename; 2482 2483 filename = xmlBuildURI(sysid, base); 2484 if (filename != NULL) { 2485 xmlCatalogEntryPtr entry; 2486 2487 entry = xmlNewCatalogEntry(type, name, filename, 2488 NULL, XML_CATA_PREFER_NONE, NULL); 2489 res = xmlHashAddEntry(catal->sgml, name, entry); 2490 if (res < 0) { 2491 xmlFreeCatalogEntry(entry); 2492 } 2493 xmlFree(filename); 2494 } 2495 2496 } else if (type == SGML_CATA_CATALOG) { 2497 if (super) { 2498 xmlCatalogEntryPtr entry; 2499 2500 entry = xmlNewCatalogEntry(type, sysid, NULL, NULL, 2501 XML_CATA_PREFER_NONE, NULL); 2502 res = xmlHashAddEntry(catal->sgml, sysid, entry); 2503 if (res < 0) { 2504 xmlFreeCatalogEntry(entry); 2505 } 2506 } else { 2507 xmlChar *filename; 2508 2509 filename = xmlBuildURI(sysid, base); 2510 if (filename != NULL) { 2511 xmlExpandCatalog(catal, (const char *)filename); 2512 xmlFree(filename); 2513 } 2514 } 2515 } 2516 /* 2517 * drop anything else we won't handle it 2518 */ 2519 if (name != NULL) 2520 xmlFree(name); 2521 if (sysid != NULL) 2522 xmlFree(sysid); 2523 } 2524 } 2525 if (base != NULL) 2526 xmlFree(base); 2527 if (cur == NULL) 2528 return(-1); 2529 return(0); 2530 } 2531 2532 /************************************************************************ 2533 * * 2534 * SGML Catalog handling * 2535 * * 2536 ************************************************************************/ 2537 2538 /** 2539 * xmlCatalogGetSGMLPublic: 2540 * @catal: an SGML catalog hash 2541 * @pubID: the public ID string 2542 * 2543 * Try to lookup the catalog local reference associated to a public ID 2544 * 2545 * Returns the local resource if found or NULL otherwise. 2546 */ 2547 static const xmlChar * 2548 xmlCatalogGetSGMLPublic(xmlHashTablePtr catal, const xmlChar *pubID) { 2549 xmlCatalogEntryPtr entry; 2550 xmlChar *normid; 2551 2552 if (catal == NULL) 2553 return(NULL); 2554 2555 normid = xmlCatalogNormalizePublic(pubID); 2556 if (normid != NULL) 2557 pubID = (*normid != 0 ? normid : NULL); 2558 2559 entry = (xmlCatalogEntryPtr) xmlHashLookup(catal, pubID); 2560 if (entry == NULL) { 2561 if (normid != NULL) 2562 xmlFree(normid); 2563 return(NULL); 2564 } 2565 if (entry->type == SGML_CATA_PUBLIC) { 2566 if (normid != NULL) 2567 xmlFree(normid); 2568 return(entry->URL); 2569 } 2570 if (normid != NULL) 2571 xmlFree(normid); 2572 return(NULL); 2573 } 2574 2575 /** 2576 * xmlCatalogGetSGMLSystem: 2577 * @catal: an SGML catalog hash 2578 * @sysID: the system ID string 2579 * 2580 * Try to lookup the catalog local reference for a system ID 2581 * 2582 * Returns the local resource if found or NULL otherwise. 2583 */ 2584 static const xmlChar * 2585 xmlCatalogGetSGMLSystem(xmlHashTablePtr catal, const xmlChar *sysID) { 2586 xmlCatalogEntryPtr entry; 2587 2588 if (catal == NULL) 2589 return(NULL); 2590 2591 entry = (xmlCatalogEntryPtr) xmlHashLookup(catal, sysID); 2592 if (entry == NULL) 2593 return(NULL); 2594 if (entry->type == SGML_CATA_SYSTEM) 2595 return(entry->URL); 2596 return(NULL); 2597 } 2598 2599 /** 2600 * xmlCatalogSGMLResolve: 2601 * @catal: the SGML catalog 2602 * @pubID: the public ID string 2603 * @sysID: the system ID string 2604 * 2605 * Do a complete resolution lookup of an External Identifier 2606 * 2607 * Returns the URI of the resource or NULL if not found 2608 */ 2609 static const xmlChar * 2610 xmlCatalogSGMLResolve(xmlCatalogPtr catal, const xmlChar *pubID, 2611 const xmlChar *sysID) { 2612 const xmlChar *ret = NULL; 2613 2614 if (catal->sgml == NULL) 2615 return(NULL); 2616 2617 if (pubID != NULL) 2618 ret = xmlCatalogGetSGMLPublic(catal->sgml, pubID); 2619 if (ret != NULL) 2620 return(ret); 2621 if (sysID != NULL) 2622 ret = xmlCatalogGetSGMLSystem(catal->sgml, sysID); 2623 if (ret != NULL) 2624 return(ret); 2625 return(NULL); 2626 } 2627 2628 /************************************************************************ 2629 * * 2630 * Specific Public interfaces * 2631 * * 2632 ************************************************************************/ 2633 2634 /** 2635 * xmlLoadSGMLSuperCatalog: 2636 * @filename: a file path 2637 * 2638 * Load an SGML super catalog. It won't expand CATALOG or DELEGATE 2639 * references. This is only needed for manipulating SGML Super Catalogs 2640 * like adding and removing CATALOG or DELEGATE entries. 2641 * 2642 * Returns the catalog parsed or NULL in case of error 2643 */ 2644 xmlCatalogPtr 2645 xmlLoadSGMLSuperCatalog(const char *filename) 2646 { 2647 xmlChar *content; 2648 xmlCatalogPtr catal; 2649 int ret; 2650 2651 content = xmlLoadFileContent(filename); 2652 if (content == NULL) 2653 return(NULL); 2654 2655 catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE, xmlCatalogDefaultPrefer); 2656 if (catal == NULL) { 2657 xmlFree(content); 2658 return(NULL); 2659 } 2660 2661 ret = xmlParseSGMLCatalog(catal, content, filename, 1); 2662 xmlFree(content); 2663 if (ret < 0) { 2664 xmlFreeCatalog(catal); 2665 return(NULL); 2666 } 2667 return (catal); 2668 } 2669 2670 /** 2671 * xmlLoadACatalog: 2672 * @filename: a file path 2673 * 2674 * Load the catalog and build the associated data structures. 2675 * This can be either an XML Catalog or an SGML Catalog 2676 * It will recurse in SGML CATALOG entries. On the other hand XML 2677 * Catalogs are not handled recursively. 2678 * 2679 * Returns the catalog parsed or NULL in case of error 2680 */ 2681 xmlCatalogPtr 2682 xmlLoadACatalog(const char *filename) 2683 { 2684 xmlChar *content; 2685 xmlChar *first; 2686 xmlCatalogPtr catal; 2687 int ret; 2688 2689 content = xmlLoadFileContent(filename); 2690 if (content == NULL) 2691 return(NULL); 2692 2693 2694 first = content; 2695 2696 while ((*first != 0) && (*first != '-') && (*first != '<') && 2697 (!(((*first >= 'A') && (*first <= 'Z')) || 2698 ((*first >= 'a') && (*first <= 'z'))))) 2699 first++; 2700 2701 if (*first != '<') { 2702 catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE, xmlCatalogDefaultPrefer); 2703 if (catal == NULL) { 2704 xmlFree(content); 2705 return(NULL); 2706 } 2707 ret = xmlParseSGMLCatalog(catal, content, filename, 0); 2708 if (ret < 0) { 2709 xmlFreeCatalog(catal); 2710 xmlFree(content); 2711 return(NULL); 2712 } 2713 } else { 2714 catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE, xmlCatalogDefaultPrefer); 2715 if (catal == NULL) { 2716 xmlFree(content); 2717 return(NULL); 2718 } 2719 catal->xml = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL, 2720 NULL, BAD_CAST filename, xmlCatalogDefaultPrefer, NULL); 2721 } 2722 xmlFree(content); 2723 return (catal); 2724 } 2725 2726 /** 2727 * xmlExpandCatalog: 2728 * @catal: a catalog 2729 * @filename: a file path 2730 * 2731 * Load the catalog and expand the existing catal structure. 2732 * This can be either an XML Catalog or an SGML Catalog 2733 * 2734 * Returns 0 in case of success, -1 in case of error 2735 */ 2736 static int 2737 xmlExpandCatalog(xmlCatalogPtr catal, const char *filename) 2738 { 2739 int ret; 2740 2741 if ((catal == NULL) || (filename == NULL)) 2742 return(-1); 2743 2744 2745 if (catal->type == XML_SGML_CATALOG_TYPE) { 2746 xmlChar *content; 2747 2748 content = xmlLoadFileContent(filename); 2749 if (content == NULL) 2750 return(-1); 2751 2752 ret = xmlParseSGMLCatalog(catal, content, filename, 0); 2753 if (ret < 0) { 2754 xmlFree(content); 2755 return(-1); 2756 } 2757 xmlFree(content); 2758 } else { 2759 xmlCatalogEntryPtr tmp, cur; 2760 tmp = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL, 2761 NULL, BAD_CAST filename, xmlCatalogDefaultPrefer, NULL); 2762 2763 cur = catal->xml; 2764 if (cur == NULL) { 2765 catal->xml = tmp; 2766 } else { 2767 while (cur->next != NULL) cur = cur->next; 2768 cur->next = tmp; 2769 } 2770 } 2771 return (0); 2772 } 2773 2774 /** 2775 * xmlACatalogResolveSystem: 2776 * @catal: a Catalog 2777 * @sysID: the system ID string 2778 * 2779 * Try to lookup the catalog resource for a system ID 2780 * 2781 * Returns the resource if found or NULL otherwise, the value returned 2782 * must be freed by the caller. 2783 */ 2784 xmlChar * 2785 xmlACatalogResolveSystem(xmlCatalogPtr catal, const xmlChar *sysID) { 2786 xmlChar *ret = NULL; 2787 2788 if ((sysID == NULL) || (catal == NULL)) 2789 return(NULL); 2790 2791 if (xmlDebugCatalogs) 2792 xmlGenericError(xmlGenericErrorContext, 2793 "Resolve sysID %s\n", sysID); 2794 2795 if (catal->type == XML_XML_CATALOG_TYPE) { 2796 ret = xmlCatalogListXMLResolve(catal->xml, NULL, sysID); 2797 if (ret == XML_CATAL_BREAK) 2798 ret = NULL; 2799 } else { 2800 const xmlChar *sgml; 2801 2802 sgml = xmlCatalogGetSGMLSystem(catal->sgml, sysID); 2803 if (sgml != NULL) 2804 ret = xmlStrdup(sgml); 2805 } 2806 return(ret); 2807 } 2808 2809 /** 2810 * xmlACatalogResolvePublic: 2811 * @catal: a Catalog 2812 * @pubID: the public ID string 2813 * 2814 * Try to lookup the catalog local reference associated to a public ID in that catalog 2815 * 2816 * Returns the local resource if found or NULL otherwise, the value returned 2817 * must be freed by the caller. 2818 */ 2819 xmlChar * 2820 xmlACatalogResolvePublic(xmlCatalogPtr catal, const xmlChar *pubID) { 2821 xmlChar *ret = NULL; 2822 2823 if ((pubID == NULL) || (catal == NULL)) 2824 return(NULL); 2825 2826 if (xmlDebugCatalogs) 2827 xmlGenericError(xmlGenericErrorContext, 2828 "Resolve pubID %s\n", pubID); 2829 2830 if (catal->type == XML_XML_CATALOG_TYPE) { 2831 ret = xmlCatalogListXMLResolve(catal->xml, pubID, NULL); 2832 if (ret == XML_CATAL_BREAK) 2833 ret = NULL; 2834 } else { 2835 const xmlChar *sgml; 2836 2837 sgml = xmlCatalogGetSGMLPublic(catal->sgml, pubID); 2838 if (sgml != NULL) 2839 ret = xmlStrdup(sgml); 2840 } 2841 return(ret); 2842 } 2843 2844 /** 2845 * xmlACatalogResolve: 2846 * @catal: a Catalog 2847 * @pubID: the public ID string 2848 * @sysID: the system ID string 2849 * 2850 * Do a complete resolution lookup of an External Identifier 2851 * 2852 * Returns the URI of the resource or NULL if not found, it must be freed 2853 * by the caller. 2854 */ 2855 xmlChar * 2856 xmlACatalogResolve(xmlCatalogPtr catal, const xmlChar * pubID, 2857 const xmlChar * sysID) 2858 { 2859 xmlChar *ret = NULL; 2860 2861 if (((pubID == NULL) && (sysID == NULL)) || (catal == NULL)) 2862 return (NULL); 2863 2864 if (xmlDebugCatalogs) { 2865 if ((pubID != NULL) && (sysID != NULL)) { 2866 xmlGenericError(xmlGenericErrorContext, 2867 "Resolve: pubID %s sysID %s\n", pubID, sysID); 2868 } else if (pubID != NULL) { 2869 xmlGenericError(xmlGenericErrorContext, 2870 "Resolve: pubID %s\n", pubID); 2871 } else { 2872 xmlGenericError(xmlGenericErrorContext, 2873 "Resolve: sysID %s\n", sysID); 2874 } 2875 } 2876 2877 if (catal->type == XML_XML_CATALOG_TYPE) { 2878 ret = xmlCatalogListXMLResolve(catal->xml, pubID, sysID); 2879 if (ret == XML_CATAL_BREAK) 2880 ret = NULL; 2881 } else { 2882 const xmlChar *sgml; 2883 2884 sgml = xmlCatalogSGMLResolve(catal, pubID, sysID); 2885 if (sgml != NULL) 2886 ret = xmlStrdup(sgml); 2887 } 2888 return (ret); 2889 } 2890 2891 /** 2892 * xmlACatalogResolveURI: 2893 * @catal: a Catalog 2894 * @URI: the URI 2895 * 2896 * Do a complete resolution lookup of an URI 2897 * 2898 * Returns the URI of the resource or NULL if not found, it must be freed 2899 * by the caller. 2900 */ 2901 xmlChar * 2902 xmlACatalogResolveURI(xmlCatalogPtr catal, const xmlChar *URI) { 2903 xmlChar *ret = NULL; 2904 2905 if ((URI == NULL) || (catal == NULL)) 2906 return(NULL); 2907 2908 if (xmlDebugCatalogs) 2909 xmlGenericError(xmlGenericErrorContext, 2910 "Resolve URI %s\n", URI); 2911 2912 if (catal->type == XML_XML_CATALOG_TYPE) { 2913 ret = xmlCatalogListXMLResolveURI(catal->xml, URI); 2914 if (ret == XML_CATAL_BREAK) 2915 ret = NULL; 2916 } else { 2917 const xmlChar *sgml; 2918 2919 sgml = xmlCatalogSGMLResolve(catal, NULL, URI); 2920 if (sgml != NULL) 2921 ret = xmlStrdup(sgml); 2922 } 2923 return(ret); 2924 } 2925 2926 #ifdef LIBXML_OUTPUT_ENABLED 2927 /** 2928 * xmlACatalogDump: 2929 * @catal: a Catalog 2930 * @out: the file. 2931 * 2932 * Dump the given catalog to the given file. 2933 */ 2934 void 2935 xmlACatalogDump(xmlCatalogPtr catal, FILE *out) { 2936 if ((out == NULL) || (catal == NULL)) 2937 return; 2938 2939 if (catal->type == XML_XML_CATALOG_TYPE) { 2940 xmlDumpXMLCatalog(out, catal->xml); 2941 } else { 2942 xmlHashScan(catal->sgml, 2943 (xmlHashScanner) xmlCatalogDumpEntry, out); 2944 } 2945 } 2946 #endif /* LIBXML_OUTPUT_ENABLED */ 2947 2948 /** 2949 * xmlACatalogAdd: 2950 * @catal: a Catalog 2951 * @type: the type of record to add to the catalog 2952 * @orig: the system, public or prefix to match 2953 * @replace: the replacement value for the match 2954 * 2955 * Add an entry in the catalog, it may overwrite existing but 2956 * different entries. 2957 * 2958 * Returns 0 if successful, -1 otherwise 2959 */ 2960 int 2961 xmlACatalogAdd(xmlCatalogPtr catal, const xmlChar * type, 2962 const xmlChar * orig, const xmlChar * replace) 2963 { 2964 int res = -1; 2965 2966 if (catal == NULL) 2967 return(-1); 2968 2969 if (catal->type == XML_XML_CATALOG_TYPE) { 2970 res = xmlAddXMLCatalog(catal->xml, type, orig, replace); 2971 } else { 2972 xmlCatalogEntryType cattype; 2973 2974 cattype = xmlGetSGMLCatalogEntryType(type); 2975 if (cattype != XML_CATA_NONE) { 2976 xmlCatalogEntryPtr entry; 2977 2978 entry = xmlNewCatalogEntry(cattype, orig, replace, NULL, 2979 XML_CATA_PREFER_NONE, NULL); 2980 if (catal->sgml == NULL) 2981 catal->sgml = xmlHashCreate(10); 2982 res = xmlHashAddEntry(catal->sgml, orig, entry); 2983 } 2984 } 2985 return (res); 2986 } 2987 2988 /** 2989 * xmlACatalogRemove: 2990 * @catal: a Catalog 2991 * @value: the value to remove 2992 * 2993 * Remove an entry from the catalog 2994 * 2995 * Returns the number of entries removed if successful, -1 otherwise 2996 */ 2997 int 2998 xmlACatalogRemove(xmlCatalogPtr catal, const xmlChar *value) { 2999 int res = -1; 3000 3001 if ((catal == NULL) || (value == NULL)) 3002 return(-1); 3003 3004 if (catal->type == XML_XML_CATALOG_TYPE) { 3005 res = xmlDelXMLCatalog(catal->xml, value); 3006 } else { 3007 res = xmlHashRemoveEntry(catal->sgml, value, 3008 (xmlHashDeallocator) xmlFreeCatalogEntry); 3009 if (res == 0) 3010 res = 1; 3011 } 3012 return(res); 3013 } 3014 3015 /** 3016 * xmlNewCatalog: 3017 * @sgml: should this create an SGML catalog 3018 * 3019 * create a new Catalog. 3020 * 3021 * Returns the xmlCatalogPtr or NULL in case of error 3022 */ 3023 xmlCatalogPtr 3024 xmlNewCatalog(int sgml) { 3025 xmlCatalogPtr catal = NULL; 3026 3027 if (sgml) { 3028 catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE, 3029 xmlCatalogDefaultPrefer); 3030 if ((catal != NULL) && (catal->sgml == NULL)) 3031 catal->sgml = xmlHashCreate(10); 3032 } else 3033 catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE, 3034 xmlCatalogDefaultPrefer); 3035 return(catal); 3036 } 3037 3038 /** 3039 * xmlCatalogIsEmpty: 3040 * @catal: should this create an SGML catalog 3041 * 3042 * Check is a catalog is empty 3043 * 3044 * Returns 1 if the catalog is empty, 0 if not, amd -1 in case of error. 3045 */ 3046 int 3047 xmlCatalogIsEmpty(xmlCatalogPtr catal) { 3048 if (catal == NULL) 3049 return(-1); 3050 3051 if (catal->type == XML_XML_CATALOG_TYPE) { 3052 if (catal->xml == NULL) 3053 return(1); 3054 if ((catal->xml->type != XML_CATA_CATALOG) && 3055 (catal->xml->type != XML_CATA_BROKEN_CATALOG)) 3056 return(-1); 3057 if (catal->xml->children == NULL) 3058 return(1); 3059 return(0); 3060 } else { 3061 int res; 3062 3063 if (catal->sgml == NULL) 3064 return(1); 3065 res = xmlHashSize(catal->sgml); 3066 if (res == 0) 3067 return(1); 3068 if (res < 0) 3069 return(-1); 3070 } 3071 return(0); 3072 } 3073 3074 /************************************************************************ 3075 * * 3076 * Public interfaces manipulating the global shared default catalog * 3077 * * 3078 ************************************************************************/ 3079 3080 /** 3081 * xmlInitializeCatalogData: 3082 * 3083 * Do the catalog initialization only of global data, doesn't try to load 3084 * any catalog actually. 3085 * this function is not thread safe, catalog initialization should 3086 * preferably be done once at startup 3087 */ 3088 static void 3089 xmlInitializeCatalogData(void) { 3090 if (xmlCatalogInitialized != 0) 3091 return; 3092 3093 if (getenv("XML_DEBUG_CATALOG")) 3094 xmlDebugCatalogs = 1; 3095 xmlCatalogMutex = xmlNewRMutex(); 3096 3097 xmlCatalogInitialized = 1; 3098 } 3099 /** 3100 * xmlInitializeCatalog: 3101 * 3102 * Do the catalog initialization. 3103 * this function is not thread safe, catalog initialization should 3104 * preferably be done once at startup 3105 */ 3106 void 3107 xmlInitializeCatalog(void) { 3108 if (xmlCatalogInitialized != 0) 3109 return; 3110 3111 xmlInitializeCatalogData(); 3112 xmlRMutexLock(xmlCatalogMutex); 3113 3114 if (getenv("XML_DEBUG_CATALOG")) 3115 xmlDebugCatalogs = 1; 3116 3117 if (xmlDefaultCatalog == NULL) { 3118 const char *catalogs; 3119 char *path; 3120 const char *cur, *paths; 3121 xmlCatalogPtr catal; 3122 xmlCatalogEntryPtr *nextent; 3123 3124 catalogs = (const char *) getenv("XML_CATALOG_FILES"); 3125 if (catalogs == NULL) 3126 #if defined(_WIN32) && defined(_MSC_VER) 3127 { 3128 void* hmodule; 3129 hmodule = GetModuleHandleA("libxml2.dll"); 3130 if (hmodule == NULL) 3131 hmodule = GetModuleHandleA(NULL); 3132 if (hmodule != NULL) { 3133 char buf[256]; 3134 unsigned long len = GetModuleFileNameA(hmodule, buf, 255); 3135 if (len != 0) { 3136 char* p = &(buf[len]); 3137 while (*p != '\\' && p > buf) 3138 p--; 3139 if (p != buf) { 3140 xmlChar* uri; 3141 strncpy(p, "\\..\\etc\\catalog", 255 - (p - buf)); 3142 uri = xmlCanonicPath((const xmlChar*)buf); 3143 if (uri != NULL) { 3144 strncpy(XML_XML_DEFAULT_CATALOG, uri, 255); 3145 xmlFree(uri); 3146 } 3147 } 3148 } 3149 } 3150 catalogs = XML_XML_DEFAULT_CATALOG; 3151 } 3152 #else 3153 catalogs = XML_XML_DEFAULT_CATALOG; 3154 #endif 3155 3156 catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE, 3157 xmlCatalogDefaultPrefer); 3158 if (catal != NULL) { 3159 /* the XML_CATALOG_FILES envvar is allowed to contain a 3160 space-separated list of entries. */ 3161 cur = catalogs; 3162 nextent = &catal->xml; 3163 while (*cur != '\0') { 3164 while (xmlIsBlank_ch(*cur)) 3165 cur++; 3166 if (*cur != 0) { 3167 paths = cur; 3168 while ((*cur != 0) && (!xmlIsBlank_ch(*cur))) 3169 cur++; 3170 path = (char *) xmlStrndup((const xmlChar *)paths, cur - paths); 3171 if (path != NULL) { 3172 *nextent = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL, 3173 NULL, BAD_CAST path, xmlCatalogDefaultPrefer, NULL); 3174 if (*nextent != NULL) 3175 nextent = &((*nextent)->next); 3176 xmlFree(path); 3177 } 3178 } 3179 } 3180 xmlDefaultCatalog = catal; 3181 } 3182 } 3183 3184 xmlRMutexUnlock(xmlCatalogMutex); 3185 } 3186 3187 3188 /** 3189 * xmlLoadCatalog: 3190 * @filename: a file path 3191 * 3192 * Load the catalog and makes its definitions effective for the default 3193 * external entity loader. It will recurse in SGML CATALOG entries. 3194 * this function is not thread safe, catalog initialization should 3195 * preferably be done once at startup 3196 * 3197 * Returns 0 in case of success -1 in case of error 3198 */ 3199 int 3200 xmlLoadCatalog(const char *filename) 3201 { 3202 int ret; 3203 xmlCatalogPtr catal; 3204 3205 if (!xmlCatalogInitialized) 3206 xmlInitializeCatalogData(); 3207 3208 xmlRMutexLock(xmlCatalogMutex); 3209 3210 if (xmlDefaultCatalog == NULL) { 3211 catal = xmlLoadACatalog(filename); 3212 if (catal == NULL) { 3213 xmlRMutexUnlock(xmlCatalogMutex); 3214 return(-1); 3215 } 3216 3217 xmlDefaultCatalog = catal; 3218 xmlRMutexUnlock(xmlCatalogMutex); 3219 return(0); 3220 } 3221 3222 ret = xmlExpandCatalog(xmlDefaultCatalog, filename); 3223 xmlRMutexUnlock(xmlCatalogMutex); 3224 return(ret); 3225 } 3226 3227 /** 3228 * xmlLoadCatalogs: 3229 * @pathss: a list of directories separated by a colon or a space. 3230 * 3231 * Load the catalogs and makes their definitions effective for the default 3232 * external entity loader. 3233 * this function is not thread safe, catalog initialization should 3234 * preferably be done once at startup 3235 */ 3236 void 3237 xmlLoadCatalogs(const char *pathss) { 3238 const char *cur; 3239 const char *paths; 3240 xmlChar *path; 3241 #ifdef _WIN32 3242 int i, iLen; 3243 #endif 3244 3245 if (pathss == NULL) 3246 return; 3247 3248 cur = pathss; 3249 while (*cur != 0) { 3250 while (xmlIsBlank_ch(*cur)) cur++; 3251 if (*cur != 0) { 3252 paths = cur; 3253 while ((*cur != 0) && (*cur != PATH_SEPARATOR) && (!xmlIsBlank_ch(*cur))) 3254 cur++; 3255 path = xmlStrndup((const xmlChar *)paths, cur - paths); 3256 #ifdef _WIN32 3257 iLen = strlen((const char*)path); 3258 for(i = 0; i < iLen; i++) { 3259 if(path[i] == '\\') { 3260 path[i] = '/'; 3261 } 3262 } 3263 #endif 3264 if (path != NULL) { 3265 xmlLoadCatalog((const char *) path); 3266 xmlFree(path); 3267 } 3268 } 3269 while (*cur == PATH_SEPARATOR) 3270 cur++; 3271 } 3272 } 3273 3274 /** 3275 * xmlCatalogCleanup: 3276 * 3277 * Free up all the memory associated with catalogs 3278 */ 3279 void 3280 xmlCatalogCleanup(void) { 3281 if (xmlCatalogInitialized == 0) 3282 return; 3283 3284 xmlRMutexLock(xmlCatalogMutex); 3285 if (xmlDebugCatalogs) 3286 xmlGenericError(xmlGenericErrorContext, 3287 "Catalogs cleanup\n"); 3288 if (xmlCatalogXMLFiles != NULL) 3289 xmlHashFree(xmlCatalogXMLFiles, 3290 (xmlHashDeallocator)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 */