blob: f4b6188b44b8851b12b08691746fc56e518d5180 [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 */
Daniel Veillard81418e32001-05-22 15:08:55 +000068static const char * catalTab[10]; /* stack of catals */
69static int catalNr = 0; /* Number of current catal streams */
70static int catalMax = 10; /* Max number of catal streams */
Daniel Veillardaf86c7f2001-05-21 14:11:26 +000071
Daniel Veillarda7374592001-05-10 14:17:55 +000072/************************************************************************
73 * *
74 * alloc or dealloc *
75 * *
76 ************************************************************************/
77
78static xmlCatalogEntryPtr
79xmlNewCatalogEntry(int type, xmlChar *name, xmlChar *value) {
80 xmlCatalogEntryPtr ret;
81
82 ret = (xmlCatalogEntryPtr) xmlMalloc(sizeof(xmlCatalogEntry));
83 if (ret == NULL) {
84 xmlGenericError(xmlGenericErrorContext,
85 "malloc of %d byte failed\n", sizeof(xmlCatalogEntry));
86 return(NULL);
87 }
88 ret->type = type;
Daniel Veillardaf86c7f2001-05-21 14:11:26 +000089 ret->name = xmlStrdup(name);
90 ret->value = xmlStrdup(value);
Daniel Veillarda7374592001-05-10 14:17:55 +000091 return(ret);
92}
93
94static void
95xmlFreeCatalogEntry(xmlCatalogEntryPtr ret) {
96 if (ret == NULL)
97 return;
98 if (ret->name != NULL)
99 xmlFree(ret->name);
100 if (ret->value != NULL)
101 xmlFree(ret->value);
102 xmlFree(ret);
103}
104
Daniel Veillard7d6fd212001-05-10 15:34:11 +0000105/**
106 * xmlCatalogDumpEntry:
107 * @entry: the
108 * @out: the file.
109 *
110 * Free up all the memory associated with catalogs
111 */
112static void
113xmlCatalogDumpEntry(xmlCatalogEntryPtr entry, FILE *out) {
114 if ((entry == NULL) || (out == NULL))
115 return;
116 switch (entry->type) {
117 case XML_CATA_ENTITY:
118 fprintf(out, "ENTITY "); break;
119 case XML_CATA_PENTITY:
120 fprintf(out, "ENTITY %%"); break;
121 case XML_CATA_DOCTYPE:
122 fprintf(out, "DOCTYPE "); break;
123 case XML_CATA_LINKTYPE:
124 fprintf(out, "LINKTYPE "); break;
125 case XML_CATA_NOTATION:
126 fprintf(out, "NOTATION "); break;
127 case XML_CATA_PUBLIC:
128 fprintf(out, "PUBLIC "); break;
129 case XML_CATA_SYSTEM:
130 fprintf(out, "SYSTEM "); break;
131 case XML_CATA_DELEGATE:
132 fprintf(out, "DELEGATE "); break;
133 case XML_CATA_BASE:
134 fprintf(out, "BASE "); break;
135 case XML_CATA_CATALOG:
136 fprintf(out, "CATALOG "); break;
137 case XML_CATA_DOCUMENT:
138 fprintf(out, "DOCUMENT "); break;
139 case XML_CATA_SGMLDECL:
140 fprintf(out, "SGMLDECL "); break;
141 default:
142 return;
143 }
144 switch (entry->type) {
145 case XML_CATA_ENTITY:
146 case XML_CATA_PENTITY:
147 case XML_CATA_DOCTYPE:
148 case XML_CATA_LINKTYPE:
149 case XML_CATA_NOTATION:
150 fprintf(out, "%s", entry->name); break;
151 case XML_CATA_PUBLIC:
152 case XML_CATA_SYSTEM:
153 case XML_CATA_SGMLDECL:
154 case XML_CATA_DOCUMENT:
155 case XML_CATA_CATALOG:
156 case XML_CATA_BASE:
157 case XML_CATA_DELEGATE:
158 fprintf(out, "\"%s\"", entry->name); break;
159 default:
160 break;
161 }
162 switch (entry->type) {
163 case XML_CATA_ENTITY:
164 case XML_CATA_PENTITY:
165 case XML_CATA_DOCTYPE:
166 case XML_CATA_LINKTYPE:
167 case XML_CATA_NOTATION:
168 case XML_CATA_PUBLIC:
169 case XML_CATA_SYSTEM:
170 case XML_CATA_DELEGATE:
171 fprintf(out, " \"%s\"", entry->value); break;
172 default:
173 break;
174 }
175 fprintf(out, "\n");
176}
177
Daniel Veillarda7374592001-05-10 14:17:55 +0000178/************************************************************************
179 * *
180 * The parser *
181 * *
182 ************************************************************************/
183
184
185#define RAW *cur
186#define NEXT cur++;
187#define SKIP(x) cur += x;
188
189#define SKIP_BLANKS while (IS_BLANK(*cur)) NEXT;
190
191static const xmlChar *
192xmlParseCatalogComment(const xmlChar *cur) {
193 if ((cur[0] != '-') || (cur[1] != '-'))
194 return(cur);
195 SKIP(2);
196 while ((cur[0] != 0) && ((cur[0] != '-') || ((cur[1] != '-'))))
197 NEXT;
198 if (cur[0] == 0) {
199 return(NULL);
200 }
Daniel Veillardaf86c7f2001-05-21 14:11:26 +0000201 return(cur + 2);
Daniel Veillarda7374592001-05-10 14:17:55 +0000202}
203
204static const xmlChar *
205xmlParseCatalogPubid(const xmlChar *cur, xmlChar **id) {
206 xmlChar *buf = NULL;
207 int len = 0;
208 int size = 50;
209 xmlChar stop;
210 int count = 0;
211
212 *id = NULL;
213
214 if (RAW == '"') {
215 NEXT;
216 stop = '"';
217 } else if (RAW == '\'') {
218 NEXT;
219 stop = '\'';
220 } else {
221 stop = ' ';
222 }
223 buf = (xmlChar *) xmlMalloc(size * sizeof(xmlChar));
224 if (buf == NULL) {
225 xmlGenericError(xmlGenericErrorContext,
226 "malloc of %d byte failed\n", size);
227 return(NULL);
228 }
229 while (xmlIsPubidChar(*cur)) {
230 if ((*cur == stop) && (stop != ' '))
231 break;
232 if ((stop == ' ') && (IS_BLANK(*cur)))
233 break;
234 if (len + 1 >= size) {
235 size *= 2;
236 buf = (xmlChar *) xmlRealloc(buf, size * sizeof(xmlChar));
237 if (buf == NULL) {
238 xmlGenericError(xmlGenericErrorContext,
239 "realloc of %d byte failed\n", size);
240 return(NULL);
241 }
242 }
243 buf[len++] = *cur;
244 count++;
245 NEXT;
246 }
247 buf[len] = 0;
248 if (stop == ' ') {
249 if (!IS_BLANK(*cur)) {
250 xmlFree(buf);
251 return(NULL);
252 }
253 } else {
254 if (*cur != stop) {
255 xmlFree(buf);
256 return(NULL);
257 }
258 NEXT;
259 }
260 *id = buf;
261 return(cur);
262}
263
264static const xmlChar *
265xmlParseCatalogName(const xmlChar *cur, xmlChar **name) {
266 xmlChar buf[XML_MAX_NAMELEN + 5];
267 int len = 0;
268 int c;
269
270 *name = NULL;
271
272 /*
273 * Handler for more complex cases
274 */
275 c = *cur;
276 if ((!IS_LETTER(c) && (c != '_') && (c != ':'))) {
277 return(NULL);
278 }
279
280 while (((IS_LETTER(c)) || (IS_DIGIT(c)) ||
281 (c == '.') || (c == '-') ||
282 (c == '_') || (c == ':'))) {
283 buf[len++] = c;
284 cur++;
285 c = *cur;
286 if (len >= XML_MAX_NAMELEN)
287 return(NULL);
288 }
289 *name = xmlStrndup(buf, len);
290 return(cur);
291}
292
293static int
294xmlParseCatalog(const xmlChar *value, const char *file) {
295 const xmlChar *cur = value;
296 xmlChar *base = NULL;
Daniel Veillardaf86c7f2001-05-21 14:11:26 +0000297 int res;
Daniel Veillarda7374592001-05-10 14:17:55 +0000298
299 if ((cur == NULL) || (file == NULL))
300 return(-1);
301 base = xmlStrdup((const xmlChar *) file);
302
303 while ((cur != NULL) && (cur[0] != '0')) {
304 SKIP_BLANKS;
305 if ((cur[0] == '-') && (cur[1] == '-')) {
306 cur = xmlParseCatalogComment(cur);
307 if (cur == NULL) {
308 /* error */
309 break;
310 }
311 } else {
312 xmlChar *sysid = NULL;
313 xmlChar *name = NULL;
314 xmlCatalogEntryType type = XML_CATA_NONE;
315
316 cur = xmlParseCatalogName(cur, &name);
317 if (name == NULL) {
318 /* error */
319 break;
320 }
321 if (!IS_BLANK(*cur)) {
322 /* error */
323 break;
324 }
325 SKIP_BLANKS;
326 if (xmlStrEqual(name, (const xmlChar *) "SYSTEM"))
327 type = XML_CATA_SYSTEM;
328 else if (xmlStrEqual(name, (const xmlChar *) "PUBLIC"))
329 type = XML_CATA_PUBLIC;
330 else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE"))
331 type = XML_CATA_DELEGATE;
332 else if (xmlStrEqual(name, (const xmlChar *) "ENTITY"))
333 type = XML_CATA_ENTITY;
334 else if (xmlStrEqual(name, (const xmlChar *) "DOCTYPE"))
335 type = XML_CATA_DOCTYPE;
336 else if (xmlStrEqual(name, (const xmlChar *) "LINKTYPE"))
337 type = XML_CATA_LINKTYPE;
338 else if (xmlStrEqual(name, (const xmlChar *) "NOTATION"))
339 type = XML_CATA_NOTATION;
340 else if (xmlStrEqual(name, (const xmlChar *) "SGMLDECL"))
341 type = XML_CATA_SGMLDECL;
342 else if (xmlStrEqual(name, (const xmlChar *) "DOCUMENT"))
343 type = XML_CATA_DOCUMENT;
344 else if (xmlStrEqual(name, (const xmlChar *) "CATALOG"))
345 type = XML_CATA_CATALOG;
346 else if (xmlStrEqual(name, (const xmlChar *) "BASE"))
347 type = XML_CATA_BASE;
348 else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE"))
349 type = XML_CATA_DELEGATE;
350 else if (xmlStrEqual(name, (const xmlChar *) "OVERRIDE")) {
351 xmlFree(name);
352 cur = xmlParseCatalogName(cur, &name);
353 if (name == NULL) {
354 /* error */
355 break;
356 }
Daniel Veillardaf86c7f2001-05-21 14:11:26 +0000357 xmlFree(name);
Daniel Veillarda7374592001-05-10 14:17:55 +0000358 continue;
359 }
360 xmlFree(name);
Daniel Veillardaf86c7f2001-05-21 14:11:26 +0000361 name = NULL;
Daniel Veillarda7374592001-05-10 14:17:55 +0000362
363 switch(type) {
364 case XML_CATA_ENTITY:
365 if (*cur == '%')
366 type = XML_CATA_PENTITY;
367 case XML_CATA_PENTITY:
368 case XML_CATA_DOCTYPE:
369 case XML_CATA_LINKTYPE:
370 case XML_CATA_NOTATION:
371 cur = xmlParseCatalogName(cur, &name);
372 if (cur == NULL) {
373 /* error */
374 break;
375 }
376 if (!IS_BLANK(*cur)) {
377 /* error */
378 break;
379 }
380 SKIP_BLANKS;
381 cur = xmlParseCatalogPubid(cur, &sysid);
382 if (cur == NULL) {
383 /* error */
384 break;
385 }
386 break;
387 case XML_CATA_PUBLIC:
388 case XML_CATA_SYSTEM:
389 case XML_CATA_DELEGATE:
390 cur = xmlParseCatalogPubid(cur, &name);
391 if (cur == NULL) {
392 /* error */
393 break;
394 }
395 if (!IS_BLANK(*cur)) {
396 /* error */
397 break;
398 }
399 SKIP_BLANKS;
400 cur = xmlParseCatalogPubid(cur, &sysid);
401 if (cur == NULL) {
402 /* error */
403 break;
404 }
405 break;
406 case XML_CATA_BASE:
407 case XML_CATA_CATALOG:
408 case XML_CATA_DOCUMENT:
409 case XML_CATA_SGMLDECL:
410 cur = xmlParseCatalogPubid(cur, &sysid);
411 if (cur == NULL) {
412 /* error */
413 break;
414 }
415 break;
416 default:
417 break;
418 }
419 if (cur == NULL) {
420 if (name != NULL)
421 xmlFree(name);
422 if (sysid != NULL)
423 xmlFree(sysid);
424 break;
425 } else if (type == XML_CATA_BASE) {
426 if (base != NULL)
427 xmlFree(base);
Daniel Veillardaf86c7f2001-05-21 14:11:26 +0000428 base = xmlStrdup(sysid);
Daniel Veillarda7374592001-05-10 14:17:55 +0000429 } else if ((type == XML_CATA_PUBLIC) ||
430 (type == XML_CATA_SYSTEM)) {
431 xmlChar *filename;
432
433 filename = xmlBuildURI(sysid, base);
434 if (filename != NULL) {
Daniel Veillardaf86c7f2001-05-21 14:11:26 +0000435 xmlCatalogEntryPtr entry;
Daniel Veillarda7374592001-05-10 14:17:55 +0000436
Daniel Veillardaf86c7f2001-05-21 14:11:26 +0000437 entry = xmlNewCatalogEntry(type, name, filename);
438 res = xmlHashAddEntry(xmlDefaultCatalog, name, entry);
439 if (res < 0) {
440 xmlFreeCatalogEntry(entry);
441 }
442 xmlFree(filename);
Daniel Veillarda7374592001-05-10 14:17:55 +0000443 }
Daniel Veillardaf86c7f2001-05-21 14:11:26 +0000444
445 } else if (type == XML_CATA_CATALOG) {
446 xmlChar *filename;
447
448 filename = xmlBuildURI(sysid, base);
449 if (filename != NULL) {
450 xmlLoadCatalog((const char *)filename);
451 xmlFree(filename);
452 }
Daniel Veillarda7374592001-05-10 14:17:55 +0000453 }
Daniel Veillardaf86c7f2001-05-21 14:11:26 +0000454 /*
455 * drop anything else we won't handle it
456 */
457 if (name != NULL)
458 xmlFree(name);
459 if (sysid != NULL)
460 xmlFree(sysid);
Daniel Veillarda7374592001-05-10 14:17:55 +0000461 }
462 }
463 if (base != NULL)
464 xmlFree(base);
465 if (cur == NULL)
466 return(-1);
467 return(0);
468}
469
470/************************************************************************
471 * *
472 * Public interfaces *
473 * *
474 ************************************************************************/
475
476/*
477 * xmlLoadCatalog:
478 * @filename: a file path
479 *
Daniel Veillard81418e32001-05-22 15:08:55 +0000480 * Load the catalog and makes its definitions effective for the default
481 * external entity loader. It will recuse in CATALOG entries.
Daniel Veillardaf86c7f2001-05-21 14:11:26 +0000482 * TODO: this function is not thread safe, catalog initialization should
483 * be done once at startup
Daniel Veillarda7374592001-05-10 14:17:55 +0000484 *
485 * Returns 0 in case of success -1 in case of error
486 */
487int
488xmlLoadCatalog(const char *filename) {
Daniel Veillardaf86c7f2001-05-21 14:11:26 +0000489 int fd, len, ret, i;
Daniel Veillarda7374592001-05-10 14:17:55 +0000490 struct stat info;
491 xmlChar *content;
492
493 if (filename == NULL)
494 return(-1);
Daniel Veillardaf86c7f2001-05-21 14:11:26 +0000495
Daniel Veillarda7374592001-05-10 14:17:55 +0000496 if (xmlDefaultCatalog == NULL)
497 xmlDefaultCatalog = xmlHashCreate(20);
498 if (xmlDefaultCatalog == NULL)
499 return(-1);
500
501 if (stat(filename, &info) < 0)
502 return(-1);
503
Daniel Veillardaf86c7f2001-05-21 14:11:26 +0000504 /*
505 * Prevent loops
506 */
507 for (i = 0;i < catalNr;i++) {
Daniel Veillard81418e32001-05-22 15:08:55 +0000508 if (xmlStrEqual((const xmlChar *)catalTab[i],
509 (const xmlChar *)filename)) {
Daniel Veillardaf86c7f2001-05-21 14:11:26 +0000510 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
Daniel Veillard81418e32001-05-22 15:08:55 +0000551/*
552 * xmlLoadCatalogs:
553 * @paths: a list of file path separated by ':' or spaces
554 *
555 * Load the catalogs and makes their definitions effective for the default
556 * external entity loader.
557 * TODO: this function is not thread safe, catalog initialization should
558 * be done once at startup
559 */
560void
561xmlLoadCatalogs(const char *pathss) {
562 const char *cur;
563 const char *paths;
564 xmlChar *path;
565
566 cur = pathss;
567 while ((cur != NULL) && (*cur != 0)) {
568 while (IS_BLANK(*cur)) cur++;
569 if (*cur != 0) {
570 paths = cur;
571 while ((*cur != 0) && (*cur != ':') && (!IS_BLANK(*cur)))
572 cur++;
573 path = xmlStrndup((const xmlChar *)paths, cur - paths);
574 if (path != NULL) {
575 xmlLoadCatalog((const char *) path);
576 xmlFree(path);
577 }
578 }
579 while (*cur == ':')
580 cur++;
581 }
582}
583
Daniel Veillarda7374592001-05-10 14:17:55 +0000584/**
585 * xmlCatalogCleanup:
586 *
587 * Free up all the memory associated with catalogs
588 */
589void
590xmlCatalogCleanup(void) {
591 if (xmlDefaultCatalog != NULL)
592 xmlHashFree(xmlDefaultCatalog,
593 (xmlHashDeallocator) xmlFreeCatalogEntry);
594 xmlDefaultCatalog = NULL;
595}
596
597/**
Daniel Veillard7d6fd212001-05-10 15:34:11 +0000598 * xmlCatalogGetSystem:
599 * @sysId: the system ID string
Daniel Veillarda7374592001-05-10 14:17:55 +0000600 *
Daniel Veillard7d6fd212001-05-10 15:34:11 +0000601 * Try to lookup the resource associated to a system ID
602 *
603 * Returns the resource name if found or NULL otherwise.
Daniel Veillarda7374592001-05-10 14:17:55 +0000604 */
Daniel Veillard7d6fd212001-05-10 15:34:11 +0000605const xmlChar *
606xmlCatalogGetSystem(const xmlChar *sysID) {
607 xmlCatalogEntryPtr entry;
608
609 if ((sysID == NULL) || (xmlDefaultCatalog == NULL))
610 return(NULL);
611 entry = (xmlCatalogEntryPtr) xmlHashLookup(xmlDefaultCatalog, sysID);
612 if (entry == NULL)
613 return(NULL);
614 if (entry->type == XML_CATA_SYSTEM)
615 return(entry->value);
616 return(NULL);
Daniel Veillarda7374592001-05-10 14:17:55 +0000617}
618
619/**
Daniel Veillard7d6fd212001-05-10 15:34:11 +0000620 * xmlCatalogGetPublic:
621 * @pubId: the public ID string
622 *
623 * Try to lookup the system ID associated to a public ID
624 *
625 * Returns the system ID if found or NULL otherwise.
626 */
627const xmlChar *
628xmlCatalogGetPublic(const xmlChar *pubID) {
629 xmlCatalogEntryPtr entry;
630
631 if ((pubID == NULL) || (xmlDefaultCatalog == NULL))
632 return(NULL);
633 entry = (xmlCatalogEntryPtr) xmlHashLookup(xmlDefaultCatalog, pubID);
634 if (entry == NULL)
635 return(NULL);
636 if (entry->type == XML_CATA_PUBLIC)
637 return(entry->value);
638 return(NULL);
639}
640/**
Daniel Veillarda7374592001-05-10 14:17:55 +0000641 * xmlCatalogDump:
642 * @out: the file.
643 *
644 * Free up all the memory associated with catalogs
645 */
646void
647xmlCatalogDump(FILE *out) {
648 if (out == NULL)
649 return;
650 if (xmlDefaultCatalog != NULL) {
651 xmlHashScan(xmlDefaultCatalog,
652 (xmlHashScanner) xmlCatalogDumpEntry, out);
653 }
654}
655#endif /* LIBXML_CATALOG_ENABLED */