ManaPlus
pugixml.cpp
Go to the documentation of this file.
1 /*
2  * The ManaPlus Client
3  * Copyright (C) 2004-2009 The Mana World Development Team
4  * Copyright (C) 2009-2010 The Mana Developers
5  * Copyright (C) 2011-2019 The ManaPlus Developers
6  * Copyright (C) 2019-2021 Andrei Karas
7  *
8  * This file is part of The ManaPlus Client.
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program. If not, see <http://www.gnu.org/licenses/>.
22  */
23 
24 #ifdef ENABLE_PUGIXML
25 
26 #include "utils/xml/pugixml.h"
27 
28 #include "fs/virtfs/fs.h"
29 
30 #include "utils/cast.h"
31 #include "utils/checkutils.h"
32 #include "utils/delete2.h"
33 #include "utils/fuzzer.h"
34 #include "utils/stringutils.h"
35 
37 
38 #include <fstream>
39 
40 #include "debug.h"
41 
42 namespace
43 {
44  bool valid = false;
45 } // namespace
46 
47 namespace XML
48 {
49  static void showErrorStatus(pugi::xml_parse_result &result)
50  {
51 /*
52  switch (result.status)
53  {
54  case pugi::status_ok:
55  break;
56  case pugi::status_file_not_found:
57  logger->log("xml error: %s", result.description());
58  break;
59  }
60 */
61  logger->log("xml error: %s", result.description());
62  }
63 
64  Document::Document(const std::string &filename,
65  const UseVirtFs useResman,
66  const SkipError skipError) :
67  Resource(),
68  mDoc(),
69  mData(nullptr),
70  mIsValid(false)
71  {
72 #ifdef USE_FUZZER
73  if (Fuzzer::conditionTerminate(filename.c_str()))
74  return;
75 #endif // USE_FUZZER
76 
77  BLOCK_START("XML::Document::Document")
78  int size = 0;
79  char *data = nullptr;
80  valid = true;
81  if (useResman == UseVirtFs_true)
82  {
83  data = const_cast<char*>(VirtFs::loadFile(
84  filename.c_str(),
85  size));
86  }
87  else
88  {
89  std::ifstream file;
90  file.open(filename.c_str(), std::ios::in);
91 
92  if (file.is_open())
93  {
94  // Get length of file
95  file.seekg(0, std::ios::end);
96  size = CAST_S32(file.tellg());
97  if (size < 0)
98  {
99  reportAlways("Error loading XML file %s",
100  filename.c_str())
101  }
102  else
103  {
104  file.seekg(0, std::ios::beg);
105  data = new char[size];
106  file.read(data, size);
107  }
108  file.close();
109  }
110  else if (skipError == SkipError_false)
111  {
112  reportAlways("Error loading XML file %s",
113  filename.c_str())
114  }
115  }
116 
117  if (data)
118  {
119  // +++ use other pugi::parse_* flags
120  pugi::xml_parse_result result = mDoc.load_buffer_inplace(data,
121  size,
122  pugi::parse_default,
123  pugi::encoding_utf8);
124  if (result.status != pugi::status_ok)
125  {
126  showErrorStatus(result);
127  delete [] data;
128  }
129  else
130  {
131  mData = data;
132  }
133 
134 // if (!mDoc)
135 // logger->log("Error parsing XML file %s", filename.c_str());
136  }
137  else if (skipError == SkipError_false)
138  {
139  reportAlways("Error loading %s", filename.c_str())
140  }
141  mIsValid = valid;
142  BLOCK_END("XML::Document::Document")
143  }
144 
145  Document::Document(const char *const data, const int size) :
146  mDoc(),
147  mData(nullptr),
148  mIsValid(true)
149  {
150  if (!data)
151  return;
152 
153  char *buf = new char[size + 1];
154  strncpy(buf, data, size);
155  buf[size] = 0;
156  pugi::xml_parse_result result = mDoc.load_buffer_inplace(buf,
157  size,
158  pugi::parse_default,
159  pugi::encoding_utf8);
160  if (result.status != pugi::status_ok)
161  {
162  showErrorStatus(result);
163  delete [] buf;
164  }
165  else
166  {
167  mData = buf;
168  }
169  }
170 
171  Document::~Document()
172  {
173  delete [] mData;
174  mData = nullptr;
175  }
176 
177  XmlNodePtr Document::rootNode()
178  {
179  return mDoc.first_child();
180  }
181 
182  int getProperty(XmlNodeConstPtr node,
183  const char *const name,
184  int def)
185  {
186  int &ret = def;
187 
188  if (!node)
189  return ret;
190  const pugi::xml_attribute &attr = node.attribute(name);
191  if (!attr.empty())
192  ret = atoi(attr.value());
193 
194  return ret;
195  }
196 
197  int getIntProperty(XmlNodeConstPtr node,
198  const char *const name,
199  int def,
200  const int min,
201  const int max)
202  {
203  int &ret = def;
204 
205  if (!node)
206  return ret;
207  const pugi::xml_attribute &attr = node.attribute(name);
208  if (!attr.empty())
209  ret = atoi(attr.value());
210 
211  if (ret < min)
212  ret = min;
213  else if (ret > max)
214  ret = max;
215  return ret;
216  }
217 
218  float getFloatProperty(XmlNodeConstPtr node,
219  const char *const name,
220  float def)
221  {
222  float &ret = def;
223 
224  if (!node)
225  return ret;
226  const pugi::xml_attribute &attr = node.attribute(name);
227  if (!attr.empty())
228  ret = atof(attr.value());
229 
230  return ret;
231  }
232 
233  double getDoubleProperty(XmlNodeConstPtr node,
234  const char *const name,
235  double def)
236  {
237  double &ret = def;
238 
239  if (!node)
240  return ret;
241  const pugi::xml_attribute &attr = node.attribute(name);
242  if (!attr.empty())
243  ret = atof(attr.value());
244 
245  return ret;
246  }
247 
248  std::string getProperty(XmlNodeConstPtr node,
249  const char *const name,
250  const std::string &def)
251  {
252  if (!node)
253  return def;
254  const pugi::xml_attribute &attr = node.attribute(name);
255  if (!attr.empty())
256  return attr.value();
257 
258  return def;
259  }
260 
261  std::string langProperty(XmlNodeConstPtr node,
262  const char *const name,
263  const std::string &def)
264  {
265  std::string str = getProperty(node, name, def);
266  if (!translator)
267  return str;
268 
269  return translator->getStr(str);
270  }
271 
272  bool getBoolProperty(XmlNodeConstPtr node,
273  const char *const name,
274  const bool def)
275  {
276  if (!node)
277  return def;
278  const pugi::xml_attribute &attr = node.attribute(name);
279  if (!attr.empty())
280  {
281  std::string val = attr.value();
282  if (val == "true")
283  return true;
284  if (val == "false")
285  return false;
286  }
287 
288  return def;
289  }
290 
291  XmlNodePtr findFirstChildByName(XmlNodeConstPtrConst parent,
292  const char *const name)
293  {
294  if (!parent || !name)
295  return pugi::xml_node();
296  for_each_xml_child_node(child, parent)
297  {
298  if (!strcmp(child.name(), name))
299  return child;
300  }
301 
302  return pugi::xml_node();
303  }
304 
305  // Initialize libxml2 and check for potential ABI mismatches between
306  // compiled version and the shared library actually used.
307  void initXML()
308  {
309 // xmlInitParser();
310 // LIBXML_TEST_VERSION;
311 
312  // Suppress libxml2 error messages
313 // xmlSetGenericErrorFunc(nullptr, &xmlErrorLogger);
314  }
315 
316  // Shutdown libxml
317  void cleanupXML()
318  {
319 // xmlCleanupParser();
320  }
321 
322  bool Document::validateXml(const std::string &fileName)
323  {
324  pugi::xml_document doc;
325  pugi::xml_parse_result result = doc.load_file(fileName.c_str(),
326  pugi::parse_default,
327  pugi::encoding_utf8);
328 
329  if (result.status != pugi::status_ok)
330  {
331  showErrorStatus(result);
332  return false;
333  }
334 
335  std::ifstream file;
336  file.open(fileName.c_str(), std::ios::in);
337  if (!file.is_open())
338  {
339  file.close();
340  return false;
341  }
342  char line[101];
343  if (!file.getline(line, 100))
344  return false;
345  file.close();
346 
347  const std::string str = line;
348  if (!strStartWith(str, "<?xml "))
349  return false;
350 
351  return true;
352  }
353 } // namespace XML
354 
355 #endif // ENABLE_PUGIXML
#define CAST_S32
Definition: cast.h:30
#define reportAlways(...)
Definition: checkutils.h:253
void log(const char *const log_text,...)
Definition: logger.cpp:269
const std::string getStr(const std::string &str)
Definition: podict.cpp:45
Document(const std::string &filename, const UseVirtFs useResman, const SkipError skipError)
Definition: libxml.cpp:87
if(!vert) return
#define for_each_xml_child_node(var, parent)
Definition: libxml.h:161
#define nullptr
Definition: localconsts.h:45
Logger * logger
Definition: logger.cpp:89
uint32_t data
int size()
Definition: emotedb.cpp:306
PlayerInfoBackend mData
Definition: playerinfo.cpp:56
const char * loadFile(std::string filename, int &fileSize)
Definition: fs.cpp:859
Definition: libxml.cpp:86
void initXML()
Definition: libxml.cpp:305
std::string langProperty(const xmlNodePtr node, const char *const name, const std::string &def)
Definition: libxml.cpp:258
float getFloatProperty(const xmlNodePtr node, const char *const name, float def)
Definition: libxml.cpp:211
bool getBoolProperty(const xmlNodePtr node, const char *const name, const bool def)
Definition: libxml.cpp:269
int getProperty(const xmlNodePtr node, const char *const name, int def)
Definition: libxml.cpp:174
int getIntProperty(const xmlNodePtr node, const char *const name, int def, const int min, const int max)
Definition: libxml.cpp:190
void cleanupXML()
Definition: libxml.cpp:315
xmlNodePtr findFirstChildByName(const xmlNode *const parent, const char *const name)
Definition: libxml.cpp:289
double getDoubleProperty(const xmlNodePtr node, const char *const name, double def)
Definition: libxml.cpp:227
#define BLOCK_END(name)
Definition: perfomance.h:80
#define BLOCK_START(name)
Definition: perfomance.h:79
PoDict * translator
Definition: podict.cpp:28
const bool SkipError_false
Definition: skiperror.h:30
bool SkipError
Definition: skiperror.h:30
bool strStartWith(const std::string &str1, const std::string &str2)
std::string fileName
Definition: testmain.cpp:39
bool UseVirtFs
Definition: usevirtfs.h:30
const bool UseVirtFs_true
Definition: usevirtfs.h:30