GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/utils/xml/libxml.cpp Lines: 110 135 81.5 %
Date: 2018-06-18 21:15:20 Branches: 50 132 37.9 %

Line Branch Exec Source
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-2018  The ManaPlus Developers
6
 *
7
 *  This file is part of The ManaPlus Client.
8
 *
9
 *  This program is free software; you can redistribute it and/or modify
10
 *  it under the terms of the GNU General Public License as published by
11
 *  the Free Software Foundation; either version 2 of the License, or
12
 *  any later version.
13
 *
14
 *  This program is distributed in the hope that it will be useful,
15
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 *  GNU General Public License for more details.
18
 *
19
 *  You should have received a copy of the GNU General Public License
20
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
 */
22
23
#ifdef ENABLE_LIBXML
24
25
#include "utils/xml/libxml.h"
26
27
#include "fs/virtfs/fs.h"
28
29
#include "utils/cast.h"
30
#include "utils/checkutils.h"
31
#include "utils/fuzzer.h"
32
#include "utils/stringutils.h"
33
34
#include "utils/translation/podict.h"
35
36
#include <fstream>
37
38
#include "debug.h"
39
40
namespace
41
{
42
    bool valid = false;
43
}  // namespace
44
45
static void xmlErrorLogger(void *ctx A_UNUSED, const char *msg A_UNUSED, ...)
46
#ifdef __GNUC__
47
#ifdef __OpenBSD__
48
    __attribute__((__format__(printf, 2, 3)))
49
#else  // __OpenBSD__
50
    __attribute__((__format__(gnu_printf, 2, 3)))
