catalog.c 96.9 KB
Newer Older
1
/**
Daniel Veillard's avatar
Daniel Veillard committed
2
 * catalog.c: set of generic Catalog related routines
3 4 5 6
 *
 * Reference:  SGML Open Technical Resolution TR9401:1997.
 *             http://www.jclark.com/sp/catalog.htm
 *
7 8 9
 *             XML Catalogs Working Draft 06 August 2001
 *             http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
 *
10 11 12 13 14
 * See Copyright for the status of this software.
 *
 * Daniel.Veillard@imag.fr
 */

15
#define IN_LIBXML
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
#include "libxml.h"

#ifdef LIBXML_CATALOG_ENABLED
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
31 32 33
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
34 35 36 37 38 39 40
#include <string.h>
#include <libxml/xmlmemory.h>
#include <libxml/hash.h>
#include <libxml/uri.h>
#include <libxml/parserInternals.h>
#include <libxml/catalog.h>
#include <libxml/xmlerror.h>
41
#include <libxml/threads.h>
42
#include <libxml/globals.h>
43

44 45
#include "buf.h"

46
#define MAX_DELEGATE	50
47
#define MAX_CATAL_DEPTH	50
48

49
#ifdef _WIN32
50
# define PATH_SEPARATOR ';'
51
#else
52
# define PATH_SEPARATOR ':'
53 54
#endif

55 56 57 58
/**
 * TODO:
 *
 * macro to flag unimplemented blocks
Jared Yanovich's avatar
Jared Yanovich committed
59
 * XML_CATALOG_PREFER user env to select between system/public preferred
60 61 62 63
 * option. C.f. Richard Tobin <richard@cogsci.ed.ac.uk>
 *> Just FYI, I am using an environment variable XML_CATALOG_PREFER with
 *> values "system" and "public".  I have made the default be "system" to
 *> match yours.
64
 */
Daniel Veillard's avatar
Daniel Veillard committed
65
#define TODO								\
66 67 68 69
    xmlGenericError(xmlGenericErrorContext,				\
	    "Unimplemented block at %s:%d\n",				\
            __FILE__, __LINE__);

70
#define XML_URN_PUBID "urn:publicid:"
71
#define XML_CATAL_BREAK ((xmlChar *) -1)
72
#ifndef XML_XML_DEFAULT_CATALOG
73
#define XML_XML_DEFAULT_CATALOG "file:///etc/xml/catalog"
74
#endif
75
#ifndef XML_SGML_DEFAULT_CATALOG
76
#define XML_SGML_DEFAULT_CATALOG "file:///etc/sgml/catalog"
77 78
#endif

79 80 81
#if defined(_WIN32) && defined(_MSC_VER)
#undef XML_XML_DEFAULT_CATALOG
static char XML_XML_DEFAULT_CATALOG[256] = "file:///etc/xml/catalog";
82 83 84 85 86
#if defined(_WIN32_WCE)
/* Windows CE don't have a A variant */
#define GetModuleHandleA GetModuleHandle
#define GetModuleFileNameA GetModuleFileName
#else
Denis Pauk's avatar
Denis Pauk committed
87
#if !defined(_WINDOWS_)
88 89 90
void* __stdcall GetModuleHandleA(const char*);
unsigned long __stdcall GetModuleFileNameA(void*, char*, unsigned long);
#endif
91
#endif
Denis Pauk's avatar
Denis Pauk committed
92
#endif
93

94
static xmlChar *xmlCatalogNormalizePublic(const xmlChar *pubID);
95
static int xmlExpandCatalog(xmlCatalogPtr catal, const char *filename);
96

97 98 99 100 101 102 103
/************************************************************************
 *									*
 *			Types, all private				*
 *									*
 ************************************************************************/

typedef enum {
104
    XML_CATA_REMOVED = -1,
105 106
    XML_CATA_NONE = 0,
    XML_CATA_CATALOG,
107
    XML_CATA_BROKEN_CATALOG,
108
    XML_CATA_NEXT_CATALOG,
109
    XML_CATA_GROUP,
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
    XML_CATA_PUBLIC,
    XML_CATA_SYSTEM,
    XML_CATA_REWRITE_SYSTEM,
    XML_CATA_DELEGATE_PUBLIC,
    XML_CATA_DELEGATE_SYSTEM,
    XML_CATA_URI,
    XML_CATA_REWRITE_URI,
    XML_CATA_DELEGATE_URI,
    SGML_CATA_SYSTEM,
    SGML_CATA_PUBLIC,
    SGML_CATA_ENTITY,
    SGML_CATA_PENTITY,
    SGML_CATA_DOCTYPE,
    SGML_CATA_LINKTYPE,
    SGML_CATA_NOTATION,
    SGML_CATA_DELEGATE,
    SGML_CATA_BASE,
    SGML_CATA_CATALOG,
    SGML_CATA_DOCUMENT,
    SGML_CATA_SGMLDECL
130 131 132 133 134
} xmlCatalogEntryType;

typedef struct _xmlCatalogEntry xmlCatalogEntry;
typedef xmlCatalogEntry *xmlCatalogEntryPtr;
struct _xmlCatalogEntry {
135 136 137
    struct _xmlCatalogEntry *next;
    struct _xmlCatalogEntry *parent;
    struct _xmlCatalogEntry *children;
138 139 140
    xmlCatalogEntryType type;
    xmlChar *name;
    xmlChar *value;
141
    xmlChar *URL;  /* The expanded URL using the base */
142
    xmlCatalogPrefer prefer;
143
    int dealloc;
144
    int depth;
145
    struct _xmlCatalogEntry *group;
146 147
};

