blob: 2ea69db87aa9a276d2c55dddf3e4e322272fe27a [file] [log] [blame]
Daniel Veillarda7374592001-05-10 14:17:55 +00001/**
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 *
Daniel Veillard344cee72001-08-20 00:08:40 +00007 * XML Catalogs Working Draft 06 August 2001
8 * http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
9 *
Daniel Veillarda7374592001-05-10 14:17:55 +000010 * See Copyright for the status of this software.
11 *
12 * Daniel.Veillard@imag.fr
13 */
14
15#include "libxml.h"
16
17#ifdef LIBXML_CATALOG_ENABLED
18#ifdef HAVE_SYS_TYPES_H
19#include <sys/types.h>
20#endif
21#ifdef HAVE_SYS_STAT_H
22#include <sys/stat.h>
23#endif
24#ifdef HAVE_UNISTD_H
25#include <unistd.h>
26#endif
27#ifdef HAVE_FCNTL_H
28#include <fcntl.h>
29#endif
30#include <string.h>
31#include <libxml/xmlmemory.h>
32#include <libxml/hash.h>
33#include <libxml/uri.h>
34#include <libxml/parserInternals.h>
35#include <libxml/catalog.h>
36#include <libxml/xmlerror.h>
37
Daniel Veillard344cee72001-08-20 00:08:40 +000038/**
39 * TODO:
40 *
41 * macro to flag unimplemented blocks
42 */
43#define TODO \
44 xmlGenericError(xmlGenericErrorContext, \
45 "Unimplemented block at %s:%d\n", \
46 __FILE__, __LINE__);
47
Daniel Veillardcda96922001-08-21 10:56:31 +000048#define XML_URN_PUBID "urn:publicid:"
Daniel Veillarde2940dd2001-08-22 00:06:49 +000049#define XML_CATAL_BREAK ((xmlChar *) -1)
50#define XML_DEFAULT_CATALOG "/etc/xml/catalog"
Daniel Veillard344cee72001-08-20 00:08:40 +000051
Daniel Veillarda7374592001-05-10 14:17:55 +000052/************************************************************************
53 * *
54 * Types, all private *
55 * *
56 ************************************************************************/
57
58typedef enum {
59 XML_CATA_NONE = 0,
Daniel Veillarda7374592001-05-10 14:17:55 +000060 XML_CATA_CATALOG,
Daniel Veillard344cee72001-08-20 00:08:40 +000061 XML_CATA_NEXT_CATALOG,
62 XML_CATA_PUBLIC,
63 XML_CATA_SYSTEM,
64 XML_CATA_REWRITE_SYSTEM,
65 XML_CATA_DELEGATE_PUBLIC,
66 XML_CATA_DELEGATE_SYSTEM,
67 XML_CATA_URI,
68 XML_CATA_REWRITE_URI,
69 XML_CATA_DELEGATE_URI,
70 SGML_CATA_SYSTEM,
71 SGML_CATA_PUBLIC,
72 SGML_CATA_ENTITY,
73 SGML_CATA_PENTITY,
74 SGML_CATA_DOCTYPE,
75 SGML_CATA_LINKTYPE,
76 SGML_CATA_NOTATION,
77 SGML_CATA_DELEGATE,
78 SGML_CATA_BASE,
79 SGML_CATA_CATALOG,
80 SGML_CATA_DOCUMENT,
81 SGML_CATA_SGMLDECL
Daniel Veillarda7374592001-05-10 14:17:55 +000082} xmlCatalogEntryType;
83
84typedef struct _xmlCatalogEntry xmlCatalogEntry;
85typedef xmlCatalogEntry *xmlCatalogEntryPtr;
86struct _xmlCatalogEntry {
Daniel Veillard344cee72001-08-20 00:08:40 +000087 struct _xmlCatalogEntry *next;
88 struct _xmlCatalogEntry *parent;
89 struct _xmlCatalogEntry *children;
Daniel Veillarda7374592001-05-10 14:17:55 +000090 xmlCatalogEntryType type;
91 xmlChar *name;
92 xmlChar *value;
Daniel Veillarde2940dd2001-08-22 00:06:49 +000093 xmlCatalogPrefer prefer;
Daniel Veillarda7374592001-05-10 14:17:55 +000094};
95
96static xmlHashTablePtr xmlDefaultCatalog;
Daniel Veillard344cee72001-08-20 00:08:40 +000097static xmlCatalogEntryPtr xmlDefaultXMLCatalogList = NULL;
Daniel Veillarde2940dd2001-08-22 00:06:49 +000098static int xmlCatalogInitialized = 0;
99static xmlCatalogPrefer xmlCatalogDefaultPrefer = XML_CATA_PREFER_SYSTEM;
100
Daniel Veillarda7374592001-05-10 14:17:55 +0000101
Daniel Veillardaf86c7f2001-05-21 14:11:26 +0000102/* Catalog stack */
Daniel Veillard81418e32001-05-22 15:08:55 +0000103static const char * catalTab[10]; /* stack of catals */
104static int catalNr = 0; /* Number of current catal streams */
105static int catalMax = 10; /* Max number of catal streams */
Daniel Veillardaf86c7f2001-05-21 14:11:26 +0000106
Daniel Veillard344cee72001-08-20 00:08:40 +0000107static int xmlDebugCatalogs = 0; /* used for debugging */
108
Daniel Veillarda7374592001-05-10 14:17:55 +0000109/************************************************************************
110 * *
111 * alloc or dealloc *
112 * *
113 ************************************************************************/
114
115static xmlCatalogEntryPtr
Daniel Veillard344cee72001-08-20 00:08:40 +0000116xmlNewCatalogEntry(xmlCatalogEntryType type, const xmlChar *name,
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000117 const xmlChar *value, xmlCatalogPrefer prefer) {
Daniel Veillarda7374592001-05-10 14:17:55 +0000118 xmlCatalogEntryPtr ret;
119
120 ret = (xmlCatalogEntryPtr) xmlMalloc(sizeof(xmlCatalogEntry));
121 if (ret == NULL) {
122 xmlGenericError(xmlGenericErrorContext,
123 "malloc of %d byte failed\n", sizeof(xmlCatalogEntry));
124 return(NULL);
125 }
Daniel Veillard344cee72001-08-20 00:08:40 +0000126 ret->next = NULL;
127 ret->parent = NULL;
128 ret->children = NULL;
Daniel Veillarda7374592001-05-10 14:17:55 +0000129 ret->type = type;
Daniel Veillard344cee72001-08-20 00:08:40 +0000130 if (name != NULL)
131 ret->name = xmlStrdup(name);
132 else
133 ret->name = NULL;
134 if (value != NULL)
135 ret->value = xmlStrdup(value);
136 else
137 ret->value = NULL;
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000138 ret->prefer = prefer;
Daniel Veillarda7374592001-05-10 14:17:55 +0000139 return(ret);
140}
141
142static void
Daniel Veillard344cee72001-08-20 00:08:40 +0000143xmlFreeCatalogEntryList(xmlCatalogEntryPtr ret);
144
145static void
Daniel Veillarda7374592001-05-10 14:17:55 +0000146xmlFreeCatalogEntry(xmlCatalogEntryPtr ret) {
147 if (ret == NULL)
148 return;
Daniel Veillard344cee72001-08-20 00:08:40 +0000149 if (ret->children != NULL)
150 xmlFreeCatalogEntryList(ret->children);
Daniel Veillarda7374592001-05-10 14:17:55 +0000151 if (ret->name != NULL)
152 xmlFree(ret->name);
153 if (ret->value != NULL)
154 xmlFree(ret->value);
155 xmlFree(ret);
156}
157
Daniel Veillard344cee72001-08-20 00:08:40 +0000158static void
159xmlFreeCatalogEntryList(xmlCatalogEntryPtr ret) {
160 xmlCatalogEntryPtr next;
161
162 while (ret != NULL) {
163 next = ret->next;
164 xmlFreeCatalogEntry(ret);
165 ret = next;
166 }
167}
168
Daniel Veillard7d6fd212001-05-10 15:34:11 +0000169/**
170 * xmlCatalogDumpEntry:
171 * @entry: the
172 * @out: the file.
173 *
174 * Free up all the memory associated with catalogs
175 */
176static void
177xmlCatalogDumpEntry(xmlCatalogEntryPtr entry, FILE *out) {
178 if ((entry == NULL) || (out == NULL))
179 return;
180 switch (entry->type) {
Daniel Veillard344cee72001-08-20 00:08:40 +0000181 case SGML_CATA_ENTITY:
Daniel Veillard7d6fd212001-05-10 15:34:11 +0000182 fprintf(out, "ENTITY "); break;
Daniel Veillard344cee72001-08-20 00:08:40 +0000183 case SGML_CATA_PENTITY:
Daniel Veillard7d6fd212001-05-10 15:34:11 +0000184 fprintf(out, "ENTITY %%"); break;
Daniel Veillard344cee72001-08-20 00:08:40 +0000185 case SGML_CATA_DOCTYPE:
Daniel Veillard7d6fd212001-05-10 15:34:11 +0000186 fprintf(out, "DOCTYPE "); break;
Daniel Veillard344cee72001-08-20 00:08:40 +0000187 case SGML_CATA_LINKTYPE:
Daniel Veillard7d6fd212001-05-10 15:34:11 +0000188 fprintf(out, "LINKTYPE "); break;
Daniel Veillard344cee72001-08-20 00:08:40 +0000189 case SGML_CATA_NOTATION:
Daniel Veillard7d6fd212001-05-10 15:34:11 +0000190 fprintf(out, "NOTATION "); break;
Daniel Veillard344cee72001-08-20 00:08:40 +0000191 case SGML_CATA_PUBLIC:
Daniel Veillard7d6fd212001-05-10 15:34:11 +0000192 fprintf(out, "PUBLIC "); break;
Daniel Veillard344cee72001-08-20 00:08:40 +0000193 case SGML_CATA_SYSTEM:
Daniel Veillard7d6fd212001-05-10 15:34:11 +0000194 fprintf(out, "SYSTEM "); break;
Daniel Veillard344cee72001-08-20 00:08:40 +0000195 case SGML_CATA_DELEGATE:
Daniel Veillard7d6fd212001-05-10 15:34:11 +0000196 fprintf(out, "DELEGATE "); break;
Daniel Veillard344cee72001-08-20 00:08:40 +0000197 case SGML_CATA_BASE:
Daniel Veillard7d6fd212001-05-10 15:34:11 +0000198 fprintf(out, "BASE "); break;
Daniel Veillard344cee72001-08-20 00:08:40 +0000199 case SGML_CATA_CATALOG:
Daniel Veillard7d6fd212001-05-10 15:34:11 +0000200 fprintf(out, "CATALOG "); break;
Daniel Veillard344cee72001-08-20 00:08:40 +0000201 case SGML_CATA_DOCUMENT:
Daniel Veillard7d6fd212001-05-10 15:34:11 +0000202 fprintf(out, "DOCUMENT "); break;
Daniel Veillard344cee72001-08-20 00:08:40 +0000203 case SGML_CATA_SGMLDECL:
Daniel Veillard7d6fd212001-05-10 15:34:11 +0000204 fprintf(out, "SGMLDECL "); break;
205 default:
206 return;
207 }
208 switch (entry->type) {
Daniel Veillard344cee72001-08-20 00:08:40 +0000209 case SGML_CATA_ENTITY:
210 case SGML_CATA_PENTITY:
211 case SGML_CATA_DOCTYPE:
212 case SGML_CATA_LINKTYPE:
213 case SGML_CATA_NOTATION:
Daniel Veillard7d6fd212001-05-10 15:34:11 +0000214 fprintf(out, "%s", entry->name); break;
Daniel Veillard344cee72001-08-20 00:08:40 +0000215 case SGML_CATA_PUBLIC:
216 case SGML_CATA_SYSTEM:
217 case SGML_CATA_SGMLDECL:
218 case SGML_CATA_DOCUMENT:
219 case SGML_CATA_CATALOG:
220 case SGML_CATA_BASE:
221 case SGML_CATA_DELEGATE:
Daniel Veillard7d6fd212001-05-10 15:34:11 +0000222 fprintf(out, "\"%s\"", entry->name); break;
223 default:
224 break;
225 }
226 switch (entry->type) {
Daniel Veillard344cee72001-08-20 00:08:40 +0000227 case SGML_CATA_ENTITY:
228 case SGML_CATA_PENTITY:
229 case SGML_CATA_DOCTYPE:
230 case SGML_CATA_LINKTYPE:
231 case SGML_CATA_NOTATION:
232 case SGML_CATA_PUBLIC:
233 case SGML_CATA_SYSTEM:
234 case SGML_CATA_DELEGATE:
Daniel Veillard7d6fd212001-05-10 15:34:11 +0000235 fprintf(out, " \"%s\"", entry->value); break;
236 default:
237 break;
238 }
239 fprintf(out, "\n");
240}
241
Daniel Veillarda7374592001-05-10 14:17:55 +0000242/************************************************************************
243 * *
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000244 * Helper function *
245 * *
246 ************************************************************************/
247
248/**
249 * xmlCatalogUnWrapURN:
250 * @urn: an "urn:publicid:" to unwrapp
251 *
252 * Expand the URN into the equivalent Public Identifier
253 *
254 * Returns the new identifier or NULL, the string must be deallocated
255 * by the caller.
256 */
257static xmlChar *
258xmlCatalogUnWrapURN(const xmlChar *urn) {
259 xmlChar result[2000];
260 unsigned int i = 0;
261
262 if (xmlStrncmp(urn, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1))
263 return(NULL);
264 urn += sizeof(XML_URN_PUBID) - 1;
265
266 while (*urn != 0) {
267 if (i > sizeof(result) - 3)
268 break;
269 if (*urn == '+') {
270 result[i++] = ' ';
271 urn++;
272 } else if (*urn == ':') {
273 result[i++] = '/';
274 result[i++] = '/';
275 urn++;
276 } else if (*urn == ';') {
277 result[i++] = ':';
278 result[i++] = ':';
279 urn++;
280 } else if (*urn == '%') {
281 if ((urn[1] == '2') && (urn[1] == 'B'))
282 result[i++] = '+';
283 else if ((urn[1] == '3') && (urn[1] == 'A'))
284 result[i++] = ':';
285 else if ((urn[1] == '2') && (urn[1] == 'F'))
286 result[i++] = '/';
287 else if ((urn[1] == '3') && (urn[1] == 'B'))
288 result[i++] = ';';
289 else if ((urn[1] == '2') && (urn[1] == '7'))
290 result[i++] = '\'';
291 else if ((urn[1] == '3') && (urn[1] == 'F'))
292 result[i++] = '?';
293 else if ((urn[1] == '2') && (urn[1] == '3'))
294 result[i++] = '#';
295 else if ((urn[1] == '2') && (urn[1] == '5'))
296 result[i++] = '%';
297 else {
298 result[i++] = *urn;
299 urn++;
300 continue;
301 }
302 urn += 3;
303 } else {
304 result[i++] = *urn;
305 urn++;
306 }
307 }
308 result[i] = 0;
309
310 return(xmlStrdup(result));
311}
312
313/************************************************************************
314 * *
Daniel Veillard344cee72001-08-20 00:08:40 +0000315 * The XML Catalog parser *
316 * *
317 ************************************************************************/
318
319static xmlCatalogEntryPtr
320xmlParseXMLCatalogFile(xmlCatalogPrefer prefer, const xmlChar *filename);
321
322static xmlCatalogEntryPtr
323xmlParseXMLCatalog(const xmlChar *value, xmlCatalogPrefer prefer,
324 const char *file);
325static void
326xmlParseXMLCatalogNodeList(xmlNodePtr cur, xmlCatalogPrefer prefer,
327 xmlCatalogEntryPtr parent);
Daniel Veillardcda96922001-08-21 10:56:31 +0000328static xmlChar *
329xmlCatalogListXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,
330 const xmlChar *sysID);
Daniel Veillard344cee72001-08-20 00:08:40 +0000331
332static xmlCatalogEntryType
333xmlGetXMLCatalogEntryType(const xmlChar *name) {
334 xmlCatalogEntryType type = XML_CATA_NONE;
335 if (xmlStrEqual(name, (const xmlChar *) "system"))
336 type = XML_CATA_SYSTEM;
337 else if (xmlStrEqual(name, (const xmlChar *) "public"))
338 type = XML_CATA_PUBLIC;
339 else if (xmlStrEqual(name, (const xmlChar *) "rewriteSystem"))
340 type = XML_CATA_REWRITE_SYSTEM;
341 else if (xmlStrEqual(name, (const xmlChar *) "delegatePublic"))
342 type = XML_CATA_DELEGATE_PUBLIC;
343 else if (xmlStrEqual(name, (const xmlChar *) "delegateSystem"))
344 type = XML_CATA_DELEGATE_SYSTEM;
345 else if (xmlStrEqual(name, (const xmlChar *) "uri"))
346 type = XML_CATA_URI;
347 else if (xmlStrEqual(name, (const xmlChar *) "rewriteURI"))
348 type = XML_CATA_REWRITE_URI;
349 else if (xmlStrEqual(name, (const xmlChar *) "delegateURI"))
350 type = XML_CATA_DELEGATE_URI;
351 else if (xmlStrEqual(name, (const xmlChar *) "nextCatalog"))
352 type = XML_CATA_NEXT_CATALOG;
353 else if (xmlStrEqual(name, (const xmlChar *) "catalog"))
354 type = XML_CATA_CATALOG;
355 return(type);
356}
357
358static xmlCatalogEntryPtr
359xmlParseXMLCatalogOneNode(xmlNodePtr cur, xmlCatalogEntryType type,
360 const xmlChar *name, const xmlChar *attrName,
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000361 const xmlChar *uriAttrName, xmlCatalogPrefer prefer) {
Daniel Veillard344cee72001-08-20 00:08:40 +0000362 int ok = 1;
363 xmlChar *uriValue;
364 xmlChar *nameValue = NULL;
365 xmlChar *base = NULL;
366 xmlChar *URL = NULL;
367 xmlCatalogEntryPtr ret = NULL;
368
369 if (attrName != NULL) {
370 nameValue = xmlGetProp(cur, attrName);
371 if (nameValue == NULL) {
372 xmlGenericError(xmlGenericErrorContext,
373 "%s entry lacks '%s'\n", name, attrName);
374 ok = 0;
375 }
376 }
377 uriValue = xmlGetProp(cur, uriAttrName);
378 if (uriValue == NULL) {
379 xmlGenericError(xmlGenericErrorContext,
380 "%s entry lacks '%s'\n", name, uriAttrName);
381 ok = 0;
382 }
383 if (!ok) {
384 if (nameValue != NULL)
385 xmlFree(nameValue);
386 if (uriValue != NULL)
387 xmlFree(uriValue);
388 return(NULL);
389 }
390
391 base = xmlNodeGetBase(cur->doc, cur);
392 URL = xmlBuildURI(uriValue, base);
393 if (URL != NULL) {
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000394 if (xmlDebugCatalogs > 1) {
Daniel Veillard344cee72001-08-20 00:08:40 +0000395 if (nameValue != NULL)
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000396 xmlGenericError(xmlGenericErrorContext,
397 "Found %s: '%s' '%s'\n", name, nameValue, URL);
Daniel Veillard344cee72001-08-20 00:08:40 +0000398 else
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000399 xmlGenericError(xmlGenericErrorContext,
400 "Found %s: '%s'\n", name, URL);
Daniel Veillard344cee72001-08-20 00:08:40 +0000401 }
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000402 ret = xmlNewCatalogEntry(type, nameValue, URL, prefer);
Daniel Veillard344cee72001-08-20 00:08:40 +0000403 } else {
404 xmlGenericError(xmlGenericErrorContext,
405 "%s entry '%s' broken ?: %s\n", name, uriAttrName, uriValue);
406 }
407 if (nameValue != NULL)
408 xmlFree(nameValue);
409 if (uriValue != NULL)
410 xmlFree(uriValue);
411 if (base != NULL)
412 xmlFree(base);
413 if (URL != NULL)
414 xmlFree(URL);
415 return(ret);
416}
417
418static void
419xmlParseXMLCatalogNode(xmlNodePtr cur, xmlCatalogPrefer prefer,
420 xmlCatalogEntryPtr parent)
421{
422 xmlChar *uri = NULL;
423 xmlChar *URL = NULL;
424 xmlChar *base = NULL;
425 xmlCatalogEntryPtr entry = NULL;
426
427 if (cur == NULL)
428 return;
429 if (xmlStrEqual(cur->name, BAD_CAST "group")) {
430 xmlChar *prop;
431
432 prop = xmlGetProp(cur, BAD_CAST "prefer");
433 if (prop != NULL) {
434 if (xmlStrEqual(prop, BAD_CAST "system")) {
435 prefer = XML_CATA_PREFER_SYSTEM;
436 } else if (xmlStrEqual(prop, BAD_CAST "public")) {
437 prefer = XML_CATA_PREFER_PUBLIC;
438 } else {
439 xmlGenericError(xmlGenericErrorContext,
440 "Invalid value for prefer: '%s'\n", prop);
441 }
442 xmlFree(prop);
443 }
444 /*
445 * Recurse to propagate prefer to the subtree
446 * (xml:base handling is automated)
447 */
448 xmlParseXMLCatalogNodeList(cur->children, prefer, parent);
449 } else if (xmlStrEqual(cur->name, BAD_CAST "public")) {
450 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_PUBLIC,
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000451 BAD_CAST "public", BAD_CAST "publicId", BAD_CAST "uri", prefer);
Daniel Veillard344cee72001-08-20 00:08:40 +0000452 } else if (xmlStrEqual(cur->name, BAD_CAST "system")) {
453 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_SYSTEM,
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000454 BAD_CAST "system", BAD_CAST "systemId", BAD_CAST "uri", prefer);
Daniel Veillard344cee72001-08-20 00:08:40 +0000455 } else if (xmlStrEqual(cur->name, BAD_CAST "rewriteSystem")) {
456 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_REWRITE_SYSTEM,
457 BAD_CAST "rewriteSystem", BAD_CAST "systemIdStartString",
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000458 BAD_CAST "rewritePrefix", prefer);
Daniel Veillard344cee72001-08-20 00:08:40 +0000459 } else if (xmlStrEqual(cur->name, BAD_CAST "delegatePublic")) {
460 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_PUBLIC,
461 BAD_CAST "delegatePublic", BAD_CAST "publicIdStartString",
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000462 BAD_CAST "catalog", prefer);
Daniel Veillard344cee72001-08-20 00:08:40 +0000463 } else if (xmlStrEqual(cur->name, BAD_CAST "delegateSystem")) {
464 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_SYSTEM,
465 BAD_CAST "delegateSystem", BAD_CAST "systemIdStartString",
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000466 BAD_CAST "catalog", prefer);
Daniel Veillard344cee72001-08-20 00:08:40 +0000467 } else if (xmlStrEqual(cur->name, BAD_CAST "uri")) {
468 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_URI,
469 BAD_CAST "uri", BAD_CAST "name",
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000470 BAD_CAST "uri", prefer);
Daniel Veillard344cee72001-08-20 00:08:40 +0000471 } else if (xmlStrEqual(cur->name, BAD_CAST "rewriteURI")) {
472 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_REWRITE_URI,
473 BAD_CAST "rewriteURI", BAD_CAST "uriStartString",
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000474 BAD_CAST "rewritePrefix", prefer);
Daniel Veillard344cee72001-08-20 00:08:40 +0000475 } else if (xmlStrEqual(cur->name, BAD_CAST "delegateURI")) {
476 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_URI,
477 BAD_CAST "delegateURI", BAD_CAST "uriStartString",
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000478 BAD_CAST "catalog", prefer);
Daniel Veillard344cee72001-08-20 00:08:40 +0000479 } else if (xmlStrEqual(cur->name, BAD_CAST "nextCatalog")) {
480 entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_NEXT_CATALOG,
481 BAD_CAST "nextCatalog", NULL,
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000482 BAD_CAST "catalog", prefer);
Daniel Veillard344cee72001-08-20 00:08:40 +0000483 }
484 if ((entry != NULL) && (parent != NULL)) {
485 entry->parent = parent;
486 if (parent->children == NULL)
487 parent->children = entry;
488 else {
489 xmlCatalogEntryPtr prev;
490
491 prev = parent->children;
492 while (prev->next != NULL)
493 prev = prev->next;
494 prev->next = entry;
495 }
496 }
497 if (base != NULL)
498 xmlFree(base);
499 if (uri != NULL)
500 xmlFree(uri);
501 if (URL != NULL)
502 xmlFree(URL);
503}
504
505static void
506xmlParseXMLCatalogNodeList(xmlNodePtr cur, xmlCatalogPrefer prefer,
507 xmlCatalogEntryPtr parent) {
508 while (cur != NULL) {
509 if ((cur->ns != NULL) && (cur->ns->href != NULL) &&
510 (xmlStrEqual(cur->ns->href, XML_CATALOGS_NAMESPACE))) {
511 xmlParseXMLCatalogNode(cur, prefer, parent);
512 }
513 cur = cur->next;
514 }
515 /* TODO: sort the list according to REWRITE lengths and prefer value */
516}
517
518static xmlCatalogEntryPtr
519xmlParseXMLCatalog(const xmlChar *value, xmlCatalogPrefer prefer,
520 const char *file) {
521 xmlDocPtr doc;
522 xmlNodePtr cur;
523 xmlChar *prop;
524 xmlCatalogEntryPtr parent = NULL;
525
526 if ((value == NULL) || (file == NULL))
527 return(NULL);
528
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000529 if (xmlDebugCatalogs)
530 xmlGenericError(xmlGenericErrorContext,
531 "Parsing catalog %s's content\n", file);
532
Daniel Veillard344cee72001-08-20 00:08:40 +0000533 doc = xmlParseDoc((xmlChar *) value);
534 if (doc == NULL)
535 return(NULL);
536 doc->URL = xmlStrdup((const xmlChar *) file);
537
538 cur = xmlDocGetRootElement(doc);
539 if ((cur != NULL) && (xmlStrEqual(cur->name, BAD_CAST "catalog")) &&
540 (cur->ns != NULL) && (cur->ns->href != NULL) &&
541 (xmlStrEqual(cur->ns->href, XML_CATALOGS_NAMESPACE))) {
542
Daniel Veillard344cee72001-08-20 00:08:40 +0000543 prop = xmlGetProp(cur, BAD_CAST "prefer");
544 if (prop != NULL) {
545 if (xmlStrEqual(prop, BAD_CAST "system")) {
546 prefer = XML_CATA_PREFER_SYSTEM;
547 } else if (xmlStrEqual(prop, BAD_CAST "public")) {
548 prefer = XML_CATA_PREFER_PUBLIC;
549 } else {
550 xmlGenericError(xmlGenericErrorContext,
551 "Invalid value for prefer: '%s'\n",
552 prop);
553 }
554 xmlFree(prop);
555 }
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000556 parent = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
557 (const xmlChar *)file, prefer);
558 if (parent == NULL) {
559 xmlFreeDoc(doc);
560 return(NULL);
561 }
562
Daniel Veillard344cee72001-08-20 00:08:40 +0000563 cur = cur->children;
564 xmlParseXMLCatalogNodeList(cur, prefer, parent);
565 } else {
566 xmlGenericError(xmlGenericErrorContext,
567 "File %s is not an XML Catalog\n", file);
568 xmlFreeDoc(doc);
569 return(NULL);
570 }
571 xmlFreeDoc(doc);
572 return(parent);
573}
574
575static xmlCatalogEntryPtr
576xmlParseXMLCatalogFile(xmlCatalogPrefer prefer, const xmlChar *filename) {
577 xmlDocPtr doc;
578 xmlNodePtr cur;
579 xmlChar *prop;
580 xmlCatalogEntryPtr parent = NULL;
581
582 if (filename == NULL)
583 return(NULL);
584
585 doc = xmlParseFile((const char *) filename);
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000586 if (doc == NULL) {
587 if (xmlDebugCatalogs)
588 xmlGenericError(xmlGenericErrorContext,
589 "Failed to parse catalog %s\n", filename);
Daniel Veillard344cee72001-08-20 00:08:40 +0000590 return(NULL);
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000591 }
592
593 if (xmlDebugCatalogs)
594 xmlGenericError(xmlGenericErrorContext,
595 "Parsing catalog %s\n", filename);
Daniel Veillard344cee72001-08-20 00:08:40 +0000596
597 cur = xmlDocGetRootElement(doc);
598 if ((cur != NULL) && (xmlStrEqual(cur->name, BAD_CAST "catalog")) &&
599 (cur->ns != NULL) && (cur->ns->href != NULL) &&
600 (xmlStrEqual(cur->ns->href, XML_CATALOGS_NAMESPACE))) {
601
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000602 parent = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
603 (const xmlChar *)filename, prefer);
Daniel Veillard344cee72001-08-20 00:08:40 +0000604 if (parent == NULL) {
605 xmlFreeDoc(doc);
606 return(NULL);
607 }
608
609 prop = xmlGetProp(cur, BAD_CAST "prefer");
610 if (prop != NULL) {
611 if (xmlStrEqual(prop, BAD_CAST "system")) {
612 prefer = XML_CATA_PREFER_SYSTEM;
613 } else if (xmlStrEqual(prop, BAD_CAST "public")) {
614 prefer = XML_CATA_PREFER_PUBLIC;
615 } else {
616 xmlGenericError(xmlGenericErrorContext,
617 "Invalid value for prefer: '%s'\n",
618 prop);
619 }
620 xmlFree(prop);
621 }
622 cur = cur->children;
623 xmlParseXMLCatalogNodeList(cur, prefer, parent);
624 } else {
625 xmlGenericError(xmlGenericErrorContext,
626 "File %s is not an XML Catalog\n", filename);
627 xmlFreeDoc(doc);
628 return(NULL);
629 }
630 xmlFreeDoc(doc);
631 return(parent);
632}
633
Daniel Veillardcda96922001-08-21 10:56:31 +0000634/**
635 * xmlFetchXMLCatalogFile:
636 * @catal: an existing but incomplete catalog entry
637 *
638 * Fetch and parse the subcatalog referenced by an entry
639 * It tries to be thread safe but by lack of an atomic test and
640 * set there is a risk of loosing memory.
641 *
642 * Returns 0 in case of success, -1 otherwise
643 */
644static int
645xmlFetchXMLCatalogFile(xmlCatalogEntryPtr catal) {
646 xmlCatalogEntryPtr children;
647
648 if (catal == NULL)
649 return(-1);
650 if (catal->value == NULL)
651 return(-1);
652 if (catal->children != NULL)
653 return(-1);
654
655 /*
656 * Fetch and parse
657 */
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000658 children = xmlParseXMLCatalogFile(catal->prefer, catal->value);
Daniel Veillardcda96922001-08-21 10:56:31 +0000659 if (children == NULL)
660 return(-1);
661
662 /*
663 * Where a real test and set would be needed !
664 */
665 if (catal->children == NULL) {
666 catal->children = children;
667 } else {
668 /*
669 * Another thread filled it before us
670 */
671 xmlFreeCatalogEntryList(children);
672 }
673 return(0);
674}
675
Daniel Veillard344cee72001-08-20 00:08:40 +0000676static int
677xmlDumpXMLCatalog(FILE *out, xmlCatalogEntryPtr catal) {
678 int ret;
679 xmlDocPtr doc;
680 xmlNsPtr ns;
681 xmlDtdPtr dtd;
682 xmlNodePtr node, catalog;
683 xmlOutputBufferPtr buf;
684 xmlCatalogEntryPtr cur;
685
686 /*
687 * Rebuild a catalog
688 */
689 doc = xmlNewDoc(NULL);
690 if (doc == NULL)
691 return(-1);
692 dtd = xmlNewDtd(doc, BAD_CAST "catalog",
693 BAD_CAST "-//OASIS//DTD Entity Resolution XML Catalog V1.0//EN",
694BAD_CAST "http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd");
695
696 xmlAddChild((xmlNodePtr) doc, (xmlNodePtr) dtd);
697
698 ns = xmlNewNs(NULL, XML_CATALOGS_NAMESPACE, NULL);
699 if (ns == NULL) {
700 xmlFreeDoc(doc);
701 return(-1);
702 }
703 catalog = xmlNewDocNode(doc, ns, BAD_CAST "catalog", NULL);
704 if (catalog == NULL) {
705 xmlFreeNs(ns);
706 xmlFreeDoc(doc);
707 return(-1);
708 }
709 catalog->nsDef = ns;
710 xmlAddChild((xmlNodePtr) doc, catalog);
711
712 /*
713 * add all the catalog entries
714 */
715 cur = catal;
716 while (cur != NULL) {
717 switch (cur->type) {
718 case XML_CATA_CATALOG:
719 if (cur == catal) {
720 cur = cur->children;
721 continue;
722 }
723 break;
724 case XML_CATA_NEXT_CATALOG:
725 node = xmlNewDocNode(doc, ns, BAD_CAST "nextCatalog", NULL);
726 xmlSetProp(node, BAD_CAST "catalog", cur->value);
727 xmlAddChild(catalog, node);
728 break;
729 case XML_CATA_NONE:
730 break;
731 case XML_CATA_PUBLIC:
732 node = xmlNewDocNode(doc, ns, BAD_CAST "public", NULL);
733 xmlSetProp(node, BAD_CAST "publicId", cur->name);
734 xmlSetProp(node, BAD_CAST "uri", cur->value);
735 xmlAddChild(catalog, node);
736 break;
737 case XML_CATA_SYSTEM:
738 node = xmlNewDocNode(doc, ns, BAD_CAST "system", NULL);
739 xmlSetProp(node, BAD_CAST "systemId", cur->name);
740 xmlSetProp(node, BAD_CAST "uri", cur->value);
741 xmlAddChild(catalog, node);
742 break;
743 case XML_CATA_REWRITE_SYSTEM:
744 node = xmlNewDocNode(doc, ns, BAD_CAST "rewriteSystem", NULL);
745 xmlSetProp(node, BAD_CAST "systemIdStartString", cur->name);
746 xmlSetProp(node, BAD_CAST "rewritePrefix", cur->value);
747 xmlAddChild(catalog, node);
748 break;
749 case XML_CATA_DELEGATE_PUBLIC:
750 node = xmlNewDocNode(doc, ns, BAD_CAST "delegatePublic", NULL);
751 xmlSetProp(node, BAD_CAST "publicIdStartString", cur->name);
752 xmlSetProp(node, BAD_CAST "catalog", cur->value);
753 xmlAddChild(catalog, node);
754 break;
755 case XML_CATA_DELEGATE_SYSTEM:
756 node = xmlNewDocNode(doc, ns, BAD_CAST "delegateSystem", NULL);
757 xmlSetProp(node, BAD_CAST "systemIdStartString", cur->name);
758 xmlSetProp(node, BAD_CAST "catalog", cur->value);
759 xmlAddChild(catalog, node);
760 break;
761 case XML_CATA_URI:
762 node = xmlNewDocNode(doc, ns, BAD_CAST "uri", NULL);
763 xmlSetProp(node, BAD_CAST "name", cur->name);
764 xmlSetProp(node, BAD_CAST "uri", cur->value);
765 xmlAddChild(catalog, node);
766 break;
767 case XML_CATA_REWRITE_URI:
768 node = xmlNewDocNode(doc, ns, BAD_CAST "rewriteURI", NULL);
769 xmlSetProp(node, BAD_CAST "uriStartString", cur->name);
770 xmlSetProp(node, BAD_CAST "rewritePrefix", cur->value);
771 xmlAddChild(catalog, node);
772 break;
773 case XML_CATA_DELEGATE_URI:
774 node = xmlNewDocNode(doc, ns, BAD_CAST "delegateURI", NULL);
775 xmlSetProp(node, BAD_CAST "uriStartString", cur->name);
776 xmlSetProp(node, BAD_CAST "catalog", cur->value);
777 xmlAddChild(catalog, node);
778 break;
779 case SGML_CATA_SYSTEM:
780 case SGML_CATA_PUBLIC:
781 case SGML_CATA_ENTITY:
782 case SGML_CATA_PENTITY:
783 case SGML_CATA_DOCTYPE:
784 case SGML_CATA_LINKTYPE:
785 case SGML_CATA_NOTATION:
786 case SGML_CATA_DELEGATE:
787 case SGML_CATA_BASE:
788 case SGML_CATA_CATALOG:
789 case SGML_CATA_DOCUMENT:
790 case SGML_CATA_SGMLDECL:
791 break;
792 }
793 cur = cur->next;
794 }
795
796 /*
797 * reserialize it
798 */
799 buf = xmlOutputBufferCreateFile(out, NULL);
800 if (buf == NULL) {
801 xmlFreeDoc(doc);
802 return(-1);
803 }
804 ret = xmlSaveFormatFileTo(buf, doc, NULL, 1);
805
806 /*
807 * Free it
808 */
809 xmlFreeDoc(doc);
810
811 return(ret);
812}
813
814/**
815 * xmlAddXMLCatalog:
816 * @catal: top of an XML catalog
817 * @type: the type of record to add to the catalog
Daniel Veillardcda96922001-08-21 10:56:31 +0000818 * @orig: the system, public or prefix to match (or NULL)
Daniel Veillard344cee72001-08-20 00:08:40 +0000819 * @replace: the replacement value for the match
820 *
821 * Add an entry in the XML catalog, it may overwrite existing but
822 * different entries.
823 *
824 * Returns 0 if successful, -1 otherwise
825 */
826static int
827xmlAddXMLCatalog(xmlCatalogEntryPtr catal, const xmlChar *type,
828 const xmlChar *orig, const xmlChar *replace) {
829 xmlCatalogEntryPtr cur;
830 xmlCatalogEntryType typ;
831
832 if ((catal == NULL) || (catal->type != XML_CATA_CATALOG))
833 return(-1);
834 typ = xmlGetXMLCatalogEntryType(type);
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000835 if (typ == XML_CATA_NONE) {
836 if (xmlDebugCatalogs)
837 xmlGenericError(xmlGenericErrorContext,
838 "Failed to add unknown element %s to catalog\n", type);
Daniel Veillard344cee72001-08-20 00:08:40 +0000839 return(-1);
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000840 }
Daniel Veillard344cee72001-08-20 00:08:40 +0000841
842 cur = catal->children;
843 /*
844 * Might be a simple "update in place"
845 */
846 if (cur != NULL) {
847 while (cur != NULL) {
Daniel Veillardcda96922001-08-21 10:56:31 +0000848 if ((orig != NULL) && (cur->type == typ) &&
849 (xmlStrEqual(orig, cur->name))) {
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000850 if (xmlDebugCatalogs)
851 xmlGenericError(xmlGenericErrorContext,
852 "Updating element %s to catalog\n", type);
Daniel Veillard344cee72001-08-20 00:08:40 +0000853 if (cur->value != NULL)
854 xmlFree(cur->value);
855 cur->value = xmlStrdup(replace);
Daniel Veillardcda96922001-08-21 10:56:31 +0000856 return(0);
Daniel Veillard344cee72001-08-20 00:08:40 +0000857 }
858 if (cur->next == NULL)
859 break;
860 cur = cur->next;
861 }
862 }
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000863 if (xmlDebugCatalogs)
864 xmlGenericError(xmlGenericErrorContext,
865 "Adding element %s to catalog\n", type);
Daniel Veillard344cee72001-08-20 00:08:40 +0000866 if (cur == NULL)
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000867 catal->children = xmlNewCatalogEntry(typ, orig, replace, catal->prefer);
Daniel Veillard344cee72001-08-20 00:08:40 +0000868 else
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000869 cur->next = xmlNewCatalogEntry(typ, orig, replace, catal->prefer);
Daniel Veillardcda96922001-08-21 10:56:31 +0000870 return(0);
871}
872
873/**
874 * xmlDelXMLCatalog:
875 * @catal: top of an XML catalog
876 * @value: the value to remove from teh catalog
877 *
878 * Remove entries in the XML catalog where the value or the URI
879 * is equal to @value
880 *
881 * Returns the number of entries removed if successful, -1 otherwise
882 */
883static int
884xmlDelXMLCatalog(xmlCatalogEntryPtr catal, const xmlChar *value) {
885 xmlCatalogEntryPtr cur, prev, tmp;
886 int ret = 0;
887
888 if ((catal == NULL) || (catal->type != XML_CATA_CATALOG))
889 return(-1);
890 if (value == NULL)
891 return(-1);
892
893 /*
894 * Scan the children
895 */
896 cur = catal->children;
897 prev = NULL;
898 while (cur != NULL) {
899 if (((cur->name != NULL) && (xmlStrEqual(value, cur->name))) ||
900 (xmlStrEqual(value, cur->value))) {
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000901 if (xmlDebugCatalogs) {
902 if (cur->name != NULL)
903 xmlGenericError(xmlGenericErrorContext,
904 "Removing element %s from catalog\n", cur->name);
905 else
906 xmlGenericError(xmlGenericErrorContext,
907 "Removing element %s from catalog\n", cur->value);
908 }
Daniel Veillardcda96922001-08-21 10:56:31 +0000909 ret++;
910 tmp = cur;
911 cur = tmp->next;
912 if (prev == NULL) {
913 catal->children = cur;
914 } else {
915 prev->next = cur;
916 }
917 xmlFreeCatalogEntry(tmp);
918 continue;
919 }
920 prev = cur;
921 cur = cur->next;
922 }
923 return(ret);
924}
925
926/**
Daniel Veillardcda96922001-08-21 10:56:31 +0000927 * xmlCatalogXMLResolve:
928 * @catal: a catalog list
929 * @pubId: the public ID string
930 * @sysId: the system ID string
931 *
932 * Do a complete resolution lookup of an External Identifier for a
933 * list of catalog entries.
934 *
935 * Implements (or tries to) 7.1. External Identifier Resolution
936 * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
937 *
938 * Returns the URI of the resource or NULL if not found
939 */
940static xmlChar *
941xmlCatalogXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,
942 const xmlChar *sysID) {
943 xmlChar *ret = NULL;
944 xmlCatalogEntryPtr cur;
945 int haveDelegate = 0;
946 int haveNext = 0;
947
948 /*
949 * First tries steps 2/ 3/ 4/ if a system ID is provided.
950 */
951 if (sysID != NULL) {
952 xmlCatalogEntryPtr rewrite = NULL;
953 int lenrewrite = 0, len;
954 cur = catal;
955 haveDelegate = 0;
956 while (cur != NULL) {
957 switch (cur->type) {
958 case XML_CATA_SYSTEM:
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000959 if (xmlStrEqual(sysID, cur->name)) {
960 if (xmlDebugCatalogs)
961 xmlGenericError(xmlGenericErrorContext,
962 "Found system match %s\n", cur->name);
Daniel Veillardcda96922001-08-21 10:56:31 +0000963 return(xmlStrdup(cur->value));
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000964 }
Daniel Veillardcda96922001-08-21 10:56:31 +0000965 break;
966 case XML_CATA_REWRITE_SYSTEM:
967 len = xmlStrlen(cur->name);
968 if ((len > lenrewrite) &&
969 (!xmlStrncmp(sysID, cur->name, len))) {
970 lenrewrite = len;
971 rewrite = cur;
972 }
973 break;
974 case XML_CATA_DELEGATE_SYSTEM:
975 if (!xmlStrncmp(sysID, cur->name, xmlStrlen(cur->name)))
976 haveDelegate++;
977 break;
978 case XML_CATA_NEXT_CATALOG:
979 haveNext++;
980 break;
981 default:
982 break;
983 }
984 cur = cur->next;
985 }
986 if (rewrite != NULL) {
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000987 if (xmlDebugCatalogs)
988 xmlGenericError(xmlGenericErrorContext,
989 "Using rewriting rule %s\n", rewrite->name);
Daniel Veillardcda96922001-08-21 10:56:31 +0000990 ret = xmlStrdup(rewrite->value);
991 if (ret != NULL)
992 ret = xmlStrcat(ret, &sysID[lenrewrite]);
993 return(ret);
994 }
995 if (haveDelegate) {
996 /*
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000997 * Assume the entries have been sorted by decreasing substring
Daniel Veillardcda96922001-08-21 10:56:31 +0000998 * matches when the list was produced.
999 */
1000 cur = catal;
1001 while (cur != NULL) {
1002 if ((cur->type == XML_CATA_DELEGATE_SYSTEM) &&
1003 (!xmlStrncmp(sysID, cur->name, xmlStrlen(cur->name)))) {
1004 if (cur->children == NULL) {
1005 xmlFetchXMLCatalogFile(cur);
1006 }
1007 if (cur->children != NULL) {
Daniel Veillarde2940dd2001-08-22 00:06:49 +00001008 if (xmlDebugCatalogs)
1009 xmlGenericError(xmlGenericErrorContext,
1010 "Trying system delegate %s\n", cur->value);
1011 ret = xmlCatalogListXMLResolve(cur->children, NULL,
1012 sysID);
1013 if (ret != NULL)
1014 return(ret);
Daniel Veillardcda96922001-08-21 10:56:31 +00001015 }
1016 }
1017 cur = cur->next;
1018 }
Daniel Veillarde2940dd2001-08-22 00:06:49 +00001019 /*
1020 * Apply the cut algorithm explained in 4/
1021 */
1022 return(XML_CATAL_BREAK);
Daniel Veillardcda96922001-08-21 10:56:31 +00001023 }
1024 }
1025 /*
1026 * Then tries 5/ 6/ if a public ID is provided
1027 */
1028 if (pubID != NULL) {
1029 cur = catal;
1030 haveDelegate = 0;
1031 while (cur != NULL) {
1032 switch (cur->type) {
1033 case XML_CATA_PUBLIC:
Daniel Veillarde2940dd2001-08-22 00:06:49 +00001034 if (xmlStrEqual(pubID, cur->name)) {
1035 if (xmlDebugCatalogs)
1036 xmlGenericError(xmlGenericErrorContext,
1037 "Found public match %s\n", cur->name);
Daniel Veillardcda96922001-08-21 10:56:31 +00001038 return(xmlStrdup(cur->value));
Daniel Veillarde2940dd2001-08-22 00:06:49 +00001039 }
Daniel Veillardcda96922001-08-21 10:56:31 +00001040 break;
1041 case XML_CATA_DELEGATE_PUBLIC:
Daniel Veillarde2940dd2001-08-22 00:06:49 +00001042 if (!xmlStrncmp(pubID, cur->name, xmlStrlen(cur->name)) &&
1043 (cur->prefer == XML_CATA_PREFER_PUBLIC))
Daniel Veillardcda96922001-08-21 10:56:31 +00001044 haveDelegate++;
1045 break;
1046 case XML_CATA_NEXT_CATALOG:
1047 if (sysID == NULL)
1048 haveNext++;
1049 break;
1050 default:
1051 break;
1052 }
1053 cur = cur->next;
1054 }
1055 if (haveDelegate) {
1056 /*
Daniel Veillarde2940dd2001-08-22 00:06:49 +00001057 * Assume the entries have been sorted by decreasing substring
Daniel Veillardcda96922001-08-21 10:56:31 +00001058 * matches when the list was produced.
1059 */
1060 cur = catal;
1061 while (cur != NULL) {
1062 if ((cur->type == XML_CATA_DELEGATE_PUBLIC) &&
Daniel Veillarde2940dd2001-08-22 00:06:49 +00001063 (cur->prefer == XML_CATA_PREFER_PUBLIC) &&
1064 (!xmlStrncmp(pubID, cur->name, xmlStrlen(cur->name)))) {
Daniel Veillardcda96922001-08-21 10:56:31 +00001065 if (cur->children == NULL) {
1066 xmlFetchXMLCatalogFile(cur);
1067 }
1068 if (cur->children != NULL) {
Daniel Veillarde2940dd2001-08-22 00:06:49 +00001069 if (xmlDebugCatalogs)
1070 xmlGenericError(xmlGenericErrorContext,
1071 "Trying public delegate %s\n", cur->value);
1072 ret = xmlCatalogListXMLResolve(cur->children, pubID,
1073 NULL);
1074 if (ret != NULL)
1075 return(ret);
Daniel Veillardcda96922001-08-21 10:56:31 +00001076 }
1077 }
1078 cur = cur->next;
1079 }
Daniel Veillarde2940dd2001-08-22 00:06:49 +00001080 /*
1081 * Apply the cut algorithm explained in 4/
1082 */
1083 return(XML_CATAL_BREAK);
Daniel Veillardcda96922001-08-21 10:56:31 +00001084 }
1085 }
1086 if (haveNext) {
1087 cur = catal;
1088 while (cur != NULL) {
1089 if (cur->type == XML_CATA_NEXT_CATALOG) {
1090 if (cur->children == NULL) {
1091 xmlFetchXMLCatalogFile(cur);
1092 }
1093 if (cur->children != NULL) {
Daniel Veillard64339542001-08-21 12:57:59 +00001094 ret = xmlCatalogListXMLResolve(cur->children, pubID, sysID);
1095 if (ret != NULL)
1096 return(ret);
Daniel Veillardcda96922001-08-21 10:56:31 +00001097 }
1098 }
1099 cur = cur->next;
1100 }
1101 }
1102
1103 return(NULL);
1104}
1105
1106/**
1107 * xmlCatalogListXMLResolve:
1108 * @catal: a catalog list
1109 * @pubId: the public ID string
1110 * @sysId: the system ID string
1111 *
1112 * Do a complete resolution lookup of an External Identifier for a
1113 * list of catalogs
1114 *
1115 * Implements (or tries to) 7.1. External Identifier Resolution
1116 * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
1117 *
1118 * Returns the URI of the resource or NULL if not found
1119 */
1120static xmlChar *
1121xmlCatalogListXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,
1122 const xmlChar *sysID) {
1123 xmlChar *ret = NULL;
Daniel Veillarde2940dd2001-08-22 00:06:49 +00001124 xmlChar *urnID = NULL;
1125
1126 if (catal == NULL)
1127 return(NULL);
1128 if ((pubID == NULL) && (sysID == NULL))
1129 return(NULL);
1130
1131 if (!xmlStrncmp(pubID, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) {
1132 urnID = xmlCatalogUnWrapURN(pubID);
1133 if (xmlDebugCatalogs) {
1134 if (urnID == NULL)
1135 xmlGenericError(xmlGenericErrorContext,
1136 "Public URN ID %s expanded to NULL\n", pubID);
1137 else
1138 xmlGenericError(xmlGenericErrorContext,
1139 "Public URN ID expanded to %s\n", urnID);
1140 }
1141 ret = xmlCatalogListXMLResolve(catal, urnID, sysID);
1142 if (urnID != NULL)
1143 xmlFree(urnID);
1144 return(ret);
Daniel Veillardcda96922001-08-21 10:56:31 +00001145 }
Daniel Veillarde2940dd2001-08-22 00:06:49 +00001146 if (!xmlStrncmp(sysID, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) {
1147 urnID = xmlCatalogUnWrapURN(sysID);
1148 if (xmlDebugCatalogs) {
1149 if (urnID == NULL)
1150 xmlGenericError(xmlGenericErrorContext,
1151 "System URN ID %s expanded to NULL\n", sysID);
1152 else
1153 xmlGenericError(xmlGenericErrorContext,
1154 "System URN ID expanded to %s\n", urnID);
1155 }
1156 if (pubID == NULL)
1157 ret = xmlCatalogListXMLResolve(catal, urnID, NULL);
1158 else if (xmlStrEqual(pubID, urnID))
1159 ret = xmlCatalogListXMLResolve(catal, pubID, NULL);
1160 else {
1161 ret = xmlCatalogListXMLResolve(catal, pubID, NULL);
1162 }
1163 if (urnID != NULL)
1164 xmlFree(urnID);
1165 return(ret);
Daniel Veillardcda96922001-08-21 10:56:31 +00001166 }
1167 while (catal != NULL) {
1168 if (catal->type == XML_CATA_CATALOG) {
1169 if (catal->children == NULL) {
Daniel Veillarde2940dd2001-08-22 00:06:49 +00001170 xmlFetchXMLCatalogFile(catal);
Daniel Veillardcda96922001-08-21 10:56:31 +00001171 }
Daniel Veillarde2940dd2001-08-22 00:06:49 +00001172 if (catal->children != NULL) {
1173 ret = xmlCatalogXMLResolve(catal->children, pubID, sysID);
1174 if (ret != NULL)
1175 return(ret);
1176 }
Daniel Veillardcda96922001-08-21 10:56:31 +00001177 }
1178 catal = catal->next;
1179 }
1180 return(ret);
Daniel Veillard344cee72001-08-20 00:08:40 +00001181}
1182
1183/************************************************************************
1184 * *
1185 * The SGML Catalog parser *
Daniel Veillarda7374592001-05-10 14:17:55 +00001186 * *
1187 ************************************************************************/
1188
1189
1190#define RAW *cur
1191#define NEXT cur++;
1192#define SKIP(x) cur += x;
1193
1194#define SKIP_BLANKS while (IS_BLANK(*cur)) NEXT;
1195
1196static const xmlChar *
Daniel Veillardcda96922001-08-21 10:56:31 +00001197xmlParseSGMLCatalogComment(const xmlChar *cur) {
Daniel Veillarda7374592001-05-10 14:17:55 +00001198 if ((cur[0] != '-') || (cur[1] != '-'))
1199 return(cur);
1200 SKIP(2);
1201 while ((cur[0] != 0) && ((cur[0] != '-') || ((cur[1] != '-'))))
1202 NEXT;
1203 if (cur[0] == 0) {
1204 return(NULL);
1205 }
Daniel Veillardaf86c7f2001-05-21 14:11:26 +00001206 return(cur + 2);
Daniel Veillarda7374592001-05-10 14:17:55 +00001207}
1208
1209static const xmlChar *
Daniel Veillardcda96922001-08-21 10:56:31 +00001210xmlParseSGMLCatalogPubid(const xmlChar *cur, xmlChar **id) {
Daniel Veillarda7374592001-05-10 14:17:55 +00001211 xmlChar *buf = NULL;
1212 int len = 0;
1213 int size = 50;
1214 xmlChar stop;
1215 int count = 0;
1216
1217 *id = NULL;
1218
1219 if (RAW == '"') {
1220 NEXT;
1221 stop = '"';
1222 } else if (RAW == '\'') {
1223 NEXT;
1224 stop = '\'';
1225 } else {
1226 stop = ' ';
1227 }
1228 buf = (xmlChar *) xmlMalloc(size * sizeof(xmlChar));
1229 if (buf == NULL) {
1230 xmlGenericError(xmlGenericErrorContext,
1231 "malloc of %d byte failed\n", size);
1232 return(NULL);
1233 }
1234 while (xmlIsPubidChar(*cur)) {
1235 if ((*cur == stop) && (stop != ' '))
1236 break;
1237 if ((stop == ' ') && (IS_BLANK(*cur)))
1238 break;
1239 if (len + 1 >= size) {
1240 size *= 2;
1241 buf = (xmlChar *) xmlRealloc(buf, size * sizeof(xmlChar));
1242 if (buf == NULL) {
1243 xmlGenericError(xmlGenericErrorContext,
1244 "realloc of %d byte failed\n", size);
1245 return(NULL);
1246 }
1247 }
1248 buf[len++] = *cur;
1249 count++;
1250 NEXT;
1251 }
1252 buf[len] = 0;
1253 if (stop == ' ') {
1254 if (!IS_BLANK(*cur)) {
1255 xmlFree(buf);
1256 return(NULL);
1257 }
1258 } else {
1259 if (*cur != stop) {
1260 xmlFree(buf);
1261 return(NULL);
1262 }
1263 NEXT;
1264 }
1265 *id = buf;
1266 return(cur);
1267}
1268
1269static const xmlChar *
Daniel Veillardcda96922001-08-21 10:56:31 +00001270xmlParseSGMLCatalogName(const xmlChar *cur, xmlChar **name) {
Daniel Veillarda7374592001-05-10 14:17:55 +00001271 xmlChar buf[XML_MAX_NAMELEN + 5];
1272 int len = 0;
1273 int c;
1274
1275 *name = NULL;
1276
1277 /*
1278 * Handler for more complex cases
1279 */
1280 c = *cur;
1281 if ((!IS_LETTER(c) && (c != '_') && (c != ':'))) {
1282 return(NULL);
1283 }
1284
1285 while (((IS_LETTER(c)) || (IS_DIGIT(c)) ||
1286 (c == '.') || (c == '-') ||
1287 (c == '_') || (c == ':'))) {
1288 buf[len++] = c;
1289 cur++;
1290 c = *cur;
1291 if (len >= XML_MAX_NAMELEN)
1292 return(NULL);
1293 }
1294 *name = xmlStrndup(buf, len);
1295 return(cur);
1296}
1297
Daniel Veillard344cee72001-08-20 00:08:40 +00001298static xmlCatalogEntryType
Daniel Veillardcda96922001-08-21 10:56:31 +00001299xmlGetSGMLCatalogEntryType(const xmlChar *name) {
Daniel Veillard344cee72001-08-20 00:08:40 +00001300 xmlCatalogEntryType type = XML_CATA_NONE;
1301 if (xmlStrEqual(name, (const xmlChar *) "SYSTEM"))
1302 type = SGML_CATA_SYSTEM;
1303 else if (xmlStrEqual(name, (const xmlChar *) "PUBLIC"))
1304 type = SGML_CATA_PUBLIC;
1305 else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE"))
1306 type = SGML_CATA_DELEGATE;
1307 else if (xmlStrEqual(name, (const xmlChar *) "ENTITY"))
1308 type = SGML_CATA_ENTITY;
1309 else if (xmlStrEqual(name, (const xmlChar *) "DOCTYPE"))
1310 type = SGML_CATA_DOCTYPE;
1311 else if (xmlStrEqual(name, (const xmlChar *) "LINKTYPE"))
1312 type = SGML_CATA_LINKTYPE;
1313 else if (xmlStrEqual(name, (const xmlChar *) "NOTATION"))
1314 type = SGML_CATA_NOTATION;
1315 else if (xmlStrEqual(name, (const xmlChar *) "SGMLDECL"))
1316 type = SGML_CATA_SGMLDECL;
1317 else if (xmlStrEqual(name, (const xmlChar *) "DOCUMENT"))
1318 type = SGML_CATA_DOCUMENT;
1319 else if (xmlStrEqual(name, (const xmlChar *) "CATALOG"))
1320 type = SGML_CATA_CATALOG;
1321 else if (xmlStrEqual(name, (const xmlChar *) "BASE"))
1322 type = SGML_CATA_BASE;
1323 else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE"))
1324 type = SGML_CATA_DELEGATE;
1325 return(type);
1326}
1327
Daniel Veillarda7374592001-05-10 14:17:55 +00001328static int
Daniel Veillardcda96922001-08-21 10:56:31 +00001329xmlParseSGMLCatalog(const xmlChar *value, const char *file) {
Daniel Veillarda7374592001-05-10 14:17:55 +00001330 const xmlChar *cur = value;
1331 xmlChar *base = NULL;
Daniel Veillardaf86c7f2001-05-21 14:11:26 +00001332 int res;
Daniel Veillarda7374592001-05-10 14:17:55 +00001333
1334 if ((cur == NULL) || (file == NULL))
1335 return(-1);
1336 base = xmlStrdup((const xmlChar *) file);
1337
1338 while ((cur != NULL) && (cur[0] != '0')) {
1339 SKIP_BLANKS;
1340 if ((cur[0] == '-') && (cur[1] == '-')) {
Daniel Veillardcda96922001-08-21 10:56:31 +00001341 cur = xmlParseSGMLCatalogComment(cur);
Daniel Veillarda7374592001-05-10 14:17:55 +00001342 if (cur == NULL) {
1343 /* error */
1344 break;
1345 }
1346 } else {
1347 xmlChar *sysid = NULL;
1348 xmlChar *name = NULL;
1349 xmlCatalogEntryType type = XML_CATA_NONE;
1350
Daniel Veillardcda96922001-08-21 10:56:31 +00001351 cur = xmlParseSGMLCatalogName(cur, &name);
Daniel Veillarda7374592001-05-10 14:17:55 +00001352 if (name == NULL) {
1353 /* error */
1354 break;
1355 }
1356 if (!IS_BLANK(*cur)) {
1357 /* error */
1358 break;
1359 }
1360 SKIP_BLANKS;
1361 if (xmlStrEqual(name, (const xmlChar *) "SYSTEM"))
Daniel Veillard344cee72001-08-20 00:08:40 +00001362 type = SGML_CATA_SYSTEM;
Daniel Veillarda7374592001-05-10 14:17:55 +00001363 else if (xmlStrEqual(name, (const xmlChar *) "PUBLIC"))
Daniel Veillard344cee72001-08-20 00:08:40 +00001364 type = SGML_CATA_PUBLIC;
Daniel Veillarda7374592001-05-10 14:17:55 +00001365 else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE"))
Daniel Veillard344cee72001-08-20 00:08:40 +00001366 type = SGML_CATA_DELEGATE;
Daniel Veillarda7374592001-05-10 14:17:55 +00001367 else if (xmlStrEqual(name, (const xmlChar *) "ENTITY"))
Daniel Veillard344cee72001-08-20 00:08:40 +00001368 type = SGML_CATA_ENTITY;
Daniel Veillarda7374592001-05-10 14:17:55 +00001369 else if (xmlStrEqual(name, (const xmlChar *) "DOCTYPE"))
Daniel Veillard344cee72001-08-20 00:08:40 +00001370 type = SGML_CATA_DOCTYPE;
Daniel Veillarda7374592001-05-10 14:17:55 +00001371 else if (xmlStrEqual(name, (const xmlChar *) "LINKTYPE"))
Daniel Veillard344cee72001-08-20 00:08:40 +00001372 type = SGML_CATA_LINKTYPE;
Daniel Veillarda7374592001-05-10 14:17:55 +00001373 else if (xmlStrEqual(name, (const xmlChar *) "NOTATION"))
Daniel Veillard344cee72001-08-20 00:08:40 +00001374 type = SGML_CATA_NOTATION;
Daniel Veillarda7374592001-05-10 14:17:55 +00001375 else if (xmlStrEqual(name, (const xmlChar *) "SGMLDECL"))
Daniel Veillard344cee72001-08-20 00:08:40 +00001376 type = SGML_CATA_SGMLDECL;
Daniel Veillarda7374592001-05-10 14:17:55 +00001377 else if (xmlStrEqual(name, (const xmlChar *) "DOCUMENT"))
Daniel Veillard344cee72001-08-20 00:08:40 +00001378 type = SGML_CATA_DOCUMENT;
Daniel Veillarda7374592001-05-10 14:17:55 +00001379 else if (xmlStrEqual(name, (const xmlChar *) "CATALOG"))
Daniel Veillard344cee72001-08-20 00:08:40 +00001380 type = SGML_CATA_CATALOG;
Daniel Veillarda7374592001-05-10 14:17:55 +00001381 else if (xmlStrEqual(name, (const xmlChar *) "BASE"))
Daniel Veillard344cee72001-08-20 00:08:40 +00001382 type = SGML_CATA_BASE;
Daniel Veillarda7374592001-05-10 14:17:55 +00001383 else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE"))
Daniel Veillard344cee72001-08-20 00:08:40 +00001384 type = SGML_CATA_DELEGATE;
Daniel Veillarda7374592001-05-10 14:17:55 +00001385 else if (xmlStrEqual(name, (const xmlChar *) "OVERRIDE")) {
1386 xmlFree(name);
Daniel Veillardcda96922001-08-21 10:56:31 +00001387 cur = xmlParseSGMLCatalogName(cur, &name);
Daniel Veillarda7374592001-05-10 14:17:55 +00001388 if (name == NULL) {
1389 /* error */
1390 break;
1391 }
Daniel Veillardaf86c7f2001-05-21 14:11:26 +00001392 xmlFree(name);
Daniel Veillarda7374592001-05-10 14:17:55 +00001393 continue;
1394 }
1395 xmlFree(name);
Daniel Veillardaf86c7f2001-05-21 14:11:26 +00001396 name = NULL;
Daniel Veillarda7374592001-05-10 14:17:55 +00001397
1398 switch(type) {
Daniel Veillard344cee72001-08-20 00:08:40 +00001399 case SGML_CATA_ENTITY:
Daniel Veillarda7374592001-05-10 14:17:55 +00001400 if (*cur == '%')
Daniel Veillard344cee72001-08-20 00:08:40 +00001401 type = SGML_CATA_PENTITY;
1402 case SGML_CATA_PENTITY:
1403 case SGML_CATA_DOCTYPE:
1404 case SGML_CATA_LINKTYPE:
1405 case SGML_CATA_NOTATION:
Daniel Veillardcda96922001-08-21 10:56:31 +00001406 cur = xmlParseSGMLCatalogName(cur, &name);
Daniel Veillarda7374592001-05-10 14:17:55 +00001407 if (cur == NULL) {
1408 /* error */
1409 break;
1410 }
1411 if (!IS_BLANK(*cur)) {
1412 /* error */
1413 break;
1414 }
1415 SKIP_BLANKS;
Daniel Veillardcda96922001-08-21 10:56:31 +00001416 cur = xmlParseSGMLCatalogPubid(cur, &sysid);
Daniel Veillarda7374592001-05-10 14:17:55 +00001417 if (cur == NULL) {
1418 /* error */
1419 break;
1420 }
1421 break;
Daniel Veillard344cee72001-08-20 00:08:40 +00001422 case SGML_CATA_PUBLIC:
1423 case SGML_CATA_SYSTEM:
1424 case SGML_CATA_DELEGATE:
Daniel Veillardcda96922001-08-21 10:56:31 +00001425 cur = xmlParseSGMLCatalogPubid(cur, &name);
Daniel Veillarda7374592001-05-10 14:17:55 +00001426 if (cur == NULL) {
1427 /* error */
1428 break;
1429 }
1430 if (!IS_BLANK(*cur)) {
1431 /* error */
1432 break;
1433 }
1434 SKIP_BLANKS;
Daniel Veillardcda96922001-08-21 10:56:31 +00001435 cur = xmlParseSGMLCatalogPubid(cur, &sysid);
Daniel Veillarda7374592001-05-10 14:17:55 +00001436 if (cur == NULL) {
1437 /* error */
1438 break;
1439 }
1440 break;
Daniel Veillard344cee72001-08-20 00:08:40 +00001441 case SGML_CATA_BASE:
1442 case SGML_CATA_CATALOG:
1443 case SGML_CATA_DOCUMENT:
1444 case SGML_CATA_SGMLDECL:
Daniel Veillardcda96922001-08-21 10:56:31 +00001445 cur = xmlParseSGMLCatalogPubid(cur, &sysid);
Daniel Veillarda7374592001-05-10 14:17:55 +00001446 if (cur == NULL) {
1447 /* error */
1448 break;
1449 }
1450 break;
1451 default:
1452 break;
1453 }
1454 if (cur == NULL) {
1455 if (name != NULL)
1456 xmlFree(name);
1457 if (sysid != NULL)
1458 xmlFree(sysid);
1459 break;
Daniel Veillard344cee72001-08-20 00:08:40 +00001460 } else if (type == SGML_CATA_BASE) {
Daniel Veillarda7374592001-05-10 14:17:55 +00001461 if (base != NULL)
1462 xmlFree(base);
Daniel Veillardaf86c7f2001-05-21 14:11:26 +00001463 base = xmlStrdup(sysid);
Daniel Veillard344cee72001-08-20 00:08:40 +00001464 } else if ((type == SGML_CATA_PUBLIC) ||
1465 (type == SGML_CATA_SYSTEM)) {
Daniel Veillarda7374592001-05-10 14:17:55 +00001466 xmlChar *filename;
1467
1468 filename = xmlBuildURI(sysid, base);
1469 if (filename != NULL) {
Daniel Veillardaf86c7f2001-05-21 14:11:26 +00001470 xmlCatalogEntryPtr entry;
Daniel Veillarda7374592001-05-10 14:17:55 +00001471
Daniel Veillarde2940dd2001-08-22 00:06:49 +00001472 entry = xmlNewCatalogEntry(type, name, filename,
1473 XML_CATA_PREFER_NONE);
Daniel Veillardaf86c7f2001-05-21 14:11:26 +00001474 res = xmlHashAddEntry(xmlDefaultCatalog, name, entry);
1475 if (res < 0) {
1476 xmlFreeCatalogEntry(entry);
1477 }
1478 xmlFree(filename);
Daniel Veillarda7374592001-05-10 14:17:55 +00001479 }
Daniel Veillardaf86c7f2001-05-21 14:11:26 +00001480
Daniel Veillard344cee72001-08-20 00:08:40 +00001481 } else if (type == SGML_CATA_CATALOG) {
Daniel Veillardaf86c7f2001-05-21 14:11:26 +00001482 xmlChar *filename;
1483
1484 filename = xmlBuildURI(sysid, base);
1485 if (filename != NULL) {
1486 xmlLoadCatalog((const char *)filename);
1487 xmlFree(filename);
1488 }
Daniel Veillarda7374592001-05-10 14:17:55 +00001489 }
Daniel Veillardaf86c7f2001-05-21 14:11:26 +00001490 /*
1491 * drop anything else we won't handle it
1492 */
1493 if (name != NULL)
1494 xmlFree(name);
1495 if (sysid != NULL)
1496 xmlFree(sysid);
Daniel Veillarda7374592001-05-10 14:17:55 +00001497 }
1498 }
1499 if (base != NULL)
1500 xmlFree(base);
1501 if (cur == NULL)
1502 return(-1);
1503 return(0);
1504}
1505
Daniel Veillardcda96922001-08-21 10:56:31 +00001506/**
1507 * xmlCatalogGetSGMLPublic:
1508 * @catal: an SGML catalog hash
1509 * @pubId: the public ID string
1510 *
1511 * Try to lookup the system ID associated to a public ID
1512 *
1513 * Returns the system ID if found or NULL otherwise.
1514 */
1515static const xmlChar *
1516xmlCatalogGetSGMLPublic(xmlHashTablePtr catal, const xmlChar *pubID) {
1517 xmlCatalogEntryPtr entry;
1518
1519 if (catal == NULL)
1520 return(NULL);
1521
1522 entry = (xmlCatalogEntryPtr) xmlHashLookup(catal, pubID);
1523 if (entry == NULL)
1524 return(NULL);
1525 if (entry->type == SGML_CATA_PUBLIC)
1526 return(entry->value);
1527 return(NULL);
1528}
1529
1530/**
Daniel Veillarde2940dd2001-08-22 00:06:49 +00001531 * xmlCatalogGetSGMLSystem:
1532 * @catal: an SGML catalog hash
1533 * @sysId: the public ID string
1534 *
1535 * Try to lookup the catalog local reference for a system ID
1536 *
1537 * Returns the system ID if found or NULL otherwise.
1538 */
1539static const xmlChar *
1540xmlCatalogGetSGMLSystem(xmlHashTablePtr catal, const xmlChar *sysID) {
1541 xmlCatalogEntryPtr entry;
1542
1543 if (catal == NULL)
1544 return(NULL);
1545
1546 entry = (xmlCatalogEntryPtr) xmlHashLookup(catal, sysID);
1547 if (entry == NULL)
1548 return(NULL);
1549 if (entry->type == SGML_CATA_SYSTEM)
1550 return(entry->value);
1551 return(NULL);
1552}
1553
1554/**
Daniel Veillardcda96922001-08-21 10:56:31 +00001555 * xmlCatalogSGMLResolve:
1556 * @pubId: the public ID string
1557 * @sysId: the system ID string
1558 *
1559 * Do a complete resolution lookup of an External Identifier
1560 *
1561 * Returns the URI of the resource or NULL if not found
1562 */
1563static const xmlChar *
1564xmlCatalogSGMLResolve(const xmlChar *pubID, const xmlChar *sysID) {
1565 TODO
1566 return(NULL);
1567}
1568
Daniel Veillarda7374592001-05-10 14:17:55 +00001569/************************************************************************
1570 * *
1571 * Public interfaces *
1572 * *
1573 ************************************************************************/
1574
Daniel Veillard5e2dace2001-07-18 19:30:27 +00001575/**
Daniel Veillarde2940dd2001-08-22 00:06:49 +00001576 * xmlInitializeCatalog:
1577 *
1578 * Do the catalog initialization.
1579 * TODO: this function is not thread safe, catalog initialization should
1580 * preferably be done once at startup
1581 */
1582void
1583xmlInitializeCatalog(void) {
1584 const char *catalogs;
1585
1586 if (xmlCatalogInitialized != 0)
1587 return;
1588
1589 if (getenv("XML_DEBUG_CATALOG"))
1590 xmlDebugCatalogs = 1;
1591 if ((xmlDefaultXMLCatalogList == NULL) && (xmlDefaultCatalog == NULL)) {
1592 catalogs = getenv("XML_CATALOG_FILES");
1593 if (catalogs == NULL)
1594 catalogs = XML_DEFAULT_CATALOG;
1595 xmlDefaultXMLCatalogList = xmlNewCatalogEntry(XML_CATA_CATALOG,
1596 NULL, BAD_CAST catalogs, xmlCatalogDefaultPrefer);
1597 }
1598
1599 xmlCatalogInitialized = 1;
1600}
1601
1602/**
Daniel Veillarda7374592001-05-10 14:17:55 +00001603 * xmlLoadCatalog:
1604 * @filename: a file path
1605 *
Daniel Veillard81418e32001-05-22 15:08:55 +00001606 * Load the catalog and makes its definitions effective for the default
1607 * external entity loader. It will recuse in CATALOG entries.
Daniel Veillardaf86c7f2001-05-21 14:11:26 +00001608 * TODO: this function is not thread safe, catalog initialization should
Daniel Veillarde2940dd2001-08-22 00:06:49 +00001609 * preferably be done once at startup
Daniel Veillarda7374592001-05-10 14:17:55 +00001610 *
1611 * Returns 0 in case of success -1 in case of error
1612 */
1613int
1614xmlLoadCatalog(const char *filename) {
Daniel Veillardaf86c7f2001-05-21 14:11:26 +00001615 int fd, len, ret, i;
Daniel Veillarda7374592001-05-10 14:17:55 +00001616 struct stat info;
1617 xmlChar *content;
1618
1619 if (filename == NULL)
1620 return(-1);
Daniel Veillardaf86c7f2001-05-21 14:11:26 +00001621
Daniel Veillarda7374592001-05-10 14:17:55 +00001622 if (xmlDefaultCatalog == NULL)
1623 xmlDefaultCatalog = xmlHashCreate(20);
1624 if (xmlDefaultCatalog == NULL)
1625 return(-1);
Daniel Veillarde2940dd2001-08-22 00:06:49 +00001626
1627 /*
1628 * Need to be done after ...
1629 */
1630 if (!xmlCatalogInitialized)
1631 xmlInitializeCatalog();
1632
1633#ifdef HAVE_STAT
Daniel Veillarda7374592001-05-10 14:17:55 +00001634 if (stat(filename, &info) < 0)
1635 return(-1);
Daniel Veillarde2940dd2001-08-22 00:06:49 +00001636#endif
Daniel Veillarda7374592001-05-10 14:17:55 +00001637
Daniel Veillardaf86c7f2001-05-21 14:11:26 +00001638 /*
1639 * Prevent loops
1640 */
1641 for (i = 0;i < catalNr;i++) {
Daniel Veillard81418e32001-05-22 15:08:55 +00001642 if (xmlStrEqual((const xmlChar *)catalTab[i],
1643 (const xmlChar *)filename)) {
Daniel Veillardaf86c7f2001-05-21 14:11:26 +00001644 xmlGenericError(xmlGenericErrorContext,
1645 "xmlLoadCatalog: %s seems to induce a loop\n",
1646 filename);
1647 return(-1);
1648 }
1649 }
1650 if (catalNr >= catalMax) {
1651 xmlGenericError(xmlGenericErrorContext,
1652 "xmlLoadCatalog: %s catalog list too deep\n",
1653 filename);
1654 return(-1);
1655 }
1656 catalTab[catalNr++] = filename;
1657
1658 if ((fd = open(filename, O_RDONLY)) < 0) {
1659 catalNr--;
Daniel Veillarda7374592001-05-10 14:17:55 +00001660 return(-1);
Daniel Veillardaf86c7f2001-05-21 14:11:26 +00001661 }
Daniel Veillarda7374592001-05-10 14:17:55 +00001662
1663 content = xmlMalloc(info.st_size + 10);
1664 if (content == NULL) {
1665 xmlGenericError(xmlGenericErrorContext,
1666 "realloc of %d byte failed\n", info.st_size + 10);
Daniel Veillardaf86c7f2001-05-21 14:11:26 +00001667 catalNr--;
1668 return(-1);
Daniel Veillarda7374592001-05-10 14:17:55 +00001669 }
1670 len = read(fd, content, info.st_size);
1671 if (len < 0) {
1672 xmlFree(content);
Daniel Veillardaf86c7f2001-05-21 14:11:26 +00001673 catalNr--;
Daniel Veillarda7374592001-05-10 14:17:55 +00001674 return(-1);
1675 }
1676 content[len] = 0;
1677 close(fd);
1678
Daniel Veillard344cee72001-08-20 00:08:40 +00001679 if ((content[0] == ' ') || (content[0] == '-') ||
1680 ((content[0] >= 'A') && (content[0] <= 'Z')) ||
1681 ((content[0] >= 'a') && (content[0] <= 'z')))
Daniel Veillardcda96922001-08-21 10:56:31 +00001682 ret = xmlParseSGMLCatalog(content, filename);
Daniel Veillard344cee72001-08-20 00:08:40 +00001683 else {
1684 xmlCatalogEntryPtr catal, tmp;
1685 /* TODO: allow to switch the default preference */
1686 catal = xmlParseXMLCatalog(content, XML_CATA_PREFER_PUBLIC, filename);
1687 if (catal != NULL) {
1688 if (xmlDefaultXMLCatalogList == NULL)
1689 xmlDefaultXMLCatalogList = catal;
1690 else {
1691 tmp = xmlDefaultXMLCatalogList;
1692 while (tmp->next != NULL)
1693 tmp = tmp->next;
1694 tmp->next = catal;
1695 }
1696 ret = 0;
1697 } else
1698 ret = -1;
1699 }
Daniel Veillarda7374592001-05-10 14:17:55 +00001700 xmlFree(content);
Daniel Veillardaf86c7f2001-05-21 14:11:26 +00001701 catalNr--;
Daniel Veillarda7374592001-05-10 14:17:55 +00001702 return(ret);
1703}
1704
Daniel Veillard5e2dace2001-07-18 19:30:27 +00001705/**
Daniel Veillard81418e32001-05-22 15:08:55 +00001706 * xmlLoadCatalogs:
1707 * @paths: a list of file path separated by ':' or spaces
1708 *
1709 * Load the catalogs and makes their definitions effective for the default
1710 * external entity loader.
1711 * TODO: this function is not thread safe, catalog initialization should
Daniel Veillarde2940dd2001-08-22 00:06:49 +00001712 * preferably be done once at startup
Daniel Veillard81418e32001-05-22 15:08:55 +00001713 */
1714void
1715xmlLoadCatalogs(const char *pathss) {
1716 const char *cur;
1717 const char *paths;
1718 xmlChar *path;
1719
1720 cur = pathss;
1721 while ((cur != NULL) && (*cur != 0)) {
1722 while (IS_BLANK(*cur)) cur++;
1723 if (*cur != 0) {
1724 paths = cur;
1725 while ((*cur != 0) && (*cur != ':') && (!IS_BLANK(*cur)))
1726 cur++;
1727 path = xmlStrndup((const xmlChar *)paths, cur - paths);
1728 if (path != NULL) {
1729 xmlLoadCatalog((const char *) path);
1730 xmlFree(path);
1731 }
1732 }
1733 while (*cur == ':')
1734 cur++;
1735 }
1736}
1737
Daniel Veillarda7374592001-05-10 14:17:55 +00001738/**
1739 * xmlCatalogCleanup:
1740 *
1741 * Free up all the memory associated with catalogs
1742 */
1743void
1744xmlCatalogCleanup(void) {
Daniel Veillarde2940dd2001-08-22 00:06:49 +00001745 if (xmlDebugCatalogs)
1746 xmlGenericError(xmlGenericErrorContext,
1747 "Catalogs cleanup\n");
Daniel Veillardcda96922001-08-21 10:56:31 +00001748 if (xmlDefaultXMLCatalogList != NULL)
1749 xmlFreeCatalogEntryList(xmlDefaultXMLCatalogList);
Daniel Veillarde2940dd2001-08-22 00:06:49 +00001750 xmlDefaultXMLCatalogList = NULL;
Daniel Veillarda7374592001-05-10 14:17:55 +00001751 if (xmlDefaultCatalog != NULL)
1752 xmlHashFree(xmlDefaultCatalog,
1753 (xmlHashDeallocator) xmlFreeCatalogEntry);
Daniel Veillarde2940dd2001-08-22 00:06:49 +00001754 xmlDebugCatalogs = 0;
Daniel Veillarda7374592001-05-10 14:17:55 +00001755 xmlDefaultCatalog = NULL;
Daniel Veillarde2940dd2001-08-22 00:06:49 +00001756 xmlCatalogInitialized = 0;
Daniel Veillarda7374592001-05-10 14:17:55 +00001757}
1758
1759/**
Daniel Veillard7d6fd212001-05-10 15:34:11 +00001760 * xmlCatalogGetSystem:
Daniel Veillarde2940dd2001-08-22 00:06:49 +00001761 * @pubId: the public ID string
Daniel Veillarda7374592001-05-10 14:17:55 +00001762 *
Daniel Veillarde2940dd2001-08-22 00:06:49 +00001763 * Try to lookup the system ID associated to a public ID
1764 * DEPRECATED, use xmlCatalogResolveSystem()
Daniel Veillard7d6fd212001-05-10 15:34:11 +00001765 *
Daniel Veillarde2940dd2001-08-22 00:06:49 +00001766 * Returns the system ID if found or NULL otherwise.
Daniel Veillarda7374592001-05-10 14:17:55 +00001767 */
Daniel Veillard7d6fd212001-05-10 15:34:11 +00001768const xmlChar *
1769xmlCatalogGetSystem(const xmlChar *sysID) {
Daniel Veillarde2940dd2001-08-22 00:06:49 +00001770 xmlChar *ret;
1771 static xmlChar result[1000];
Daniel Veillard7d6fd212001-05-10 15:34:11 +00001772
Daniel Veillarde2940dd2001-08-22 00:06:49 +00001773 if (sysID == NULL)
Daniel Veillard7d6fd212001-05-10 15:34:11 +00001774 return(NULL);
Daniel Veillarde2940dd2001-08-22 00:06:49 +00001775
1776 if (!xmlCatalogInitialized)
1777 xmlInitializeCatalog();
1778
1779 /*
1780 * Check first the XML catalogs
1781 */
1782 if (xmlDefaultXMLCatalogList != NULL) {
1783 ret = xmlCatalogListXMLResolve(xmlDefaultXMLCatalogList, NULL, sysID);
1784 if (ret != NULL) {
1785 snprintf((char *) result, sizeof(result) - 1, "%s", (char *) ret);
1786 result[sizeof(result) - 1] = 0;
1787 return(result);
1788 }
1789 }
1790
1791 if (xmlDefaultCatalog != NULL)
1792 return(xmlCatalogGetSGMLSystem(xmlDefaultCatalog, sysID));
1793 return(NULL);
1794}
1795
1796/**
1797 * xmlCatalogResolveSystem:
1798 * @sysId: the public ID string
1799 *
1800 * Try to lookup the catalog resource for a system ID
1801 *
1802 * Returns the system ID if found or NULL otherwise, the value returned
1803 * must be freed by the caller.
1804 */
1805xmlChar *
1806xmlCatalogResolveSystem(const xmlChar *sysID) {
1807 xmlCatalogEntryPtr catal;
1808 xmlChar *ret;
1809 const xmlChar *sgml;
1810
1811 if (sysID == NULL)
Daniel Veillard7d6fd212001-05-10 15:34:11 +00001812 return(NULL);
Daniel Veillarde2940dd2001-08-22 00:06:49 +00001813
1814 if (!xmlCatalogInitialized)
1815 xmlInitializeCatalog();
1816
1817 /*
1818 * Check first the XML catalogs
1819 */
1820 catal = xmlDefaultXMLCatalogList;
1821 if (catal != NULL) {
1822 ret = xmlCatalogListXMLResolve(xmlDefaultXMLCatalogList, NULL, sysID);
1823 if ((ret != NULL) && (ret != XML_CATAL_BREAK))
1824 return(ret);
1825 }
1826
1827 if (xmlDefaultCatalog != NULL) {
1828 sgml = xmlCatalogGetSGMLSystem(xmlDefaultCatalog, sysID);
1829 if (sgml != NULL)
1830 return(xmlStrdup(sgml));
1831 }
Daniel Veillard344cee72001-08-20 00:08:40 +00001832 return(NULL);
1833}
1834
1835/**
Daniel Veillard7d6fd212001-05-10 15:34:11 +00001836 * xmlCatalogGetPublic:
1837 * @pubId: the public ID string
1838 *
1839 * Try to lookup the system ID associated to a public ID
Daniel Veillarde2940dd2001-08-22 00:06:49 +00001840 * DEPRECATED, use xmlCatalogResolvePublic()
Daniel Veillard7d6fd212001-05-10 15:34:11 +00001841 *
1842 * Returns the system ID if found or NULL otherwise.
1843 */
1844const xmlChar *
1845xmlCatalogGetPublic(const xmlChar *pubID) {
Daniel Veillarde2940dd2001-08-22 00:06:49 +00001846 xmlChar *ret;
1847 static xmlChar result[1000];
Daniel Veillard7d6fd212001-05-10 15:34:11 +00001848
Daniel Veillard344cee72001-08-20 00:08:40 +00001849 if (pubID == NULL)
Daniel Veillard7d6fd212001-05-10 15:34:11 +00001850 return(NULL);
Daniel Veillard344cee72001-08-20 00:08:40 +00001851
Daniel Veillarde2940dd2001-08-22 00:06:49 +00001852 if (!xmlCatalogInitialized)
1853 xmlInitializeCatalog();
1854
1855 /*
1856 * Check first the XML catalogs
1857 */
1858 if (xmlDefaultXMLCatalogList != NULL) {
1859 ret = xmlCatalogListXMLResolve(xmlDefaultXMLCatalogList, pubID, NULL);
1860 if ((ret != NULL) && (ret != XML_CATAL_BREAK)) {
1861 snprintf((char *) result, sizeof(result) - 1, "%s", (char *) ret);
1862 result[sizeof(result) - 1] = 0;
1863 return(result);
1864 }
1865 }
1866
1867 if (xmlDefaultCatalog != NULL)
1868 return(xmlCatalogGetSGMLPublic(xmlDefaultCatalog, pubID));
1869 return(NULL);
1870}
1871
1872/**
1873 * xmlCatalogResolvePublic:
1874 * @pubId: the public ID string
1875 *
1876 * Try to lookup the system ID associated to a public ID
1877 *
1878 * Returns the system ID if found or NULL otherwise, the value returned
1879 * must be freed by the caller.
1880 */
1881xmlChar *
1882xmlCatalogResolvePublic(const xmlChar *pubID) {
1883 xmlCatalogEntryPtr catal;
1884 xmlChar *ret;
1885 const xmlChar *sgml;
1886
1887 if (pubID == NULL)
1888 return(NULL);
1889
1890 if (!xmlCatalogInitialized)
1891 xmlInitializeCatalog();
1892
Daniel Veillard344cee72001-08-20 00:08:40 +00001893 /*
1894 * Check first the XML catalogs
1895 */
1896 catal = xmlDefaultXMLCatalogList;
1897 if (catal != NULL) {
Daniel Veillarde2940dd2001-08-22 00:06:49 +00001898 ret = xmlCatalogListXMLResolve(xmlDefaultXMLCatalogList, pubID, NULL);
1899 if ((ret != NULL) && (ret != XML_CATAL_BREAK))
Daniel Veillard344cee72001-08-20 00:08:40 +00001900 return(ret);
1901 }
1902
Daniel Veillarde2940dd2001-08-22 00:06:49 +00001903 if (xmlDefaultCatalog != NULL) {
1904 sgml = xmlCatalogGetSGMLPublic(xmlDefaultCatalog, pubID);
1905 if (sgml != NULL)
1906 return(xmlStrdup(sgml));
1907 }
Daniel Veillard7d6fd212001-05-10 15:34:11 +00001908 return(NULL);
1909}
Daniel Veillard344cee72001-08-20 00:08:40 +00001910
Daniel Veillard7d6fd212001-05-10 15:34:11 +00001911/**
Daniel Veillardcda96922001-08-21 10:56:31 +00001912 * xmlCatalogResolve:
1913 * @pubId: the public ID string
1914 * @sysId: the system ID string
1915 *
1916 * Do a complete resolution lookup of an External Identifier
1917 *
1918 * Returns the URI of the resource or NULL if not found, it must be freed
1919 * by the caller.
1920 */
1921xmlChar *
1922xmlCatalogResolve(const xmlChar *pubID, const xmlChar *sysID) {
Daniel Veillarde2940dd2001-08-22 00:06:49 +00001923 if (!xmlCatalogInitialized)
1924 xmlInitializeCatalog();
1925
Daniel Veillardcda96922001-08-21 10:56:31 +00001926 if (xmlDefaultXMLCatalogList != NULL) {
1927 return(xmlCatalogListXMLResolve(xmlDefaultXMLCatalogList, pubID, sysID));
1928 } else {
Daniel Veillarde2940dd2001-08-22 00:06:49 +00001929 const xmlChar *ret;
1930
1931 ret = xmlCatalogSGMLResolve(pubID, sysID);
1932 if (ret != NULL)
1933 return(xmlStrdup(ret));
Daniel Veillardcda96922001-08-21 10:56:31 +00001934 }
Daniel Veillarde2940dd2001-08-22 00:06:49 +00001935 return(NULL);
Daniel Veillardcda96922001-08-21 10:56:31 +00001936}
1937
1938/**
Daniel Veillarda7374592001-05-10 14:17:55 +00001939 * xmlCatalogDump:
1940 * @out: the file.
1941 *
1942 * Free up all the memory associated with catalogs
1943 */
1944void
1945xmlCatalogDump(FILE *out) {
1946 if (out == NULL)
1947 return;
Daniel Veillard344cee72001-08-20 00:08:40 +00001948
1949 if (xmlDefaultXMLCatalogList != NULL) {
1950 xmlDumpXMLCatalog(out, xmlDefaultXMLCatalogList);
1951 } else if (xmlDefaultCatalog != NULL) {
Daniel Veillarda7374592001-05-10 14:17:55 +00001952 xmlHashScan(xmlDefaultCatalog,
1953 (xmlHashScanner) xmlCatalogDumpEntry, out);
Daniel Veillard344cee72001-08-20 00:08:40 +00001954 }
1955}
1956
1957/**
1958 * xmlCatalogAdd:
1959 * @type: the type of record to add to the catalog
1960 * @orig: the system, public or prefix to match
1961 * @replace: the replacement value for the match
1962 *
1963 * Add an entry in the catalog, it may overwrite existing but
1964 * different entries.
1965 *
1966 * Returns 0 if successful, -1 otherwise
1967 */
1968int
1969xmlCatalogAdd(const xmlChar *type, const xmlChar *orig, const xmlChar *replace) {
1970 int res = -1;
1971
Daniel Veillarde2940dd2001-08-22 00:06:49 +00001972 if (!xmlCatalogInitialized)
1973 xmlInitializeCatalog();
1974
Daniel Veillard344cee72001-08-20 00:08:40 +00001975 if (xmlDefaultXMLCatalogList != NULL) {
1976 res = xmlAddXMLCatalog(xmlDefaultXMLCatalogList, type, orig, replace);
1977 } else if (xmlDefaultCatalog != NULL) {
1978 xmlCatalogEntryType typ;
1979
Daniel Veillardcda96922001-08-21 10:56:31 +00001980 typ = xmlGetSGMLCatalogEntryType(type);
Daniel Veillard344cee72001-08-20 00:08:40 +00001981 if (type != XML_CATA_NONE) {
1982 xmlCatalogEntryPtr entry;
Daniel Veillarde2940dd2001-08-22 00:06:49 +00001983 entry = xmlNewCatalogEntry(typ, orig, replace, XML_CATA_PREFER_NONE);
Daniel Veillard344cee72001-08-20 00:08:40 +00001984 res = xmlHashAddEntry(xmlDefaultCatalog, orig, entry);
1985 }
1986 }
1987 return(res);
1988}
1989
1990/**
1991 * xmlCatalogRemove:
1992 * @value: the value to remove
1993 *
1994 * Remove an entry from the catalog
1995 *
1996 * Returns 0 if successful, -1 otherwise
1997 */
1998int
1999xmlCatalogRemove(const xmlChar *value) {
Daniel Veillardcda96922001-08-21 10:56:31 +00002000 int res = -1;
2001
Daniel Veillarde2940dd2001-08-22 00:06:49 +00002002 if (!xmlCatalogInitialized)
2003 xmlInitializeCatalog();
2004
Daniel Veillardcda96922001-08-21 10:56:31 +00002005 if (xmlDefaultXMLCatalogList != NULL) {
2006 res = xmlDelXMLCatalog(xmlDefaultXMLCatalogList, value);
2007 } else if (xmlDefaultCatalog != NULL) {
2008 TODO
2009 }
2010 return(res);
Daniel Veillard344cee72001-08-20 00:08:40 +00002011}
2012
2013/**
Daniel Veillarde2940dd2001-08-22 00:06:49 +00002014 * xmlCatalogSetDefaultPrefer:
2015 * @prefer: the default preference for delegation
2016 *
2017 * Allows to set the preference between public and system for deletion
2018 * in XML Catalog resolution. C.f. section 4.1.1 of the spec
2019 *
2020 * Returns the previous value of the default preference for delegation
2021 */
2022xmlCatalogPrefer
2023xmlCatalogSetDefaultPrefer(xmlCatalogPrefer prefer) {
2024 xmlCatalogPrefer ret = xmlCatalogDefaultPrefer;
2025
2026 xmlCatalogDefaultPrefer = prefer;
2027 return(ret);
2028}
2029
2030/**
Daniel Veillard344cee72001-08-20 00:08:40 +00002031 * xmlCatalogSetDebug:
2032 * @level: the debug level of catalogs required
2033 *
2034 * Used to set the debug level for catalog operation, 0 disable
2035 * debugging, 1 enable it
2036 *
2037 * Returns the previous value of the catalog debugging level
2038 */
2039int
2040xmlCatalogSetDebug(int level) {
2041 int ret = xmlDebugCatalogs;
2042
2043 if (level <= 0)
2044 xmlDebugCatalogs = 0;
2045 else
2046 xmlDebugCatalogs = level;
2047 return(ret);
Daniel Veillarda7374592001-05-10 14:17:55 +00002048}
2049#endif /* LIBXML_CATALOG_ENABLED */