51
#endif  // __OpenBSD__
52
#endif  // __GNUC__
53
;
54
55
12
static void xmlErrorLogger(void *ctx A_UNUSED, const char *msg, ...)
56
{
57
12
    if (msg == nullptr)
58
        return;
59
60
12
    size_t size = 1024;
61
12
    const size_t msgSize = strlen(msg);
62
12
    if (msgSize * 3 > size)
63
        size = msgSize * 3;
64
65
12
    char* buf = new char[size + 1];
66
    va_list ap;
67
68
    // Use a temporary buffer to fill in the variables
69
12
    va_start(ap, msg);
70
12
    vsnprintf(buf, size, msg, ap);
71
12
    buf[size] = 0;
72
12
    va_end(ap);
73
74
12
    if (logger != nullptr)
75
12
        logger->log_r("%s", buf);
76
    else
77
        puts(buf);
78
79
    // Delete temporary buffer
80
12
    delete [] buf;
81
12
    valid = false;
82
}
83
84
namespace XML
85
{
86
5798
    Document::Document(const std::string &filename,
87
                       const UseVirtFs useResman,
88
5798
                       const SkipError skipError) :
89
        Resource(),
90
        mDoc(nullptr),
91
11596
        mIsValid(false)
92
    {
93
#ifdef USE_FUZZER
94
        if (Fuzzer::conditionTerminate(filename.c_str()))
95
            return;
96
#endif  // USE_FUZZER
97
98
        BLOCK_START("XML::Document::Document")
99
5798
        int size = 0;
100
5798
        char *data = nullptr;
101
5798
        valid = true;
102
5798
        if (useResman == UseVirtFs_true)
103
        {
104
10068
            data = const_cast<char*>(VirtFs::loadFile(
105
                filename,
106
                size));
107
        }
108
        else
109
        {
110
1528
            std::ifstream file;
111
764
            file.open(filename.c_str(), std::ios::in);
112
113
764
            if (file.is_open())
114
            {
115
                // Get length of file
116
764
                file.seekg(0, std::ios::end);
117
764
                size = CAST_S32(file.tellg());
118
764
                if (size < 0)
119
                {
120
                    reportAlways("Error loading XML file %s",
121
                        filename.c_str());
122
                }
123
                else
124
                {
125
764
                    file.seekg(0, std::ios::beg);
126
764
                    data = new char[size];
127
764
                    file.read(data, size);
128
                }
129
764
                file.close();
130
            }
131
            else if (skipError == SkipError_false)
132
            {
133
                reportAlways("Error loading XML file %s",
134
                    filename.c_str());
135
            }
136
        }
137
138
5798
        if (data != nullptr)
139
        {
140
5636
            mDoc = xmlParseMemory(data, size);
141
5636
            delete [] data;
142
143
5636
            if (mDoc == nullptr)
144
            {
145
                reportAlways("Error parsing XML file %s", filename.c_str());
146
            }
147
        }
148
162
        else if (skipError == SkipError_false)
149
        {
150
            reportAlways("Error loading XML file %s", filename.c_str());
151
        }
152
5798
        mIsValid = valid;
153
        BLOCK_END("XML::Document::Document")
154
5798
    }
155
156
10
    Document::Document(const char *const data, const int size) :
157

10
        mDoc(data != nullptr ? xmlParseMemory(data, size) : nullptr),
158
30
        mIsValid(true)
159
    {
160
10
    }
161
162
16272
    Document::~Document()
163
    {
164
5808
        if (mDoc != nullptr)
165
5646
            xmlFreeDoc(mDoc);
166
10464
    }
167
168
6684
    XmlNodePtr Document::rootNode()
169
    {
170
6684
        return mDoc != nullptr ? xmlDocGetRootElement(mDoc) : nullptr;
171
    }
172
173
94226
    int getProperty(XmlNodeConstPtr node,
174
                    const char *const name,
175
                    int def)
176
    {
177
94226
        int &ret = def;
178
179
94226
        xmlChar *const prop = XmlGetProp(node, name);
180
94226
        if (prop != nullptr)
181
        {
182
66300
            ret = atoi(reinterpret_cast<const char*>(prop));
183
66300
            xmlFree(prop);
184
        }
185
186
94226
        return ret;
187
    }
188
189
18442
    int getIntProperty(XmlNodeConstPtr node,
190
                       const char *const name,
191
                       int def,
192
                       const int min,
193
                       const int max)
194
    {
195
18442
        int &ret = def;
196
197
18442
        xmlChar *const prop = XmlGetProp(node, name);
198
18442
        if (prop != nullptr)
199
        {
200
2632
            ret = atoi(reinterpret_cast<char*>(prop));
201
2632
            xmlFree(prop);
202
        }
203
18442
        if (ret < min)
204
            ret = min;
205
18442
        else if (ret > max)
206
            ret = max;
207
18442
        return ret;
208
    }
209
210
220
    float getFloatProperty(XmlNodeConstPtr node,
211
                           const char *const name,
212
                           float def)
213
    {
214
220
        float &ret = def;
215
216
220
        xmlChar *const prop = XmlGetProp(node, name);
217
220
        if (prop != nullptr)
218
        {
219
4
            ret = static_cast<float>(atof(reinterpret_cast<char*>(prop)));
220
4
            xmlFree(prop);
221
        }
222
223
220
        return ret;
224
    }
225
226
    double getDoubleProperty(XmlNodeConstPtr node,
227
                             const char *const name,
228
                             double def)
229
    {
230
        double &ret = def;
231
232
        xmlChar *const prop = XmlGetProp(node, name);
233
        if (prop != nullptr)
234
        {
235
            ret = atof(reinterpret_cast<char*>(prop));
236
            xmlFree(prop);
237
        }
238
239
        return ret;
240
    }
241
242
985318
    std::string getProperty(XmlNodeConstPtr node,
243
                            const char *const name,
244
                            const std::string &def)
245
    {
246
985318
        xmlChar *const prop = XmlGetProp(node, name);
247
985318
        if (prop != nullptr)
248
        {
249
3092320
            std::string val = reinterpret_cast<char*>(prop);
250
773080
            xmlFree(prop);
251
773080
            return val;
252
        }
253
254
        return def;
255
    }
256
257
378
    std::string langProperty(XmlNodeConstPtr node,
258
                             const char *const name,
259
                             const std::string &def)
260
    {
261
756
        std::string str = getProperty(node, name, def);
262
378
        if (translator == nullptr)
263
            return str;
264
265
372
        return translator->getStr(str);
266
    }
267
268
90
    bool getBoolProperty(XmlNodeConstPtr node,
269
                         const char *const name,
270
                         const bool def)
271
    {
272
90
        xmlChar *const prop = XmlGetProp(node, name);
273
274
90
        if (XmlStrEqual(prop, "true"))
275
        {
276
4
            XmlFree(prop);
277
4
            return true;
278
        }
279
86
        if (XmlStrEqual(prop, "false"))
280
        {
281
4
            XmlFree(prop);
282
4
            return false;
283
        }
284
82
        XmlFree(prop);
285
82
        return def;
286
    }
287
288
50
    XmlNodePtr findFirstChildByName(XmlNodeConstPtrConst parent,
289
                                    const char *const name)
290
    {
291
50
        if (parent == nullptr)
292
            return nullptr;
293
458
        for_each_xml_child_node(child, parent)
294
        {
295
458
            if (xmlNameEqual(child, name))
296
                return child;
297
        }
298
299
        return nullptr;
300
    }
301
302
    // Initialize libxml2 and check for potential ABI mismatches between
303
    // compiled version and the shared library actually used.
304
366
    void initXML()
305
    {
306
366
        xmlInitParser();
307
366
        LIBXML_TEST_VERSION;
308
309
        // Suppress libxml2 error messages
310
366
        xmlSetGenericErrorFunc(nullptr, &xmlErrorLogger);
311
366
    }
312
313
    // Shutdown libxml
314
428
    void cleanupXML()
315
    {
316
428
        xmlCleanupParser();
317
428
    }
318
319
4
    bool Document::validateXml(const std::string &fileName)
320
    {
321
4
        const xmlDocPtr doc = xmlReadFile(fileName.c_str(),
322
4
            nullptr, XML_PARSE_PEDANTIC);
323
4
        const bool valid1(doc != nullptr);
324
4
        xmlFreeDoc(doc);
325
4
        if (!valid1)
326
            return false;
327
328
        std::ifstream file;
329
        file.open(fileName.c_str(), std::ios::in);
330
        if (!file.is_open())
331
        {
332
            file.close();
333
            return false;
334
        }
335
        char line[101];
336
        if (!file.getline(line, 100))
337
            return false;
338
        file.close();
339
340
        const std::string str = line;
341
        if (!strStartWith(str, "<?xml "))
342
            return false;
343
344
        return true;
345
    }
346
}  // namespace XML
347
348
#endif  // ENABLE_LIBXML