Use yaz_read_UTF8_char instead of xmlGetUTF8Char
[yaz-moved-to-github.git] / src / nfaxml.c
1 /*  Copyright (C) 2006, Index Data ApS
2  *  See the file LICENSE for details.
3  * 
4  *  $Id: nfaxml.c,v 1.10 2006-08-04 14:35:40 adam Exp $ 
5  */
6
7 /**
8  * \file nfaxml.c
9  * \brief Routines for reading a NFA spec from an XML file
10  *
11  */
12
13 #if YAZ_HAVE_XML2
14
15 #include <string.h>
16
17 /* #include <libxml/parser.h> */
18 #include <libxml/tree.h>
19 #include <libxml/xinclude.h>
20
21 #include <yaz/nfa.h>
22 #include <yaz/nmem.h> 
23 #include <yaz/yconfig.h>
24 #include <yaz/nfa.h>
25 #include <yaz/yaz-iconv.h>
26 #include <yaz/nfaxml.h>
27 #include <yaz/libxml2_error.h>
28
29 /** \brief How long strings we are willing to handle here */
30 #define MAXDATALEN 200 
31
32 /** \brief Get content of a node, in utf16, for yaz_nfa */
33 static int utf16_content(xmlNodePtr node, yaz_nfa_char *buf, int maxlen,
34         const char *filename, int rulenumber)
35 {
36     int bufidx=0;
37     xmlChar *content = xmlNodeGetContent(node);
38     xmlChar *cp = content;
39     size_t conlen = strlen((char *)content);
40     while (*cp && bufidx<maxlen )
41     {
42         int error;
43         size_t no_read;
44         int res = yaz_read_UTF8_char(cp, conlen, &no_read, &error);
45         if (res == 0) {
46             /* should be caught earlier */
47             yaz_log(YLOG_FATAL,"Illegal utf-8 sequence "
48                     "%d bytes into '%s' in %s, rule %d ",
49                     cp-content, content, filename, rulenumber);
50             xmlFree(content);
51             return -1;
52         }
53         buf[bufidx++] = res;
54         cp += no_read;
55         conlen -= no_read;
56     }
57     buf[bufidx]=0;
58     xmlFree(content);
59     return bufidx;
60 }
61
62 static int parse_range(xmlNodePtr node, 
63         yaz_nfa_char *range_start,
64         yaz_nfa_char *range_end,
65         const char *filename, int rulenumber )
66 {
67     xmlChar *content = xmlNodeGetContent(node);
68     xmlChar *cp=content;
69     size_t conlen = strlen((char *)content);
70     size_t no_read;
71     int error;
72     int res = yaz_read_UTF8_char(cp, conlen, &no_read, &error);
73     if ( res != 0 ) {
74         *range_start=res;
75         cp += no_read;
76         conlen -= no_read;
77         res = yaz_read_UTF8_char(cp, conlen, &no_read, &error);
78         if (res != '-' )
79             res = 0;
80     }
81     if ( res != 0 ) {
82         cp += no_read;
83         conlen -= no_read;
84         res = yaz_read_UTF8_char(cp, conlen, &no_read, &error);
85     }
86     if ( res != 0) {
87         *range_end = res;
88     }
89     xmlFree(content);
90     if (res == 0) {
91         yaz_log(YLOG_FATAL,"Illegal range. '%s'. Must be like 'a-z' "
92                 "'in %s, rule %d ",
93                 content, filename, rulenumber);
94         return 0;
95     }
96     return 1;
97 } /* parserange */
98
99
100 /** \brief Parse a fromstring clause */
101 static yaz_nfa_state *parse_fromstring(yaz_nfa *nfa, 
102         xmlNodePtr node, const char *filename, int rulenumber )
103 {
104     yaz_nfa_char buf[MAXDATALEN];
105     yaz_nfa_state *state;
106     int bufidx=utf16_content(node, buf, MAXDATALEN, filename, rulenumber);
107     if (bufidx<0) 
108         return 0;
109     state=yaz_nfa_add_sequence(nfa, 0, buf, bufidx);
110     return state;
111 } /* parse_fromstring */
112
113 /** \brief Parse a tostring clause */
114 static yaz_nfa_converter *parse_tostring(yaz_nfa *nfa,
115                 xmlNodePtr node, const char *filename, int rulenumber )
116 {
117     yaz_nfa_char buf[MAXDATALEN];
118     yaz_nfa_converter *conv;
119     int bufidx=utf16_content(node, buf, MAXDATALEN, filename, rulenumber);
120     if (bufidx<0) 
121         return 0;
122     conv=yaz_nfa_create_string_converter(nfa, buf, bufidx);
123     return conv;
124 } /* parse_tostring */
125
126 static yaz_nfa_state * parse_fromrange(yaz_nfa *nfa,
127                 xmlNodePtr node, 
128                 yaz_nfa_char *from_begin,
129                 yaz_nfa_char *from_end,
130                 const char *filename, int rulenumber )
131 {
132     yaz_nfa_char begin;
133     yaz_nfa_char end;
134     yaz_nfa_state *state;
135     int rc;
136     rc=parse_range(node, &begin, &end, filename, rulenumber);
137     if (!rc)
138         return 0;
139     *from_begin=begin;
140     *from_end=end; /* save for calculating the to-range */
141     state=yaz_nfa_add_range(nfa, 0, begin, end);
142     return state;
143 } /* parse_fromrange */
144
145 static yaz_nfa_converter *parse_torange(yaz_nfa *nfa,
146              xmlNodePtr node, yaz_nfa_char from_begin, yaz_nfa_char from_end,
147              const char *filename, int rulenumber )
148 {
149     yaz_nfa_char begin;
150     yaz_nfa_char end;
151     yaz_nfa_converter *conv;
152     int rc;
153     rc=parse_range(node, &begin, &end, filename, rulenumber);
154     if (!rc)
155         return 0;
156     if ( from_end - from_begin != end - begin ) {
157         yaz_log(YLOG_FATAL,"From-range not as long as to-range: "
158                 "from=%x-%x to=%x-%x in rule %d in %s",
159                 from_begin, from_end,  begin, end, rulenumber, filename);
160         return 0;
161     }
162     conv=yaz_nfa_create_range_converter(nfa, 0, from_begin, begin);
163     return conv;
164 } /* parse_torange */
165
166 /** \brief Parse one rule from an XML node */
167 static int parse_rule(yaz_nfa *nfa, xmlNodePtr rulenode, 
168         const char *filename, int rulenumber ) 
169 {
170     yaz_nfa_state *state=0;
171     yaz_nfa_converter *conv=0;
172     yaz_nfa_char range_begin=0, range_end=0;
173     xmlNodePtr node;
174     int clauses=0;
175     for (node = rulenode->children; node; node = node->next)
176     {
177         if (node->type != XML_ELEMENT_NODE)
178             continue;
179         clauses++;
180         if (!strcmp((const char *) node->name, "fromstring")) 
181         {
182             state = parse_fromstring(nfa, node, filename, rulenumber );
183             if (!state)
184                 return 0;
185         } else if (!strcmp((const char *) node->name, "tostring")) 
186         {
187             conv = parse_tostring(nfa, node, filename, rulenumber );
188             if (!conv)
189                 return 0;
190         } else if (!strcmp((const char *) node->name, "fromrange")) 
191         {
192             state = parse_fromrange(nfa, node, 
193                     &range_begin, &range_end, filename, rulenumber );
194             if (!state)
195                 return 0;
196         } else if (!strcmp((const char *) node->name, "torange")) 
197         {
198             conv = parse_torange(nfa, node, 
199                     range_begin, range_end, filename, rulenumber );
200             if (!conv)
201                 return 0;
202         } else {
203             yaz_log(YLOG_FATAL,"Unknown clause '%s' in %s rule %d",
204                     node->name, filename,rulenumber);
205             return 0;
206         }
207     } /* for child */
208     if (!state) {
209         yaz_log(YLOG_FATAL,"No 'from' clause in a rule %d in %s", 
210                 rulenumber,filename);
211         return 0;
212     }
213     if (!conv) {
214         yaz_log(YLOG_FATAL,"No 'to' clause in a rule %d in %s",
215                 rulenumber,filename);
216         return 0;
217     }
218     if (clauses != 2) {
219         yaz_log(YLOG_FATAL,"Must have exactly one 'from' and one 'to' clause "
220                 "in rule %d in %s", rulenumber,filename);
221         return 0;
222     }
223     if ( YAZ_NFA_SUCCESS == yaz_nfa_set_result(nfa,state,conv))
224         return 1; 
225     yaz_log(YLOG_FATAL,"Conflicting rules in %s rule %d",
226             filename, rulenumber);
227     return 0;
228 } /* parse_rule */
229
230
231 /** \brief Parse the NFA from a XML document 
232  */
233 yaz_nfa *yaz_nfa_parse_xml_doc(xmlDocPtr doc, const char *filename)
234 {
235     xmlNodePtr node;
236     yaz_nfa *nfa;
237     int rulenumber=0;
238
239     if (!doc)
240         return 0;
241     libxml2_error_to_yazlog(YLOG_FATAL, "yaz_nfa_parse_doc");
242     node = xmlDocGetRootElement(doc);
243     if (!node || node->type != XML_ELEMENT_NODE ||
244         strcmp((const char *) node->name, "ruleset")) 
245     {
246         yaz_log(YLOG_FATAL,"nfa_parse_xml: Could not find root element 'ruleset' "
247                 "in %s", filename);
248         return 0;
249     }
250     nfa= yaz_nfa_init();
251     if (!nfa) 
252     {
253         yaz_log(YLOG_FATAL,"nfa_parse_xml: Creating nfa failed, can't parse %s",
254                 filename);
255         return 0;
256     }
257         
258     for (node = node->children; node; node = node->next)
259     {
260         if (node->type != XML_ELEMENT_NODE)
261             continue;
262          if (!strcmp((const char *) node->name, "rule")) {
263              if (!parse_rule(nfa,node,filename,rulenumber++))
264                  return 0;
265          } else {
266             yaz_log(YLOG_FATAL,"nfa_parse_xml: "
267                     "expected 'rule', found '%s' in %s", 
268                     (const char *) node->name,filename);
269             return 0;
270          }
271     } /* for */
272     return nfa;
273 } /* yaz_nfa_parse_xml_doc */
274
275
276 /** \brief Parse the NFA from a file 
277  */
278 yaz_nfa *yaz_nfa_parse_xml_file(const char *filepath) 
279 {
280     int nSubst;
281     xmlDocPtr doc;
282     if (!filepath) 
283     {
284         yaz_log(YLOG_FATAL,"yaz_nfa_parse_xml_file called with NULL");
285         return 0;
286     }
287     libxml2_error_to_yazlog(YLOG_FATAL, "yaz_nfa_parse_xml_file");
288
289     doc = xmlParseFile(filepath);
290     if (!doc) {
291         return 0;
292     }
293     nSubst=xmlXIncludeProcess(doc);
294     if (nSubst==-1) {
295         return 0;
296     }
297     return yaz_nfa_parse_xml_doc(doc, filepath);
298 }
299
300 /** \brief Parse the NFA from a memory buffer
301  */
302 yaz_nfa *yaz_nfa_parse_xml_memory(const char *xmlbuff, const char *filename) {
303     xmlDocPtr doc;
304     if (!xmlbuff) 
305     {
306         yaz_log(YLOG_FATAL,"yaz_nfa_parse_memroy called with NULL");
307         return 0;
308     }
309     libxml2_error_to_yazlog(YLOG_FATAL, "yaz_nfa_parse_xml_memory");
310     doc = xmlParseMemory(xmlbuff, strlen(xmlbuff));
311     return yaz_nfa_parse_xml_doc(doc,filename);
312 }
313
314
315
316 #endif /* YAZ_HAVE_XML2 */
317
318
319 /*
320  * Local variables:
321  * c-basic-offset: 4
322  * indent-tabs-mode: nil
323  * End:
324  * vim: shiftwidth=4 tabstop=8 expandtab
325  */