148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
typedef enum {
    XML_XML_CATALOG_TYPE = 1,
    XML_SGML_CATALOG_TYPE
} xmlCatalogType;

#define XML_MAX_SGML_CATA_DEPTH 10
struct _xmlCatalog {
    xmlCatalogType type;	/* either XML or SGML */

    /*
     * SGML Catalogs are stored as a simple hash table of catalog entries
     * Catalog stack to check against overflows when building the
     * SGML catalog
     */
    char *catalTab[XML_MAX_SGML_CATA_DEPTH];	/* stack of catals */
    int          catalNr;	/* Number of current catal streams */
    int          catalMax;	/* Max number of catal streams */
    xmlHashTablePtr sgml;

    /*
     * XML Catalogs are stored as a tree of Catalog entries
     */
    xmlCatalogPrefer prefer;
    xmlCatalogEntryPtr xml;
};

/************************************************************************
 *									*
 *			Global variables				*
 *									*
 ************************************************************************/

180 181 182 183
/*
 * Those are preferences
 */
static int xmlDebugCatalogs = 0;   /* used for debugging */
184
static xmlCatalogAllow xmlCatalogDefaultAllow = XML_CATA_ALLOW_ALL;
185
static xmlCatalogPrefer xmlCatalogDefaultPrefer = XML_CATA_PREFER_PUBLIC;
186 187 188 189 190

/*
 * Hash table containing all the trees of XML catalogs parsed by
 * the application.
 */
191
static xmlHashTablePtr xmlCatalogXMLFiles = NULL;
192

193 194 195 196 197
/*
 * The default catalog in use by the application
 */
static xmlCatalogPtr xmlDefaultCatalog = NULL;

198 199 200 201 202 203 204 205
/*
 * A mutex for modifying the shared global catalog(s)
 * xmlDefaultCatalog tree.
 * It also protects xmlCatalogXMLFiles
 * The core of this readers/writer scheme is in xmlFetchXMLCatalogFile()
 */
static xmlRMutexPtr xmlCatalogMutex = NULL;

206 207 208 209
/*
 * Whether the catalog support was initialized.
 */
static int xmlCatalogInitialized = 0;
210

211 212
/************************************************************************
 *									*
Daniel Veillard's avatar
Daniel Veillard committed
213
 *			Catalog error handlers				*
214 215 216 217 218 219 220 221 222 223 224 225
 *									*
 ************************************************************************/

/**
 * xmlCatalogErrMemory:
 * @extra:  extra informations
 *
 * Handle an out of memory condition
 */
static void
xmlCatalogErrMemory(const char *extra)
{
226
    __xmlRaiseError(NULL, NULL, NULL, NULL, NULL, XML_FROM_CATALOG,
227 228 229 230 231 232 233 234 235 236 237 238 239 240
                    XML_ERR_NO_MEMORY, XML_ERR_ERROR, NULL, 0,
		    extra, NULL, NULL, 0, 0,
		    "Memory allocation failed : %s\n", extra);
}

/**
 * xmlCatalogErr:
 * @catal: the Catalog entry
 * @node: the context node
 * @msg:  the error message
 * @extra:  extra informations
 *
 * Handle a catalog error
 */
241
static void LIBXML_ATTR_FORMAT(4,0)
242 243 244 245
xmlCatalogErr(xmlCatalogEntryPtr catal, xmlNodePtr node, int error,
               const char *msg, const xmlChar *str1, const xmlChar *str2,
	       const xmlChar *str3)
{
246
    __xmlRaiseError(NULL, NULL, NULL, catal, node, XML_FROM_CATALOG,
247 248 249 250 251 252
                    error, XML_ERR_ERROR, NULL, 0,
		    (const char *) str1, (const char *) str2,
		    (const char *) str3, 0, 0,
		    msg, str1, str2, str3);
}

253

254 255
/************************************************************************
 *									*
256
 *			Allocation and Freeing				*
257 258 259
 *									*
 ************************************************************************/

260 261 262 263 264 265
/**
 * xmlNewCatalogEntry:
 * @type:  type of entry
 * @name:  name of the entry
 * @value:  value of the entry
 * @prefer:  the PUBLIC vs. SYSTEM current preference value
Daniel Veillard's avatar
Daniel Veillard committed
266
 * @group:  for members of a group, the group entry
267
 *
Daniel Veillard's avatar
Daniel Veillard committed
268
 * create a new Catalog entry, this type is shared both by XML and
269 270 271 272
 * SGML catalogs, but the acceptable types values differs.
 *
 * Returns the xmlCatalogEntryPtr or NULL in case of error
 */
273
static xmlCatalogEntryPtr
274
xmlNewCatalogEntry(xmlCatalogEntryType type, const xmlChar *name,
275 276
	   const xmlChar *value, const xmlChar *URL, xmlCatalogPrefer prefer,
	   xmlCatalogEntryPtr group) {
277
    xmlCatalogEntryPtr ret;
278
    xmlChar *normid = NULL;
279 280 281

    ret = (xmlCatalogEntryPtr) xmlMalloc(sizeof(xmlCatalogEntry));
    if (ret == NULL) {
282
        xmlCatalogErrMemory("allocating catalog entry");
283 284
	return(NULL);
    }
285 286 287
    ret->next = NULL;
    ret->parent = NULL;
    ret->children = NULL;
288
    ret->type = type;
289 290 291 292 293
    if (type == XML_CATA_PUBLIC || type == XML_CATA_DELEGATE_PUBLIC) {
        normid = xmlCatalogNormalizePublic(name);
        if (normid != NULL)
            name = (*normid != 0 ? normid : NULL);
    }
294 295 296 297
    if (name != NULL)
	ret->name = xmlStrdup(name);
    else
	ret->name = NULL;
298 299
    if (normid != NULL)
        xmlFree(normid);
300 301 302 303
    if (value != NULL)
	ret->value = xmlStrdup(value);
    else
	ret->value = NULL;
304 305 306 307 308 309
    if (URL == NULL)
	URL = value;
    if (URL != NULL)
	ret->URL = xmlStrdup(URL);
    else
	ret->URL = NULL;
310
    ret->prefer = prefer;
311
    ret->dealloc = 0;
312
    ret->depth = 0;
313
    ret->group = group;
314 315 316
    return(ret);
}

