VirtualBox

source: vbox/trunk/src/VBox/Main/xml/xml.cpp@ 14922

Last change on this file since 14922 was 14922, checked in by vboxsync, 16 years ago

Main: more XML reader implemenation.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.0 KB
Line 
1/** @file
2 * VirtualBox XML Manipulation API.
3 */
4
5/*
6 * Copyright (C) 2007-2008 Sun Microsystems, Inc.
7 *
8 * This file is part of VirtualBox Open Source Edition (OSE), as
9 * available from http://www.virtualbox.org. This file is free software;
10 * you can redistribute it and/or modify it under the terms of the GNU
11 * General Public License (GPL) as published by the Free Software
12 * Foundation, in version 2 as it comes in the "COPYING" file of the
13 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
14 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
15 *
16 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
17 * Clara, CA 95054 USA or visit http://www.sun.com if you need
18 * additional information or have any questions.
19 */
20
21#include "Logging.h"
22
23#include <iprt/err.h>
24#include <iprt/file.h>
25#include <iprt/lock.h>
26#include <iprt/string.h>
27
28#include <libxml/tree.h>
29#include <libxml/parser.h>
30#include <libxml/globals.h>
31#include <libxml/xmlIO.h>
32#include <libxml/xmlsave.h>
33#include <libxml/uri.h>
34
35#include <libxml/xmlschemas.h>
36
37#include <string>
38
39#include "VBox/xml.h"
40
41
42/**
43 * Global module initialization structure.
44 *
45 * The constructor and destructor of this structure are used to perform global
46 * module initiaizaton and cleanup. Thee must be only one global variable of
47 * this structure.
48 */
49static
50class Global
51{
52public:
53
54 Global()
55 {
56 /* Check the parser version. The docs say it will kill the app if
57 * there is a serious version mismatch, but I couldn't find it in the
58 * source code (it only prints the error/warning message to the console) so
59 * let's leave it as is for informational purposes. */
60 LIBXML_TEST_VERSION
61
62 /* Init libxml */
63 xmlInitParser();
64
65 /* Save the default entity resolver before someone has replaced it */
66 xml.defaultEntityLoader = xmlGetExternalEntityLoader();
67 }
68
69 ~Global()
70 {
71 /* Shutdown libxml */
72 xmlCleanupParser();
73 }
74
75 struct
76 {
77 xmlExternalEntityLoader defaultEntityLoader;
78
79 /** Used to provide some thread safety missing in libxml2 (see e.g.
80 * XmlTreeBackend::read()) */
81 RTLockMtx lock;
82 }
83 xml;
84}
85gGlobal;
86
87
88
89namespace xml
90{
91
92//////////////////////////////////////////////////////////////////////////////
93// Exceptions
94//////////////////////////////////////////////////////////////////////////////
95
96LogicError::LogicError(RT_SRC_POS_DECL)
97 : Error(NULL)
98{
99 char *msg = NULL;
100 RTStrAPrintf(&msg, "In '%s', '%s' at #%d",
101 pszFunction, pszFile, iLine);
102 setWhat(msg);
103 RTStrFree(msg);
104}
105
106XmlError::XmlError(xmlErrorPtr aErr)
107{
108 if (!aErr)
109 throw EInvalidArg (RT_SRC_POS);
110
111 char *msg = Format (aErr);
112 setWhat (msg);
113 RTStrFree (msg);
114}
115
116/**
117 * Composes a single message for the given error. The caller must free the
118 * returned string using RTStrFree() when no more necessary.
119 */
120// static
121char *XmlError::Format(xmlErrorPtr aErr)
122{
123 const char *msg = aErr->message ? aErr->message : "<none>";
124 size_t msgLen = strlen (msg);
125 /* strip spaces, trailing EOLs and dot-like char */
126 while (msgLen && strchr (" \n.?!", msg [msgLen - 1]))
127 -- msgLen;
128
129 char *finalMsg = NULL;
130 RTStrAPrintf (&finalMsg, "%.*s.\nLocation: '%s', line %d (%d), column %d",
131 msgLen, msg, aErr->file, aErr->line, aErr->int1, aErr->int2);
132
133 return finalMsg;
134}
135
136EIPRTFailure::EIPRTFailure(int aRC)
137 : RuntimeError(NULL),
138 mRC(aRC)
139{
140 char *newMsg = NULL;
141 RTStrAPrintf(&newMsg, "Runtime error: %d (%s)", aRC, RTErrGetShort(aRC));
142 setWhat(newMsg);
143 RTStrFree(newMsg);
144}
145
146//////////////////////////////////////////////////////////////////////////////
147// File Class
148//////////////////////////////////////////////////////////////////////////////
149
150struct File::Data
151{
152 Data()
153 : fileName (NULL), handle (NIL_RTFILE), opened (false) {}
154
155 char *fileName;
156 RTFILE handle;
157 bool opened : 1;
158};
159
160File::File(Mode aMode, const char *aFileName)
161 : m (new Data())
162{
163 m->fileName = RTStrDup (aFileName);
164 if (m->fileName == NULL)
165 throw ENoMemory();
166
167 unsigned flags = 0;
168 switch (aMode)
169 {
170 case Mode_Read:
171 flags = RTFILE_O_READ;
172 break;
173 case Mode_Write:
174 flags = RTFILE_O_WRITE | RTFILE_O_CREATE;
175 break;
176 case Mode_ReadWrite:
177 flags = RTFILE_O_READ | RTFILE_O_WRITE;
178 }
179
180 int vrc = RTFileOpen (&m->handle, aFileName, flags);
181 if (RT_FAILURE (vrc))
182 throw EIPRTFailure (vrc);
183
184 m->opened = true;
185}
186
187File::File (RTFILE aHandle, const char *aFileName /* = NULL */)
188 : m (new Data())
189{
190 if (aHandle == NIL_RTFILE)
191 throw EInvalidArg (RT_SRC_POS);
192
193 m->handle = aHandle;
194
195 if (aFileName)
196 {
197 m->fileName = RTStrDup (aFileName);
198 if (m->fileName == NULL)
199 throw ENoMemory();
200 }
201
202 setPos (0);
203}
204
205File::~File()
206{
207 if (m->opened)
208 RTFileClose (m->handle);
209
210 RTStrFree (m->fileName);
211}
212
213const char *File::uri() const
214{
215 return m->fileName;
216}
217
218uint64_t File::pos() const
219{
220 uint64_t p = 0;
221 int vrc = RTFileSeek (m->handle, 0, RTFILE_SEEK_CURRENT, &p);
222 if (RT_SUCCESS (vrc))
223 return p;
224
225 throw EIPRTFailure (vrc);
226}
227
228void File::setPos (uint64_t aPos)
229{
230 uint64_t p = 0;
231 unsigned method = RTFILE_SEEK_BEGIN;
232 int vrc = VINF_SUCCESS;
233
234 /* check if we overflow int64_t and move to INT64_MAX first */
235 if (((int64_t) aPos) < 0)
236 {
237 vrc = RTFileSeek (m->handle, INT64_MAX, method, &p);
238 aPos -= (uint64_t) INT64_MAX;
239 method = RTFILE_SEEK_CURRENT;
240 }
241 /* seek the rest */
242 if (RT_SUCCESS (vrc))
243 vrc = RTFileSeek (m->handle, (int64_t) aPos, method, &p);
244 if (RT_SUCCESS (vrc))
245 return;
246
247 throw EIPRTFailure (vrc);
248}
249
250int File::read (char *aBuf, int aLen)
251{
252 size_t len = aLen;
253 int vrc = RTFileRead (m->handle, aBuf, len, &len);
254 if (RT_SUCCESS (vrc))
255 return len;
256
257 throw EIPRTFailure (vrc);
258}
259
260int File::write (const char *aBuf, int aLen)
261{
262 size_t len = aLen;
263 int vrc = RTFileWrite (m->handle, aBuf, len, &len);
264 if (RT_SUCCESS (vrc))
265 return len;
266
267 throw EIPRTFailure (vrc);
268
269 return -1 /* failure */;
270}
271
272void File::truncate()
273{
274 int vrc = RTFileSetSize (m->handle, pos());
275 if (RT_SUCCESS (vrc))
276 return;
277
278 throw EIPRTFailure (vrc);
279}
280
281//////////////////////////////////////////////////////////////////////////////
282// MemoryBuf Class
283//////////////////////////////////////////////////////////////////////////////
284
285struct MemoryBuf::Data
286{
287 Data()
288 : buf (NULL), len (0), uri (NULL), pos (0) {}
289
290 const char *buf;
291 size_t len;
292 char *uri;
293
294 size_t pos;
295};
296
297MemoryBuf::MemoryBuf (const char *aBuf, size_t aLen, const char *aURI /* = NULL */)
298 : m (new Data())
299{
300 if (aBuf == NULL)
301 throw EInvalidArg (RT_SRC_POS);
302
303 m->buf = aBuf;
304 m->len = aLen;
305 m->uri = RTStrDup (aURI);
306}
307
308MemoryBuf::~MemoryBuf()
309{
310 RTStrFree (m->uri);
311}
312
313const char *MemoryBuf::uri() const
314{
315 return m->uri;
316}
317
318uint64_t MemoryBuf::pos() const
319{
320 return m->pos;
321}
322
323void MemoryBuf::setPos (uint64_t aPos)
324{
325 size_t pos = (size_t) aPos;
326 if ((uint64_t) pos != aPos)
327 throw EInvalidArg();
328
329 if (pos > m->len)
330 throw EInvalidArg();
331
332 m->pos = pos;
333}
334
335int MemoryBuf::read (char *aBuf, int aLen)
336{
337 if (m->pos >= m->len)
338 return 0 /* nothing to read */;
339
340 size_t len = m->pos + aLen < m->len ? aLen : m->len - m->pos;
341 memcpy (aBuf, m->buf + m->pos, len);
342 m->pos += len;
343
344 return len;
345}
346
347/*
348 * GlobalLock
349 *
350 *
351 */
352
353struct GlobalLock::Data
354{
355 PFNEXTERNALENTITYLOADER pOldLoader;
356 RTLock lock;
357
358 Data()
359 : pOldLoader(NULL),
360 lock(gGlobal.xml.lock)
361 {
362 }
363};
364
365GlobalLock::GlobalLock()
366 : m(new Data())
367{
368}
369
370GlobalLock::~GlobalLock()
371{
372 if (m->pOldLoader)
373 xmlSetExternalEntityLoader(m->pOldLoader);
374}
375
376void GlobalLock::setExternalEntityLoader(PFNEXTERNALENTITYLOADER pLoader)
377{
378 m->pOldLoader = xmlGetExternalEntityLoader();
379 xmlSetExternalEntityLoader(pLoader);
380}
381
382// static
383xmlParserInput* GlobalLock::callDefaultLoader(const char *aURI,
384 const char *aID,
385 xmlParserCtxt *aCtxt)
386{
387 return gGlobal.xml.defaultEntityLoader(aURI, aID, aCtxt);
388}
389
390/*
391 * XmlParserBase
392 *
393 *
394 */
395
396XmlParserBase::XmlParserBase()
397{
398 m_ctxt = xmlNewParserCtxt();
399 if (m_ctxt == NULL)
400 throw ENoMemory();
401}
402
403XmlParserBase::~XmlParserBase()
404{
405 xmlFreeParserCtxt (m_ctxt);
406 m_ctxt = NULL;
407}
408
409/*
410 * XmlFileParser
411 *
412 *
413 */
414
415struct XmlFileParser::Data
416{
417 xmlParserCtxtPtr ctxt;
418 std::string strXmlFilename;
419
420 Data()
421 {
422 if (!(ctxt = xmlNewParserCtxt()))
423 throw xml::ENoMemory();
424 }
425
426 ~Data()
427 {
428 xmlFreeParserCtxt(ctxt);
429 ctxt = NULL;
430 }
431};
432
433XmlFileParser::XmlFileParser()
434 : XmlParserBase(),
435 m(new Data())
436{
437}
438
439XmlFileParser::~XmlFileParser()
440{
441}
442
443struct ReadContext
444{
445 File file;
446 std::string error;
447
448 ReadContext(const char *pcszFilename)
449 : file(File::Mode_Read, pcszFilename)
450 {
451 }
452
453 void setError(const xml::Error &x)
454 {
455 error = x.what();
456 }
457
458 void setError(const std::exception &x)
459 {
460 error = x.what();
461 }
462};
463
464void XmlFileParser::read(const char *pcszFilename)
465{
466 GlobalLock lock();
467// global.setExternalEntityLoader(ExternalEntityLoader);
468
469 xmlDocPtr doc = NULL;
470 ReadContext *pContext = NULL;
471
472 m->strXmlFilename = pcszFilename;
473
474 try
475 {
476 pContext = new ReadContext(pcszFilename);
477 doc = xmlCtxtReadIO(m->ctxt,
478 ReadCallback,
479 CloseCallback,
480 pContext,
481 pcszFilename,
482 NULL, // encoding
483 XML_PARSE_NOBLANKS);
484 if (doc == NULL)
485 {
486 throw XmlError(xmlCtxtGetLastError(m->ctxt));
487 }
488
489 xmlFreeDoc(doc);
490 doc = NULL;
491
492 delete pContext;
493 pContext = NULL;
494 }
495 catch (...)
496 {
497 if (doc != NULL)
498 xmlFreeDoc(doc);
499
500 if (pContext)
501 delete pContext;
502
503 throw;
504 }
505}
506
507// static
508int XmlFileParser::ReadCallback(void *aCtxt, char *aBuf, int aLen)
509{
510 ReadContext *pContext = static_cast<ReadContext*>(aCtxt);
511
512 /* To prevent throwing exceptions while inside libxml2 code, we catch
513 * them and forward to our level using a couple of variables. */
514
515 try
516 {
517 return pContext->file.read(aBuf, aLen);
518 }
519 catch (const xml::EIPRTFailure &err) { pContext->setError(err); }
520 catch (const xml::Error &err) { pContext->setError(err); }
521 catch (const std::exception &err) { pContext->setError(err); }
522 catch (...) { pContext->setError(xml::LogicError(RT_SRC_POS)); }
523
524 return -1 /* failure */;
525}
526
527int XmlFileParser::CloseCallback(void *aCtxt)
528{
529 /// @todo to be written
530
531 return -1;
532}
533
534
535} // end namespace xml
536
537
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette