catalog.c revision 81418e38c80cf1ddac6fe1426d8037a3da39853f
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 * See Copyright for the status of this software. 8 * 9 * Daniel.Veillard@imag.fr 10 */ 11 12#include "libxml.h" 13 14#ifdef LIBXML_CATALOG_ENABLED 15#ifdef HAVE_SYS_TYPES_H 16#include <sys/types.h> 17#endif 18#ifdef HAVE_SYS_STAT_H 19#include <sys/stat.h> 20#endif 21#ifdef HAVE_UNISTD_H 22#include <unistd.h> 23#endif 24#ifdef HAVE_FCNTL_H 25#include <fcntl.h> 26#endif 27#include <string.h> 28#include <libxml/xmlmemory.h> 29#include <libxml/hash.h> 30#include <libxml/uri.h> 31#include <libxml/parserInternals.h> 32#include <libxml/catalog.h> 33#include <libxml/xmlerror.h> 34 35/************************************************************************ 36 * * 37 * Types, all private * 38 * * 39 ************************************************************************/ 40 41typedef enum { 42 XML_CATA_NONE = 0, 43 XML_CATA_SYSTEM, 44 XML_CATA_PUBLIC, 45 XML_CATA_ENTITY, 46 XML_CATA_PENTITY, 47 XML_CATA_DOCTYPE, 48 XML_CATA_LINKTYPE, 49 XML_CATA_NOTATION, 50 XML_CATA_DELEGATE, 51 XML_CATA_BASE, 52 XML_CATA_CATALOG, 53 XML_CATA_DOCUMENT, 54 XML_CATA_SGMLDECL 55} xmlCatalogEntryType; 56 57typedef struct _xmlCatalogEntry xmlCatalogEntry; 58typedef xmlCatalogEntry *xmlCatalogEntryPtr; 59struct _xmlCatalogEntry { 60 xmlCatalogEntryType type; 61 xmlChar *name; 62 xmlChar *value; 63}; 64 65static xmlHashTablePtr xmlDefaultCatalog; 66 67/* Catalog stack */ 68static const char * catalTab[10]; /* stack of catals */ 69static int catalNr = 0; /* Number of current catal streams */ 70static int catalMax = 10; /* Max number of catal streams */ 71 72/************************************************************************ 73 * * 74 * alloc or dealloc * 75 * * 76 ************************************************************************/ 77 78static xmlCatalogEntryPtr 79xmlNewCatalogEntry(int type, xmlChar *name, xmlChar *value) { 80 xmlCatalogEntryPtr ret; 81 82 ret = (xmlCatalogEntryPtr) xmlMalloc(sizeof(xmlCatalogEntry)); 83 if (ret == NULL) { 84 xmlGenericError(xmlGenericErrorContext, 85 "malloc of %d byte failed\n", sizeof(xmlCatalogEntry)); 86 return(NULL); 87 } 88 ret->type = type; 89 ret->name = xmlStrdup(name); 90 ret->value = xmlStrdup(value); 91 return(ret); 92} 93 94static void 95xmlFreeCatalogEntry(xmlCatalogEntryPtr ret) { 96 if (ret == NULL) 97 return; 98 if (ret->name != NULL) 99 xmlFree(ret->name); 100 if (ret->value != NULL) 101 xmlFree(ret->value); 102 xmlFree(ret); 103} 104 105/** 106 * xmlCatalogDumpEntry: 107 * @entry: the 108 * @out: the file. 109 * 110 * Free up all the memory associated with catalogs 111 */ 112static void 113xmlCatalogDumpEntry(xmlCatalogEntryPtr entry, FILE *out) { 114 if ((entry == NULL) || (out == NULL)) 115 return; 116 switch (entry->type) { 117 case XML_CATA_ENTITY: 118 fprintf(out, "ENTITY "); break; 119 case XML_CATA_PENTITY: 120 fprintf(out, "ENTITY %%"); break; 121 case XML_CATA_DOCTYPE: 122 fprintf(out, "DOCTYPE "); break; 123 case XML_CATA_LINKTYPE: 124 fprintf(out, "LINKTYPE "); break; 125 case XML_CATA_NOTATION: 126 fprintf(out, "NOTATION "); break; 127 case XML_CATA_PUBLIC: 128 fprintf(out, "PUBLIC "); break; 129 case XML_CATA_SYSTEM: 130 fprintf(out, "SYSTEM "); break; 131 case XML_CATA_DELEGATE: 132 fprintf(out, "DELEGATE "); break; 133 case XML_CATA_BASE: 134 fprintf(out, "BASE "); break; 135 case XML_CATA_CATALOG: 136 fprintf(out, "CATALOG "); break; 137 case XML_CATA_DOCUMENT: 138 fprintf(out, "DOCUMENT "); break; 139 case XML_CATA_SGMLDECL: 140 fprintf(out, "SGMLDECL "); break; 141 default: 142 return; 143 } 144 switch (entry->type) { 145 case XML_CATA_ENTITY: 146 case XML_CATA_PENTITY: 147 case XML_CATA_DOCTYPE: 148 case XML_CATA_LINKTYPE: 149 case XML_CATA_NOTATION: 150 fprintf(out, "%s", entry->name); break; 151 case XML_CATA_PUBLIC: 152 case XML_CATA_SYSTEM: 153 case XML_CATA_SGMLDECL: 154 case XML_CATA_DOCUMENT: 155 case XML_CATA_CATALOG: 156 case XML_CATA_BASE: 157 case XML_CATA_DELEGATE: 158 fprintf(out, "\"%s\"", entry->name); break; 159 default: 160 break; 161 } 162 switch (entry->type) { 163 case XML_CATA_ENTITY: 164 case XML_CATA_PENTITY: 165 case XML_CATA_DOCTYPE: 166 case XML_CATA_LINKTYPE: 167 case XML_CATA_NOTATION: 168 case XML_CATA_PUBLIC: 169 case XML_CATA_SYSTEM: 170 case XML_CATA_DELEGATE: 171 fprintf(out, " \"%s\"", entry->value); break; 172 default: 173 break; 174 } 175 fprintf(out, "\n"); 176} 177 178/************************************************************************ 179 * * 180 * The parser * 181 * * 182 ************************************************************************/ 183 184 185#define RAW *cur 186#define NEXT cur++; 187#define SKIP(x) cur += x; 188 189#define SKIP_BLANKS while (IS_BLANK(*cur)) NEXT; 190 191static const xmlChar * 192xmlParseCatalogComment(const xmlChar *cur) { 193 if ((cur[0] != '-') || (cur[1] != '-')) 194 return(cur); 195 SKIP(2); 196 while ((cur[0] != 0) && ((cur[0] != '-') || ((cur[1] != '-')))) 197 NEXT; 198 if (cur[0] == 0) { 199 return(NULL); 200 } 201 return(cur + 2); 202} 203 204static const xmlChar * 205xmlParseCatalogPubid(const xmlChar *cur, xmlChar **id) { 206 xmlChar *buf = NULL; 207 int len = 0; 208 int size = 50; 209 xmlChar stop; 210 int count = 0; 211 212 *id = NULL; 213 214 if (RAW == '"') { 215 NEXT; 216 stop = '"'; 217 } else if (RAW == '\'') { 218 NEXT; 219 stop = '\''; 220 } else { 221 stop = ' '; 222 } 223 buf = (xmlChar *) xmlMalloc(size * sizeof(xmlChar)); 224 if (buf == NULL) { 225 xmlGenericError(xmlGenericErrorContext, 226 "malloc of %d byte failed\n", size); 227 return(NULL); 228 } 229 while (xmlIsPubidChar(*cur)) { 230 if ((*cur == stop) && (stop != ' ')) 231 break; 232 if ((stop == ' ') && (IS_BLANK(*cur))) 233 break; 234 if (len + 1 >= size) { 235 size *= 2; 236 buf = (xmlChar *) xmlRealloc(buf, size * sizeof(xmlChar)); 237 if (buf == NULL) { 238 xmlGenericError(xmlGenericErrorContext, 239 "realloc of %d byte failed\n", size); 240 return(NULL); 241 } 242 } 243 buf[len++] = *cur; 244 count++; 245 NEXT; 246 } 247 buf[len] = 0; 248 if (stop == ' ') { 249 if (!IS_BLANK(*cur)) { 250 xmlFree(buf); 251 return(NULL); 252 } 253 } else { 254 if (*cur != stop) { 255 xmlFree(buf); 256 return(NULL); 257 } 258 NEXT; 259 } 260 *id = buf; 261 return(cur); 262} 263 264static const xmlChar * 265xmlParseCatalogName(const xmlChar *cur, xmlChar **name) { 266 xmlChar buf[XML_MAX_NAMELEN + 5]; 267 int len = 0; 268 int c; 269 270 *name = NULL; 271 272 /* 273 * Handler for more complex cases 274 */ 275 c = *cur; 276 if ((!IS_LETTER(c) && (c != '_') && (c != ':'))) { 277 return(NULL); 278 } 279 280 while (((IS_LETTER(c)) || (IS_DIGIT(c)) || 281 (c == '.') || (c == '-') || 282 (c == '_') || (c == ':'))) { 283 buf[len++] = c; 284 cur++; 285 c = *cur; 286 if (len >= XML_MAX_NAMELEN) 287 return(NULL); 288 } 289 *name = xmlStrndup(buf, len); 290 return(cur); 291} 292 293static int 294xmlParseCatalog(const xmlChar *value, const char *file) { 295 const xmlChar *cur = value; 296 xmlChar *base = NULL; 297 int res; 298 299 if ((cur == NULL) || (file == NULL)) 300 return(-1); 301 base = xmlStrdup((const xmlChar *) file); 302 303 while ((cur != NULL) && (cur[0] != '0')) { 304 SKIP_BLANKS; 305 if ((cur[0] == '-') && (cur[1] == '-')) { 306 cur = xmlParseCatalogComment(cur); 307 if (cur == NULL) { 308 /* error */ 309 break; 310 } 311 } else { 312 xmlChar *sysid = NULL; 313 xmlChar *name = NULL; 314 xmlCatalogEntryType type = XML_CATA_NONE; 315 316 cur = xmlParseCatalogName(cur, &name); 317 if (name == NULL) { 318 /* error */ 319 break; 320 } 321 if (!IS_BLANK(*cur)) { 322 /* error */ 323 break; 324 } 325 SKIP_BLANKS; 326 if (xmlStrEqual(name, (const xmlChar *) "SYSTEM")) 327 type = XML_CATA_SYSTEM; 328 else if (xmlStrEqual(name, (const xmlChar *) "PUBLIC")) 329 type = XML_CATA_PUBLIC; 330 else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE")) 331 type = XML_CATA_DELEGATE; 332 else if (xmlStrEqual(name, (const xmlChar *) "ENTITY")) 333 type = XML_CATA_ENTITY; 334 else if (xmlStrEqual(name, (const xmlChar *) "DOCTYPE")) 335 type = XML_CATA_DOCTYPE; 336 else if (xmlStrEqual(name, (const xmlChar *) "LINKTYPE")) 337 type = XML_CATA_LINKTYPE; 338 else if (xmlStrEqual(name, (const xmlChar *) "NOTATION")) 339 type = XML_CATA_NOTATION; 340 else if (xmlStrEqual(name, (const xmlChar *) "SGMLDECL")) 341 type = XML_CATA_SGMLDECL; 342 else if (xmlStrEqual(name, (const xmlChar *) "DOCUMENT")) 343 type = XML_CATA_DOCUMENT; 344 else if (xmlStrEqual(name, (const xmlChar *) "CATALOG")) 345 type = XML_CATA_CATALOG; 346 else if (xmlStrEqual(name, (const xmlChar *) "BASE")) 347 type = XML_CATA_BASE; 348 else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE")) 349 type = XML_CATA_DELEGATE; 350 else if (xmlStrEqual(name, (const xmlChar *) "OVERRIDE")) { 351 xmlFree(name); 352 cur = xmlParseCatalogName(cur, &name); 353 if (name == NULL) { 354 /* error */ 355 break; 356 } 357 xmlFree(name); 358 continue; 359 } 360 xmlFree(name); 361 name = NULL; 362 363 switch(type) { 364 case XML_CATA_ENTITY: 365 if (*cur == '%') 366 type = XML_CATA_PENTITY; 367 case XML_CATA_PENTITY: 368 case XML_CATA_DOCTYPE: 369 case XML_CATA_LINKTYPE: 370 case XML_CATA_NOTATION: 371 cur = xmlParseCatalogName(cur, &name); 372 if (cur == NULL) { 373 /* error */ 374 break; 375 } 376 if (!IS_BLANK(*cur)) { 377 /* error */ 378 break; 379 } 380 SKIP_BLANKS; 381 cur = xmlParseCatalogPubid(cur, &sysid); 382 if (cur == NULL) { 383 /* error */ 384 break; 385 } 386 break; 387 case XML_CATA_PUBLIC: 388 case XML_CATA_SYSTEM: 389 case XML_CATA_DELEGATE: 390 cur = xmlParseCatalogPubid(cur, &name); 391 if (cur == NULL) { 392 /* error */ 393 break; 394 } 395 if (!IS_BLANK(*cur)) { 396 /* error */ 397 break; 398 } 399 SKIP_BLANKS; 400 cur = xmlParseCatalogPubid(cur, &sysid); 401 if (cur == NULL) { 402 /* error */ 403 break; 404 } 405 break; 406 case XML_CATA_BASE: 407 case XML_CATA_CATALOG: 408 case XML_CATA_DOCUMENT: 409 case XML_CATA_SGMLDECL: 410 cur = xmlParseCatalogPubid(cur, &sysid); 411 if (cur == NULL) { 412 /* error */ 413 break; 414 } 415 break; 416 default: 417 break; 418 } 419 if (cur == NULL) { 420 if (name != NULL) 421 xmlFree(name); 422 if (sysid != NULL) 423 xmlFree(sysid); 424 break; 425 } else if (type == XML_CATA_BASE) { 426 if (base != NULL) 427 xmlFree(base); 428 base = xmlStrdup(sysid); 429 } else if ((type == XML_CATA_PUBLIC) || 430 (type == XML_CATA_SYSTEM)) { 431 xmlChar *filename; 432 433 filename = xmlBuildURI(sysid, base); 434 if (filename != NULL) { 435 xmlCatalogEntryPtr entry; 436 437 entry = xmlNewCatalogEntry(type, name, filename); 438 res = xmlHashAddEntry(xmlDefaultCatalog, name, entry); 439 if (res < 0) { 440 xmlFreeCatalogEntry(entry); 441 } 442 xmlFree(filename); 443 } 444 445 } else if (type == XML_CATA_CATALOG) { 446 xmlChar *filename; 447 448 filename = xmlBuildURI(sysid, base); 449 if (filename != NULL) { 450 xmlLoadCatalog((const char *)filename); 451 xmlFree(filename); 452 } 453 } 454 /* 455 * drop anything else we won't handle it 456 */ 457 if (name != NULL) 458 xmlFree(name); 459 if (sysid != NULL) 460 xmlFree(sysid); 461 } 462 } 463 if (base != NULL) 464 xmlFree(base); 465 if (cur == NULL) 466 return(-1); 467 return(0); 468} 469 470/************************************************************************ 471 * * 472 * Public interfaces * 473 * * 474 ************************************************************************/ 475 476/* 477 * xmlLoadCatalog: 478 * @filename: a file path 479 * 480 * Load the catalog and makes its definitions effective for the default 481 * external entity loader. It will recuse in CATALOG entries. 482 * TODO: this function is not thread safe, catalog initialization should 483 * be done once at startup 484 * 485 * Returns 0 in case of success -1 in case of error 486 */ 487int 488xmlLoadCatalog(const char *filename) { 489 int fd, len, ret, i; 490 struct stat info; 491 xmlChar *content; 492 493 if (filename == NULL) 494 return(-1); 495 496 if (xmlDefaultCatalog == NULL) 497 xmlDefaultCatalog = xmlHashCreate(20); 498 if (xmlDefaultCatalog == NULL) 499 return(-1); 500 501 if (stat(filename, &info) < 0) 502 return(-1); 503 504 /* 505 * Prevent loops 506 */ 507 for (i = 0;i < catalNr;i++) { 508 if (xmlStrEqual((const xmlChar *)catalTab[i], 509 (const xmlChar *)filename)) { 510 xmlGenericError(xmlGenericErrorContext, 511 "xmlLoadCatalog: %s seems to induce a loop\n", 512 filename); 513 return(-1); 514 } 515 } 516 if (catalNr >= catalMax) { 517 xmlGenericError(xmlGenericErrorContext, 518 "xmlLoadCatalog: %s catalog list too deep\n", 519 filename); 520 return(-1); 521 } 522 catalTab[catalNr++] = filename; 523 524 if ((fd = open(filename, O_RDONLY)) < 0) { 525 catalNr--; 526 return(-1); 527 } 528 529 content = xmlMalloc(info.st_size + 10); 530 if (content == NULL) { 531 xmlGenericError(xmlGenericErrorContext, 532 "realloc of %d byte failed\n", info.st_size + 10); 533 catalNr--; 534 return(-1); 535 } 536 len = read(fd, content, info.st_size); 537 if (len < 0) { 538 xmlFree(content); 539 catalNr--; 540 return(-1); 541 } 542 content[len] = 0; 543 close(fd); 544 545 ret = xmlParseCatalog(content, filename); 546 xmlFree(content); 547 catalNr--; 548 return(ret); 549} 550 551/* 552 * xmlLoadCatalogs: 553 * @paths: a list of file path separated by ':' or spaces 554 * 555 * Load the catalogs and makes their definitions effective for the default 556 * external entity loader. 557 * TODO: this function is not thread safe, catalog initialization should 558 * be done once at startup 559 */ 560void 561xmlLoadCatalogs(const char *pathss) { 562 const char *cur; 563 const char *paths; 564 xmlChar *path; 565 566 cur = pathss; 567 while ((cur != NULL) && (*cur != 0)) { 568 while (IS_BLANK(*cur)) cur++; 569 if (*cur != 0) { 570 paths = cur; 571 while ((*cur != 0) && (*cur != ':') && (!IS_BLANK(*cur))) 572 cur++; 573 path = xmlStrndup((const xmlChar *)paths, cur - paths); 574 if (path != NULL) { 575 xmlLoadCatalog((const char *) path); 576 xmlFree(path); 577 } 578 } 579 while (*cur == ':') 580 cur++; 581 } 582} 583 584/** 585 * xmlCatalogCleanup: 586 * 587 * Free up all the memory associated with catalogs 588 */ 589void 590xmlCatalogCleanup(void) { 591 if (xmlDefaultCatalog != NULL) 592 xmlHashFree(xmlDefaultCatalog, 593 (xmlHashDeallocator) xmlFreeCatalogEntry); 594 xmlDefaultCatalog = NULL; 595} 596 597/** 598 * xmlCatalogGetSystem: 599 * @sysId: the system ID string 600 * 601 * Try to lookup the resource associated to a system ID 602 * 603 * Returns the resource name if found or NULL otherwise. 604 */ 605const xmlChar * 606xmlCatalogGetSystem(const xmlChar *sysID) { 607 xmlCatalogEntryPtr entry; 608 609 if ((sysID == NULL) || (xmlDefaultCatalog == NULL)) 610 return(NULL); 611 entry = (xmlCatalogEntryPtr) xmlHashLookup(xmlDefaultCatalog, sysID); 612 if (entry == NULL) 613 return(NULL); 614 if (entry->type == XML_CATA_SYSTEM) 615 return(entry->value); 616 return(NULL); 617} 618 619/** 620 * xmlCatalogGetPublic: 621 * @pubId: the public ID string 622 * 623 * Try to lookup the system ID associated to a public ID 624 * 625 * Returns the system ID if found or NULL otherwise. 626 */ 627const xmlChar * 628xmlCatalogGetPublic(const xmlChar *pubID) { 629 xmlCatalogEntryPtr entry; 630 631 if ((pubID == NULL) || (xmlDefaultCatalog == NULL)) 632 return(NULL); 633 entry = (xmlCatalogEntryPtr) xmlHashLookup(xmlDefaultCatalog, pubID); 634 if (entry == NULL) 635 return(NULL); 636 if (entry->type == XML_CATA_PUBLIC) 637 return(entry->value); 638 return(NULL); 639} 640/** 641 * xmlCatalogDump: 642 * @out: the file. 643 * 644 * Free up all the memory associated with catalogs 645 */ 646void 647xmlCatalogDump(FILE *out) { 648 if (out == NULL) 649 return; 650 if (xmlDefaultCatalog != NULL) { 651 xmlHashScan(xmlDefaultCatalog, 652 (xmlHashScanner) xmlCatalogDumpEntry, out); 653 } 654} 655#endif /* LIBXML_CATALOG_ENABLED */ 656