317 318 319
static void
xmlFreeCatalogEntryList(xmlCatalogEntryPtr ret);

320 321
/**
 * xmlFreeCatalogEntry:
Nick Wellnhofer's avatar
Nick Wellnhofer committed
322
 * @payload:  a Catalog entry
323 324 325
 *
 * Free the memory allocated to a Catalog entry
 */
326
static void
Nick Wellnhofer's avatar
Nick Wellnhofer committed
327 328
xmlFreeCatalogEntry(void *payload, const xmlChar *name ATTRIBUTE_UNUSED) {
    xmlCatalogEntryPtr ret = (xmlCatalogEntryPtr) payload;
329 330
    if (ret == NULL)
	return;
331
    /*
332
     * Entries stored in the file hash must be deallocated
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349
     * only by the file hash cleaner !
     */
    if (ret->dealloc == 1)
	return;

    if (xmlDebugCatalogs) {
	if (ret->name != NULL)
	    xmlGenericError(xmlGenericErrorContext,
		    "Free catalog entry %s\n", ret->name);
	else if (ret->value != NULL)
	    xmlGenericError(xmlGenericErrorContext,
		    "Free catalog entry %s\n", ret->value);
	else
	    xmlGenericError(xmlGenericErrorContext,
		    "Free catalog entry\n");
    }

350 351 352 353
    if (ret->name != NULL)
	xmlFree(ret->name);
    if (ret->value != NULL)
	xmlFree(ret->value);
354 355
    if (ret->URL != NULL)
	xmlFree(ret->URL);
356 357 358
    xmlFree(ret);
}

359 360 361 362 363 364
/**
 * xmlFreeCatalogEntryList:
 * @ret:  a Catalog entry list
 *
 * Free the memory allocated to a full chained list of Catalog entries
 */
365 366 367 368 369 370
static void
xmlFreeCatalogEntryList(xmlCatalogEntryPtr ret) {
    xmlCatalogEntryPtr next;

    while (ret != NULL) {
	next = ret->next;
Nick Wellnhofer's avatar
Nick Wellnhofer committed
371
	xmlFreeCatalogEntry(ret, NULL);
372 373 374 375
	ret = next;
    }
}

376 377
/**
 * xmlFreeCatalogHashEntryList:
Nick Wellnhofer's avatar
Nick Wellnhofer committed
378
 * @payload:  a Catalog entry list
379 380 381 382 383
 *
 * Free the memory allocated to list of Catalog entries from the
 * catalog file hash.
 */
static void
Nick Wellnhofer's avatar
Nick Wellnhofer committed
384 385 386
xmlFreeCatalogHashEntryList(void *payload,
                            const xmlChar *name ATTRIBUTE_UNUSED) {
    xmlCatalogEntryPtr catal = (xmlCatalogEntryPtr) payload;
387 388 389 390 391 392 393 394 395 396
    xmlCatalogEntryPtr children, next;

    if (catal == NULL)
	return;

    children = catal->children;
    while (children != NULL) {
	next = children->next;
	children->dealloc = 0;
	children->children = NULL;
Nick Wellnhofer's avatar
Nick Wellnhofer committed
397
	xmlFreeCatalogEntry(children, NULL);
398 399 400
	children = next;
    }
    catal->dealloc = 0;
Nick Wellnhofer's avatar
Nick Wellnhofer committed
401
    xmlFreeCatalogEntry(catal, NULL);
402 403
}

404
/**
405
 * xmlCreateNewCatalog:
406 407 408
 * @type:  type of catalog
 * @prefer:  the PUBLIC vs. SYSTEM current preference value
 *
Daniel Veillard's avatar
Daniel Veillard committed
409
 * create a new Catalog, this type is shared both by XML and
410 411 412 413 414
 * SGML catalogs, but the acceptable types values differs.
 *
 * Returns the xmlCatalogPtr or NULL in case of error
 */
static xmlCatalogPtr
415
xmlCreateNewCatalog(xmlCatalogType type, xmlCatalogPrefer prefer) {
416 417 418 419
    xmlCatalogPtr ret;

    ret = (xmlCatalogPtr) xmlMalloc(sizeof(xmlCatalog));
    if (ret == NULL) {
420
        xmlCatalogErrMemory("allocating catalog");
421 422 423 424 425 426 427
	return(NULL);
    }
    memset(ret, 0, sizeof(xmlCatalog));
    ret->type = type;
    ret->catalNr = 0;
    ret->catalMax = XML_MAX_SGML_CATA_DEPTH;
    ret->prefer = prefer;
428 429
    if (ret->type == XML_SGML_CATALOG_TYPE)
	ret->sgml = xmlHashCreate(10);
430 431 432 433 434
    return(ret);
}

/**
 * xmlFreeCatalog:
435
 * @catal:  a Catalog
436 437 438 439 440 441 442 443 444 445
 *
 * Free the memory allocated to a Catalog
 */
