Coverage Report

Created: 2025-06-22 06:55

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