catalog.c revision 99784ff899ed1e87a232cb9016e6ba3ee7642a4b
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#include "libxml.h" 16 17#ifdef LIBXML_CATALOG_ENABLED 18#ifdef HAVE_SYS_TYPES_H 19#include <sys/types.h> 20#endif 21#ifdef HAVE_SYS_STAT_H 22#include <sys/stat.h> 23#endif 24#ifdef HAVE_UNISTD_H 25#include <unistd.h> 26#endif 27#ifdef HAVE_FCNTL_H 28#include <fcntl.h> 29#endif 30#include <string.h> 31#include <libxml/xmlmemory.h> 32#include <libxml/hash.h> 33#include <libxml/uri.h> 34#include <libxml/parserInternals.h> 35#include <libxml/catalog.h> 36#include <libxml/xmlerror.h> 37 38#define MAX_DELEGATE 50 39 40/** 41 * TODO: 42 * 43 * macro to flag unimplemented blocks 44 */ 45#define TODO \ 46 xmlGenericError(xmlGenericErrorContext, \ 47 "Unimplemented block at %s:%d\n", \ 48 __FILE__, __LINE__); 49 50#define XML_URN_PUBID "urn:publicid:" 51#define XML_CATAL_BREAK ((xmlChar *) -1) 52#ifndef XML_DEFAULT_CATALOG 53#define XML_DEFAULT_CATALOG "/etc/xml/catalog" 54#endif 55 56/************************************************************************ 57 * * 58 * Types, all private * 59 * * 60 ************************************************************************/ 61 62typedef enum { 63 XML_CATA_NONE = 0, 64 XML_CATA_CATALOG, 65 XML_CATA_BROKEN_CATALOG, 66 XML_CATA_NEXT_CATALOG, 67 XML_CATA_PUBLIC, 68 XML_CATA_SYSTEM, 69 XML_CATA_REWRITE_SYSTEM, 70 XML_CATA_DELEGATE_PUBLIC, 71 XML_CATA_DELEGATE_SYSTEM, 72 XML_CATA_URI, 73 XML_CATA_REWRITE_URI, 74 XML_CATA_DELEGATE_URI, 75 SGML_CATA_SYSTEM, 76 SGML_CATA_PUBLIC, 77 SGML_CATA_ENTITY, 78 SGML_CATA_PENTITY, 79 SGML_CATA_DOCTYPE, 80 SGML_CATA_LINKTYPE, 81 SGML_CATA_NOTATION, 82 SGML_CATA_DELEGATE, 83 SGML_CATA_BASE, 84 SGML_CATA_CATALOG, 85 SGML_CATA_DOCUMENT, 86 SGML_CATA_SGMLDECL 87} xmlCatalogEntryType; 88 89typedef struct _xmlCatalogEntry xmlCatalogEntry; 90typedef xmlCatalogEntry *xmlCatalogEntryPtr; 91struct _xmlCatalogEntry { 92 struct _xmlCatalogEntry *next; 93 struct _xmlCatalogEntry *parent; 94 struct _xmlCatalogEntry *children; 95 xmlCatalogEntryType type; 96 xmlChar *name; 97 xmlChar *value; 98 xmlCatalogPrefer prefer; 99 int dealloc; 100}; 101 102static xmlCatalogAllow xmlCatalogDefaultAllow = XML_CATA_ALLOW_ALL; 103static xmlCatalogPrefer xmlCatalogDefaultPrefer = XML_CATA_PREFER_PUBLIC; 104static xmlHashTablePtr xmlDefaultCatalog; 105static xmlHashTablePtr xmlCatalogXMLFiles = NULL; 106static xmlCatalogEntryPtr xmlDefaultXMLCatalogList = NULL; 107static int xmlCatalogInitialized = 0; 108 109 110/* Catalog stack */ 111static const char * catalTab[10]; /* stack of catals */ 112static int catalNr = 0; /* Number of current catal streams */ 113static int catalMax = 10; /* Max number of catal streams */ 114 115static int xmlDebugCatalogs = 0; /* used for debugging */ 116 117/************************************************************************ 118 * * 119 * alloc or dealloc * 120 * * 121 ************************************************************************/ 122 123static xmlCatalogEntryPtr 124xmlNewCatalogEntry(xmlCatalogEntryType type, const xmlChar *name, 125 const xmlChar *value, xmlCatalogPrefer prefer) { 126 xmlCatalogEntryPtr ret; 127 128 ret = (xmlCatalogEntryPtr) xmlMalloc(sizeof(xmlCatalogEntry)); 129 if (ret == NULL) { 130 xmlGenericError(xmlGenericErrorContext, 131 "malloc of %d byte failed\n", sizeof(xmlCatalogEntry)); 132 return(NULL); 133 } 134 ret->next = NULL; 135 ret->parent = NULL; 136 ret->children = NULL; 137 ret->type = type; 138 if (name != NULL) 139 ret->name = xmlStrdup(name); 140 else 141 ret->name = NULL; 142 if (value != NULL) 143 ret->value = xmlStrdup(value); 144 else 145 ret->value = NULL; 146 ret->prefer = prefer; 147 ret->dealloc = 1; 148 return(ret); 149} 150 151static void 152xmlFreeCatalogEntryList(xmlCatalogEntryPtr ret); 153 154static void 155xmlFreeCatalogEntry(xmlCatalogEntryPtr ret) { 156 if (ret == NULL) 157 return; 158 if ((ret->children != NULL) && (ret->dealloc == 1)) 159 xmlFreeCatalogEntryList(ret->children); 160 if (ret->name != NULL) 161 xmlFree(ret->name); 162 if (ret->value != NULL) 163 xmlFree(ret->value); 164 xmlFree(ret); 165} 166 167static void 168xmlFreeCatalogEntryList(xmlCatalogEntryPtr ret) { 169 xmlCatalogEntryPtr next; 170 171 while (ret != NULL) { 172 next = ret->next; 173 xmlFreeCatalogEntry(ret); 174 ret = next; 175 } 176} 177 178/** 179 * xmlCatalogDumpEntry: 180 * @entry: the 181 * @out: the file. 182 * 183 * Free up all the memory associated with catalogs 184 */ 185static void 186xmlCatalogDumpEntry(xmlCatalogEntryPtr entry, FILE *out) { 187 if ((entry == NULL) || (out == NULL)) 188 return; 189 switch (entry->type) { 190 case SGML_CATA_ENTITY: 191 fprintf(out, "ENTITY "); break; 192 case SGML_CATA_PENTITY: 193 fprintf(out, "ENTITY %%"); break; 194 case SGML_CATA_DOCTYPE: 195 fprintf(out, "DOCTYPE "); break; 196 case SGML_CATA_LINKTYPE: 197 fprintf(out, "LINKTYPE "); break; 198 case SGML_CATA_NOTATION: 199 fprintf(out, "NOTATION "); break; 200 case SGML_CATA_PUBLIC: 201 fprintf(out, "PUBLIC "); break; 202 case SGML_CATA_SYSTEM: 203 fprintf(out, "SYSTEM "); break; 204 case SGML_CATA_DELEGATE: 205 fprintf(out, "DELEGATE "); break; 206 case SGML_CATA_BASE: 207 fprintf(out, "BASE "); break; 208 case SGML_CATA_CATALOG: 209 fprintf(out, "CATALOG "); break; 210 case SGML_CATA_DOCUMENT: 211 fprintf(out, "DOCUMENT "); break; 212 case SGML_CATA_SGMLDECL: 213 fprintf(out, "SGMLDECL "); break; 214 default: 215 return; 216 } 217 switch (entry->type) { 218 case SGML_CATA_ENTITY: 219 case SGML_CATA_PENTITY: 220 case SGML_CATA_DOCTYPE: 221 case SGML_CATA_LINKTYPE: 222 case SGML_CATA_NOTATION: 223 fprintf(out, "%s", entry->name); break; 224 case SGML_CATA_PUBLIC: 225 case SGML_CATA_SYSTEM: 226 case SGML_CATA_SGMLDECL: 227 case SGML_CATA_DOCUMENT: 228 case SGML_CATA_CATALOG: 229 case SGML_CATA_BASE: 230 case SGML_CATA_DELEGATE: 231 fprintf(out, "\"%s\"", entry->name); break; 232 default: 233 break; 234 } 235 switch (entry->type) { 236 case SGML_CATA_ENTITY: 237 case SGML_CATA_PENTITY: 238 case SGML_CATA_DOCTYPE: 239 case SGML_CATA_LINKTYPE: 240 case SGML_CATA_NOTATION: 241 case SGML_CATA_PUBLIC: 242 case SGML_CATA_SYSTEM: 243 case SGML_CATA_DELEGATE: 244 fprintf(out, " \"%s\"", entry->value); break; 245 default: 246 break; 247 } 248 fprintf(out, "\n"); 249} 250 251/** 252 * xmlCatalogConvertEntry: 253 * @entry: the entry 254 * @res: pointer to te number converted 255 * 256 * Free up all the memory associated with catalogs 257 */ 258static void 259xmlCatalogConvertEntry(xmlCatalogEntryPtr entry, int *res) { 260 if ((entry == NULL) || (xmlDefaultXMLCatalogList == NULL)) 261 return; 262 switch (entry->type) { 263 case SGML_CATA_ENTITY: 264 entry->type = XML_CATA_PUBLIC; 265 break; 266 case SGML_CATA_PENTITY: 267 entry->type = XML_CATA_PUBLIC; 268 break; 269 case SGML_CATA_DOCTYPE: 270 entry->type = XML_CATA_PUBLIC; 271 break; 272 case SGML_CATA_LINKTYPE: 273 entry->type = XML_CATA_PUBLIC; 274 break; 275 case SGML_CATA_NOTATION: 276 entry->type = XML_CATA_PUBLIC; 277 break; 278 case SGML_CATA_PUBLIC: 279 entry->type = XML_CATA_PUBLIC; 280 break; 281 case SGML_CATA_SYSTEM: 282 entry->type = XML_CATA_SYSTEM; 283 break; 284 case SGML_CATA_DELEGATE: 285 entry->type = XML_CATA_DELEGATE_PUBLIC; 286 break; 287 case SGML_CATA_CATALOG: 288 entry->type = XML_CATA_CATALOG; 289 break; 290 default: 291 xmlHashRemoveEntry(xmlDefaultCatalog, entry->name, 292 (xmlHashDeallocator) xmlFreeCatalogEntry); 293 return; 294 } 295 /* 296 * Conversion successful, remove from the SGML catalog 297 * and add it to the default XML one 298 */ 299 xmlHashRemoveEntry(xmlDefaultCatalog, entry->name, NULL); 300 entry->parent = xmlDefaultXMLCatalogList; 301 entry->next = NULL; 302 if (xmlDefaultXMLCatalogList->children == NULL) 303 xmlDefaultXMLCatalogList->children = entry; 304 else { 305 xmlCatalogEntryPtr prev; 306 307 prev = xmlDefaultXMLCatalogList->children; 308 while (prev->next != NULL) 309 prev = prev->next; 310 prev->next = entry; 311 } 312 if (res != NULL) 313 (*res)++; 314} 315 316/************************************************************************ 317 * * 318 * Helper function * 319 * * 320 ************************************************************************/ 321 322/** 323 * xmlCatalogUnWrapURN: 324 * @urn: an "urn:publicid:" to unwrapp 325 * 326 * Expand the URN into the equivalent Public Identifier 327 * 328 * Returns the new identifier or NULL, the string must be deallocated 329 * by the caller. 330 */ 331static xmlChar * 332xmlCatalogUnWrapURN(const xmlChar *urn) { 333 xmlChar result[2000]; 334 unsigned int i = 0; 335 336 if (xmlStrncmp(urn, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) 337 return(NULL); 338 urn += sizeof(XML_URN_PUBID) - 1; 339 340 while (*urn != 0) { 341 if (i > sizeof(result) - 3) 342 break; 343 if (*urn == '+') { 344 result[i++] = ' '; 345 urn++; 346 } else if (*urn == ':') { 347 result[i++] = '/'; 348 result[i++] = '/'; 349 urn++; 350 } else if (*urn == ';') { 351 result[i++] = ':'; 352 result[i++] = ':'; 353 urn++; 354 } else if (*urn == '%') { 355 if ((urn[1] == '2') && (urn[1] == 'B')) 356 result[i++] = '+'; 357 else if ((urn[1] == '3') && (urn[1] == 'A')) 358 result[i++] = ':'; 359 else if ((urn[1] == '2') && (urn[1] == 'F')) 360 result[i++] = '/'; 361 else if ((urn[1] == '3') && (urn[1] == 'B')) 362 result[i++] = ';'; 363 else if ((urn[1] == '2') && (urn[1] == '7')) 364 result[i++] = '\''; 365 else if ((urn[1] == '3') && (urn[1] == 'F')) 366 result[i++] = '?'; 367 else if ((urn[1] == '2') && (urn[1] == '3')) 368 result[i++] = '#'; 369 else if ((urn[1] == '2') && (urn[1] == '5')) 370 result[i++] = '%'; 371 else { 372 result[i++] = *urn; 373 urn++; 374 continue; 375 } 376 urn += 3; 377 } else { 378 result[i++] = *urn; 379 urn++; 380 } 381 } 382 result[i] = 0; 383 384 return(xmlStrdup(result)); 385} 386 387/** 388 * xmlParseCatalogFile: 389 * @filename: the filename 390 * 391 * parse an XML file and build a tree. It's like xmlParseFile() 392 * except it bypass all catalog lookups. 393 * 394 * Returns the resulting document tree or NULL in case of error 395 */ 396 397xmlDocPtr 398xmlParseCatalogFile(const char *filename) { 399 xmlDocPtr ret; 400 xmlParserCtxtPtr ctxt; 401 char *directory = NULL; 402 xmlParserInputPtr inputStream; 403 xmlParserInputBufferPtr buf; 404 405 ctxt = xmlNewParserCtxt(); 406 if (ctxt == NULL) { 407 if (xmlDefaultSAXHandler.error != NULL) { 408 xmlDefaultSAXHandler.error(NULL, "out of memory\n"); 409 } 410 return(NULL); 411 } 412 413 buf = xmlParserInputBufferCreateFilename(filename, XML_CHAR_ENCODING_NONE); 414 if (buf == NULL) { 415 xmlFreeParserCtxt(ctxt); 416 return(NULL); 417 } 418 419 inputStream = xmlNewInputStream(ctxt); 420 if (inputStream == NULL) { 421 xmlFreeParserCtxt(ctxt); 422 return(NULL); 423 } 424 425 inputStream->filename = xmlMemStrdup(filename); 426 inputStream->buf = buf; 427 inputStream->base = inputStream->buf->buffer->content; 428 inputStream->cur = inputStream->buf->buffer->content; 429 inputStream->end = 430 &inputStream->buf->buffer->content[inputStream->buf->buffer->use]; 431 432 inputPush(ctxt, inputStream); 433 if ((ctxt->directory == NULL) && (directory == NULL)) 434 directory = xmlParserGetDirectory(filename); 435 if ((ctxt->directory == NULL) && (directory != NULL)) 436 ctxt->directory = directory; 437 ctxt->valid = 0; 438 ctxt->validate = 0; 439 ctxt->loadsubset = 0; 440 ctxt->pedantic = 0; 441 442 xmlParseDocument(ctxt); 443 444 if (ctxt->wellFormed) 445 ret = ctxt->myDoc; 446 else { 447 ret = NULL; 448 xmlFreeDoc(ctxt->myDoc); 449 ctxt->myDoc = NULL; 450 } 451 xmlFreeParserCtxt(ctxt); 452 453 return(ret); 454} 455 456/************************************************************************ 457 * * 458 * The XML Catalog parser * 459 * * 460 ************************************************************************/ 461 462static xmlCatalogEntryPtr 463xmlParseXMLCatalogFile(xmlCatalogPrefer prefer, const xmlChar *filename); 464static xmlCatalogEntryPtr 465xmlParseXMLCatalog(const xmlChar *value, xmlCatalogPrefer prefer, 466 const char *file); 467static void 468xmlParseXMLCatalogNodeList(xmlNodePtr cur, xmlCatalogPrefer prefer, 469 xmlCatalogEntryPtr parent); 470static xmlChar * 471xmlCatalogListXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID, 472 const xmlChar *sysID); 473static xmlChar * 474xmlCatalogListXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI); 475 476 477static xmlCatalogEntryType 478xmlGetXMLCatalogEntryType(const xmlChar *name) { 479 xmlCatalogEntryType type = XML_CATA_NONE; 480 if (xmlStrEqual(name, (const xmlChar *) "system")) 481 type = XML_CATA_SYSTEM; 482 else if (xmlStrEqual(name, (const xmlChar *) "public")) 483 type = XML_CATA_PUBLIC; 484 else if (xmlStrEqual(name, (const xmlChar *) "rewriteSystem")) 485 type = XML_CATA_REWRITE_SYSTEM; 486 else if (xmlStrEqual(name, (const xmlChar *) "delegatePublic")) 487 type = XML_CATA_DELEGATE_PUBLIC; 488 else if (xmlStrEqual(name, (const xmlChar *) "delegateSystem")) 489 type = XML_CATA_DELEGATE_SYSTEM; 490 else if (xmlStrEqual(name, (const xmlChar *) "uri")) 491 type = XML_CATA_URI; 492 else if (xmlStrEqual(name, (const xmlChar *) "rewriteURI")) 493 type = XML_CATA_REWRITE_URI; 494 else if (xmlStrEqual(name, (const xmlChar *) "delegateURI")) 495 type = XML_CATA_DELEGATE_URI; 496 else if (xmlStrEqual(name, (const xmlChar *) "nextCatalog")) 497 type = XML_CATA_NEXT_CATALOG; 498 else if (xmlStrEqual(name, (const xmlChar *) "catalog")) 499 type = XML_CATA_CATALOG; 500 return(type); 501} 502 503static xmlCatalogEntryPtr 504xmlParseXMLCatalogOneNode(xmlNodePtr cur, xmlCatalogEntryType type, 505 const xmlChar *name, const xmlChar *attrName, 506 const xmlChar *uriAttrName, xmlCatalogPrefer prefer) { 507 int ok = 1; 508 xmlChar *uriValue; 509 xmlChar *nameValue = NULL; 510 xmlChar *base = NULL; 511 xmlChar *URL = NULL; 512 xmlCatalogEntryPtr ret = NULL; 513 514 if (attrName != NULL) { 515 nameValue = xmlGetProp(cur, attrName); 516 if (nameValue == NULL) { 517 xmlGenericError(xmlGenericErrorContext, 518 "%s entry lacks '%s'\n", name, attrName); 519 ok = 0; 520 } 521 } 522 uriValue = xmlGetProp(cur, uriAttrName); 523 if (uriValue == NULL) { 524 xmlGenericError(xmlGenericErrorContext, 525 "%s entry lacks '%s'\n", name, uriAttrName); 526 ok = 0; 527 } 528 if (!ok) { 529 if (nameValue != NULL) 530 xmlFree(nameValue); 531 if (uriValue != NULL) 532 xmlFree(uriValue); 533 return(NULL); 534 } 535 536 base = xmlNodeGetBase(cur->doc, cur); 537 URL = xmlBuildURI(uriValue, base); 538 if (URL != NULL) { 539 if (xmlDebugCatalogs > 1) { 540 if (nameValue != NULL) 541 xmlGenericError(xmlGenericErrorContext, 542 "Found %s: '%s' '%s'\n", name, nameValue, URL); 543 else 544 xmlGenericError(xmlGenericErrorContext, 545 "Found %s: '%s'\n", name, URL); 546 } 547 ret = xmlNewCatalogEntry(type, nameValue, URL, prefer); 548 } else { 549 xmlGenericError(xmlGenericErrorContext, 550 "%s entry '%s' broken ?: %s\n", name, uriAttrName, uriValue); 551 } 552 if (nameValue != NULL) 553 xmlFree(nameValue); 554 if (uriValue != NULL) 555 xmlFree(uriValue); 556 if (base != NULL) 557 xmlFree(base); 558 if (URL != NULL) 559 xmlFree(URL); 560 return(ret); 561} 562 563static void 564xmlParseXMLCatalogNode(xmlNodePtr cur, xmlCatalogPrefer prefer, 565 xmlCatalogEntryPtr parent) 566{ 567 xmlChar *uri = NULL; 568 xmlChar *URL = NULL; 569 xmlChar *base = NULL; 570 xmlCatalogEntryPtr entry = NULL; 571 572 if (cur == NULL) 573 return; 574 if (xmlStrEqual(cur->name, BAD_CAST "group")) { 575 xmlChar *prop; 576 577 prop = xmlGetProp(cur, BAD_CAST "prefer"); 578 if (prop != NULL) { 579 if (xmlStrEqual(prop, BAD_CAST "system")) { 580 prefer = XML_CATA_PREFER_SYSTEM; 581 } else if (xmlStrEqual(prop, BAD_CAST "public")) { 582 prefer = XML_CATA_PREFER_PUBLIC; 583 } else { 584 xmlGenericError(xmlGenericErrorContext, 585 "Invalid value for prefer: '%s'\n", prop); 586 } 587 xmlFree(prop); 588 } 589 /* 590 * Recurse to propagate prefer to the subtree 591 * (xml:base handling is automated) 592 */ 593 xmlParseXMLCatalogNodeList(cur->children, prefer, parent); 594 } else if (xmlStrEqual(cur->name, BAD_CAST "public")) { 595 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_PUBLIC, 596 BAD_CAST "public", BAD_CAST "publicId", BAD_CAST "uri", prefer); 597 } else if (xmlStrEqual(cur->name, BAD_CAST "system")) { 598 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_SYSTEM, 599 BAD_CAST "system", BAD_CAST "systemId", BAD_CAST "uri", prefer); 600 } else if (xmlStrEqual(cur->name, BAD_CAST "rewriteSystem")) { 601 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_REWRITE_SYSTEM, 602 BAD_CAST "rewriteSystem", BAD_CAST "systemIdStartString", 603 BAD_CAST "rewritePrefix", prefer); 604 } else if (xmlStrEqual(cur->name, BAD_CAST "delegatePublic")) { 605 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_PUBLIC, 606 BAD_CAST "delegatePublic", BAD_CAST "publicIdStartString", 607 BAD_CAST "catalog", prefer); 608 } else if (xmlStrEqual(cur->name, BAD_CAST "delegateSystem")) { 609 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_SYSTEM, 610 BAD_CAST "delegateSystem", BAD_CAST "systemIdStartString", 611 BAD_CAST "catalog", prefer); 612 } else if (xmlStrEqual(cur->name, BAD_CAST "uri")) { 613 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_URI, 614 BAD_CAST "uri", BAD_CAST "name", 615 BAD_CAST "uri", prefer); 616 } else if (xmlStrEqual(cur->name, BAD_CAST "rewriteURI")) { 617 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_REWRITE_URI, 618 BAD_CAST "rewriteURI", BAD_CAST "uriStartString", 619 BAD_CAST "rewritePrefix", prefer); 620 } else if (xmlStrEqual(cur->name, BAD_CAST "delegateURI")) { 621 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_URI, 622 BAD_CAST "delegateURI", BAD_CAST "uriStartString", 623 BAD_CAST "catalog", prefer); 624 } else if (xmlStrEqual(cur->name, BAD_CAST "nextCatalog")) { 625 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_NEXT_CATALOG, 626 BAD_CAST "nextCatalog", NULL, 627 BAD_CAST "catalog", prefer); 628 } 629 if ((entry != NULL) && (parent != NULL)) { 630 entry->parent = parent; 631 if (parent->children == NULL) 632 parent->children = entry; 633 else { 634 xmlCatalogEntryPtr prev; 635 636 prev = parent->children; 637 while (prev->next != NULL) 638 prev = prev->next; 639 prev->next = entry; 640 } 641 } 642 if (base != NULL) 643 xmlFree(base); 644 if (uri != NULL) 645 xmlFree(uri); 646 if (URL != NULL) 647 xmlFree(URL); 648} 649 650static void 651xmlParseXMLCatalogNodeList(xmlNodePtr cur, xmlCatalogPrefer prefer, 652 xmlCatalogEntryPtr parent) { 653 while (cur != NULL) { 654 if ((cur->ns != NULL) && (cur->ns->href != NULL) && 655 (xmlStrEqual(cur->ns->href, XML_CATALOGS_NAMESPACE))) { 656 xmlParseXMLCatalogNode(cur, prefer, parent); 657 } 658 cur = cur->next; 659 } 660 /* TODO: sort the list according to REWRITE lengths and prefer value */ 661} 662 663static xmlCatalogEntryPtr 664xmlParseXMLCatalog(const xmlChar *value, xmlCatalogPrefer prefer, 665 const char *file) { 666 xmlDocPtr doc; 667 xmlNodePtr cur; 668 xmlChar *prop; 669 xmlCatalogEntryPtr parent = NULL; 670 671 if ((value == NULL) || (file == NULL)) 672 return(NULL); 673 674 if (xmlDebugCatalogs) 675 xmlGenericError(xmlGenericErrorContext, 676 "Parsing catalog %s's content\n", file); 677 678 doc = xmlParseDoc((xmlChar *) value); 679 if (doc == NULL) 680 return(NULL); 681 doc->URL = xmlStrdup((const xmlChar *) file); 682 683 cur = xmlDocGetRootElement(doc); 684 if ((cur != NULL) && (xmlStrEqual(cur->name, BAD_CAST "catalog")) && 685 (cur->ns != NULL) && (cur->ns->href != NULL) && 686 (xmlStrEqual(cur->ns->href, XML_CATALOGS_NAMESPACE))) { 687 688 prop = xmlGetProp(cur, BAD_CAST "prefer"); 689 if (prop != NULL) { 690 if (xmlStrEqual(prop, BAD_CAST "system")) { 691 prefer = XML_CATA_PREFER_SYSTEM; 692 } else if (xmlStrEqual(prop, BAD_CAST "public")) { 693 prefer = XML_CATA_PREFER_PUBLIC; 694 } else { 695 xmlGenericError(xmlGenericErrorContext, 696 "Invalid value for prefer: '%s'\n", 697 prop); 698 } 699 xmlFree(prop); 700 } 701 parent = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL, 702 (const xmlChar *)file, prefer); 703 if (parent == NULL) { 704 xmlFreeDoc(doc); 705 return(NULL); 706 } 707 708 cur = cur->children; 709 xmlParseXMLCatalogNodeList(cur, prefer, parent); 710 } else { 711 xmlGenericError(xmlGenericErrorContext, 712 "File %s is not an XML Catalog\n", file); 713 xmlFreeDoc(doc); 714 return(NULL); 715 } 716 xmlFreeDoc(doc); 717 return(parent); 718} 719 720static xmlCatalogEntryPtr 721xmlParseXMLCatalogFile(xmlCatalogPrefer prefer, const xmlChar *filename) { 722 xmlDocPtr doc; 723 xmlNodePtr cur; 724 xmlChar *prop; 725 xmlCatalogEntryPtr parent = NULL; 726 727 if (filename == NULL) 728 return(NULL); 729 730 doc = xmlParseCatalogFile((const char *) filename); 731 if (doc == NULL) { 732 if (xmlDebugCatalogs) 733 xmlGenericError(xmlGenericErrorContext, 734 "Failed to parse catalog %s\n", filename); 735 return(NULL); 736 } 737 738 if (xmlDebugCatalogs) 739 xmlGenericError(xmlGenericErrorContext, 740 "Parsing catalog %s\n", filename); 741 742 cur = xmlDocGetRootElement(doc); 743 if ((cur != NULL) && (xmlStrEqual(cur->name, BAD_CAST "catalog")) && 744 (cur->ns != NULL) && (cur->ns->href != NULL) && 745 (xmlStrEqual(cur->ns->href, XML_CATALOGS_NAMESPACE))) { 746 747 parent = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL, 748 (const xmlChar *)filename, prefer); 749 if (parent == NULL) { 750 xmlFreeDoc(doc); 751 return(NULL); 752 } 753 754 prop = xmlGetProp(cur, BAD_CAST "prefer"); 755 if (prop != NULL) { 756 if (xmlStrEqual(prop, BAD_CAST "system")) { 757 prefer = XML_CATA_PREFER_SYSTEM; 758 } else if (xmlStrEqual(prop, BAD_CAST "public")) { 759 prefer = XML_CATA_PREFER_PUBLIC; 760 } else { 761 xmlGenericError(xmlGenericErrorContext, 762 "Invalid value for prefer: '%s'\n", 763 prop); 764 } 765 xmlFree(prop); 766 } 767 cur = cur->children; 768 xmlParseXMLCatalogNodeList(cur, prefer, parent); 769 } else { 770 xmlGenericError(xmlGenericErrorContext, 771 "File %s is not an XML Catalog\n", filename); 772 xmlFreeDoc(doc); 773 return(NULL); 774 } 775 xmlFreeDoc(doc); 776 return(parent); 777} 778 779/** 780 * xmlFetchXMLCatalogFile: 781 * @catal: an existing but incomplete catalog entry 782 * 783 * Fetch and parse the subcatalog referenced by an entry 784 * It tries to be thread safe but by lack of an atomic test and 785 * set there is a risk of loosing memory. 786 * 787 * Returns 0 in case of success, -1 otherwise 788 */ 789static int 790xmlFetchXMLCatalogFile(xmlCatalogEntryPtr catal) { 791 xmlCatalogEntryPtr children = NULL, doc; 792 793 if (catal == NULL) 794 return(-1); 795 if (catal->value == NULL) 796 return(-1); 797 if (catal->children != NULL) 798 return(-1); 799 800 if (xmlCatalogXMLFiles != NULL) 801 children = (xmlCatalogEntryPtr) 802 xmlHashLookup(xmlCatalogXMLFiles, catal->value); 803 if (children != NULL) { 804 catal->children = children; 805 catal->dealloc = 0; 806 return(0); 807 } 808 809 /* 810 * Fetch and parse 811 */ 812 doc = xmlParseXMLCatalogFile(catal->prefer, catal->value); 813 if (doc == NULL) { 814 catal->type = XML_CATA_BROKEN_CATALOG; 815 return(-1); 816 } 817 if ((catal->type == XML_CATA_CATALOG) && 818 (doc->type == XML_CATA_CATALOG)) { 819 children = doc->children; 820 doc->children = NULL; 821 xmlFreeCatalogEntryList(doc); 822 } else { 823 children = doc; 824 } 825 826 /* 827 * Where a real test and set would be needed ! 828 */ 829 if (catal->children == NULL) { 830 catal->children = children; 831 catal->dealloc = 1; 832 if (xmlCatalogXMLFiles == NULL) 833 xmlCatalogXMLFiles = xmlHashCreate(10); 834 if (xmlCatalogXMLFiles != NULL) { 835 if (children != NULL) 836 xmlHashAddEntry(xmlCatalogXMLFiles, catal->value, children); 837 } 838 } else { 839 /* 840 * Another thread filled it before us 841 */ 842 xmlFreeCatalogEntryList(children); 843 } 844 return(0); 845} 846 847static int 848xmlDumpXMLCatalog(FILE *out, xmlCatalogEntryPtr catal) { 849 int ret; 850 xmlDocPtr doc; 851 xmlNsPtr ns; 852 xmlDtdPtr dtd; 853 xmlNodePtr node, catalog; 854 xmlOutputBufferPtr buf; 855 xmlCatalogEntryPtr cur; 856 857 /* 858 * Rebuild a catalog 859 */ 860 doc = xmlNewDoc(NULL); 861 if (doc == NULL) 862 return(-1); 863 dtd = xmlNewDtd(doc, BAD_CAST "catalog", 864 BAD_CAST "-//OASIS//DTD Entity Resolution XML Catalog V1.0//EN", 865BAD_CAST "http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd"); 866 867 xmlAddChild((xmlNodePtr) doc, (xmlNodePtr) dtd); 868 869 ns = xmlNewNs(NULL, XML_CATALOGS_NAMESPACE, NULL); 870 if (ns == NULL) { 871 xmlFreeDoc(doc); 872 return(-1); 873 } 874 catalog = xmlNewDocNode(doc, ns, BAD_CAST "catalog", NULL); 875 if (catalog == NULL) { 876 xmlFreeNs(ns); 877 xmlFreeDoc(doc); 878 return(-1); 879 } 880 catalog->nsDef = ns; 881 xmlAddChild((xmlNodePtr) doc, catalog); 882 883 /* 884 * add all the catalog entries 885 */ 886 cur = catal; 887 while (cur != NULL) { 888 switch (cur->type) { 889 case XML_CATA_BROKEN_CATALOG: 890 case XML_CATA_CATALOG: 891 if (cur == catal) { 892 cur = cur->children; 893 continue; 894 } 895 break; 896 case XML_CATA_NEXT_CATALOG: 897 node = xmlNewDocNode(doc, ns, BAD_CAST "nextCatalog", NULL); 898 xmlSetProp(node, BAD_CAST "catalog", cur->value); 899 xmlAddChild(catalog, node); 900 break; 901 case XML_CATA_NONE: 902 break; 903 case XML_CATA_PUBLIC: 904 node = xmlNewDocNode(doc, ns, BAD_CAST "public", NULL); 905 xmlSetProp(node, BAD_CAST "publicId", cur->name); 906 xmlSetProp(node, BAD_CAST "uri", cur->value); 907 xmlAddChild(catalog, node); 908 break; 909 case XML_CATA_SYSTEM: 910 node = xmlNewDocNode(doc, ns, BAD_CAST "system", NULL); 911 xmlSetProp(node, BAD_CAST "systemId", cur->name); 912 xmlSetProp(node, BAD_CAST "uri", cur->value); 913 xmlAddChild(catalog, node); 914 break; 915 case XML_CATA_REWRITE_SYSTEM: 916 node = xmlNewDocNode(doc, ns, BAD_CAST "rewriteSystem", NULL); 917 xmlSetProp(node, BAD_CAST "systemIdStartString", cur->name); 918 xmlSetProp(node, BAD_CAST "rewritePrefix", cur->value); 919 xmlAddChild(catalog, node); 920 break; 921 case XML_CATA_DELEGATE_PUBLIC: 922 node = xmlNewDocNode(doc, ns, BAD_CAST "delegatePublic", NULL); 923 xmlSetProp(node, BAD_CAST "publicIdStartString", cur->name); 924 xmlSetProp(node, BAD_CAST "catalog", cur->value); 925 xmlAddChild(catalog, node); 926 break; 927 case XML_CATA_DELEGATE_SYSTEM: 928 node = xmlNewDocNode(doc, ns, BAD_CAST "delegateSystem", NULL); 929 xmlSetProp(node, BAD_CAST "systemIdStartString", cur->name); 930 xmlSetProp(node, BAD_CAST "catalog", cur->value); 931 xmlAddChild(catalog, node); 932 break; 933 case XML_CATA_URI: 934 node = xmlNewDocNode(doc, ns, BAD_CAST "uri", NULL); 935 xmlSetProp(node, BAD_CAST "name", cur->name); 936 xmlSetProp(node, BAD_CAST "uri", cur->value); 937 xmlAddChild(catalog, node); 938 break; 939 case XML_CATA_REWRITE_URI: 940 node = xmlNewDocNode(doc, ns, BAD_CAST "rewriteURI", NULL); 941 xmlSetProp(node, BAD_CAST "uriStartString", cur->name); 942 xmlSetProp(node, BAD_CAST "rewritePrefix", cur->value); 943 xmlAddChild(catalog, node); 944 break; 945 case XML_CATA_DELEGATE_URI: 946 node = xmlNewDocNode(doc, ns, BAD_CAST "delegateURI", NULL); 947 xmlSetProp(node, BAD_CAST "uriStartString", cur->name); 948 xmlSetProp(node, BAD_CAST "catalog", cur->value); 949 xmlAddChild(catalog, node); 950 break; 951 case SGML_CATA_SYSTEM: 952 case SGML_CATA_PUBLIC: 953 case SGML_CATA_ENTITY: 954 case SGML_CATA_PENTITY: 955 case SGML_CATA_DOCTYPE: 956 case SGML_CATA_LINKTYPE: 957 case SGML_CATA_NOTATION: 958 case SGML_CATA_DELEGATE: 959 case SGML_CATA_BASE: 960 case SGML_CATA_CATALOG: 961 case SGML_CATA_DOCUMENT: 962 case SGML_CATA_SGMLDECL: 963 break; 964 } 965 cur = cur->next; 966 } 967 968 /* 969 * reserialize it 970 */ 971 buf = xmlOutputBufferCreateFile(out, NULL); 972 if (buf == NULL) { 973 xmlFreeDoc(doc); 974 return(-1); 975 } 976 ret = xmlSaveFormatFileTo(buf, doc, NULL, 1); 977 978 /* 979 * Free it 980 */ 981 xmlFreeDoc(doc); 982 983 return(ret); 984} 985 986/** 987 * xmlAddXMLCatalog: 988 * @catal: top of an XML catalog 989 * @type: the type of record to add to the catalog 990 * @orig: the system, public or prefix to match (or NULL) 991 * @replace: the replacement value for the match 992 * 993 * Add an entry in the XML catalog, it may overwrite existing but 994 * different entries. 995 * 996 * Returns 0 if successful, -1 otherwise 997 */ 998static int 999xmlAddXMLCatalog(xmlCatalogEntryPtr catal, const xmlChar *type, 1000 const xmlChar *orig, const xmlChar *replace) { 1001 xmlCatalogEntryPtr cur; 1002 xmlCatalogEntryType typ; 1003 1004 if ((catal == NULL) || 1005 ((catal->type != XML_CATA_CATALOG) && 1006 (catal->type != XML_CATA_BROKEN_CATALOG))) 1007 return(-1); 1008 typ = xmlGetXMLCatalogEntryType(type); 1009 if (typ == XML_CATA_NONE) { 1010 if (xmlDebugCatalogs) 1011 xmlGenericError(xmlGenericErrorContext, 1012 "Failed to add unknown element %s to catalog\n", type); 1013 return(-1); 1014 } 1015 1016 cur = catal->children; 1017 /* 1018 * Might be a simple "update in place" 1019 */ 1020 if (cur != NULL) { 1021 while (cur != NULL) { 1022 if ((orig != NULL) && (cur->type == typ) && 1023 (xmlStrEqual(orig, cur->name))) { 1024 if (xmlDebugCatalogs) 1025 xmlGenericError(xmlGenericErrorContext, 1026 "Updating element %s to catalog\n", type); 1027 if (cur->value != NULL) 1028 xmlFree(cur->value); 1029 cur->value = xmlStrdup(replace); 1030 return(0); 1031 } 1032 if (cur->next == NULL) 1033 break; 1034 cur = cur->next; 1035 } 1036 } 1037 if (xmlDebugCatalogs) 1038 xmlGenericError(xmlGenericErrorContext, 1039 "Adding element %s to catalog\n", type); 1040 if (cur == NULL) 1041 catal->children = xmlNewCatalogEntry(typ, orig, replace, catal->prefer); 1042 else 1043 cur->next = xmlNewCatalogEntry(typ, orig, replace, catal->prefer); 1044 return(0); 1045} 1046 1047/** 1048 * xmlDelXMLCatalog: 1049 * @catal: top of an XML catalog 1050 * @value: the value to remove from teh catalog 1051 * 1052 * Remove entries in the XML catalog where the value or the URI 1053 * is equal to @value 1054 * 1055 * Returns the number of entries removed if successful, -1 otherwise 1056 */ 1057static int 1058xmlDelXMLCatalog(xmlCatalogEntryPtr catal, const xmlChar *value) { 1059 xmlCatalogEntryPtr cur, prev, tmp; 1060 int ret = 0; 1061 1062 if ((catal == NULL) || 1063 ((catal->type != XML_CATA_CATALOG) && 1064 (catal->type != XML_CATA_BROKEN_CATALOG))) 1065 return(-1); 1066 if (value == NULL) 1067 return(-1); 1068 1069 /* 1070 * Scan the children 1071 */ 1072 cur = catal->children; 1073 prev = NULL; 1074 while (cur != NULL) { 1075 if (((cur->name != NULL) && (xmlStrEqual(value, cur->name))) || 1076 (xmlStrEqual(value, cur->value))) { 1077 if (xmlDebugCatalogs) { 1078 if (cur->name != NULL) 1079 xmlGenericError(xmlGenericErrorContext, 1080 "Removing element %s from catalog\n", cur->name); 1081 else 1082 xmlGenericError(xmlGenericErrorContext, 1083 "Removing element %s from catalog\n", cur->value); 1084 } 1085 ret++; 1086 tmp = cur; 1087 cur = tmp->next; 1088 if (prev == NULL) { 1089 catal->children = cur; 1090 } else { 1091 prev->next = cur; 1092 } 1093 xmlFreeCatalogEntry(tmp); 1094 continue; 1095 } 1096 prev = cur; 1097 cur = cur->next; 1098 } 1099 return(ret); 1100} 1101 1102/** 1103 * xmlCatalogXMLResolve: 1104 * @catal: a catalog list 1105 * @pubId: the public ID string 1106 * @sysId: the system ID string 1107 * 1108 * Do a complete resolution lookup of an External Identifier for a 1109 * list of catalog entries. 1110 * 1111 * Implements (or tries to) 7.1. External Identifier Resolution 1112 * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html 1113 * 1114 * Returns the URI of the resource or NULL if not found 1115 */ 1116static xmlChar * 1117xmlCatalogXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID, 1118 const xmlChar *sysID) { 1119 xmlChar *ret = NULL; 1120 xmlCatalogEntryPtr cur; 1121 int haveDelegate = 0; 1122 int haveNext = 0; 1123 1124 /* 1125 * First tries steps 2/ 3/ 4/ if a system ID is provided. 1126 */ 1127 if (sysID != NULL) { 1128 xmlCatalogEntryPtr rewrite = NULL; 1129 int lenrewrite = 0, len; 1130 cur = catal; 1131 haveDelegate = 0; 1132 while (cur != NULL) { 1133 switch (cur->type) { 1134 case XML_CATA_SYSTEM: 1135 if (xmlStrEqual(sysID, cur->name)) { 1136 if (xmlDebugCatalogs) 1137 xmlGenericError(xmlGenericErrorContext, 1138 "Found system match %s\n", cur->name); 1139 return(xmlStrdup(cur->value)); 1140 } 1141 break; 1142 case XML_CATA_REWRITE_SYSTEM: 1143 len = xmlStrlen(cur->name); 1144 if ((len > lenrewrite) && 1145 (!xmlStrncmp(sysID, cur->name, len))) { 1146 lenrewrite = len; 1147 rewrite = cur; 1148 } 1149 break; 1150 case XML_CATA_DELEGATE_SYSTEM: 1151 if (!xmlStrncmp(sysID, cur->name, xmlStrlen(cur->name))) 1152 haveDelegate++; 1153 break; 1154 case XML_CATA_NEXT_CATALOG: 1155 haveNext++; 1156 break; 1157 default: 1158 break; 1159 } 1160 cur = cur->next; 1161 } 1162 if (rewrite != NULL) { 1163 if (xmlDebugCatalogs) 1164 xmlGenericError(xmlGenericErrorContext, 1165 "Using rewriting rule %s\n", rewrite->name); 1166 ret = xmlStrdup(rewrite->value); 1167 if (ret != NULL) 1168 ret = xmlStrcat(ret, &sysID[lenrewrite]); 1169 return(ret); 1170 } 1171 if (haveDelegate) { 1172 const xmlChar *delegates[MAX_DELEGATE]; 1173 int nbList = 0, i; 1174 1175 /* 1176 * Assume the entries have been sorted by decreasing substring 1177 * matches when the list was produced. 1178 */ 1179 cur = catal; 1180 while (cur != NULL) { 1181 if ((cur->type == XML_CATA_DELEGATE_SYSTEM) && 1182 (!xmlStrncmp(sysID, cur->name, xmlStrlen(cur->name)))) { 1183 for (i = 0;i < nbList;i++) 1184 if (xmlStrEqual(cur->value, delegates[i])) 1185 break; 1186 if (i < nbList) { 1187 cur = cur->next; 1188 continue; 1189 } 1190 if (nbList < MAX_DELEGATE) 1191 delegates[nbList++] = cur->value; 1192 1193 if (cur->children == NULL) { 1194 xmlFetchXMLCatalogFile(cur); 1195 } 1196 if (cur->children != NULL) { 1197 if (xmlDebugCatalogs) 1198 xmlGenericError(xmlGenericErrorContext, 1199 "Trying system delegate %s\n", cur->value); 1200 ret = xmlCatalogListXMLResolve(cur->children, NULL, 1201 sysID); 1202 if (ret != NULL) 1203 return(ret); 1204 } 1205 } 1206 cur = cur->next; 1207 } 1208 /* 1209 * Apply the cut algorithm explained in 4/ 1210 */ 1211 return(XML_CATAL_BREAK); 1212 } 1213 } 1214 /* 1215 * Then tries 5/ 6/ if a public ID is provided 1216 */ 1217 if (pubID != NULL) { 1218 cur = catal; 1219 haveDelegate = 0; 1220 while (cur != NULL) { 1221 switch (cur->type) { 1222 case XML_CATA_PUBLIC: 1223 if (xmlStrEqual(pubID, cur->name)) { 1224 if (xmlDebugCatalogs) 1225 xmlGenericError(xmlGenericErrorContext, 1226 "Found public match %s\n", cur->name); 1227 return(xmlStrdup(cur->value)); 1228 } 1229 break; 1230 case XML_CATA_DELEGATE_PUBLIC: 1231 if (!xmlStrncmp(pubID, cur->name, xmlStrlen(cur->name)) && 1232 (cur->prefer == XML_CATA_PREFER_PUBLIC)) 1233 haveDelegate++; 1234 break; 1235 case XML_CATA_NEXT_CATALOG: 1236 if (sysID == NULL) 1237 haveNext++; 1238 break; 1239 default: 1240 break; 1241 } 1242 cur = cur->next; 1243 } 1244 if (haveDelegate) { 1245 const xmlChar *delegates[MAX_DELEGATE]; 1246 int nbList = 0, i; 1247 1248 /* 1249 * Assume the entries have been sorted by decreasing substring 1250 * matches when the list was produced. 1251 */ 1252 cur = catal; 1253 while (cur != NULL) { 1254 if ((cur->type == XML_CATA_DELEGATE_PUBLIC) && 1255 (cur->prefer == XML_CATA_PREFER_PUBLIC) && 1256 (!xmlStrncmp(pubID, cur->name, xmlStrlen(cur->name)))) { 1257 1258 for (i = 0;i < nbList;i++) 1259 if (xmlStrEqual(cur->value, delegates[i])) 1260 break; 1261 if (i < nbList) { 1262 cur = cur->next; 1263 continue; 1264 } 1265 if (nbList < MAX_DELEGATE) 1266 delegates[nbList++] = cur->value; 1267 1268 if (cur->children == NULL) { 1269 xmlFetchXMLCatalogFile(cur); 1270 } 1271 if (cur->children != NULL) { 1272 if (xmlDebugCatalogs) 1273 xmlGenericError(xmlGenericErrorContext, 1274 "Trying public delegate %s\n", cur->value); 1275 ret = xmlCatalogListXMLResolve(cur->children, pubID, 1276 NULL); 1277 if (ret != NULL) 1278 return(ret); 1279 } 1280 } 1281 cur = cur->next; 1282 } 1283 /* 1284 * Apply the cut algorithm explained in 4/ 1285 */ 1286 return(XML_CATAL_BREAK); 1287 } 1288 } 1289 if (haveNext) { 1290 cur = catal; 1291 while (cur != NULL) { 1292 if (cur->type == XML_CATA_NEXT_CATALOG) { 1293 if (cur->children == NULL) { 1294 xmlFetchXMLCatalogFile(cur); 1295 } 1296 if (cur->children != NULL) { 1297 ret = xmlCatalogListXMLResolve(cur->children, pubID, sysID); 1298 if (ret != NULL) 1299 return(ret); 1300 } 1301 } 1302 cur = cur->next; 1303 } 1304 } 1305 1306 return(NULL); 1307} 1308 1309/** 1310 * xmlCatalogXMLResolveURI: 1311 * @catal: a catalog list 1312 * @URI: the URI 1313 * @sysId: the system ID string 1314 * 1315 * Do a complete resolution lookup of an External Identifier for a 1316 * list of catalog entries. 1317 * 1318 * Implements (or tries to) 7.2.2. URI Resolution 1319 * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html 1320 * 1321 * Returns the URI of the resource or NULL if not found 1322 */ 1323static xmlChar * 1324xmlCatalogXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI) { 1325 xmlChar *ret = NULL; 1326 xmlCatalogEntryPtr cur; 1327 int haveDelegate = 0; 1328 int haveNext = 0; 1329 xmlCatalogEntryPtr rewrite = NULL; 1330 int lenrewrite = 0, len; 1331 1332 if (catal == NULL) 1333 return(NULL); 1334 1335 if (URI == NULL) 1336 return(NULL); 1337 1338 /* 1339 * First tries steps 2/ 3/ 4/ if a system ID is provided. 1340 */ 1341 cur = catal; 1342 haveDelegate = 0; 1343 while (cur != NULL) { 1344 switch (cur->type) { 1345 case XML_CATA_URI: 1346 if (xmlStrEqual(URI, cur->name)) { 1347 if (xmlDebugCatalogs) 1348 xmlGenericError(xmlGenericErrorContext, 1349 "Found URI match %s\n", cur->name); 1350 return(xmlStrdup(cur->value)); 1351 } 1352 break; 1353 case XML_CATA_REWRITE_URI: 1354 len = xmlStrlen(cur->name); 1355 if ((len > lenrewrite) && 1356 (!xmlStrncmp(URI, cur->name, len))) { 1357 lenrewrite = len; 1358 rewrite = cur; 1359 } 1360 break; 1361 case XML_CATA_DELEGATE_URI: 1362 if (!xmlStrncmp(URI, cur->name, xmlStrlen(cur->name))) 1363 haveDelegate++; 1364 break; 1365 case XML_CATA_NEXT_CATALOG: 1366 haveNext++; 1367 break; 1368 default: 1369 break; 1370 } 1371 cur = cur->next; 1372 } 1373 if (rewrite != NULL) { 1374 if (xmlDebugCatalogs) 1375 xmlGenericError(xmlGenericErrorContext, 1376 "Using rewriting rule %s\n", rewrite->name); 1377 ret = xmlStrdup(rewrite->value); 1378 if (ret != NULL) 1379 ret = xmlStrcat(ret, &URI[lenrewrite]); 1380 return(ret); 1381 } 1382 if (haveDelegate) { 1383 const xmlChar *delegates[MAX_DELEGATE]; 1384 int nbList = 0, i; 1385 1386 /* 1387 * Assume the entries have been sorted by decreasing substring 1388 * matches when the list was produced. 1389 */ 1390 cur = catal; 1391 while (cur != NULL) { 1392 if ((cur->type == XML_CATA_DELEGATE_SYSTEM) && 1393 (!xmlStrncmp(URI, cur->name, xmlStrlen(cur->name)))) { 1394 for (i = 0;i < nbList;i++) 1395 if (xmlStrEqual(cur->value, delegates[i])) 1396 break; 1397 if (i < nbList) { 1398 cur = cur->next; 1399 continue; 1400 } 1401 if (nbList < MAX_DELEGATE) 1402 delegates[nbList++] = cur->value; 1403 1404 if (cur->children == NULL) { 1405 xmlFetchXMLCatalogFile(cur); 1406 } 1407 if (cur->children != NULL) { 1408 if (xmlDebugCatalogs) 1409 xmlGenericError(xmlGenericErrorContext, 1410 "Trying URI delegate %s\n", cur->value); 1411 ret = xmlCatalogListXMLResolveURI(cur->children, URI); 1412 if (ret != NULL) 1413 return(ret); 1414 } 1415 } 1416 cur = cur->next; 1417 } 1418 /* 1419 * Apply the cut algorithm explained in 4/ 1420 */ 1421 return(XML_CATAL_BREAK); 1422 } 1423 if (haveNext) { 1424 cur = catal; 1425 while (cur != NULL) { 1426 if (cur->type == XML_CATA_NEXT_CATALOG) { 1427 if (cur->children == NULL) { 1428 xmlFetchXMLCatalogFile(cur); 1429 } 1430 if (cur->children != NULL) { 1431 ret = xmlCatalogListXMLResolveURI(cur->children, URI); 1432 if (ret != NULL) 1433 return(ret); 1434 } 1435 } 1436 cur = cur->next; 1437 } 1438 } 1439 1440 return(NULL); 1441} 1442 1443/** 1444 * xmlCatalogListXMLResolve: 1445 * @catal: a catalog list 1446 * @pubId: the public ID string 1447 * @sysId: the system ID string 1448 * 1449 * Do a complete resolution lookup of an External Identifier for a 1450 * list of catalogs 1451 * 1452 * Implements (or tries to) 7.1. External Identifier Resolution 1453 * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html 1454 * 1455 * Returns the URI of the resource or NULL if not found 1456 */ 1457static xmlChar * 1458xmlCatalogListXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID, 1459 const xmlChar *sysID) { 1460 xmlChar *ret = NULL; 1461 xmlChar *urnID = NULL; 1462 1463 if (catal == NULL) 1464 return(NULL); 1465 if ((pubID == NULL) && (sysID == NULL)) 1466 return(NULL); 1467 1468 if (!xmlStrncmp(pubID, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) { 1469 urnID = xmlCatalogUnWrapURN(pubID); 1470 if (xmlDebugCatalogs) { 1471 if (urnID == NULL) 1472 xmlGenericError(xmlGenericErrorContext, 1473 "Public URN ID %s expanded to NULL\n", pubID); 1474 else 1475 xmlGenericError(xmlGenericErrorContext, 1476 "Public URN ID expanded to %s\n", urnID); 1477 } 1478 ret = xmlCatalogListXMLResolve(catal, urnID, sysID); 1479 if (urnID != NULL) 1480 xmlFree(urnID); 1481 return(ret); 1482 } 1483 if (!xmlStrncmp(sysID, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) { 1484 urnID = xmlCatalogUnWrapURN(sysID); 1485 if (xmlDebugCatalogs) { 1486 if (urnID == NULL) 1487 xmlGenericError(xmlGenericErrorContext, 1488 "System URN ID %s expanded to NULL\n", sysID); 1489 else 1490 xmlGenericError(xmlGenericErrorContext, 1491 "System URN ID expanded to %s\n", urnID); 1492 } 1493 if (pubID == NULL) 1494 ret = xmlCatalogListXMLResolve(catal, urnID, NULL); 1495 else if (xmlStrEqual(pubID, urnID)) 1496 ret = xmlCatalogListXMLResolve(catal, pubID, NULL); 1497 else { 1498 ret = xmlCatalogListXMLResolve(catal, pubID, NULL); 1499 } 1500 if (urnID != NULL) 1501 xmlFree(urnID); 1502 return(ret); 1503 } 1504 while (catal != NULL) { 1505 if (catal->type == XML_CATA_CATALOG) { 1506 if (catal->children == NULL) { 1507 xmlFetchXMLCatalogFile(catal); 1508 } 1509 if (catal->children != NULL) { 1510 ret = xmlCatalogXMLResolve(catal->children, pubID, sysID); 1511 if (ret != NULL) 1512 return(ret); 1513 } 1514 } 1515 catal = catal->next; 1516 } 1517 return(ret); 1518} 1519 1520/** 1521 * xmlCatalogListXMLResolveURI: 1522 * @catal: a catalog list 1523 * @URI: the URI 1524 * 1525 * Do a complete resolution lookup of an URI for a list of catalogs 1526 * 1527 * Implements (or tries to) 7.2. URI Resolution 1528 * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html 1529 * 1530 * Returns the URI of the resource or NULL if not found 1531 */ 1532static xmlChar * 1533xmlCatalogListXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI) { 1534 xmlChar *ret = NULL; 1535 xmlChar *urnID = NULL; 1536 1537 if (catal == NULL) 1538 return(NULL); 1539 if (URI == NULL) 1540 return(NULL); 1541 1542 if (!xmlStrncmp(URI, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) { 1543 urnID = xmlCatalogUnWrapURN(URI); 1544 if (xmlDebugCatalogs) { 1545 if (urnID == NULL) 1546 xmlGenericError(xmlGenericErrorContext, 1547 "URN ID %s expanded to NULL\n", URI); 1548 else 1549 xmlGenericError(xmlGenericErrorContext, 1550 "URN ID expanded to %s\n", urnID); 1551 } 1552 ret = xmlCatalogListXMLResolve(catal, urnID, NULL); 1553 if (urnID != NULL) 1554 xmlFree(urnID); 1555 return(ret); 1556 } 1557 while (catal != NULL) { 1558 if (catal->type == XML_CATA_CATALOG) { 1559 if (catal->children == NULL) { 1560 xmlFetchXMLCatalogFile(catal); 1561 } 1562 if (catal->children != NULL) { 1563 ret = xmlCatalogXMLResolveURI(catal->children, URI); 1564 if (ret != NULL) 1565 return(ret); 1566 } 1567 } 1568 catal = catal->next; 1569 } 1570 return(ret); 1571} 1572 1573/************************************************************************ 1574 * * 1575 * The SGML Catalog parser * 1576 * * 1577 ************************************************************************/ 1578 1579 1580#define RAW *cur 1581#define NEXT cur++; 1582#define SKIP(x) cur += x; 1583 1584#define SKIP_BLANKS while (IS_BLANK(*cur)) NEXT; 1585 1586static const xmlChar * 1587xmlParseSGMLCatalogComment(const xmlChar *cur) { 1588 if ((cur[0] != '-') || (cur[1] != '-')) 1589 return(cur); 1590 SKIP(2); 1591 while ((cur[0] != 0) && ((cur[0] != '-') || ((cur[1] != '-')))) 1592 NEXT; 1593 if (cur[0] == 0) { 1594 return(NULL); 1595 } 1596 return(cur + 2); 1597} 1598 1599static const xmlChar * 1600xmlParseSGMLCatalogPubid(const xmlChar *cur, xmlChar **id) { 1601 xmlChar *buf = NULL; 1602 int len = 0; 1603 int size = 50; 1604 xmlChar stop; 1605 int count = 0; 1606 1607 *id = NULL; 1608 1609 if (RAW == '"') { 1610 NEXT; 1611 stop = '"'; 1612 } else if (RAW == '\'') { 1613 NEXT; 1614 stop = '\''; 1615 } else { 1616 stop = ' '; 1617 } 1618 buf = (xmlChar *) xmlMalloc(size * sizeof(xmlChar)); 1619 if (buf == NULL) { 1620 xmlGenericError(xmlGenericErrorContext, 1621 "malloc of %d byte failed\n", size); 1622 return(NULL); 1623 } 1624 while (xmlIsPubidChar(*cur)) { 1625 if ((*cur == stop) && (stop != ' ')) 1626 break; 1627 if ((stop == ' ') && (IS_BLANK(*cur))) 1628 break; 1629 if (len + 1 >= size) { 1630 size *= 2; 1631 buf = (xmlChar *) xmlRealloc(buf, size * sizeof(xmlChar)); 1632 if (buf == NULL) { 1633 xmlGenericError(xmlGenericErrorContext, 1634 "realloc of %d byte failed\n", size); 1635 return(NULL); 1636 } 1637 } 1638 buf[len++] = *cur; 1639 count++; 1640 NEXT; 1641 } 1642 buf[len] = 0; 1643 if (stop == ' ') { 1644 if (!IS_BLANK(*cur)) { 1645 xmlFree(buf); 1646 return(NULL); 1647 } 1648 } else { 1649 if (*cur != stop) { 1650 xmlFree(buf); 1651 return(NULL); 1652 } 1653 NEXT; 1654 } 1655 *id = buf; 1656 return(cur); 1657} 1658 1659static const xmlChar * 1660xmlParseSGMLCatalogName(const xmlChar *cur, xmlChar **name) { 1661 xmlChar buf[XML_MAX_NAMELEN + 5]; 1662 int len = 0; 1663 int c; 1664 1665 *name = NULL; 1666 1667 /* 1668 * Handler for more complex cases 1669 */ 1670 c = *cur; 1671 if ((!IS_LETTER(c) && (c != '_') && (c != ':'))) { 1672 return(NULL); 1673 } 1674 1675 while (((IS_LETTER(c)) || (IS_DIGIT(c)) || 1676 (c == '.') || (c == '-') || 1677 (c == '_') || (c == ':'))) { 1678 buf[len++] = c; 1679 cur++; 1680 c = *cur; 1681 if (len >= XML_MAX_NAMELEN) 1682 return(NULL); 1683 } 1684 *name = xmlStrndup(buf, len); 1685 return(cur); 1686} 1687 1688static xmlCatalogEntryType 1689xmlGetSGMLCatalogEntryType(const xmlChar *name) { 1690 xmlCatalogEntryType type = XML_CATA_NONE; 1691 if (xmlStrEqual(name, (const xmlChar *) "SYSTEM")) 1692 type = SGML_CATA_SYSTEM; 1693 else if (xmlStrEqual(name, (const xmlChar *) "PUBLIC")) 1694 type = SGML_CATA_PUBLIC; 1695 else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE")) 1696 type = SGML_CATA_DELEGATE; 1697 else if (xmlStrEqual(name, (const xmlChar *) "ENTITY")) 1698 type = SGML_CATA_ENTITY; 1699 else if (xmlStrEqual(name, (const xmlChar *) "DOCTYPE")) 1700 type = SGML_CATA_DOCTYPE; 1701 else if (xmlStrEqual(name, (const xmlChar *) "LINKTYPE")) 1702 type = SGML_CATA_LINKTYPE; 1703 else if (xmlStrEqual(name, (const xmlChar *) "NOTATION")) 1704 type = SGML_CATA_NOTATION; 1705 else if (xmlStrEqual(name, (const xmlChar *) "SGMLDECL")) 1706 type = SGML_CATA_SGMLDECL; 1707 else if (xmlStrEqual(name, (const xmlChar *) "DOCUMENT")) 1708 type = SGML_CATA_DOCUMENT; 1709 else if (xmlStrEqual(name, (const xmlChar *) "CATALOG")) 1710 type = SGML_CATA_CATALOG; 1711 else if (xmlStrEqual(name, (const xmlChar *) "BASE")) 1712 type = SGML_CATA_BASE; 1713 else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE")) 1714 type = SGML_CATA_DELEGATE; 1715 return(type); 1716} 1717 1718static int 1719xmlParseSGMLCatalog(const xmlChar *value, const char *file) { 1720 const xmlChar *cur = value; 1721 xmlChar *base = NULL; 1722 int res; 1723 1724 if ((cur == NULL) || (file == NULL)) 1725 return(-1); 1726 base = xmlStrdup((const xmlChar *) file); 1727 1728 while ((cur != NULL) && (cur[0] != 0)) { 1729 SKIP_BLANKS; 1730 if (cur[0] == 0) 1731 break; 1732 if ((cur[0] == '-') && (cur[1] == '-')) { 1733 cur = xmlParseSGMLCatalogComment(cur); 1734 if (cur == NULL) { 1735 /* error */ 1736 break; 1737 } 1738 } else { 1739 xmlChar *sysid = NULL; 1740 xmlChar *name = NULL; 1741 xmlCatalogEntryType type = XML_CATA_NONE; 1742 1743 cur = xmlParseSGMLCatalogName(cur, &name); 1744 if (name == NULL) { 1745 /* error */ 1746 break; 1747 } 1748 if (!IS_BLANK(*cur)) { 1749 /* error */ 1750 break; 1751 } 1752 SKIP_BLANKS; 1753 if (xmlStrEqual(name, (const xmlChar *) "SYSTEM")) 1754 type = SGML_CATA_SYSTEM; 1755 else if (xmlStrEqual(name, (const xmlChar *) "PUBLIC")) 1756 type = SGML_CATA_PUBLIC; 1757 else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE")) 1758 type = SGML_CATA_DELEGATE; 1759 else if (xmlStrEqual(name, (const xmlChar *) "ENTITY")) 1760 type = SGML_CATA_ENTITY; 1761 else if (xmlStrEqual(name, (const xmlChar *) "DOCTYPE")) 1762 type = SGML_CATA_DOCTYPE; 1763 else if (xmlStrEqual(name, (const xmlChar *) "LINKTYPE")) 1764 type = SGML_CATA_LINKTYPE; 1765 else if (xmlStrEqual(name, (const xmlChar *) "NOTATION")) 1766 type = SGML_CATA_NOTATION; 1767 else if (xmlStrEqual(name, (const xmlChar *) "SGMLDECL")) 1768 type = SGML_CATA_SGMLDECL; 1769 else if (xmlStrEqual(name, (const xmlChar *) "DOCUMENT")) 1770 type = SGML_CATA_DOCUMENT; 1771 else if (xmlStrEqual(name, (const xmlChar *) "CATALOG")) 1772 type = SGML_CATA_CATALOG; 1773 else if (xmlStrEqual(name, (const xmlChar *) "BASE")) 1774 type = SGML_CATA_BASE; 1775 else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE")) 1776 type = SGML_CATA_DELEGATE; 1777 else if (xmlStrEqual(name, (const xmlChar *) "OVERRIDE")) { 1778 xmlFree(name); 1779 cur = xmlParseSGMLCatalogName(cur, &name); 1780 if (name == NULL) { 1781 /* error */ 1782 break; 1783 } 1784 xmlFree(name); 1785 continue; 1786 } 1787 xmlFree(name); 1788 name = NULL; 1789 1790 switch(type) { 1791 case SGML_CATA_ENTITY: 1792 if (*cur == '%') 1793 type = SGML_CATA_PENTITY; 1794 case SGML_CATA_PENTITY: 1795 case SGML_CATA_DOCTYPE: 1796 case SGML_CATA_LINKTYPE: 1797 case SGML_CATA_NOTATION: 1798 cur = xmlParseSGMLCatalogName(cur, &name); 1799 if (cur == NULL) { 1800 /* error */ 1801 break; 1802 } 1803 if (!IS_BLANK(*cur)) { 1804 /* error */ 1805 break; 1806 } 1807 SKIP_BLANKS; 1808 cur = xmlParseSGMLCatalogPubid(cur, &sysid); 1809 if (cur == NULL) { 1810 /* error */ 1811 break; 1812 } 1813 break; 1814 case SGML_CATA_PUBLIC: 1815 case SGML_CATA_SYSTEM: 1816 case SGML_CATA_DELEGATE: 1817 cur = xmlParseSGMLCatalogPubid(cur, &name); 1818 if (cur == NULL) { 1819 /* error */ 1820 break; 1821 } 1822 if (!IS_BLANK(*cur)) { 1823 /* error */ 1824 break; 1825 } 1826 SKIP_BLANKS; 1827 cur = xmlParseSGMLCatalogPubid(cur, &sysid); 1828 if (cur == NULL) { 1829 /* error */ 1830 break; 1831 } 1832 break; 1833 case SGML_CATA_BASE: 1834 case SGML_CATA_CATALOG: 1835 case SGML_CATA_DOCUMENT: 1836 case SGML_CATA_SGMLDECL: 1837 cur = xmlParseSGMLCatalogPubid(cur, &sysid); 1838 if (cur == NULL) { 1839 /* error */ 1840 break; 1841 } 1842 break; 1843 default: 1844 break; 1845 } 1846 if (cur == NULL) { 1847 if (name != NULL) 1848 xmlFree(name); 1849 if (sysid != NULL) 1850 xmlFree(sysid); 1851 break; 1852 } else if (type == SGML_CATA_BASE) { 1853 if (base != NULL) 1854 xmlFree(base); 1855 base = xmlStrdup(sysid); 1856 } else if ((type == SGML_CATA_PUBLIC) || 1857 (type == SGML_CATA_SYSTEM)) { 1858 xmlChar *filename; 1859 1860 filename = xmlBuildURI(sysid, base); 1861 if (filename != NULL) { 1862 xmlCatalogEntryPtr entry; 1863 1864 entry = xmlNewCatalogEntry(type, name, filename, 1865 XML_CATA_PREFER_NONE); 1866 res = xmlHashAddEntry(xmlDefaultCatalog, name, entry); 1867 if (res < 0) { 1868 xmlFreeCatalogEntry(entry); 1869 } 1870 xmlFree(filename); 1871 } 1872 1873 } else if (type == SGML_CATA_CATALOG) { 1874 xmlChar *filename; 1875 1876 filename = xmlBuildURI(sysid, base); 1877 if (filename != NULL) { 1878 xmlLoadCatalog((const char *)filename); 1879 xmlFree(filename); 1880 } 1881 } 1882 /* 1883 * drop anything else we won't handle it 1884 */ 1885 if (name != NULL) 1886 xmlFree(name); 1887 if (sysid != NULL) 1888 xmlFree(sysid); 1889 } 1890 } 1891 if (base != NULL) 1892 xmlFree(base); 1893 if (cur == NULL) 1894 return(-1); 1895 return(0); 1896} 1897 1898/** 1899 * xmlCatalogGetSGMLPublic: 1900 * @catal: an SGML catalog hash 1901 * @pubId: the public ID string 1902 * 1903 * Try to lookup the system ID associated to a public ID 1904 * 1905 * Returns the system ID if found or NULL otherwise. 1906 */ 1907static const xmlChar * 1908xmlCatalogGetSGMLPublic(xmlHashTablePtr catal, const xmlChar *pubID) { 1909 xmlCatalogEntryPtr entry; 1910 1911 if (catal == NULL) 1912 return(NULL); 1913 1914 entry = (xmlCatalogEntryPtr) xmlHashLookup(catal, pubID); 1915 if (entry == NULL) 1916 return(NULL); 1917 if (entry->type == SGML_CATA_PUBLIC) 1918 return(entry->value); 1919 return(NULL); 1920} 1921 1922/** 1923 * xmlCatalogGetSGMLSystem: 1924 * @catal: an SGML catalog hash 1925 * @sysId: the public ID string 1926 * 1927 * Try to lookup the catalog local reference for a system ID 1928 * 1929 * Returns the system ID if found or NULL otherwise. 1930 */ 1931static const xmlChar * 1932xmlCatalogGetSGMLSystem(xmlHashTablePtr catal, const xmlChar *sysID) { 1933 xmlCatalogEntryPtr entry; 1934 1935 if (catal == NULL) 1936 return(NULL); 1937 1938 entry = (xmlCatalogEntryPtr) xmlHashLookup(catal, sysID); 1939 if (entry == NULL) 1940 return(NULL); 1941 if (entry->type == SGML_CATA_SYSTEM) 1942 return(entry->value); 1943 return(NULL); 1944} 1945 1946/** 1947 * xmlCatalogSGMLResolve: 1948 * @pubId: the public ID string 1949 * @sysId: the system ID string 1950 * 1951 * Do a complete resolution lookup of an External Identifier 1952 * 1953 * Returns the URI of the resource or NULL if not found 1954 */ 1955static const xmlChar * 1956xmlCatalogSGMLResolve(const xmlChar *pubID, const xmlChar *sysID) { 1957 const xmlChar *ret = NULL; 1958 1959 if (xmlDefaultCatalog == NULL) 1960 return(NULL); 1961 1962 if (pubID != NULL) 1963 ret = xmlCatalogGetSGMLPublic(xmlDefaultCatalog, pubID); 1964 if (ret != NULL) 1965 return(ret); 1966 if (sysID != NULL) 1967 ret = xmlCatalogGetSGMLSystem(xmlDefaultCatalog, sysID); 1968 return(NULL); 1969} 1970 1971/************************************************************************ 1972 * * 1973 * Public interfaces * 1974 * * 1975 ************************************************************************/ 1976 1977/** 1978 * xmlInitializeCatalog: 1979 * 1980 * Do the catalog initialization. 1981 * TODO: this function is not thread safe, catalog initialization should 1982 * preferably be done once at startup 1983 */ 1984void 1985xmlInitializeCatalog(void) { 1986 const char *catalogs; 1987 1988 if (xmlCatalogInitialized != 0) 1989 return; 1990 1991 if (getenv("XML_DEBUG_CATALOG")) 1992 xmlDebugCatalogs = 1; 1993 if ((xmlDefaultXMLCatalogList == NULL) && (xmlDefaultCatalog == NULL)) { 1994 catalogs = getenv("XML_CATALOG_FILES"); 1995 if (catalogs == NULL) 1996 catalogs = XML_DEFAULT_CATALOG; 1997 xmlDefaultXMLCatalogList = xmlNewCatalogEntry(XML_CATA_CATALOG, 1998 NULL, BAD_CAST catalogs, xmlCatalogDefaultPrefer); 1999 } 2000 2001 xmlCatalogInitialized = 1; 2002} 2003 2004/** 2005 * xmlLoadCatalog: 2006 * @filename: a file path 2007 * 2008 * Load the catalog and makes its definitions effective for the default 2009 * external entity loader. It will recurse in SGML CATALOG entries. 2010 * TODO: this function is not thread safe, catalog initialization should 2011 * preferably be done once at startup 2012 * 2013 * Returns 0 in case of success -1 in case of error 2014 */ 2015int 2016xmlLoadCatalog(const char *filename) { 2017 int fd, len, ret, i; 2018 struct stat info; 2019 xmlChar *content; 2020 2021 if (filename == NULL) 2022 return(-1); 2023 2024 if (xmlDefaultCatalog == NULL) 2025 xmlDefaultCatalog = xmlHashCreate(20); 2026 if (xmlDefaultCatalog == NULL) 2027 return(-1); 2028 2029 /* 2030 * Need to be done after ... 2031 */ 2032 if (!xmlCatalogInitialized) 2033 xmlInitializeCatalog(); 2034 2035#ifdef HAVE_STAT 2036 if (stat(filename, &info) < 0) 2037 return(-1); 2038#endif 2039 2040 /* 2041 * Prevent loops 2042 */ 2043 for (i = 0;i < catalNr;i++) { 2044 if (xmlStrEqual((const xmlChar *)catalTab[i], 2045 (const xmlChar *)filename)) { 2046 xmlGenericError(xmlGenericErrorContext, 2047 "xmlLoadCatalog: %s seems to induce a loop\n", 2048 filename); 2049 return(-1); 2050 } 2051 } 2052 if (catalNr >= catalMax) { 2053 xmlGenericError(xmlGenericErrorContext, 2054 "xmlLoadCatalog: %s catalog list too deep\n", 2055 filename); 2056 return(-1); 2057 } 2058 catalTab[catalNr++] = filename; 2059 2060 if ((fd = open(filename, O_RDONLY)) < 0) { 2061 catalNr--; 2062 return(-1); 2063 } 2064 2065 content = xmlMalloc(info.st_size + 10); 2066 if (content == NULL) { 2067 xmlGenericError(xmlGenericErrorContext, 2068 "realloc of %d byte failed\n", info.st_size + 10); 2069 catalNr--; 2070 return(-1); 2071 } 2072 len = read(fd, content, info.st_size); 2073 if (len < 0) { 2074 xmlFree(content); 2075 catalNr--; 2076 return(-1); 2077 } 2078 content[len] = 0; 2079 close(fd); 2080 2081 if ((content[0] == ' ') || (content[0] == '-') || 2082 ((content[0] >= 'A') && (content[0] <= 'Z')) || 2083 ((content[0] >= 'a') && (content[0] <= 'z'))) 2084 ret = xmlParseSGMLCatalog(content, filename); 2085 else { 2086 xmlCatalogEntryPtr catal, tmp; 2087 /* TODO: allow to switch the default preference */ 2088 catal = xmlParseXMLCatalog(content, XML_CATA_PREFER_PUBLIC, filename); 2089 if (catal != NULL) { 2090 if (xmlDefaultXMLCatalogList == NULL) 2091 xmlDefaultXMLCatalogList = catal; 2092 else { 2093 tmp = xmlDefaultXMLCatalogList; 2094 while (tmp->next != NULL) 2095 tmp = tmp->next; 2096 tmp->next = catal; 2097 } 2098 ret = 0; 2099 } else 2100 ret = -1; 2101 } 2102 xmlFree(content); 2103 catalNr--; 2104 return(ret); 2105} 2106 2107/** 2108 * xmlLoadCatalogs: 2109 * @paths: a list of file path separated by ':' or spaces 2110 * 2111 * Load the catalogs and makes their definitions effective for the default 2112 * external entity loader. 2113 * TODO: this function is not thread safe, catalog initialization should 2114 * preferably be done once at startup 2115 */ 2116void 2117xmlLoadCatalogs(const char *pathss) { 2118 const char *cur; 2119 const char *paths; 2120 xmlChar *path; 2121 2122 if (pathss == NULL) 2123 return; 2124 2125 cur = pathss; 2126 while ((cur != NULL) && (*cur != 0)) { 2127 while (IS_BLANK(*cur)) cur++; 2128 if (*cur != 0) { 2129 paths = cur; 2130 while ((*cur != 0) && (*cur != ':') && (!IS_BLANK(*cur))) 2131 cur++; 2132 path = xmlStrndup((const xmlChar *)paths, cur - paths); 2133 if (path != NULL) { 2134 xmlLoadCatalog((const char *) path); 2135 xmlFree(path); 2136 } 2137 } 2138 while (*cur == ':') 2139 cur++; 2140 } 2141} 2142 2143/** 2144 * xmlCatalogCleanup: 2145 * 2146 * Free up all the memory associated with catalogs 2147 */ 2148void 2149xmlCatalogCleanup(void) { 2150 if (xmlDebugCatalogs) 2151 xmlGenericError(xmlGenericErrorContext, 2152 "Catalogs cleanup\n"); 2153 if (xmlCatalogXMLFiles != NULL) 2154 xmlHashFree(xmlCatalogXMLFiles, NULL); 2155 xmlCatalogXMLFiles = NULL; 2156 if (xmlDefaultXMLCatalogList != NULL) 2157 xmlFreeCatalogEntryList(xmlDefaultXMLCatalogList); 2158 xmlDefaultXMLCatalogList = NULL; 2159 if (xmlDefaultCatalog != NULL) 2160 xmlHashFree(xmlDefaultCatalog, 2161 (xmlHashDeallocator) xmlFreeCatalogEntry); 2162 xmlDefaultCatalog = NULL; 2163 xmlDebugCatalogs = 0; 2164 xmlDefaultCatalog = NULL; 2165 xmlCatalogInitialized = 0; 2166} 2167 2168/** 2169 * xmlCatalogGetSystem: 2170 * @pubId: the public ID string 2171 * 2172 * Try to lookup the system ID associated to a public ID 2173 * DEPRECATED, use xmlCatalogResolveSystem() 2174 * 2175 * Returns the system ID if found or NULL otherwise. 2176 */ 2177const xmlChar * 2178xmlCatalogGetSystem(const xmlChar *sysID) { 2179 xmlChar *ret; 2180 static xmlChar result[1000]; 2181 2182 if (sysID == NULL) 2183 return(NULL); 2184 2185 if (!xmlCatalogInitialized) 2186 xmlInitializeCatalog(); 2187 2188 /* 2189 * Check first the XML catalogs 2190 */ 2191 if (xmlDefaultXMLCatalogList != NULL) { 2192 ret = xmlCatalogListXMLResolve(xmlDefaultXMLCatalogList, NULL, sysID); 2193 if (ret != NULL) { 2194 snprintf((char *) result, sizeof(result) - 1, "%s", (char *) ret); 2195 result[sizeof(result) - 1] = 0; 2196 return(result); 2197 } 2198 } 2199 2200 if (xmlDefaultCatalog != NULL) 2201 return(xmlCatalogGetSGMLSystem(xmlDefaultCatalog, sysID)); 2202 return(NULL); 2203} 2204 2205/** 2206 * xmlCatalogResolveSystem: 2207 * @sysId: the public ID string 2208 * 2209 * Try to lookup the catalog resource for a system ID 2210 * 2211 * Returns the system ID if found or NULL otherwise, the value returned 2212 * must be freed by the caller. 2213 */ 2214xmlChar * 2215xmlCatalogResolveSystem(const xmlChar *sysID) { 2216 xmlCatalogEntryPtr catal; 2217 xmlChar *ret; 2218 const xmlChar *sgml; 2219 2220 if (sysID == NULL) 2221 return(NULL); 2222 2223 if (!xmlCatalogInitialized) 2224 xmlInitializeCatalog(); 2225 2226 /* 2227 * Check first the XML catalogs 2228 */ 2229 catal = xmlDefaultXMLCatalogList; 2230 if (catal != NULL) { 2231 ret = xmlCatalogListXMLResolve(xmlDefaultXMLCatalogList, NULL, sysID); 2232 if ((ret != NULL) && (ret != XML_CATAL_BREAK)) 2233 return(ret); 2234 } 2235 2236 if (xmlDefaultCatalog != NULL) { 2237 sgml = xmlCatalogGetSGMLSystem(xmlDefaultCatalog, sysID); 2238 if (sgml != NULL) 2239 return(xmlStrdup(sgml)); 2240 } 2241 return(NULL); 2242} 2243 2244/** 2245 * xmlCatalogGetPublic: 2246 * @pubId: the public ID string 2247 * 2248 * Try to lookup the system ID associated to a public ID 2249 * DEPRECATED, use xmlCatalogResolvePublic() 2250 * 2251 * Returns the system ID if found or NULL otherwise. 2252 */ 2253const xmlChar * 2254xmlCatalogGetPublic(const xmlChar *pubID) { 2255 xmlChar *ret; 2256 static xmlChar result[1000]; 2257 2258 if (pubID == NULL) 2259 return(NULL); 2260 2261 if (!xmlCatalogInitialized) 2262 xmlInitializeCatalog(); 2263 2264 /* 2265 * Check first the XML catalogs 2266 */ 2267 if (xmlDefaultXMLCatalogList != NULL) { 2268 ret = xmlCatalogListXMLResolve(xmlDefaultXMLCatalogList, pubID, NULL); 2269 if ((ret != NULL) && (ret != XML_CATAL_BREAK)) { 2270 snprintf((char *) result, sizeof(result) - 1, "%s", (char *) ret); 2271 result[sizeof(result) - 1] = 0; 2272 return(result); 2273 } 2274 } 2275 2276 if (xmlDefaultCatalog != NULL) 2277 return(xmlCatalogGetSGMLPublic(xmlDefaultCatalog, pubID)); 2278 return(NULL); 2279} 2280 2281/** 2282 * xmlCatalogResolvePublic: 2283 * @pubId: the public ID string 2284 * 2285 * Try to lookup the system ID associated to a public ID 2286 * 2287 * Returns the system ID if found or NULL otherwise, the value returned 2288 * must be freed by the caller. 2289 */ 2290xmlChar * 2291xmlCatalogResolvePublic(const xmlChar *pubID) { 2292 xmlCatalogEntryPtr catal; 2293 xmlChar *ret; 2294 const xmlChar *sgml; 2295 2296 if (pubID == NULL) 2297 return(NULL); 2298 2299 if (!xmlCatalogInitialized) 2300 xmlInitializeCatalog(); 2301 2302 /* 2303 * Check first the XML catalogs 2304 */ 2305 catal = xmlDefaultXMLCatalogList; 2306 if (catal != NULL) { 2307 ret = xmlCatalogListXMLResolve(xmlDefaultXMLCatalogList, pubID, NULL); 2308 if ((ret != NULL) && (ret != XML_CATAL_BREAK)) 2309 return(ret); 2310 } 2311 2312 if (xmlDefaultCatalog != NULL) { 2313 sgml = xmlCatalogGetSGMLPublic(xmlDefaultCatalog, pubID); 2314 if (sgml != NULL) 2315 return(xmlStrdup(sgml)); 2316 } 2317 return(NULL); 2318} 2319 2320/** 2321 * xmlCatalogResolve: 2322 * @pubId: the public ID string 2323 * @sysId: the system ID string 2324 * 2325 * Do a complete resolution lookup of an External Identifier 2326 * 2327 * Returns the URI of the resource or NULL if not found, it must be freed 2328 * by the caller. 2329 */ 2330xmlChar * 2331xmlCatalogResolve(const xmlChar *pubID, const xmlChar *sysID) { 2332 if ((pubID == NULL) && (sysID == NULL)) 2333 return(NULL); 2334 2335 if (!xmlCatalogInitialized) 2336 xmlInitializeCatalog(); 2337 2338 if (xmlDebugCatalogs) { 2339 if (pubID != NULL) { 2340 xmlGenericError(xmlGenericErrorContext, 2341 "Resolve: pubID %s\n", pubID); 2342 } else { 2343 xmlGenericError(xmlGenericErrorContext, 2344 "Resolve: sysID %s\n", sysID); 2345 } 2346 } 2347 2348 if (xmlDefaultXMLCatalogList != NULL) { 2349 xmlChar *ret; 2350 ret = xmlCatalogListXMLResolve(xmlDefaultXMLCatalogList, pubID, sysID); 2351 if ((ret != NULL) && (ret != XML_CATAL_BREAK)) 2352 return(ret); 2353 } else { 2354 const xmlChar *ret; 2355 2356 ret = xmlCatalogSGMLResolve(pubID, sysID); 2357 if (ret != NULL) 2358 return(xmlStrdup(ret)); 2359 } 2360 return(NULL); 2361} 2362 2363/** 2364 * xmlCatalogResolveURI: 2365 * @pubId: the URI 2366 * 2367 * Do a complete resolution lookup of an URI 2368 * 2369 * Returns the URI of the resource or NULL if not found, it must be freed 2370 * by the caller. 2371 */ 2372xmlChar * 2373xmlCatalogResolveURI(const xmlChar *URI) { 2374 if (!xmlCatalogInitialized) 2375 xmlInitializeCatalog(); 2376 2377 if (URI == NULL) 2378 return(NULL); 2379 2380 if (xmlDebugCatalogs) 2381 xmlGenericError(xmlGenericErrorContext, 2382 "Resolve URI %s\n", URI); 2383 2384 if (xmlDefaultXMLCatalogList != NULL) { 2385 xmlChar *ret; 2386 2387 ret = xmlCatalogListXMLResolveURI(xmlDefaultXMLCatalogList, URI); 2388 if ((ret != NULL) && (ret != XML_CATAL_BREAK)) 2389 return(ret); 2390 } else { 2391 const xmlChar *ret; 2392 2393 ret = xmlCatalogSGMLResolve(NULL, URI); 2394 if (ret != NULL) 2395 return(xmlStrdup(ret)); 2396 } 2397 return(NULL); 2398} 2399 2400/** 2401 * xmlCatalogDump: 2402 * @out: the file. 2403 * 2404 * Free up all the memory associated with catalogs 2405 */ 2406void 2407xmlCatalogDump(FILE *out) { 2408 if (out == NULL) 2409 return; 2410 2411 if (xmlDefaultXMLCatalogList != NULL) { 2412 xmlDumpXMLCatalog(out, xmlDefaultXMLCatalogList); 2413 } else if (xmlDefaultCatalog != NULL) { 2414 xmlHashScan(xmlDefaultCatalog, 2415 (xmlHashScanner) xmlCatalogDumpEntry, out); 2416 } 2417} 2418 2419/** 2420 * xmlCatalogAdd: 2421 * @type: the type of record to add to the catalog 2422 * @orig: the system, public or prefix to match 2423 * @replace: the replacement value for the match 2424 * 2425 * Add an entry in the catalog, it may overwrite existing but 2426 * different entries. 2427 * 2428 * Returns 0 if successful, -1 otherwise 2429 */ 2430int 2431xmlCatalogAdd(const xmlChar *type, const xmlChar *orig, const xmlChar *replace) { 2432 int res = -1; 2433 2434 if ((xmlDefaultXMLCatalogList == NULL) && 2435 (xmlStrEqual(type, BAD_CAST "catalog"))) { 2436 xmlDefaultXMLCatalogList = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL, 2437 orig, xmlCatalogDefaultPrefer); 2438 return(0); 2439 } 2440 2441 if (!xmlCatalogInitialized) 2442 xmlInitializeCatalog(); 2443 2444 if (xmlDefaultXMLCatalogList != NULL) { 2445 res = xmlAddXMLCatalog(xmlDefaultXMLCatalogList, type, orig, replace); 2446 } else if (xmlDefaultCatalog != NULL) { 2447 xmlCatalogEntryType typ; 2448 2449 typ = xmlGetSGMLCatalogEntryType(type); 2450 if (type != XML_CATA_NONE) { 2451 xmlCatalogEntryPtr entry; 2452 entry = xmlNewCatalogEntry(typ, orig, replace, 2453 XML_CATA_PREFER_NONE); 2454 res = xmlHashAddEntry(xmlDefaultCatalog, orig, entry); 2455 } 2456 } 2457 return(res); 2458} 2459 2460/** 2461 * xmlCatalogRemove: 2462 * @value: the value to remove 2463 * 2464 * Remove an entry from the catalog 2465 * 2466 * Returns 0 if successful, -1 otherwise 2467 */ 2468int 2469xmlCatalogRemove(const xmlChar *value) { 2470 int res = -1; 2471 2472 if (!xmlCatalogInitialized) 2473 xmlInitializeCatalog(); 2474 2475 if (xmlDefaultXMLCatalogList != NULL) { 2476 res = xmlDelXMLCatalog(xmlDefaultXMLCatalogList, value); 2477 } else if (xmlDefaultCatalog != NULL) { 2478 TODO 2479 } 2480 return(res); 2481} 2482 2483/** 2484 * xmlCatalogConvert: 2485 * 2486 * Convert all the SGML catalog entries as XML ones 2487 * 2488 * Returns the number of entries converted if successful, -1 otherwise 2489 */ 2490int 2491xmlCatalogConvert(void) { 2492 int res = -1; 2493 2494 if (!xmlCatalogInitialized) 2495 xmlInitializeCatalog(); 2496 2497 if (xmlDebugCatalogs) { 2498 xmlGenericError(xmlGenericErrorContext, 2499 "Converting SGML catalog to XML\n"); 2500 } 2501 2502 if (xmlDefaultXMLCatalogList == NULL) { 2503 xmlDefaultXMLCatalogList = xmlNewCatalogEntry(XML_CATA_CATALOG, 2504 NULL, BAD_CAST "NewCatalog.xml", 2505 xmlCatalogDefaultPrefer); 2506 } 2507 if (xmlDefaultCatalog != NULL) { 2508 res = 0; 2509 2510 xmlHashScan(xmlDefaultCatalog, 2511 (xmlHashScanner) xmlCatalogConvertEntry, &res); 2512 } 2513 return(res); 2514} 2515 2516/** 2517 * xmlCatalogGetDefaults: 2518 * 2519 * Used to get the user preference w.r.t. to what catalogs should 2520 * be accepted 2521 * 2522 * Returns the current xmlCatalogAllow value 2523 */ 2524xmlCatalogAllow 2525xmlCatalogGetDefaults(void) { 2526 return(xmlCatalogDefaultAllow); 2527} 2528 2529/** 2530 * xmlCatalogSetDefaults: 2531 * 2532 * Used to set the user preference w.r.t. to what catalogs should 2533 * be accepted 2534 */ 2535void 2536xmlCatalogSetDefaults(xmlCatalogAllow allow) { 2537 if (!xmlCatalogInitialized) 2538 xmlInitializeCatalog(); 2539 if (xmlDebugCatalogs) { 2540 switch (allow) { 2541 case XML_CATA_ALLOW_NONE: 2542 xmlGenericError(xmlGenericErrorContext, 2543 "Disabling catalog usage\n"); 2544 break; 2545 case XML_CATA_ALLOW_GLOBAL: 2546 xmlGenericError(xmlGenericErrorContext, 2547 "Allowing only global catalogs\n"); 2548 break; 2549 case XML_CATA_ALLOW_DOCUMENT: 2550 xmlGenericError(xmlGenericErrorContext, 2551 "Allowing only catalogs from the document\n"); 2552 break; 2553 case XML_CATA_ALLOW_ALL: 2554 xmlGenericError(xmlGenericErrorContext, 2555 "Allowing all catalogs\n"); 2556 break; 2557 } 2558 } 2559 xmlCatalogDefaultAllow = allow; 2560} 2561 2562/** 2563 * xmlCatalogSetDefaultPrefer: 2564 * @prefer: the default preference for delegation 2565 * 2566 * Allows to set the preference between public and system for deletion 2567 * in XML Catalog resolution. C.f. section 4.1.1 of the spec 2568 * Values accepted are XML_CATA_PREFER_PUBLIC or XML_CATA_PREFER_SYSTEM 2569 * 2570 * Returns the previous value of the default preference for delegation 2571 */ 2572xmlCatalogPrefer 2573xmlCatalogSetDefaultPrefer(xmlCatalogPrefer prefer) { 2574 xmlCatalogPrefer ret = xmlCatalogDefaultPrefer; 2575 2576 if (!xmlCatalogInitialized) 2577 xmlInitializeCatalog(); 2578 if (prefer == XML_CATA_PREFER_NONE) 2579 return(ret); 2580 2581 if (xmlDebugCatalogs) { 2582 switch (prefer) { 2583 case XML_CATA_PREFER_PUBLIC: 2584 xmlGenericError(xmlGenericErrorContext, 2585 "Setting catalog preference to PUBLIC\n"); 2586 break; 2587 case XML_CATA_PREFER_SYSTEM: 2588 xmlGenericError(xmlGenericErrorContext, 2589 "Setting catalog preference to SYSTEM\n"); 2590 break; 2591 case XML_CATA_PREFER_NONE: 2592 break; 2593 } 2594 } 2595 xmlCatalogDefaultPrefer = prefer; 2596 return(ret); 2597} 2598 2599/** 2600 * xmlCatalogSetDebug: 2601 * @level: the debug level of catalogs required 2602 * 2603 * Used to set the debug level for catalog operation, 0 disable 2604 * debugging, 1 enable it 2605 * 2606 * Returns the previous value of the catalog debugging level 2607 */ 2608int 2609xmlCatalogSetDebug(int level) { 2610 int ret = xmlDebugCatalogs; 2611 2612 if (level <= 0) 2613 xmlDebugCatalogs = 0; 2614 else 2615 xmlDebugCatalogs = level; 2616 return(ret); 2617} 2618 2619/** 2620 * xmlCatalogFreeLocal: 2621 * @catalogs: a document's list of catalogs 2622 * 2623 * Free up the memory associated to the catalog list 2624 */ 2625void 2626xmlCatalogFreeLocal(void *catalogs) { 2627 xmlCatalogEntryPtr catal; 2628 2629 catal = (xmlCatalogEntryPtr) catalogs; 2630 if (catal != NULL) 2631 xmlFreeCatalogEntryList(catal); 2632} 2633 2634 2635/** 2636 * xmlCatalogAddLocal: 2637 * @catalogs: a document's list of catalogs 2638 * @URL: the URL to a new local catalog 2639 * 2640 * Add the new entry to the catalog list 2641 * 2642 * Returns the updated list 2643 */ 2644void * 2645xmlCatalogAddLocal(void *catalogs, const xmlChar *URL) { 2646 xmlCatalogEntryPtr catal, add; 2647 2648 if (!xmlCatalogInitialized) 2649 xmlInitializeCatalog(); 2650 if (URL == NULL) 2651 return(catalogs); 2652 2653 if (xmlDebugCatalogs) 2654 xmlGenericError(xmlGenericErrorContext, 2655 "Adding document catalog %s\n", URL); 2656 2657 add = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL, URL, 2658 xmlCatalogDefaultPrefer); 2659 if (add == NULL) 2660 return(catalogs); 2661 2662 catal = (xmlCatalogEntryPtr) catalogs; 2663 if (catal == NULL) 2664 return((void *) add); 2665 2666 while (catal->next != NULL) 2667 catal = catal->next; 2668 catal->next = add; 2669 return(catalogs); 2670} 2671 2672/** 2673 * xmlCatalogLocalResolve: 2674 * @catalogs: a document's list of catalogs 2675 * @pubId: the public ID string 2676 * @sysId: the system ID string 2677 * 2678 * Do a complete resolution lookup of an External Identifier using a 2679 * document's private catalog list 2680 * 2681 * Returns the URI of the resource or NULL if not found, it must be freed 2682 * by the caller. 2683 */ 2684xmlChar * 2685xmlCatalogLocalResolve(void *catalogs, const xmlChar *pubID, 2686 const xmlChar *sysID) { 2687 xmlCatalogEntryPtr catal; 2688 xmlChar *ret; 2689 2690 if ((pubID == NULL) && (sysID == NULL)) 2691 return(NULL); 2692 2693 if (!xmlCatalogInitialized) 2694 xmlInitializeCatalog(); 2695 2696 if (xmlDebugCatalogs) { 2697 if (pubID != NULL) { 2698 xmlGenericError(xmlGenericErrorContext, 2699 "Local resolve: pubID %s\n", pubID); 2700 } else { 2701 xmlGenericError(xmlGenericErrorContext, 2702 "Local resolve: sysID %s\n", sysID); 2703 } 2704 } 2705 2706 catal = (xmlCatalogEntryPtr) catalogs; 2707 if (catal == NULL) 2708 return(NULL); 2709 ret = xmlCatalogListXMLResolve(catal, pubID, sysID); 2710 if ((ret != NULL) && (ret != XML_CATAL_BREAK)) 2711 return(ret); 2712 return(NULL); 2713} 2714 2715/** 2716 * xmlCatalogLocalResolveURI: 2717 * @catalogs: a document's list of catalogs 2718 * @pubId: the URI 2719 * 2720 * Do a complete resolution lookup of an URI using a 2721 * document's private catalog list 2722 * 2723 * Returns the URI of the resource or NULL if not found, it must be freed 2724 * by the caller. 2725 */ 2726xmlChar * 2727xmlCatalogLocalResolveURI(void *catalogs, const xmlChar *URI) { 2728 xmlCatalogEntryPtr catal; 2729 xmlChar *ret; 2730 2731 if (URI == NULL) 2732 return(NULL); 2733 2734 if (!xmlCatalogInitialized) 2735 xmlInitializeCatalog(); 2736 2737 if (xmlDebugCatalogs) 2738 xmlGenericError(xmlGenericErrorContext, 2739 "Resolve URI %s\n", URI); 2740 2741 catal = (xmlCatalogEntryPtr) catalogs; 2742 if (catal == NULL) 2743 return(NULL); 2744 ret = xmlCatalogListXMLResolveURI(catal, URI); 2745 if ((ret != NULL) && (ret != XML_CATAL_BREAK)) 2746 return(ret); 2747 return(NULL); 2748} 2749 2750#endif /* LIBXML_CATALOG_ENABLED */ 2751