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