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