Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GML Reader
4 : * Purpose: Convenience functions for parsing with Xerces-C library
5 : * Functions for translating back and forth between XMLCh and char.
6 : * We assume that XMLCh is a simple numeric type that we can
7 : * correspond 1:1 with char values, but that it likely is larger
8 : * than a char.
9 : * Author: Frank Warmerdam, warmerdam@pobox.com
10 : * Author: Even Rouault, <even.rouault at spatialys.com>
11 : *
12 : ******************************************************************************
13 : * Copyright (c) 2002, Frank Warmerdam
14 : * Copyright (c) 2016, Even Rouault <even.rouault at spatialys.com>
15 : *
16 : * Permission is hereby granted, free of charge, to any person obtaining a
17 : * copy of this software and associated documentation files (the "Software"),
18 : * to deal in the Software without restriction, including without limitation
19 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
20 : * and/or sell copies of the Software, and to permit persons to whom the
21 : * Software is furnished to do so, subject to the following conditions:
22 : *
23 : * The above copyright notice and this permission notice shall be included
24 : * in all copies or substantial portions of the Software.
25 : *
26 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
27 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
29 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
31 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
32 : * DEALINGS IN THE SOFTWARE.
33 : ****************************************************************************/
34 :
35 : #include "ogr_xerces.h"
36 :
37 : #include "cpl_port.h"
38 : #include "cpl_error.h"
39 : #include "cpl_multiproc.h"
40 : #include "cpl_string.h"
41 :
42 : #include <algorithm>
43 : #include <limits>
44 : #include <map>
45 :
46 : #ifdef HAVE_XERCES
47 :
48 : class OGRXercesStandardMemoryManager;
49 : class OGRXercesInstrumentedMemoryManager;
50 :
51 : /************************************************************************/
52 : /* CPLGettimeofday() */
53 : /************************************************************************/
54 :
55 : #if defined(_WIN32) && !defined(__CYGWIN__)
56 : #include <sys/timeb.h>
57 :
58 : namespace
59 : {
60 : struct CPLTimeVal
61 : {
62 : time_t tv_sec; /* seconds */
63 : long tv_usec; /* and microseconds */
64 : };
65 : } // namespace
66 :
67 : static int CPLGettimeofday(struct CPLTimeVal *tp, void * /* timezonep*/)
68 : {
69 : struct _timeb theTime;
70 :
71 : _ftime(&theTime);
72 : tp->tv_sec = static_cast<time_t>(theTime.time);
73 : tp->tv_usec = theTime.millitm * 1000;
74 : return 0;
75 : }
76 : #else
77 : #include <sys/time.h> /* for gettimeofday() */
78 : #define CPLTimeVal timeval
79 : #define CPLGettimeofday(t, u) gettimeofday(t, u)
80 : #endif
81 :
82 : namespace
83 : {
84 : struct LimitationStruct
85 : {
86 : size_t maxMemAlloc = 0;
87 : std::string osMsgMaxMemAlloc{};
88 : double timeOut = 0;
89 : std::string osMsgTimeout{};
90 :
91 : CPLTimeVal initTV{0, 0};
92 : CPLTimeVal lastTV{0, 0};
93 : size_t totalAllocSize = 0;
94 : size_t allocCount = 0;
95 : };
96 : } // namespace
97 :
98 : static CPLMutex *hOGRXercesMutex = nullptr;
99 : static int nCounter = 0;
100 : static bool bXercesWasAlreadyInitializedBeforeUs = false;
101 : static OGRXercesStandardMemoryManager *gpExceptionMemoryManager = nullptr;
102 : static OGRXercesInstrumentedMemoryManager *gpMemoryManager = nullptr;
103 : static std::map<GIntBig, LimitationStruct> *gpoMapThreadTimeout = nullptr;
104 :
105 : /************************************************************************/
106 : /* OGRXercesStandardMemoryManager */
107 : /************************************************************************/
108 :
109 : class OGRXercesStandardMemoryManager final : public MemoryManager
110 : {
111 : public:
112 156 : OGRXercesStandardMemoryManager() = default;
113 :
114 0 : MemoryManager *getExceptionMemoryManager() override
115 : {
116 0 : return this;
117 : }
118 :
119 : void *allocate(XMLSize_t size) override;
120 :
121 : void deallocate(void *p) override;
122 : };
123 :
124 825 : void *OGRXercesStandardMemoryManager::allocate(XMLSize_t size)
125 : {
126 825 : void *memptr = VSIMalloc(size);
127 825 : if (memptr == nullptr && size != 0)
128 0 : throw OutOfMemoryException();
129 825 : return memptr;
130 : }
131 :
132 825 : void OGRXercesStandardMemoryManager::deallocate(void *p)
133 : {
134 825 : if (p)
135 825 : VSIFree(p);
136 825 : }
137 :
138 : /************************************************************************/
139 : /* OGRXercesInstrumentedMemoryManager */
140 : /************************************************************************/
141 :
142 : class OGRXercesInstrumentedMemoryManager final : public MemoryManager
143 : {
144 : public:
145 156 : OGRXercesInstrumentedMemoryManager() = default;
146 :
147 287 : MemoryManager *getExceptionMemoryManager() override
148 : {
149 287 : return gpExceptionMemoryManager;
150 : }
151 :
152 : void *allocate(XMLSize_t size) override;
153 :
154 : void deallocate(void *p) override;
155 : };
156 :
157 22263600 : void *OGRXercesInstrumentedMemoryManager::allocate(XMLSize_t size)
158 : {
159 22263600 : if (size > std::numeric_limits<size_t>::max() - 8U)
160 0 : throw OutOfMemoryException();
161 22263600 : void *memptr = VSIMalloc(size + 8);
162 22263600 : if (memptr == nullptr)
163 0 : throw OutOfMemoryException();
164 22263600 : memcpy(memptr, &size, sizeof(XMLSize_t));
165 :
166 22263600 : LimitationStruct *pLimitation = nullptr;
167 : {
168 44527100 : CPLMutexHolderD(&hOGRXercesMutex);
169 :
170 22263600 : if (gpoMapThreadTimeout)
171 : {
172 13834200 : auto iter = gpoMapThreadTimeout->find(CPLGetPID());
173 13834200 : if (iter != gpoMapThreadTimeout->end())
174 : {
175 13834200 : pLimitation = &(iter->second);
176 : }
177 : }
178 : }
179 :
180 : // Big memory allocation can happen in cases like
181 : // https://issues.apache.org/jira/browse/XERCESC-1051
182 22263600 : if (pLimitation && pLimitation->maxMemAlloc > 0)
183 : {
184 13834200 : if (pLimitation->totalAllocSize + size > pLimitation->maxMemAlloc)
185 : {
186 1 : pLimitation->maxMemAlloc = 0;
187 1 : VSIFree(memptr);
188 1 : if (!pLimitation->osMsgMaxMemAlloc.empty())
189 : {
190 1 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
191 : pLimitation->osMsgMaxMemAlloc.c_str());
192 : }
193 1 : throw OutOfMemoryException();
194 : }
195 : }
196 :
197 : // Quite a hack, but some pathologic schema can cause excessive
198 : // processing time. As memory allocations are regularly done, we
199 : // measure the time of those consecutive allocations and check it
200 : // does not exceed a threshold set by OGRStartXercesTimeoutForThisThread()
201 : // Can happen in cases like
202 : // https://issues.apache.org/jira/browse/XERCESC-1051
203 22263600 : if (pLimitation && pLimitation->timeOut > 0)
204 : {
205 6553250 : ++pLimitation->allocCount;
206 6553250 : if (pLimitation->allocCount == 1000)
207 : {
208 6435 : pLimitation->allocCount = 0;
209 :
210 : CPLTimeVal tv;
211 6435 : CPLGettimeofday(&tv, nullptr);
212 12655 : if (pLimitation->initTV.tv_sec == 0 ||
213 : // Reset the counter if the delay between the last 1000 memory
214 : // allocations is too large. This enables being tolerant to
215 : // network requests.
216 6220 : tv.tv_sec + tv.tv_usec * 1e-6 -
217 6220 : (pLimitation->lastTV.tv_sec +
218 6220 : pLimitation->lastTV.tv_usec * 1e-6) >
219 12655 : std::min(0.1, pLimitation->timeOut / 10))
220 : {
221 235 : pLimitation->initTV = tv;
222 : }
223 6200 : else if (tv.tv_sec + tv.tv_usec * 1e-6 -
224 6200 : (pLimitation->initTV.tv_sec +
225 6200 : pLimitation->initTV.tv_usec * 1e-6) >
226 6200 : pLimitation->timeOut)
227 : {
228 1 : pLimitation->timeOut = 0;
229 1 : VSIFree(memptr);
230 1 : if (!pLimitation->osMsgTimeout.empty())
231 : {
232 1 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
233 : pLimitation->osMsgTimeout.c_str());
234 : }
235 1 : throw OutOfMemoryException();
236 : }
237 6434 : pLimitation->lastTV = tv;
238 : }
239 : }
240 :
241 22263600 : if (pLimitation && pLimitation->maxMemAlloc > 0)
242 : {
243 13834200 : pLimitation->totalAllocSize += size;
244 : }
245 :
246 22263600 : return static_cast<char *>(memptr) + 8;
247 : }
248 :
249 17975200 : void OGRXercesInstrumentedMemoryManager::deallocate(void *p)
250 : {
251 17975200 : if (p)
252 : {
253 14841600 : void *rawptr =
254 14841600 : reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(p) - 8);
255 : XMLSize_t size;
256 14841600 : memcpy(&size, rawptr, sizeof(XMLSize_t));
257 14841600 : VSIFree(rawptr);
258 :
259 14841600 : LimitationStruct *pLimitation = nullptr;
260 : {
261 29683200 : CPLMutexHolderD(&hOGRXercesMutex);
262 :
263 14841600 : if (gpoMapThreadTimeout)
264 : {
265 3239600 : auto iter = gpoMapThreadTimeout->find(CPLGetPID());
266 3239600 : if (iter != gpoMapThreadTimeout->end())
267 : {
268 3239600 : pLimitation = &(iter->second);
269 : }
270 : }
271 : }
272 14841600 : if (pLimitation && pLimitation->maxMemAlloc > 0)
273 : {
274 : // Memory allocations aren't necessarily paired within
275 : // a OGRStartXercesLimitsForThisThread() /
276 : // OGRStopXercesLimitsForThisThread() session. Probably due to
277 : // some caching with Xerces. So handle this gracefully to avoid
278 : // unsigned integer underflow.
279 3239360 : if (pLimitation->totalAllocSize >= size)
280 3239360 : pLimitation->totalAllocSize -= size;
281 : else
282 0 : pLimitation->totalAllocSize = 0;
283 : }
284 : }
285 17975200 : }
286 :
287 : /************************************************************************/
288 : /* OGRStartXercesLimitsForThisThread() */
289 : /************************************************************************/
290 :
291 257 : void OGRStartXercesLimitsForThisThread(size_t nMaxMemAlloc,
292 : const char *pszMsgMaxMemAlloc,
293 : double dfTimeoutSecond,
294 : const char *pszMsgTimeout)
295 : {
296 514 : CPLMutexHolderD(&hOGRXercesMutex);
297 257 : if (gpoMapThreadTimeout == nullptr)
298 : {
299 257 : gpoMapThreadTimeout = new std::map<GIntBig, LimitationStruct>();
300 : }
301 257 : LimitationStruct limitation;
302 257 : limitation.maxMemAlloc = nMaxMemAlloc;
303 257 : if (pszMsgMaxMemAlloc)
304 257 : limitation.osMsgMaxMemAlloc = pszMsgMaxMemAlloc;
305 257 : limitation.timeOut = dfTimeoutSecond;
306 257 : if (pszMsgTimeout)
307 257 : limitation.osMsgTimeout = pszMsgTimeout;
308 257 : (*gpoMapThreadTimeout)[CPLGetPID()] = std::move(limitation);
309 257 : }
310 :
311 : /************************************************************************/
312 : /* OGRStopXercesLimitsForThisThread() */
313 : /************************************************************************/
314 :
315 257 : void OGRStopXercesLimitsForThisThread()
316 : {
317 514 : CPLMutexHolderD(&hOGRXercesMutex);
318 257 : (*gpoMapThreadTimeout).erase(CPLGetPID());
319 257 : if (gpoMapThreadTimeout->empty())
320 : {
321 257 : delete gpoMapThreadTimeout;
322 257 : gpoMapThreadTimeout = nullptr;
323 : }
324 257 : }
325 :
326 : /************************************************************************/
327 : /* OGRXercesBinInputStream */
328 : /************************************************************************/
329 :
330 : class OGRXercesBinInputStream final : public BinInputStream
331 : {
332 : CPL_DISALLOW_COPY_ASSIGN(OGRXercesBinInputStream)
333 :
334 : VSILFILE *fp = nullptr;
335 : bool bOwnFP = false;
336 : XMLCh emptyString = 0;
337 :
338 : public:
339 : explicit OGRXercesBinInputStream(VSILFILE *fpIn, bool bOwnFPIn);
340 : ~OGRXercesBinInputStream() override;
341 :
342 : XMLFilePos curPos() const override;
343 : XMLSize_t readBytes(XMLByte *const toFill,
344 : const XMLSize_t maxToRead) override;
345 :
346 0 : const XMLCh *getContentType() const override
347 : {
348 0 : return &emptyString;
349 : }
350 : };
351 :
352 : /************************************************************************/
353 : /* OGRXercesNetAccessor */
354 : /************************************************************************/
355 :
356 : class OGRXercesNetAccessor final : public XMLNetAccessor
357 : {
358 : public:
359 156 : OGRXercesNetAccessor() = default;
360 :
361 : BinInputStream *makeNew(const XMLURL &urlSource,
362 : const XMLNetHTTPInfo *httpInfo) override;
363 :
364 0 : const XMLCh *getId() const override
365 : {
366 0 : return fgMyName;
367 : }
368 :
369 : private:
370 : static const XMLCh fgMyName[];
371 :
372 : OGRXercesNetAccessor(const OGRXercesNetAccessor &);
373 : OGRXercesNetAccessor &operator=(const OGRXercesNetAccessor &);
374 : };
375 :
376 : const XMLCh OGRXercesNetAccessor::fgMyName[] = {
377 : chLatin_O, chLatin_G, chLatin_R, chLatin_X, chLatin_e, chLatin_r, chLatin_c,
378 : chLatin_e, chLatin_s, chLatin_N, chLatin_e, chLatin_t, chLatin_A, chLatin_c,
379 : chLatin_c, chLatin_e, chLatin_s, chLatin_s, chLatin_o, chLatin_r, chNull};
380 :
381 : BinInputStream *
382 0 : OGRXercesNetAccessor::makeNew(const XMLURL &urlSource,
383 : const XMLNetHTTPInfo * /*httpInfo*/)
384 : {
385 : const std::string osURL =
386 0 : "/vsicurl_streaming/" + transcode(urlSource.getURLText());
387 0 : VSILFILE *fp = VSIFOpenL(osURL.c_str(), "rb");
388 0 : if (!fp)
389 0 : return nullptr;
390 0 : return new OGRXercesBinInputStream(fp, true);
391 : }
392 :
393 : /************************************************************************/
394 : /* OGRInitializeXerces() */
395 : /************************************************************************/
396 :
397 195 : bool OGRInitializeXerces()
398 : {
399 390 : CPLMutexHolderD(&hOGRXercesMutex);
400 :
401 195 : if (nCounter > 0)
402 : {
403 39 : nCounter++;
404 39 : return true;
405 : }
406 :
407 156 : if (XMLPlatformUtils::fgMemoryManager != nullptr)
408 : {
409 0 : CPLDebug("OGR", "Xerces-C already initialized before GDAL");
410 0 : bXercesWasAlreadyInitializedBeforeUs = true;
411 0 : nCounter = 1;
412 0 : return true;
413 : }
414 : else
415 : {
416 156 : gpExceptionMemoryManager = new OGRXercesStandardMemoryManager();
417 156 : gpMemoryManager = new OGRXercesInstrumentedMemoryManager();
418 :
419 : try
420 : {
421 156 : CPLDebug("OGR", "XMLPlatformUtils::Initialize()");
422 156 : XMLPlatformUtils::Initialize(XMLUni::fgXercescDefaultLocale,
423 : nullptr, /* nlsHome */
424 : nullptr, /* panicHandler */
425 : gpMemoryManager);
426 :
427 : // Install our own network accessor instead of the default Xerces-C
428 : // one This enables us in particular to honour GDAL_HTTP_TIMEOUT
429 156 : if (CPLTestBool(CPLGetConfigOption(
430 : "OGR_XERCES_USE_OGR_NET_ACCESSOR", "YES")))
431 : {
432 156 : auto oldNetAccessor = XMLPlatformUtils::fgNetAccessor;
433 156 : XMLPlatformUtils::fgNetAccessor = new OGRXercesNetAccessor();
434 156 : delete oldNetAccessor;
435 : }
436 :
437 156 : nCounter = 1;
438 156 : return true;
439 : }
440 0 : catch (const XMLException &toCatch)
441 : {
442 0 : CPLError(CE_Failure, CPLE_AppDefined,
443 : "Exception initializing Xerces: %s",
444 0 : transcode(toCatch.getMessage()).c_str());
445 0 : return false;
446 : }
447 : }
448 : }
449 :
450 : /************************************************************************/
451 : /* OGRDeinitializeXerces() */
452 : /************************************************************************/
453 :
454 195 : void OGRDeinitializeXerces()
455 : {
456 195 : CPLMutexHolderD(&hOGRXercesMutex);
457 195 : if (nCounter == 0)
458 : {
459 0 : CPLError(CE_Failure, CPLE_AppDefined,
460 : "Unpaired OGRInitializeXerces / OGRDeinitializeXerces calls");
461 0 : return;
462 : }
463 195 : nCounter--;
464 195 : if (nCounter == 0)
465 : {
466 312 : if (!bXercesWasAlreadyInitializedBeforeUs &&
467 156 : CPLTestBool(CPLGetConfigOption("OGR_XERCES_TERMINATE", "YES")))
468 : {
469 156 : CPLDebug("OGR", "XMLPlatformUtils::Terminate()");
470 156 : XMLPlatformUtils::Terminate();
471 :
472 156 : delete gpMemoryManager;
473 156 : gpMemoryManager = nullptr;
474 156 : delete gpExceptionMemoryManager;
475 156 : gpExceptionMemoryManager = nullptr;
476 : }
477 : }
478 : }
479 :
480 : /************************************************************************/
481 : /* OGRCleanupXercesMutex() */
482 : /************************************************************************/
483 :
484 852 : void OGRCleanupXercesMutex()
485 : {
486 852 : if (hOGRXercesMutex != nullptr)
487 3 : CPLDestroyMutex(hOGRXercesMutex);
488 852 : hOGRXercesMutex = nullptr;
489 852 : }
490 :
491 : namespace OGR
492 : {
493 :
494 : /************************************************************************/
495 : /* transcode() */
496 : /************************************************************************/
497 :
498 3793430 : CPLString transcode(const XMLCh *panXMLString, int nLimitingChars)
499 : {
500 3793430 : CPLString osRet;
501 3793430 : transcode(panXMLString, osRet, nLimitingChars);
502 3793430 : return osRet;
503 : }
504 :
505 4325080 : CPLString &transcode(const XMLCh *panXMLString, CPLString &osRet,
506 : int nLimitingChars)
507 : {
508 4325080 : if (panXMLString == nullptr)
509 : {
510 0 : osRet = "(null)";
511 0 : return osRet;
512 : }
513 :
514 4325080 : osRet.clear();
515 4325080 : if (nLimitingChars > 0)
516 105306 : osRet.reserve(nLimitingChars);
517 :
518 4325080 : bool bSimpleASCII = true;
519 4325080 : int nChars = 0;
520 4325080 : for (int i = 0;
521 145144000 : panXMLString[i] != 0 && (nLimitingChars < 0 || i < nLimitingChars);
522 : i++)
523 : {
524 140818000 : if (panXMLString[i] > 127)
525 : {
526 2416 : bSimpleASCII = false;
527 : }
528 140818000 : osRet += static_cast<char>(panXMLString[i]);
529 140818000 : nChars++;
530 : }
531 :
532 4325080 : if (bSimpleASCII)
533 4323880 : return osRet;
534 :
535 : /* -------------------------------------------------------------------- */
536 : /* The simple translation was wrong, because the source is not */
537 : /* all simple ASCII characters. Redo using the more expensive */
538 : /* recoding API. */
539 : /* -------------------------------------------------------------------- */
540 : wchar_t *pwszSource =
541 1199 : static_cast<wchar_t *>(CPLMalloc(sizeof(wchar_t) * (nChars + 1)));
542 664922 : for (int i = 0; i < nChars; i++)
543 663723 : pwszSource[i] = panXMLString[i];
544 1199 : pwszSource[nChars] = 0;
545 :
546 1199 : char *pszResult = CPLRecodeFromWChar(pwszSource, "WCHAR_T", CPL_ENC_UTF8);
547 :
548 1199 : osRet = pszResult;
549 :
550 1199 : CPLFree(pwszSource);
551 1199 : CPLFree(pszResult);
552 :
553 1199 : return osRet;
554 : }
555 :
556 : } // namespace OGR
557 :
558 : /************************************************************************/
559 : /* OGRXercesInputSource */
560 : /************************************************************************/
561 :
562 22 : class OGRXercesInputSource : public InputSource
563 : {
564 : OGRXercesBinInputStream *pBinInputStream;
565 :
566 : CPL_DISALLOW_COPY_ASSIGN(OGRXercesInputSource)
567 :
568 : public:
569 : explicit OGRXercesInputSource(
570 : VSILFILE *fp,
571 : MemoryManager *const manager = XMLPlatformUtils::fgMemoryManager);
572 : ~OGRXercesInputSource() override;
573 :
574 11 : BinInputStream *makeStream() const override
575 : {
576 11 : return pBinInputStream;
577 : }
578 : };
579 :
580 : /************************************************************************/
581 : /* OGRXercesBinInputStream() */
582 : /************************************************************************/
583 :
584 11 : OGRXercesBinInputStream::OGRXercesBinInputStream(VSILFILE *fpIn, bool bOwnFPIn)
585 11 : : fp(fpIn), bOwnFP(bOwnFPIn)
586 : {
587 11 : }
588 :
589 : /************************************************************************/
590 : /* ~OGRXercesBinInputStream() */
591 : /************************************************************************/
592 :
593 22 : OGRXercesBinInputStream::~OGRXercesBinInputStream()
594 : {
595 11 : if (bOwnFP)
596 0 : VSIFCloseL(fp);
597 22 : }
598 :
599 : /************************************************************************/
600 : /* curPos() */
601 : /************************************************************************/
602 :
603 0 : XMLFilePos OGRXercesBinInputStream::curPos() const
604 : {
605 0 : return static_cast<XMLFilePos>(VSIFTellL(fp));
606 : }
607 :
608 : /************************************************************************/
609 : /* readBytes() */
610 : /************************************************************************/
611 :
612 23 : XMLSize_t OGRXercesBinInputStream::readBytes(XMLByte *const toFill,
613 : const XMLSize_t maxToRead)
614 : {
615 23 : return static_cast<XMLSize_t>(VSIFReadL(toFill, 1, maxToRead, fp));
616 : }
617 :
618 : /************************************************************************/
619 : /* OGRXercesInputSource() */
620 : /************************************************************************/
621 :
622 11 : OGRXercesInputSource::OGRXercesInputSource(VSILFILE *fp,
623 11 : MemoryManager *const manager)
624 : : InputSource(manager),
625 11 : pBinInputStream(new OGRXercesBinInputStream(fp, false))
626 : {
627 11 : }
628 :
629 : /************************************************************************/
630 : /* ~OGRXercesInputSource() */
631 : /************************************************************************/
632 :
633 : OGRXercesInputSource::~OGRXercesInputSource() = default;
634 :
635 : /************************************************************************/
636 : /* OGRCreateXercesInputSource() */
637 : /************************************************************************/
638 :
639 11 : InputSource *OGRCreateXercesInputSource(VSILFILE *fp)
640 : {
641 11 : return new OGRXercesInputSource(fp);
642 : }
643 :
644 : /************************************************************************/
645 : /* OGRDestroyXercesInputSource() */
646 : /************************************************************************/
647 :
648 514 : void OGRDestroyXercesInputSource(InputSource *is)
649 : {
650 514 : delete is;
651 514 : }
652 :
653 : #endif // HAVE_XERCES
|