CAEN Utility  2.0.2
Utilities for CAEN projects
CAENXMLParser.c
Go to the documentation of this file.
1 /******************************************************************************
2 *
3 * CAEN SpA - Software Division
4 * Via Vetraia, 11 - 55049 - Viareggio ITALY
5 * +39 0594 388 398 - www.caen.it
6 *
7 *******************************************************************************
8 *
9 * Copyright (C) 2019-2022 CAEN SpA
10 *
11 * This file is part of the CAEN Utility.
12 *
13 * The CAEN Utility is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 3 of the License, or (at your option) any later version.
17 *
18 * The CAEN Utility is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
22 *
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with the CAEN Utility; if not, see
25 * https://www.gnu.org/licenses/.
26 *
27 * SPDX-License-Identifier: LGPL-3.0-or-later
28 *
29 ***************************************************************************/
37 #ifndef _NOUSEXML
38 
39 #include <CAENXMLParser.h>
40 
41 #include <libxml/xpathInternals.h>
42 #include <libxml/globals.h>
43 #include <libxml/parser.h>
44 #include <libxml/xmlversion.h>
45 #include <libxml/xpath.h>
46 
47 #include <CAENMultiplatform.h>
48 #include <CAENLogger.h>
49 
50 static c_libhandle_t libHandle = NULL;
51 
52 INIT_C_LOGGER("CAENXMLParserLog.txt", "CAENXMLParser.c");
53 
55  c_xmlfile_t *file = c_malloc(sizeof(*file));
56  if (file == NULL) {
57  logMsg(c_logger_Severity_ERROR, "%s(): Can't allocate space for c_xmlfile_t.", __func__);
58  return file;
59  }
60 
61  file->doc = xmlReadFile(filename, NULL, XML_PARSE_NOBLANKS | XML_PARSE_PEDANTIC);
62  if (file->doc == NULL) {
63  logMsg(c_logger_Severity_ERROR, "%s(): Can't open XML file '%s'.", __func__, filename);
64  c_free(file);
65  return NULL;
66  }
67 
68  file->name = c_strdup(filename);
69  if (file->name == NULL) {
70  logMsg(c_logger_Severity_ERROR, "%s(): Can't alloc space for filename '%s'.", __func__, filename);
71  xmlFreeDoc(file->doc);
72  c_free(file);
73  return NULL;
74  }
75 
76  return file;
77 }
78 
80  c_xmlfile_t *file = c_malloc(sizeof(*file));
81  if (file == NULL) {
82  logMsg(c_logger_Severity_ERROR, "%s(): Can't allocate space for c_xmlfile_t.", __func__);
83  return file;
84  }
85 
86  char filename[] = "memory";
87 
88  file->doc = xmlReadMemory(buffer, size, filename, NULL, XML_PARSE_NOBLANKS | XML_PARSE_PEDANTIC);
89  if (file->doc == NULL) {
90  logMsg(c_logger_Severity_ERROR, "%s(): Can't open XML buffer.", __func__);
91  c_free(file);
92  return NULL;
93  }
94 
95  file->name = c_strdup(filename);
96  if (file->name == NULL) {
97  logMsg(c_logger_Severity_ERROR, "%s(): Can't alloc space for filename '%s'.", __func__, filename);
98  xmlFreeDoc(file->doc);
99  c_free(file);
100  return NULL;
101  }
102 
103  return file;
104 }
105 
107  if (file == NULL)
108  return;
109 
110  if (file->doc != NULL)
111  xmlFreeDoc(file->doc);
112 
113  c_free(file->name);
114  c_free(file);
115 }
116 
118  return xmlDocGetRootElement(file->doc);
119 }
120 
121 c_use_decl_annotations xmlNodeSet* c_xml_getnodeset(c_xmlfile_t *file, const xmlChar *query) {
122  xmlXPathContext *context = xmlXPathNewContext(file->doc);
123  if (context == NULL) {
124  logMsg(c_logger_Severity_ERROR, "%s(): Error in xmlXPathNewContext.", __func__);
125  return NULL;
126  }
127  xmlXPathObject *result = xmlXPathEval(query, context);
128  xmlXPathFreeContext(context);
129  if (result == NULL) {
130  logMsg(c_logger_Severity_ERROR, "%s(): Error in xmlXPathEval.", __func__);
131  return NULL;
132  }
133  if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
134  xmlXPathFreeObject(result);
135  return NULL;
136  }
137 
138  // Copy the nodeset (copy done if the first argument of xmlXPathNodeSetMerge() is NULL)
139  xmlNodeSet *nodeset = xmlXPathNodeSetMerge(NULL, result->nodesetval);
140 
141  xmlXPathFreeObject(result);
142 
143  return nodeset;
144 }
145 
146 void c_xml_freenodeset(xmlNodeSet *nodeset) {
147  if (nodeset != NULL)
148  xmlXPathFreeNodeSet(nodeset);
149 }
150 
151 c_use_decl_annotations const char* c_xml_getattributevalue(const xmlNode *node, const char *attribute, bool warnEnable) {
152  xmlAttr *prop = xmlHasProp(node, (const xmlChar*)attribute);
153  if (prop != NULL)
154  return (const char *)XML_GET_CONTENT(prop->children);
155 
156  if (warnEnable)
157  logMsg(c_logger_Severity_WARNING, "%s(): Node '%s' doesn't have attribute '%s'", __func__, node->name, attribute);
158 
159  return NULL;
160 }
161 
163  xmlNodeSet *nodeset = c_xml_getnodeset(file, query);
164  xmlNode *result;
165  if (xmlXPathNodeSetGetLength(nodeset) != 1) {
166  logMsg(c_logger_Severity_ERROR, "%s(): More than one element match query '%s'", __func__, query);
167  result = NULL;
168  }
169  else {
170  result = xmlXPathNodeSetItem(nodeset, 0);
171  }
172  c_xml_freenodeset(nodeset);
173  return result;
174 }
175 
176 c_use_decl_annotations xmlNode* c_xml_getnextelementfromname(xmlNode *node, const char *nodename) {
177  while (node != NULL) {
178  if (node->type == XML_ELEMENT_NODE && xmlStrEqual(node->name, (const xmlChar*)nodename)) {
179  return node;
180  }
181  else {
182  node = xmlNextElementSibling(node);
183  }
184  }
185  return node;
186 }
187 
188 c_use_decl_annotations xmlNode* c_getfirstsiblingelementfromname(xmlNode *node, const char *nodename) {
189  if (node == NULL) {
190  logMsg(c_logger_Severity_ERROR, "%s(): Invalid argument. 'node' cannot be NULL to get first sibling from name.", __func__);
191  return NULL;
192  }
193 
194  return c_xml_getnextelementfromname(xmlNextElementSibling(node), nodename);
195 }
196 
197 c_use_decl_annotations xmlNode* c_xml_getfirstchildrenelementfromname(xmlNode *node, const char *nodename) {
198  if (node == NULL) {
199  logMsg(c_logger_Severity_ERROR, "%s(): Invalid argument. 'node' cannot be NULL to get first child from name.", __func__);
200  return NULL;
201  }
202 
203  return c_xml_getnextelementfromname(xmlFirstElementChild(node), nodename);
204 }
205 
206 // same as 'c_xml_getfirstchildrenelementfromname' but also navigates sublevels
207 c_use_decl_annotations xmlNode* c_xml_getfirstdescendantelementfromname(xmlNode *node, const char *nodename) {
208  char *str;
209  char *level;
210  const char delim[] = "/";
211 
212  if (node == NULL) {
213  logMsg(c_logger_Severity_ERROR, "%s(): Invalid argument. 'node' cannot be NULL to get first descendant from name.", __func__);
214  return NULL;
215  }
216  if (nodename == NULL) {
217  logMsg(c_logger_Severity_ERROR, "%s(): Invalid argument. 'nodename' cannot be NULL to get first descendant from name.", __func__);
218  return NULL;
219  }
220 
221  str = c_strdup(nodename);
222  if (str == NULL) {
223  logMsg(c_logger_Severity_ERROR, "%s(): Can't allocate a string.", __func__);
224  return NULL;
225  }
226 
227  level = strtok(str, delim);
228  while (level != NULL) {
229  node = c_xml_getfirstchildrenelementfromname(node, level);
230  if (node == NULL)
231  break;
232  level = strtok(NULL, delim);
233  }
234  c_free(str);
235  return node;
236 }
237 
239  if (file == NULL)
240  return NULL;
241  return file->name;
242 }
243 
244 int32_t c_xml_getcodefromnodechild(const xmlNode *node, double *code) {
245  if (node == NULL || code == NULL) {
246  logMsg(c_logger_Severity_ERROR, "%s(): Invalid argument.", __func__);
247  return c_xml_RetCode_NULL;
248  }
249 
250  xmlChar *valuestr = xmlNodeGetContent(node);
251  if (valuestr == NULL)
252  return c_xml_RetCode_NotFound;
253 
254  char *end_ptr;
255  double res = strtod((char*)valuestr, &end_ptr);
256 
257  // If no conversion can be performed, strtod returns 0 and end_ptr is set to valuestr.
258  if (end_ptr == (char*)valuestr) {
259  logMsg(c_logger_Severity_ERROR, "%s(): Can't parse a value from string '%s'.", __func__, valuestr);
260  xmlFree(valuestr);
262  }
263 
264  *code = res;
265 
266  xmlFree(valuestr);
267  return c_xml_RetCode_Success;
268 }
269 
270 int32_t c_xml_getcodefromnode(const xmlNode *node, double *code) {
271  if (node == NULL || code == NULL) {
272  logMsg(c_logger_Severity_ERROR, "%s(): Invalid argument.", __func__);
273  return c_xml_RetCode_NULL;
274  }
275 
276  const char *valuestr = c_xml_getattributevalue(node, "code", TRUE);
277  if (valuestr == NULL)
278  return c_xml_RetCode_NotFound;
279 
280  char *end_ptr;
281  double res = strtod(valuestr, &end_ptr);
282 
283  // If no conversion can be performed, strtod returns 0 and end_ptr is set to valuestr.
284  if (end_ptr == valuestr) {
285  logMsg(c_logger_Severity_ERROR, "%s(): Can't parse a value from string '%s'.", __func__, valuestr);
287  }
288 
289  *code = res;
290  return c_xml_RetCode_Success;
291 }
292 
293 int32_t c_xml_getcodefromcodename(const c_xmlfile_t *file, const char *__restrict category, const char *__restrict codename, double *code) {
294  if (codename == NULL) {
295  logMsg(c_logger_Severity_ERROR, "%s(): Invalid argument. 'codename' cannot be NULL.", __func__);
296  return c_xml_RetCode_NULL;
297  }
298 
299  xmlNode *node = c_xml_getrootnode(file);
300  // don't control here if ROOT is 'Definitions', we assume it.
301  node = c_xml_getfirstdescendantelementfromname(node, category);
302  node = c_xml_getfirstchildrenelementfromname(node, codename);
303 
304  if (node == NULL) {
305  logMsg(c_logger_Severity_ERROR, "%s(): Cannot find a node named '%s/%s' in definition's XML.", __func__, category, codename);
306  return c_xml_RetCode_NotFound;
307  }
308 
309  int32_t ret = c_xml_getcodefromnodechild(node, code);
310  if (ret != c_xml_RetCode_Success) {
311  logMsg(c_logger_Severity_ERROR, "%s(): Can't get code of parameter '%s'", __func__, codename);
312  return ret;
313  }
314 
315  return c_xml_RetCode_Success;
316 }
317 
318 // NOTE: we can return the pointer here since it is a member of structure 'xmlNode', which lives inside
319 // definitions document. So, since definitions documents lives, the pointer to the attribute name lives too.
320 c_use_decl_annotations const char * c_xml_getattributevaluefromcodename(const c_xmlfile_t *file, const char *__restrict category, const char *__restrict codename, const char *__restrict attrname) {
321  const char *valuestr;
322  xmlNode *node = NULL;
323 
324  if (codename == NULL) {
325  logMsg(c_logger_Severity_ERROR, "%s(): Invalid argument. 'codename' cannot be NULL.", __func__);
326  return NULL;
327  }
328 
329  node = xmlDocGetRootElement(file->doc);
330  // don't control here if ROOT is 'Definitions', we assume it.
331  node = c_xml_getfirstdescendantelementfromname(node, category);
332  node = c_xml_getfirstchildrenelementfromname(node, codename);
333 
334  if (node == NULL) {
335  logMsg(c_logger_Severity_ERROR, "%s(): Cannot find a node named '%s/%s' in definition's XML.", __func__, category, codename);
336  return NULL;
337  }
338 
339  valuestr = c_xml_getattributevalue(node, attrname, TRUE);
340  if (valuestr == NULL) {
341  logMsg(c_logger_Severity_ERROR, "%s(): Node named '%s/%s' doesn't have attribute '%s' in definition's XML.", __func__, category, codename, attrname);
342  return NULL;
343  }
344 
345  return valuestr;
346 }
347 
348 //void * c_xml_getfunctionpointerfromcodename(const char *codename) {
350  xmlNode *node;
351  const char *libname;
352  int32_t ret;
353  c_fcnhandle_t fcnHandle;
354 
355  node = xmlDocGetRootElement(file->doc);
356  // don't control here if ROOT is 'Definitions', we assume it.
357  if (node != NULL)
358  node = c_xml_getfirstchildrenelementfromname(node, "Functions");
359  if (node != NULL)
360  node = c_xml_getfirstchildrenelementfromname(node, codename);
361 
362  if (node == NULL) {
363  logMsg(c_logger_Severity_ERROR, "%s(): Cannot find a node named '%s' in definition's XML.", __func__, codename);
364  return NULL;
365  }
366 
367  libname = c_xml_getattributevalue(node, "libname", TRUE);
368  if (libname == NULL) {
369  logMsg(c_logger_Severity_ERROR, "%s(): Missing mandatory attribute 'libname' for function '%s'", __func__, codename);
370  return NULL;
371  }
372 
373  if (libHandle == NULL) {
374  if ((ret = c_dlload(libname, &libHandle)) != MP_code_Success) {
375  logMsg(c_logger_Severity_ERROR, "%s(): Library '%s' load failed with DynUtils error %"PRIi32".", __func__, libname, ret);
376  return NULL;
377  }
378  }
379  if ((ret = c_dfload(libHandle, codename, &fcnHandle)) != MP_code_Success) {
380  logMsg(c_logger_Severity_ERROR, "%s(): Function '%s' load from library '%s' failed with DynUtils error %"PRIi32".", __func__, codename, libname, ret);
381  return NULL;
382  }
383 
384  //return (void*)fcnHandle;
385  return fcnHandle;
386 }
387 
389  if (libHandle == NULL)
390  return;
391 
392  int32_t ret = c_dlclose(libHandle);
393  if (ret != MP_code_Success) {
394  logMsg(c_logger_Severity_ERROR, "%s(): Can't close library", __func__);
395  return;
396  }
397 
398  libHandle = NULL;
399 }
400 
401 int c_xml_snprintf(xmlChar *buf, int len, const char *msg, ...) {
402  va_list args;
403  va_start(args, msg);
404 
405 #if LIBXML_VERSION < 20904
406  // third argument is (const char *) only since version 2.9.4
407  int ret = xmlStrVPrintf(buf, len, (const xmlChar *)msg, args);
408 #else
409  int ret = xmlStrVPrintf(buf, len, msg, args);
410 #endif
411 
412  va_end(args);
413  return ret;
414 }
415 
416 int c_xml_strlen(const xmlChar *str) {
417  return xmlStrlen(str);
418 }
419 
420 const char * c_xml_parserversion(void) {
421  return xmlParserVersion;
422 }
423 
424 #endif // _NOUSEXML
int c_xml_snprintf(xmlChar *buf, int len, const char *msg,...)
int32_t c_dfload(c_libhandle_t hLib, const char *fcnName, c_fcnhandle_t *funcPtr)
const char * c_xml_getattributevalue(const xmlNode *node, const char *attribute, bool warnEnable)
int32_t c_dlload(const char *libName, c_libhandle_t *libHandle)
void c_free(void *ptr)
void c_xml_freefile(c_xmlfile_t *file)
xmlNode * c_xml_getfirstdescendantelementfromname(xmlNode *node, const char *nodename)
A wrapper to xmlDoc.
int32_t c_dlclose(c_libhandle_t hLib)
void * c_fcnhandle_t
void c_xml_freenodeset(xmlNodeSet *nodeset)
Tools to parse an XML file using libxml.
char * name
the file path (or memory if a memory document passed by c_xml_newfile_from_memory()) ...
int c_xml_strlen(const xmlChar *str)
static c_libhandle_t libHandle
Definition: CAENXMLParser.c:50
int32_t c_xml_getcodefromnode(const xmlNode *node, double *code)
c_xmlfile_t * c_xml_newfile_from_memory(const char *buffer, int size)
Definition: CAENXMLParser.c:79
#define TRUE
void * c_malloc(size_t size)
xmlNode * c_xml_getnextelementfromname(xmlNode *node, const char *nodename)
Generic wrappers to platform-dependent functions.
c_fcnhandle_t c_xml_getfunctionpointerfromcodename(const c_xmlfile_t *file, const char *codename)
NULL pointer passed as argument.
xmlNode * c_xml_getfirstchildrenelementfromname(xmlNode *node, const char *nodename)
#define INIT_C_LOGGER(fname, mname)
Definition: CAENLogger.h:139
const char * c_xml_parserversion(void)
int32_t c_xml_getcodefromcodename(const c_xmlfile_t *file, const char *__restrict category, const char *__restrict codename, double *code)
xmlDoc * doc
the xmlDoc
Logger implemented in C.
#define logMsg(s,...)
Definition: CAENLogger.h:157
const char * c_xml_getattributevaluefromcodename(const c_xmlfile_t *file, const char *__restrict category, const char *__restrict codename, const char *__restrict attrname)
#define c_use_decl_annotations
Definition: CAENUtility.h:278
xmlNode * c_xml_getrootnode(const c_xmlfile_t *file)
void c_xml_parsercleanup(void)
xmlNode * c_getfirstsiblingelementfromname(xmlNode *node, const char *nodename)
xmlNode * c_xml_getfirstnodefromxpathquery(c_xmlfile_t *file, const xmlChar *query)
char * c_strdup(const char *str)
Wrapper to strdup(str), with check for NULL arguments. To be freed with c_free(). ...
void * c_libhandle_t
xmlNodeSet * c_xml_getnodeset(c_xmlfile_t *file, const xmlChar *query)
c_xmlfile_t * c_xml_newfile(const char *filename)
Definition: CAENXMLParser.c:54
int32_t c_xml_getcodefromnodechild(const xmlNode *node, double *code)
const char * c_xml_getpath(const c_xmlfile_t *file)