void
xmlFreeCatalog(xmlCatalogPtr catal) {
    if (catal == NULL)
	return;
    if (catal->xml != NULL)
	xmlFreeCatalogEntryList(catal->xml);
    if (catal->sgml != NULL)
Nick Wellnhofer's avatar
Nick Wellnhofer committed
446
	xmlHashFree(catal->sgml, xmlFreeCatalogEntry);
447 448 449 450 451 452 453 454 455
    xmlFree(catal);
}

/************************************************************************
 *									*
 *			Serializing Catalogs				*
 *									*
 ************************************************************************/

456
#ifdef LIBXML_OUTPUT_ENABLED
457 458
/**
 * xmlCatalogDumpEntry:
459
 * @entry:  the catalog entry
460 461
 * @out:  the file.
 *
462
 * Serialize an SGML Catalog entry
463 464
 */
static void
Nick Wellnhofer's avatar
Nick Wellnhofer committed
465 466 467 468
xmlCatalogDumpEntry(void *payload, void *data,
                    const xmlChar *name ATTRIBUTE_UNUSED) {
    xmlCatalogEntryPtr entry = (xmlCatalogEntryPtr) payload;
    FILE *out = (FILE *) data;
469 470 471
    if ((entry == NULL) || (out == NULL))
	return;
    switch (entry->type) {
472
	case SGML_CATA_ENTITY:
473
	    fprintf(out, "ENTITY "); break;
474
	case SGML_CATA_PENTITY:
475
	    fprintf(out, "ENTITY %%"); break;
476
	case SGML_CATA_DOCTYPE:
477
	    fprintf(out, "DOCTYPE "); break;
478
	case SGML_CATA_LINKTYPE:
479
	    fprintf(out, "LINKTYPE "); break;
480
	case SGML_CATA_NOTATION:
481
	    fprintf(out, "NOTATION "); break;
482
	case SGML_CATA_PUBLIC:
483
	    fprintf(out, "PUBLIC "); break;
484
	case SGML_CATA_SYSTEM:
485
	    fprintf(out, "SYSTEM "); break;
486
	case SGML_CATA_DELEGATE:
487
	    fprintf(out, "DELEGATE "); break;
488
	case SGML_CATA_BASE:
489
	    fprintf(out, "BASE "); break;
490
	case SGML_CATA_CATALOG:
491
	    fprintf(out, "CATALOG "); break;
492
	case SGML_CATA_DOCUMENT:
493
	    fprintf(out, "DOCUMENT "); break;
494
	case SGML_CATA_SGMLDECL:
495 496 497 498 499
	    fprintf(out, "SGMLDECL "); break;
	default:
	    return;
    }
    switch (entry->type) {
500 501 502 503 504
	case SGML_CATA_ENTITY:
	case SGML_CATA_PENTITY:
	case SGML_CATA_DOCTYPE:
	case SGML_CATA_LINKTYPE:
	case SGML_CATA_NOTATION:
505
	    fprintf(out, "%s", (const char *) entry->name); break;
506 507 508 509 510 511 512
	case SGML_CATA_PUBLIC:
	case SGML_CATA_SYSTEM:
	case SGML_CATA_SGMLDECL:
	case SGML_CATA_DOCUMENT:
	case SGML_CATA_CATALOG:
	case SGML_CATA_BASE:
	case SGML_CATA_DELEGATE:
513 514 515 516 517
	    fprintf(out, "\"%s\"", entry->name); break;
	default:
	    break;
    }
    switch (entry->type) {
518 519 520 521 522 523 524 525
	case SGML_CATA_ENTITY:
	case SGML_CATA_PENTITY:
	case SGML_CATA_DOCTYPE:
	case SGML_CATA_LINKTYPE:
	case SGML_CATA_NOTATION:
	case SGML_CATA_PUBLIC:
	case SGML_CATA_SYSTEM:
	case SGML_CATA_DELEGATE:
526 527 528 529 530 531 532
	    fprintf(out, " \"%s\"", entry->value); break;
	default:
	    break;
    }
    fprintf(out, "\n");
}

533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573
/**
 * xmlDumpXMLCatalogNode:
 * @catal:  top catalog entry
 * @catalog: pointer to the xml tree
 * @doc: the containing document
 * @ns: the current namespace
 * @cgroup: group node for group members
 *
 * Serializes a Catalog entry, called by xmlDumpXMLCatalog and recursively
 * for group entries
 */
