XML Include: do not substitute root node
[yaz-moved-to-github.git] / src / xml_include.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2010 Index Data
3  * See the file LICENSE for details.
4  */
5
6 /** \file 
7     \brief XML Include (not to be confused with W3C XInclude)
8 */
9 #if HAVE_CONFIG_H
10 #include <config.h>
11 #endif
12
13 #if HAVE_GLOB_H
14 #define USE_POSIX_GLOB 1
15 #else
16 #define USE_POSIX_GLOB 0
17 #endif
18
19 #if USE_POSIX_GLOB
20 #include <glob.h>
21 #endif
22
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <assert.h>
28 #include <yaz/wrbuf.h>
29 #include <yaz/tpath.h>
30 #include <yaz/log.h>
31 #include <yaz/xml_include.h>
32
33 #if YAZ_HAVE_XML2
34
35 #include <libxml/parser.h>
36 #include <libxml/tree.h>
37
38 struct yaz_xml_include_s {
39     const char *confdir;
40 };
41
42 typedef struct yaz_xml_include_s *yaz_xml_include_t;
43
44 static int process_config_includes(yaz_xml_include_t config, xmlNode *n);
45
46 static void conf_dir_path(yaz_xml_include_t config, WRBUF w, const char *src)
47 {
48     if (config->confdir && *config->confdir > 0 &&
49         !yaz_is_abspath(src))
50     {
51         wrbuf_printf(w, "%s/%s", config->confdir, src);
52     }
53     else
54         wrbuf_puts(w, src);
55 }
56
57 static int config_include_one(yaz_xml_include_t config, xmlNode **sib,
58                               const char *path)
59 {
60     struct stat st;
61     if (stat(path, &st) < 0)
62     {
63         yaz_log(YLOG_FATAL|YLOG_ERRNO, "stat %s", path);
64         return -1;
65     }
66     else
67     {
68         if ((st.st_mode & S_IFMT) == S_IFREG)
69         {
70             xmlDoc *doc = xmlParseFile(path);
71             if (doc)
72             {
73                 xmlNodePtr t = xmlDocGetRootElement(doc);
74                 int ret = process_config_includes(config, t);
75                 *sib = xmlAddNextSibling(*sib, xmlCopyNode(t, 1));
76                 xmlFreeDoc(doc);
77                 if (ret)
78                     return -1;
79             }
80             else
81             {
82                 yaz_log(YLOG_FATAL, "Could not parse %s", path);
83                 return -1;
84             }
85         }
86     }
87     return 0;
88 }
89
90 static int config_include_src(yaz_xml_include_t config, xmlNode **np,
91                               const char *src)
92 {
93     int ret = 0; /* return code. OK so far */
94     WRBUF w = wrbuf_alloc();
95     xmlNodePtr sib; /* our sibling that we append */
96     xmlNodePtr c; /* tmp node */
97
98     wrbuf_printf(w, " begin include src=\"%s\" ", src);
99
100     /* replace include element with a 'begin' comment */
101     sib = xmlNewComment((const xmlChar *) wrbuf_cstr(w));
102     xmlReplaceNode(*np, sib);
103
104     xmlFreeNode(*np);
105
106     wrbuf_rewind(w);
107     conf_dir_path(config, w, src);
108 #if USE_POSIX_GLOB
109     {
110         size_t i;
111         glob_t glob_res;
112         glob(wrbuf_cstr(w), 0 /* flags */, 0 /* errfunc */, &glob_res);
113         
114         for (i = 0; ret == 0 && i < glob_res.gl_pathc; i++)
115         {
116             const char *path = glob_res.gl_pathv[i];
117             ret = config_include_one(config, &sib, path);
118         }
119         globfree(&glob_res);
120     }
121 #else
122     ret = config_include_one(config, &sib, wrbuf_cstr(w));
123 #endif
124     wrbuf_rewind(w);
125     wrbuf_printf(w, " end include src=\"%s\" ", src);
126     c = xmlNewComment((const xmlChar *) wrbuf_cstr(w));
127     sib = xmlAddNextSibling(sib, c);
128     
129     *np = sib;
130     wrbuf_destroy(w);
131     return ret;
132 }
133
134 static int process_config_includes(yaz_xml_include_t config, xmlNode *n)
135 {
136     for (n = n->children; n; n = n->next)
137     {
138         if (n->type == XML_ELEMENT_NODE)
139         {
140             if (!strcmp((const char *) n->name, "include"))
141             {
142                 xmlChar *src = xmlGetProp(n, (xmlChar *) "src");
143                 if (src)
144                 {
145                     int ret = config_include_src(config, &n,
146                                                  (const char *) src);
147                     xmlFree(src);
148                     if (ret)
149                         return ret;
150                         
151                 }
152             }
153             else
154             {
155                 if (process_config_includes(config, n))
156                     return -1;
157             }
158         }
159     }
160     return 0;
161 }
162
163 int yaz_xml_include_simple(xmlNode *n, const char *base_path)
164 {
165     struct yaz_xml_include_s s;
166
167     s.confdir = base_path;
168     process_config_includes(&s, n);
169     return 0;
170 }
171
172 /* YAZ_HAVE_XML2 */
173 #endif
174
175 /*
176  * Local variables:
177  * c-basic-offset: 4
178  * c-file-style: "Stroustrup"
179  * indent-tabs-mode: nil
180  * End:
181  * vim: shiftwidth=4 tabstop=8 expandtab
182  */
183