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