static void xmlDumpXMLCatalogNode(xmlCatalogEntryPtr catal, xmlNodePtr catalog,
		    xmlDocPtr doc, xmlNsPtr ns, xmlCatalogEntryPtr cgroup) {
    xmlNodePtr node;
    xmlCatalogEntryPtr cur;
    /*
     * add all the catalog entries
     */
    cur = catal;
    while (cur != NULL) {
        if (cur->group == cgroup) {
	    switch (cur->type) {
	        case XML_CATA_REMOVED:
		    break;
	        case XML_CATA_BROKEN_CATALOG:
	        case XML_CATA_CATALOG:
		    if (cur == catal) {
			cur = cur->children;
		        continue;
		    }
		    break;
		case XML_CATA_NEXT_CATALOG:
		    node = xmlNewDocNode(doc, ns, BAD_CAST "nextCatalog", NULL);
		    xmlSetProp(node, BAD_CAST "catalog", cur->value);
		    xmlAddChild(catalog, node);
                    break;
		case XML_CATA_NONE:
		    break;
		case XML_CATA_GROUP:
		    node = xmlNewDocNode(doc, ns, BAD_CAST "group", NULL);
		    xmlSetProp(node, BAD_CAST "id", cur->name);
574 575 576 577 578
		    if (cur->value != NULL) {
		        xmlNsPtr xns;
			xns = xmlSearchNsByHref(doc, node, XML_XML_NAMESPACE);
			if (xns != NULL)
			    xmlSetNsProp(node, xns, BAD_CAST "base",
Daniel Veillard's avatar
Daniel Veillard committed
579
					 cur->value);
580
		    }
581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660
		    switch (cur->prefer) {
			case XML_CATA_PREFER_NONE:
		            break;
			case XML_CATA_PREFER_PUBLIC:
		            xmlSetProp(node, BAD_CAST "prefer", BAD_CAST "public");
			    break;
			case XML_CATA_PREFER_SYSTEM:
		            xmlSetProp(node, BAD_CAST "prefer", BAD_CAST "system");
			    break;
		    }
		    xmlDumpXMLCatalogNode(cur->next, node, doc, ns, cur);
		    xmlAddChild(catalog, node);
	            break;
		case XML_CATA_PUBLIC:
		    node = xmlNewDocNode(doc, ns, BAD_CAST "public", NULL);
		    xmlSetProp(node, BAD_CAST "publicId", cur->name);
		    xmlSetProp(node, BAD_CAST "uri", cur->value);
		    xmlAddChild(catalog, node);
		    break;
		case XML_CATA_SYSTEM:
		    node = xmlNewDocNode(doc, ns, BAD_CAST "system", NULL);
		    xmlSetProp(node, BAD_CAST "systemId", cur->name);
		    xmlSetProp(node, BAD_CAST "uri", cur->value);
		    xmlAddChild(catalog, node);
		    break;
		case XML_CATA_REWRITE_SYSTEM:
		    node = xmlNewDocNode(doc, ns, BAD_CAST "rewriteSystem", NULL);
		    xmlSetProp(node, BAD_CAST "systemIdStartString", cur->name);
		    xmlSetProp(node, BAD_CAST "rewritePrefix", cur->value);
		    xmlAddChild(catalog, node);
		    break;
		case XML_CATA_DELEGATE_PUBLIC:
		    node = xmlNewDocNode(doc, ns, BAD_CAST "delegatePublic", NULL);
		    xmlSetProp(node, BAD_CAST "publicIdStartString", cur->name);
		    xmlSetProp(node, BAD_CAST "catalog", cur->value);
		    xmlAddChild(catalog, node);
		    break;
		case XML_CATA_DELEGATE_SYSTEM:
		    node = xmlNewDocNode(doc, ns, BAD_CAST "delegateSystem", NULL);
		    xmlSetProp(node, BAD_CAST "systemIdStartString", cur->name);
		    xmlSetProp(node, BAD_CAST "catalog", cur->value);
		    xmlAddChild(catalog, node);
		    break;
		case XML_CATA_URI:
		    node = xmlNewDocNode(doc, ns, BAD_CAST "uri", NULL);
		    xmlSetProp(node, BAD_CAST "name", cur->name);
		    xmlSetProp(node, BAD_CAST "uri", cur->value);
		    xmlAddChild(catalog, node);
		    break;
		case XML_CATA_REWRITE_URI:
		    node = xmlNewDocNode(doc, ns, BAD_CAST "rewriteURI", NULL);
		    xmlSetProp(node, BAD_CAST "uriStartString", cur->name);
		    xmlSetProp(node, BAD_CAST "rewritePrefix", cur->value);
		    xmlAddChild(catalog, node);
		    break;
		case XML_CATA_DELEGATE_URI:
		    node = xmlNewDocNode(doc, ns, BAD_CAST "delegateURI", NULL);
		    xmlSetProp(node, BAD_CAST "uriStartString", cur->name);
		    xmlSetProp(node, BAD_CAST "catalog", cur->value);
		    xmlAddChild(catalog, node);
		    break;
		case SGML_CATA_SYSTEM:
		case SGML_CATA_PUBLIC:
		case SGML_CATA_ENTITY:
		case SGML_CATA_PENTITY:
		case SGML_CATA_DOCTYPE:
		case SGML_CATA_LINKTYPE:
		case SGML_CATA_NOTATION:
		case SGML_CATA_DELEGATE:
		case SGML_CATA_BASE:
		case SGML_CATA_CATALOG:
		case SGML_CATA_DOCUMENT:
		case SGML_CATA_SGMLDECL:
		    break;
	    }
        }
	cur = cur->next;
    }
}

661 662 663 664 665 666
static int
xmlDumpXMLCatalog(FILE *out, xmlCatalogEntryPtr catal) {
    int ret;
    xmlDocPtr doc;
    xmlNsPtr ns;
    xmlDtdPtr dtd;
667
    xmlNodePtr catalog;
668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695
    xmlOutputBufferPtr buf;

    /*
     * Rebuild a catalog
     */
    doc = xmlNewDoc(NULL);
    if (doc == NULL)
	return(-1);
    dtd = xmlNewDtd(doc, BAD_CAST "catalog",
	       BAD_CAST "-//OASIS//DTD Entity Resolution XML Catalog V1.0//EN",
BAD_CAST "http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd");

    xmlAddChild((xmlNodePtr) doc, (xmlNodePtr) dtd);

    ns = xmlNewNs(NULL, XML_CATALOGS_NAMESPACE, NULL);
    if (ns == NULL) {
	xmlFreeDoc(doc);
	return(-1);
    }
    catalog = xmlNewDocNode(doc, ns, BAD_CAST "catalog", NULL);
    if (catalog == NULL) {
	xmlFreeNs(ns);
	xmlFreeDoc(doc);
	return(-1);
    }
    catalog->nsDef = ns;
    xmlAddChild((xmlNodePtr) doc, catalog);

696
    xmlDumpXMLCatalogNode(catal, catalog, doc, ns, NULL);
Daniel Veillard's avatar
Daniel Veillard committed
697

698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714
    /*
     * reserialize it
     */
    buf = xmlOutputBufferCreateFile(out, NULL);
    if (buf == NULL) {
	xmlFreeDoc(doc);
	return(-1);
    }
    ret = xmlSaveFormatFileTo(buf, doc, NULL, 1);

    /*
     * Free it
     */
    xmlFreeDoc(doc);

    return(ret);
}
715
#endif /* LIBXML_OUTPUT_ENABLED */
716 717 718 719 720 721 722

