GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/utils/xml/libxml.cpp Lines: 110 135 81.5 %
Date: 2017-11-29 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-2017  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 "debug.h"
37
38
namespace
39
{
40
    bool valid = false;
41
}  // namespace
42
43
static void xmlErrorLogger(void *ctx A_UNUSED, const char *msg A_UNUSED, ...)
44
#ifdef __GNUC__
45
#ifdef __OpenBSD__
46
    __attribute__((__format__(printf, 2, 3)))
47
#else  // __OpenBSD__
48
    __attribute__((__format__(gnu_printf, 2, 3)))
49
#endif  // __OpenBSD__
50
#endif  // __GNUC__
51
;
52
53
12
static void xmlErrorLogger(void *ctx A_UNUSED, const char *msg, ...)
54
{
55
12
    if (msg == nullptr)
56
        return;
57
58
12
    size_t size = 1024;
59
12
    const size_t msgSize = strlen(msg);
60
12
    if (msgSize * 3 > size)
61
        size = msgSize * 3;
62
63
12
    char* buf = new char[size + 1];
64
    va_list ap;
65
66
    // Use a temporary buffer to fill in the variables
67
12
    va_start(ap, msg);
68
12
    vsnprintf(buf, size, msg, ap);
69
12
    buf[size] = 0;
70
12
    va_end(ap);
71
72
12
    if (logger != nullptr)
73
12
        logger->log_r("%s", buf);
74
    else
75
        puts(buf);
76
77
    // Delete temporary buffer
78
12
    delete [] buf;
79
12
    valid = false;
80
}
81
82
namespace XML
83
{
84
5366
    Document::Document(const std::string &filename,
85
                       const UseVirtFs useResman,
86
5366
                       const SkipError skipError) :
87
        Resource(),
88
        mDoc(nullptr),
89
10732
        mIsValid(false)
90
    {
91
#ifdef USE_FUZZER
92
        if (Fuzzer::conditionTerminate(filename.c_str()))
93
            return;
94
#endif  // USE_FUZZER
95
96
        BLOCK_START("XML::Document::Document")
97
5366
        int size = 0;
98
5366
        char *data = nullptr;
99
5366
        valid = true;
100
5366
        if (useResman == UseVirtFs_true)
101
        {
102
9296
            data = const_cast<char*>(VirtFs::loadFile(
103
                filename,
104
                size));
105
        }
106
        else
107
        {
108
1436
            std::ifstream file;
109
718
            file.open(filename.c_str(), std::ios::in);
110
111
718
            if (file.is_open())
112
            {
113
                // Get length of file
114
718
                file.seekg(0, std::ios::end);
115
718
                size = CAST_S32(file.tellg());
116
718
                if (size < 0)
117
                {
118
                    reportAlways("Error loading XML file %s",
119
                        filename.c_str());
120
                }
121
                else
122
                {
123
718
                    file.seekg(0, std::ios::beg);
124
718
                    data = new char[size];
125
718
                    file.read(data, size);
126
                }
127
718
                file.close();
128
            }
129
            else if (skipError == SkipError_false)
130
            {
131
                reportAlways("Error loading XML file %s",
132
                    filename.c_str());
133
            }
134
        }
135
136
5366
        if (data != nullptr)
137
        {
138
5206
            mDoc = xmlParseMemory(data, size);
139
5206
            delete [] data;
140
141
5206
            if (mDoc == nullptr)
142
            {
143
                reportAlways("Error parsing XML file %s", filename.c_str());
144
            }
145
        }
146
160
        else if (skipError == SkipError_false)
147
        {
148
            reportAlways("Error loading XML file %s", filename.c_str());
149
        }
150
5366
        mIsValid = valid;
151
        BLOCK_END("XML::Document::Document")
152
5366
    }
153
154
10
    Document::Document(const char *const data, const int size) :
155

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