/************************************************************************
 *									*
 *			Converting SGML Catalogs to XML			*
 *									*
 ************************************************************************/

723 724 725
/**
 * xmlCatalogConvertEntry:
 * @entry:  the entry
726
 * @catal:  pointer to the catalog being converted
727
 *
728
 * Convert one entry from the catalog
729 730
 */
static void
Nick Wellnhofer's avatar
Nick Wellnhofer committed
731 732 733 734
xmlCatalogConvertEntry(void *payload, void *data,
                       const xmlChar *name ATTRIBUTE_UNUSED) {
    xmlCatalogEntryPtr entry = (xmlCatalogEntryPtr) payload;
    xmlCatalogPtr catal = (xmlCatalogPtr) data;
735 736
    if ((entry == NULL) || (catal == NULL) || (catal->sgml == NULL) ||
	(catal->xml == NULL))
737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766
	return;
    switch (entry->type) {
	case SGML_CATA_ENTITY:
	    entry->type = XML_CATA_PUBLIC;
	    break;
	case SGML_CATA_PENTITY:
	    entry->type = XML_CATA_PUBLIC;
	    break;
	case SGML_CATA_DOCTYPE:
	    entry->type = XML_CATA_PUBLIC;
	    break;
	case SGML_CATA_LINKTYPE:
	    entry->type = XML_CATA_PUBLIC;
	    break;
	case SGML_CATA_NOTATION:
	    entry->type = XML_CATA_PUBLIC;
	    break;
	case SGML_CATA_PUBLIC:
	    entry->type = XML_CATA_PUBLIC;
	    break;
	case SGML_CATA_SYSTEM:
	    entry->type = XML_CATA_SYSTEM;
	    break;
	case SGML_CATA_DELEGATE:
	    entry->type = XML_CATA_DELEGATE_PUBLIC;
	    break;
	case SGML_CATA_CATALOG:
	    entry->type = XML_CATA_CATALOG;
	    break;
	default:
Nick Wellnhofer's avatar
Nick Wellnhofer committed
767
	    xmlHashRemoveEntry(catal->sgml, entry->name, xmlFreeCatalogEntry);
768 769 770 771 772 773
	    return;
    }
    /*
     * Conversion successful, remove from the SGML catalog
     * and add it to the default XML one
     */
774 775
    xmlHashRemoveEntry(catal->sgml, entry->name, NULL);
    entry->parent = catal->xml;
776
    entry->next = NULL;
777 778
    if (catal->xml->children == NULL)
	catal->xml->children = entry;
779 780 781
    else {
	xmlCatalogEntryPtr prev;

782
	prev = catal->xml->children;
783 784 785 786
	while (prev->next != NULL)
	    prev = prev->next;
	prev->next = entry;
    }
787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806
}

/**
 * xmlConvertSGMLCatalog:
 * @catal: the catalog
 *
 * Convert all the SGML catalog entries as XML ones
 *
 * Returns the number of entries converted if successful, -1 otherwise
 */
int
xmlConvertSGMLCatalog(xmlCatalogPtr catal) {

    if ((catal == NULL) || (catal->type != XML_SGML_CATALOG_TYPE))
	return(-1);

    if (xmlDebugCatalogs) {
	xmlGenericError(xmlGenericErrorContext,
		"Converting SGML catalog to XML\n");
    }
Nick Wellnhofer's avatar
Nick Wellnhofer committed
807
    xmlHashScan(catal->sgml, xmlCatalogConvertEntry, &catal);
808
    return(0);
809 810
}

811 812 813 814 815 816 817 818
/************************************************************************
 *									*
 *			Helper function					*
 *									*
 ************************************************************************/

/**
 * xmlCatalogUnWrapURN:
819
 * @urn:  an "urn:publicid:" to unwrap
820 821 822 823 824 825 826 827 828 829 830 831 832 833
 *
 * Expand the URN into the equivalent Public Identifier
 *
 * Returns the new identifier or NULL, the string must be deallocated
 *         by the caller.
 */
static xmlChar *
xmlCatalogUnWrapURN(const xmlChar *urn) {
    xmlChar result[2000];
    unsigned int i = 0;

    if (xmlStrncmp(urn, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1))
	return(NULL);
    urn += sizeof(XML_URN_PUBID) - 1;
Daniel Veillard's avatar
Daniel Veillard committed
834

835
    while (*urn != 0) {
836
	if (i > sizeof(result) - 4)
837 838 839 840 841 842 843 844 845 846 847 848 849
	    break;
	if (*urn == '+') {
	    result[i++] = ' ';
	    urn++;
	} else if (*urn == ':') {
	    result[i++] = '/';
	    result[i++] = '/';
	    urn++;
	} else if (*urn == ';') {
	    result[i++] = ':';
	    result[i++] = ':';
	    urn++;
	} else if (*urn == '%') {
850
	    if ((urn[1] == '2') && (urn[2] == 'B'))
851
		result[i++] = '+';
852
	    else if ((urn[1] == '3') && (urn[2] == 'A'))
853
		result[i++] = ':';
854
	    else if ((urn[1] == '2') && (urn[2] == 'F'))
855
		result[i++] = '/';
856
	    else if ((urn[1] == '3') && (urn[2] == 'B'))
857
		result[i++] = ';';
858
	    else if ((urn[1] == '2') && (urn[2] == '7'))
859
		result[i++] = '\'';
860
	    else if ((urn[1] == '3') && (urn[2] == 'F'))
861
		result[i++] = '?';
862
	    else if ((urn[1] == '2') && (urn[2] == '3'))
863
		result[i++] = '#';
864
	    else if ((urn[1] == '2') && (urn[2] == '5'))
865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881
		result[i++] = '%';
	    else {
		result[i++] = *urn;
		urn++;
		continue;
	    }
	    urn += 3;
	} else {
	    result[i++] = *urn;
	    urn++;
	}
    }
    result[i] = 0;

    return(xmlStrdup(result));
}

882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901
/**
 * xmlParseCatalogFile:
 * @filename:  the filename
 *
 * parse an XML file and build a tree. It's like xmlParseFile()
 * except it bypass all catalog lookups.
 *
 * Returns the resulting document tree or NULL in case of error
 */

xmlDocPtr
xmlParseCatalogFile(const char *filename) {
    xmlDocPtr ret;
    xmlParserCtxtPtr ctxt;
    char *directory = NULL;
    xmlParserInputPtr inputStream;
    xmlParserInputBufferPtr buf;

    ctxt = xmlNewParserCtxt();
    if (ctxt == NULL) {
902
#ifdef LIBXML_SAX1_ENABLED
903 904 905
	if (xmlDefaultSAXHandler.error != NULL) {
	    xmlDefaultSAXHandler.error(NULL, "out of memory\n");
	}
906
#endif
907 908 909 910 911 912 913 914 915 916 917 918 919 920 921
	return(NULL);
    }

    buf = xmlParserInputBufferCreateFilename(filename, XML_CHAR_ENCODING_NONE);
    if (buf == NULL) {
	xmlFreeParserCtxt(ctxt);
	return(NULL);
    }

    inputStream = xmlNewInputStream(ctxt);
    if (inputStream == NULL) {
	xmlFreeParserCtxt(ctxt);
	return(NULL);
    }

922
    inputStream->filename = (char *) xmlCanonicPath((const xmlChar *)filename);
923
    inputStream->buf = buf;
924
    xmlBufResetInput(buf->buffer, inputStream);
925 926 927 928 929 930

    inputPush(ctxt, inputStream);
    if ((ctxt->directory == NULL) && (directory == NULL))
        directory = xmlParserGetDirectory(filename);
    if ((ctxt->directory == NULL) && (directory != NULL))
        ctxt->directory = directory;
931 932 933 934
    ctxt->valid = 0;
    ctxt->validate = 0;
    ctxt->loadsubset = 0;
    ctxt->pedantic = 0;
935
    ctxt->dictNames = 1;
936 937 938 939 940 941 942 943 944 945 946

    xmlParseDocument(ctxt);

    if (ctxt->wellFormed)
	ret = ctxt->myDoc;
    else {
        ret = NULL;
        xmlFreeDoc(ctxt->myDoc);
        ctxt->myDoc = NULL;
    }
    xmlFreeParserCtxt(ctxt);
Daniel Veillard's avatar
Daniel Veillard committed
947

948 949 950
    return(ret);
}

951 952 953 954 955 956 957 958
/**
 * xmlLoadFileContent:
 * @filename:  a file path
 *
 * Load a file content into memory.
 *
 * Returns a pointer to the 0 terminated string or NULL in case of error
 */
959
static xmlChar *
960 961 962 963 964 965 966 967 968
xmlLoadFileContent(const char *filename)
{
#ifdef HAVE_STAT
    int fd;
#else
    FILE *fd;
#endif
    int len;
    long size;
969

970 971 972 973 974 975 976 977 978 979 980 981 982 983
#ifdef HAVE_STAT
    struct stat info;
#endif
    xmlChar *content;

    if (filename == NULL)
        return (NULL);

#ifdef HAVE_STAT
    if (stat(filename, &info) < 0)
        return (NULL);
#endif

#ifdef HAVE_STAT
Daniel Veillard's avatar
Daniel Veillard committed
984
    if ((fd = open(filename, O_RDONLY)) < 0)
985
#else
Daniel Veillard's avatar
Daniel Veillard committed
986
    if ((fd = fopen(filename, "rb")) == NULL)
987
#endif
Daniel Veillard's avatar
Daniel Veillard committed
988
    {
989 990 991 992 993 994 995 996 997 998
        return (NULL);
    }
#ifdef HAVE_STAT
    size = info.st_size;
#else
    if (fseek(fd, 0, SEEK_END) || (size = ftell(fd)) == EOF || fseek(fd, 0, SEEK_SET)) {        /* File operations denied? ok, just close and return failure */
        fclose(fd);
        return (NULL);
    }
#endif
Denis Pauk's avatar
Denis Pauk committed
999
    content = (xmlChar*)xmlMallocAtomic(size + 10);
1000
    if (content == NULL) {
1001
        xmlCatalogErrMemory("allocating catalog data");
1002 1003 1004 1005 1006
#ifdef HAVE_STAT
	close(fd);
#else
	fclose(fd);
#endif
1007 1008 1009 1010
        return (NULL);
    }
#ifdef HAVE_STAT
    len = read(fd, content, size);
1011
    close(fd);
1012 1013
#else
    len = fread(content, 1, size, fd);
1014
    fclose(fd);
1015 1016 1017 1018 1019 1020 1021 1022 1023 1024
#endif
    if (len < 0) {
        xmlFree(content);
        return (NULL);
    }
    content[len] = 0;

    return(content);
}

1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079
/**
 * xmlCatalogNormalizePublic:
 * @pubID:  the public ID string
 *
 *  Normalizes the Public Identifier
 *
 * Implements 6.2. Public Identifier Normalization
 * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
 *
 * Returns the new string or NULL, the string must be deallocated
 *         by the caller.
 */
static xmlChar *
xmlCatalogNormalizePublic(const xmlChar *pubID)
{
    int ok = 1;
    int white;
    const xmlChar *p;
    xmlChar *ret;
    xmlChar *q;

    if (pubID == NULL)
        return(NULL);

    white = 1;
    for (p = pubID;*p != 0 && ok;p++) {
        if (!xmlIsBlank_ch(*p))
            white = 0;
        else if (*p == 0x20 && !white)
            white = 1;
        else
            ok = 0;
    }
    if (ok && !white)	/* is normalized */
        return(NULL);

    ret = xmlStrdup(pubID);
    q = ret;
    white = 0;
    for (p = pubID;*p != 0;p++) {
        if (xmlIsBlank_ch(*p)) {
            if (q != ret)
                white = 1;
        } else {
            if (white) {
                *(q++) = 0x20;
                white = 0;
            }
            *(q++) = *p;
        }
    }
    *q = 0;
    return(ret);
}

1080 1081 1082 1083 1084 1085 1086 1087 1088 1089
/************************************************************************
 *									*
 *			The XML Catalog parser				*
 *									*
 ************************************************************************/

static xmlCatalogEntryPtr
xmlParseXMLCatalogFile(xmlCatalogPrefer prefer, const xmlChar *filename);
static void
xmlParseXMLCatalogNodeList(xmlNodePtr cur, xmlCatalogPrefer prefer,
1090
	                   xmlCatalogEntryPtr parent, xmlCatalogEntryPtr cgroup);
1091 1092 1093 1094 1095
static xmlChar *
xmlCatalogListXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,
	              const xmlChar *sysID);
static xmlChar *
xmlCatalogListXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI);
1096

1097 1098 1099 1100 1101 1102 1103

/**
 * xmlGetXMLCatalogEntryType:
 * @name:  the name
 *
 * lookup the internal type associated to an XML catalog entry name
 *
1104
 * Returns the type associated with that name
1105
 */
1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131
static xmlCatalogEntryType
xmlGetXMLCatalogEntryType(const xmlChar *name) {
    xmlCatalogEntryType type = XML_CATA_NONE;
    if (xmlStrEqual(name, (const xmlChar *) "system"))
	type = XML_CATA_SYSTEM;
    else if (xmlStrEqual(name, (const xmlChar *) "public"))
	type = XML_CATA_PUBLIC;
    else if (xmlStrEqual(name, (const xmlChar *) "rewriteSystem"))
	type = XML_CATA_REWRITE_SYSTEM;
    else if (xmlStrEqual(name, (const xmlChar *) "delegatePublic"))
	type = XML_CATA_DELEGATE_PUBLIC;
    else if (xmlStrEqual(name, (const xmlChar *) "delegateSystem"))
	type = XML_CATA_DELEGATE_SYSTEM;
    else if (xmlStrEqual(name, (const xmlChar *) "uri"))
	type = XML_CATA_URI;
    else if (xmlStrEqual(name, (const xmlChar *) "rewriteURI"))
	type = XML_CATA_REWRITE_URI;
    else if (xmlStrEqual(name, (const xmlChar *) "delegateURI"))
	type = XML_CATA_DELEGATE_URI;
    else if (xmlStrEqual(name, (const xmlChar *) "nextCatalog"))
	type = XML_CATA_NEXT_CATALOG;
    else if (xmlStrEqual(name, (const xmlChar *) "catalog"))
	type = XML_CATA_CATALOG;
    return(type);
}

1132 1133 1134 1135 1136 1137 1138 1139
/**
 * xmlParseXMLCatalogOneNode:
 * @cur:  the XML node
 * @type:  the type of Catalog entry
 * @name:  the name of the node
 * @attrName:  the attribute holding the value
 * @uriAttrName:  the attribute holding the URI-Reference
 * @prefer:  the PUBLIC vs. SYSTEM current preference value
1140
 * @cgroup:  the group which includes this node
1141 1142 1143 1144 1145 1146
 *
 * Finishes the examination of an XML tree node of a catalog and build
 * a Catalog entry from it.
 *
 * Returns the new Catalog entry node or NULL in case of error.
 */
1147 1148 1149
static xmlCatalogEntryPtr
xmlParseXMLCatalogOneNode(xmlNodePtr cur, xmlCatalogEntryType type,
			  const xmlChar *name, const xmlChar *attrName,
1150 1151
			  const xmlChar *uriAttrName, xmlCatalogPrefer prefer,
			  xmlCatalogEntryPtr cgroup) {
1152 1153 1154 1155 1156 1157 1158 1159 1160 1161
    int ok = 1;
    xmlChar *uriValue;
    xmlChar *nameValue = NULL;
    xmlChar *base = NULL;
    xmlChar *URL = NULL;
    xmlCatalogEntryPtr ret = NULL;

    if (attrName != NULL) {
	nameValue = xmlGetProp(cur, attrName);
	if (nameValue == NULL) {
1162 1163
	    xmlCatalogErr(ret, cur, XML_CATALOG_MISSING_ATTR,
			  "%s entry lacks '%s'\n", name, attrName, NULL);
1164