Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: ISO 8211 Access
4 : * Purpose: Implements the DDFModule class.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 1999, Frank Warmerdam
9 : * Copyright (c) 2011-2013, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "cpl_port.h"
15 : #include "iso8211.h"
16 :
17 : #include <algorithm>
18 : #include <array>
19 : #include <cstdio>
20 : #include <cstring>
21 :
22 : #include "cpl_conv.h"
23 : #include "cpl_error.h"
24 : #include "cpl_vsi.h"
25 :
26 : /************************************************************************/
27 : /* DDFModule() */
28 : /************************************************************************/
29 :
30 : /**
31 : * The constructor.
32 : */
33 :
34 : DDFModule::DDFModule() = default;
35 :
36 : /************************************************************************/
37 : /* ~DDFModule() */
38 : /************************************************************************/
39 :
40 : /**
41 : * The destructor.
42 : */
43 :
44 698 : DDFModule::~DDFModule()
45 :
46 : {
47 698 : Close();
48 698 : }
49 :
50 : /************************************************************************/
51 : /* Close() */
52 : /* */
53 : /* Note that closing a file also destroys essentially all other */
54 : /* module datastructures. */
55 : /************************************************************************/
56 :
57 : /**
58 : * Close an ISO 8211 file.
59 : */
60 :
61 881 : void DDFModule::Close()
62 :
63 : {
64 : /* -------------------------------------------------------------------- */
65 : /* Close the file. */
66 : /* -------------------------------------------------------------------- */
67 881 : if (fpDDF != nullptr)
68 : {
69 640 : CPL_IGNORE_RET_VAL(VSIFCloseL(fpDDF));
70 640 : fpDDF = nullptr;
71 : }
72 :
73 881 : poRecord.reset();
74 :
75 881 : apoFieldDefns.clear();
76 881 : }
77 :
78 : /************************************************************************/
79 : /* Open() */
80 : /* */
81 : /* Open an ISO 8211 file, and read the DDR record to build the */
82 : /* field definitions. */
83 : /************************************************************************/
84 :
85 : /**
86 : * Open a ISO 8211 (DDF) file for reading.
87 : *
88 : * If the open succeeds the data descriptive record (DDR) will have been
89 : * read, and all the field and subfield definitions will be available.
90 : *
91 : * @param pszFilename The name of the file to open.
92 : * @param bFailQuietly If FALSE a CPL Error is issued for non-8211 files,
93 : * otherwise quietly return NULL.
94 : * @param fpDDFIn The open file, or nullptr. Ownership is transferred to the
95 : * DDFModule.
96 : *
97 : * @return FALSE if the open fails or TRUE if it succeeds. Errors messages
98 : * are issued internally with CPLError().
99 : */
100 :
101 500 : int DDFModule::Open(const char *pszFilename, int bFailQuietly,
102 : VSILFILE *fpDDFIn)
103 :
104 : {
105 500 : constexpr int nLeaderSize = 24;
106 :
107 : /* -------------------------------------------------------------------- */
108 : /* Close the existing file if there is one. */
109 : /* -------------------------------------------------------------------- */
110 500 : if (fpDDF != nullptr)
111 0 : Close();
112 :
113 : /* -------------------------------------------------------------------- */
114 : /* Open the file. */
115 : /* -------------------------------------------------------------------- */
116 : VSIStatBufL sStat;
117 500 : if (fpDDFIn)
118 : {
119 355 : fpDDF = fpDDFIn;
120 355 : CPL_IGNORE_RET_VAL(VSIFSeekL(fpDDF, 0, SEEK_SET));
121 : }
122 : else
123 : {
124 145 : if (VSIStatL(pszFilename, &sStat) == 0 && !VSI_ISDIR(sStat.st_mode))
125 106 : fpDDF = VSIFOpenL(pszFilename, "rb");
126 :
127 145 : if (fpDDF == nullptr)
128 : {
129 39 : if (!bFailQuietly)
130 0 : CPLError(CE_Failure, CPLE_OpenFailed,
131 : "Unable to open DDF file `%s'.", pszFilename);
132 39 : return FALSE;
133 : }
134 : }
135 :
136 : /* -------------------------------------------------------------------- */
137 : /* Read the 24 byte leader. */
138 : /* -------------------------------------------------------------------- */
139 : char achLeader[nLeaderSize];
140 :
141 461 : if (static_cast<int>(VSIFReadL(achLeader, 1, nLeaderSize, fpDDF)) !=
142 : nLeaderSize)
143 : {
144 0 : CPL_IGNORE_RET_VAL(VSIFCloseL(fpDDF));
145 0 : fpDDF = nullptr;
146 :
147 0 : if (!bFailQuietly)
148 0 : CPLError(CE_Failure, CPLE_FileIO,
149 : "Leader is short on DDF file `%s'.", pszFilename);
150 :
151 0 : return FALSE;
152 : }
153 :
154 : /* -------------------------------------------------------------------- */
155 : /* Verify that this appears to be a valid DDF file. */
156 : /* -------------------------------------------------------------------- */
157 461 : int i, bValid = TRUE;
158 :
159 11525 : for (i = 0; i < nLeaderSize; i++)
160 : {
161 11064 : if (achLeader[i] < 32 || achLeader[i] > 126)
162 0 : bValid = FALSE;
163 : }
164 :
165 461 : if (achLeader[5] != '1' && achLeader[5] != '2' && achLeader[5] != '3')
166 0 : bValid = FALSE;
167 :
168 461 : if (achLeader[6] != 'L')
169 0 : bValid = FALSE;
170 461 : if (achLeader[8] != '1' && achLeader[8] != ' ')
171 0 : bValid = FALSE;
172 :
173 : /* -------------------------------------------------------------------- */
174 : /* Extract information from leader. */
175 : /* -------------------------------------------------------------------- */
176 :
177 461 : if (bValid)
178 : {
179 461 : _recLength = DDFScanInt(achLeader + 0, 5);
180 461 : _interchangeLevel = achLeader[5];
181 461 : _leaderIden = achLeader[6];
182 461 : _inlineCodeExtensionIndicator = achLeader[7];
183 461 : _versionNumber = achLeader[8];
184 461 : _appIndicator = achLeader[9];
185 461 : _fieldControlLength = DDFScanInt(achLeader + 10, 2);
186 461 : _fieldAreaStart = DDFScanInt(achLeader + 12, 5);
187 461 : _extendedCharSet[0] = achLeader[17];
188 461 : _extendedCharSet[1] = achLeader[18];
189 461 : _extendedCharSet[2] = achLeader[19];
190 461 : _sizeFieldLength = DDFScanInt(achLeader + 20, 1);
191 461 : _sizeFieldPos = DDFScanInt(achLeader + 21, 1);
192 461 : _sizeFieldTag = DDFScanInt(achLeader + 23, 1);
193 :
194 461 : if (_recLength < nLeaderSize || _fieldControlLength <= 0 ||
195 461 : _fieldAreaStart < 24 || _sizeFieldLength <= 0 ||
196 461 : _sizeFieldPos <= 0 || _sizeFieldTag <= 0)
197 : {
198 0 : bValid = FALSE;
199 : }
200 : }
201 :
202 : /* -------------------------------------------------------------------- */
203 : /* If the header is invalid, then clean up, report the error */
204 : /* and return. */
205 : /* -------------------------------------------------------------------- */
206 461 : if (!bValid)
207 : {
208 0 : CPL_IGNORE_RET_VAL(VSIFCloseL(fpDDF));
209 0 : fpDDF = nullptr;
210 :
211 0 : if (!bFailQuietly)
212 0 : CPLError(CE_Failure, CPLE_AppDefined,
213 : "File `%s' does not appear to have\n"
214 : "a valid ISO 8211 header.\n",
215 : pszFilename);
216 0 : return FALSE;
217 : }
218 :
219 : /* -------------------------------------------------------------------- */
220 : /* Read the whole record info memory. */
221 : /* -------------------------------------------------------------------- */
222 922 : std::string osRecord;
223 461 : osRecord.assign(achLeader, nLeaderSize);
224 461 : osRecord.resize(_recLength);
225 :
226 461 : if (static_cast<int>(VSIFReadL(osRecord.data() + nLeaderSize, 1,
227 461 : _recLength - nLeaderSize, fpDDF)) !=
228 461 : _recLength - nLeaderSize)
229 : {
230 0 : if (!bFailQuietly)
231 0 : CPLError(CE_Failure, CPLE_FileIO,
232 : "Header record is short on DDF file `%s'.", pszFilename);
233 :
234 0 : return FALSE;
235 : }
236 :
237 : /* -------------------------------------------------------------------- */
238 : /* First make a pass counting the directory entries. */
239 : /* -------------------------------------------------------------------- */
240 461 : int nFieldEntryWidth, nFDCount = 0;
241 :
242 461 : nFieldEntryWidth = _sizeFieldLength + _sizeFieldPos + _sizeFieldTag;
243 :
244 7027 : for (i = nLeaderSize; i + nFieldEntryWidth <= _recLength;
245 6566 : i += nFieldEntryWidth)
246 : {
247 7027 : if (osRecord[i] == DDF_FIELD_TERMINATOR)
248 461 : break;
249 :
250 6566 : nFDCount++;
251 : }
252 :
253 : /* -------------------------------------------------------------------- */
254 : /* Allocate, and read field definitions. */
255 : /* -------------------------------------------------------------------- */
256 7027 : for (i = 0; i < nFDCount; i++)
257 : {
258 : char szTag[128];
259 6566 : int nEntryOffset = nLeaderSize + i * nFieldEntryWidth;
260 : int nFieldLength, nFieldPos;
261 :
262 6566 : strncpy(szTag, osRecord.c_str() + nEntryOffset, _sizeFieldTag);
263 6566 : szTag[_sizeFieldTag] = '\0';
264 :
265 6566 : nEntryOffset += _sizeFieldTag;
266 : nFieldLength =
267 6566 : DDFScanInt(osRecord.c_str() + nEntryOffset, _sizeFieldLength);
268 :
269 6566 : nEntryOffset += _sizeFieldLength;
270 6566 : nFieldPos = DDFScanInt(osRecord.c_str() + nEntryOffset, _sizeFieldPos);
271 :
272 6566 : if (nFieldPos < 0 || nFieldPos > INT_MAX - _fieldAreaStart ||
273 : nFieldLength <
274 6566 : 2 || // DDFFieldDefn::Initialize() assumes at least 2 bytes
275 6566 : _recLength - (_fieldAreaStart + nFieldPos) < nFieldLength)
276 : {
277 0 : if (!bFailQuietly)
278 0 : CPLError(CE_Failure, CPLE_FileIO,
279 : "Header record invalid on DDF file `%s'.",
280 : pszFilename);
281 :
282 0 : return FALSE;
283 : }
284 :
285 13132 : auto poFDefn = std::make_unique<DDFFieldDefn>();
286 6566 : if (poFDefn->Initialize(this, szTag, nFieldLength,
287 6566 : osRecord.c_str() + _fieldAreaStart + nFieldPos))
288 6566 : AddField(std::move(poFDefn));
289 : }
290 :
291 : /* -------------------------------------------------------------------- */
292 : /* Record the current file offset, the beginning of the first */
293 : /* data record. */
294 : /* -------------------------------------------------------------------- */
295 461 : nFirstRecordOffset = static_cast<long>(VSIFTellL(fpDDF));
296 :
297 461 : return TRUE;
298 : }
299 :
300 : /************************************************************************/
301 : /* Initialize() */
302 : /************************************************************************/
303 :
304 180 : int DDFModule::Initialize(char chInterchangeLevel, char chLeaderIden,
305 : char chCodeExtensionIndicator, char chVersionNumber,
306 : char chAppIndicator,
307 : const std::array<char, 3> &achExtendedCharSet,
308 : int nSizeFieldLength, int nSizeFieldPos,
309 : int nSizeFieldTag)
310 :
311 : {
312 180 : _interchangeLevel = chInterchangeLevel;
313 180 : _leaderIden = chLeaderIden;
314 180 : _inlineCodeExtensionIndicator = chCodeExtensionIndicator;
315 180 : _versionNumber = chVersionNumber;
316 180 : _appIndicator = chAppIndicator;
317 180 : _extendedCharSet = achExtendedCharSet;
318 180 : _sizeFieldLength = nSizeFieldLength;
319 180 : _sizeFieldPos = nSizeFieldPos;
320 180 : _sizeFieldTag = nSizeFieldTag;
321 :
322 180 : return TRUE;
323 : }
324 :
325 : /************************************************************************/
326 : /* Create() */
327 : /************************************************************************/
328 :
329 180 : int DDFModule::Create(const char *pszFilename)
330 :
331 : {
332 180 : CPLAssert(fpDDF == nullptr);
333 :
334 : /* -------------------------------------------------------------------- */
335 : /* Create the file on disk. */
336 : /* -------------------------------------------------------------------- */
337 180 : fpDDF = VSIFOpenL(pszFilename, "wb+");
338 180 : if (fpDDF == nullptr)
339 : {
340 1 : CPLError(CE_Failure, CPLE_OpenFailed,
341 : "Failed to create file %s, check path and permissions.",
342 : pszFilename);
343 1 : return FALSE;
344 : }
345 :
346 179 : bReadOnly = FALSE;
347 :
348 : /* -------------------------------------------------------------------- */
349 : /* Prepare all the field definition information. */
350 : /* -------------------------------------------------------------------- */
351 179 : _recLength =
352 : 24 +
353 179 : GetFieldCount() * (_sizeFieldLength + _sizeFieldPos + _sizeFieldTag) +
354 : 1;
355 :
356 179 : _fieldAreaStart = _recLength;
357 :
358 2784 : for (auto &poFieldDefn : apoFieldDefns)
359 : {
360 : int nLength;
361 :
362 2605 : poFieldDefn->GenerateDDREntry(this, nullptr, &nLength);
363 2605 : _recLength += nLength;
364 : }
365 :
366 : /* -------------------------------------------------------------------- */
367 : /* Setup 24 byte leader. */
368 : /* -------------------------------------------------------------------- */
369 : char achLeader[25];
370 :
371 179 : snprintf(achLeader + 0, sizeof(achLeader) - 0, "%05d", _recLength);
372 179 : achLeader[5] = _interchangeLevel;
373 179 : achLeader[6] = _leaderIden;
374 179 : achLeader[7] = _inlineCodeExtensionIndicator;
375 179 : achLeader[8] = _versionNumber;
376 179 : achLeader[9] = _appIndicator;
377 179 : snprintf(achLeader + 10, sizeof(achLeader) - 10, "%02d",
378 : _fieldControlLength);
379 179 : snprintf(achLeader + 12, sizeof(achLeader) - 12, "%05d", _fieldAreaStart);
380 179 : memcpy(achLeader + 17, _extendedCharSet.data(), 3);
381 179 : snprintf(achLeader + 20, sizeof(achLeader) - 20, "%1d", _sizeFieldLength);
382 179 : snprintf(achLeader + 21, sizeof(achLeader) - 21, "%1d", _sizeFieldPos);
383 179 : achLeader[22] = '0';
384 179 : snprintf(achLeader + 23, sizeof(achLeader) - 23, "%1d", _sizeFieldTag);
385 179 : int bRet = VSIFWriteL(achLeader, 24, 1, fpDDF) > 0;
386 :
387 : /* -------------------------------------------------------------------- */
388 : /* Write out directory entries. */
389 : /* -------------------------------------------------------------------- */
390 179 : int nOffset = 0;
391 2784 : for (auto &poFieldDefn : apoFieldDefns)
392 : {
393 : char achDirEntry[255];
394 : char szFormat[32];
395 : int nLength;
396 :
397 2605 : CPLAssert(_sizeFieldLength + _sizeFieldPos + _sizeFieldTag <
398 : static_cast<int>(sizeof(achDirEntry)));
399 :
400 2605 : poFieldDefn->GenerateDDREntry(this, nullptr, &nLength);
401 :
402 2605 : CPLAssert(static_cast<int>(strlen(poFieldDefn->GetName())) ==
403 : _sizeFieldTag);
404 2605 : snprintf(achDirEntry, sizeof(achDirEntry), "%s",
405 : poFieldDefn->GetName());
406 2605 : snprintf(szFormat, sizeof(szFormat), "%%0%dd", _sizeFieldLength);
407 2605 : snprintf(achDirEntry + _sizeFieldTag,
408 2605 : sizeof(achDirEntry) - _sizeFieldTag, szFormat, nLength);
409 2605 : snprintf(szFormat, sizeof(szFormat), "%%0%dd", _sizeFieldPos);
410 2605 : snprintf(achDirEntry + _sizeFieldTag + _sizeFieldLength,
411 2605 : sizeof(achDirEntry) - _sizeFieldTag - _sizeFieldLength,
412 : szFormat, nOffset);
413 2605 : nOffset += nLength;
414 :
415 5210 : bRet &= VSIFWriteL(achDirEntry,
416 2605 : _sizeFieldLength + _sizeFieldPos + _sizeFieldTag, 1,
417 2605 : fpDDF) > 0;
418 : }
419 :
420 179 : char chUT = DDF_FIELD_TERMINATOR;
421 179 : bRet &= VSIFWriteL(&chUT, 1, 1, fpDDF) > 0;
422 :
423 : /* -------------------------------------------------------------------- */
424 : /* Write out the field descriptions themselves. */
425 : /* -------------------------------------------------------------------- */
426 2784 : for (auto &poFieldDefn : apoFieldDefns)
427 : {
428 2605 : char *pachData = nullptr;
429 2605 : int nLength = 0;
430 :
431 2605 : poFieldDefn->GenerateDDREntry(this, &pachData, &nLength);
432 2605 : bRet &= VSIFWriteL(pachData, nLength, 1, fpDDF) > 0;
433 2605 : CPLFree(pachData);
434 : }
435 :
436 179 : return bRet ? TRUE : FALSE;
437 : }
438 :
439 : /************************************************************************/
440 : /* Dump() */
441 : /************************************************************************/
442 :
443 : /**
444 : * Write out module info to debugging file.
445 : *
446 : * A variety of information about the module is written to the debugging
447 : * file. This includes all the field and subfield definitions read from
448 : * the header.
449 : *
450 : * @param fp The standard IO file handle to write to. i.e. stderr.
451 : */
452 :
453 0 : void DDFModule::Dump(FILE *fp, int nNestingLevel) const
454 :
455 : {
456 0 : std::string osIndent;
457 0 : for (int i = 0; i < nNestingLevel; ++i)
458 0 : osIndent += " ";
459 :
460 : #define Print(...) \
461 : do \
462 : { \
463 : fprintf(fp, "%s", osIndent.c_str()); \
464 : fprintf(fp, __VA_ARGS__); \
465 : } while (0)
466 :
467 0 : Print("DDFModule:\n");
468 0 : Print(" _recLength = %d\n", _recLength);
469 0 : Print(" _interchangeLevel = %c\n", _interchangeLevel);
470 0 : Print(" _leaderIden = %c\n", _leaderIden);
471 0 : Print(" _inlineCodeExtensionIndicator = %c\n",
472 : _inlineCodeExtensionIndicator);
473 0 : Print(" _versionNumber = %c\n", _versionNumber);
474 0 : Print(" _appIndicator = %c\n", _appIndicator);
475 0 : Print(" _extendedCharSet = `%c%c%c'\n", _extendedCharSet[0],
476 : _extendedCharSet[1], _extendedCharSet[2]);
477 0 : Print(" _fieldControlLength = %d\n", _fieldControlLength);
478 0 : Print(" _fieldAreaStart = %d\n", _fieldAreaStart);
479 0 : Print(" _sizeFieldLength = %d\n", _sizeFieldLength);
480 0 : Print(" _sizeFieldPos = %d\n", _sizeFieldPos);
481 0 : Print(" _sizeFieldTag = %d\n", _sizeFieldTag);
482 :
483 0 : for (const auto &poFieldDefn : apoFieldDefns)
484 : {
485 0 : poFieldDefn->Dump(fp, nNestingLevel + 1);
486 : }
487 0 : }
488 :
489 : /************************************************************************/
490 : /* FindFieldDefn() */
491 : /************************************************************************/
492 :
493 : /**
494 : * Fetch the definition of the named field.
495 : *
496 : * This function will scan the DDFFieldDefn's on this module, to find
497 : * one with the indicated field name.
498 : *
499 : * @param pszFieldName The name of the field to search for. The comparison is
500 : * case insensitive.
501 : *
502 : * @return A pointer to the request DDFFieldDefn object is returned, or NULL
503 : * if none matching the name are found. The return object remains owned by
504 : * the DDFModule, and should not be deleted by application code.
505 : */
506 :
507 122033 : const DDFFieldDefn *DDFModule::FindFieldDefn(const char *pszFieldName) const
508 :
509 : {
510 : /* -------------------------------------------------------------------- */
511 : /* This pass tries to reduce the cost of comparing strings by */
512 : /* first checking the first character, and by using strcmp() */
513 : /* -------------------------------------------------------------------- */
514 1045840 : for (const auto &poFieldDefn : apoFieldDefns)
515 : {
516 1045830 : const char *pszThisName = poFieldDefn->GetName();
517 :
518 1045830 : if (*pszThisName == *pszFieldName && *pszFieldName != '\0' &&
519 228183 : strcmp(pszFieldName + 1, pszThisName + 1) == 0)
520 122028 : return poFieldDefn.get();
521 : }
522 :
523 : /* -------------------------------------------------------------------- */
524 : /* Now do a more general check. Application code may not */
525 : /* always use the correct name case. */
526 : /* -------------------------------------------------------------------- */
527 23 : for (const auto &poFieldDefn : apoFieldDefns)
528 : {
529 18 : if (EQUAL(pszFieldName, poFieldDefn->GetName()))
530 0 : return poFieldDefn.get();
531 : }
532 :
533 5 : return nullptr;
534 : }
535 :
536 : /************************************************************************/
537 : /* ReadRecord() */
538 : /* */
539 : /* Read one record from the file, and return to the */
540 : /* application. The returned record is owned by the module, */
541 : /* and is reused from call to call in order to preserve headers */
542 : /* when they aren't being re-read from record to record. */
543 : /************************************************************************/
544 :
545 : /**
546 : * Read one record from the file.
547 : *
548 : * @return A pointer to a DDFRecord object is returned, or NULL if a read
549 : * error, or end of file occurs. The returned record is owned by the
550 : * module, and should not be deleted by the application. The record is
551 : * only valid until the next ReadRecord() at which point it is overwritten.
552 : */
553 :
554 30503 : DDFRecord *DDFModule::ReadRecord()
555 :
556 : {
557 30503 : if (poRecord == nullptr)
558 459 : poRecord = std::make_unique<DDFRecord>(this);
559 :
560 30503 : if (poRecord->Read())
561 30155 : return poRecord.get();
562 : else
563 348 : return nullptr;
564 : }
565 :
566 : /************************************************************************/
567 : /* AddField() */
568 : /************************************************************************/
569 :
570 : /**
571 : * Add new field definition.
572 : *
573 : * Field definitions may only be added to DDFModules being used for
574 : * writing, not those being used for reading. Ownership of the
575 : * DDFFieldDefn object is taken by the DDFModule.
576 : *
577 : * @param poNewFDefn definition to be added to the module.
578 : */
579 :
580 9191 : void DDFModule::AddField(std::unique_ptr<DDFFieldDefn> poNewFDefn)
581 :
582 : {
583 9191 : apoFieldDefns.push_back(std::move(poNewFDefn));
584 9191 : }
585 :
586 : /************************************************************************/
587 : /* GetField() */
588 : /************************************************************************/
589 :
590 : /**
591 : * Fetch a field definition by index.
592 : *
593 : * @param i (from 0 to GetFieldCount() - 1.
594 : * @return the returned field pointer or NULL if the index is out of range.
595 : */
596 :
597 42 : DDFFieldDefn *DDFModule::GetField(int i)
598 :
599 : {
600 42 : if (i < 0 || static_cast<size_t>(i) >= apoFieldDefns.size())
601 0 : return nullptr;
602 : else
603 42 : return apoFieldDefns[i].get();
604 : }
605 :
606 : /************************************************************************/
607 : /* Rewind() */
608 : /************************************************************************/
609 :
610 : /**
611 : * Return to first record.
612 : *
613 : * The next call to ReadRecord() will read the first data record in the file.
614 : *
615 : * @param nOffset the offset in the file to return to. By default this is
616 : * -1, a special value indicating that reading should return to the first
617 : * data record. Otherwise it is an absolute byte offset in the file.
618 : */
619 :
620 0 : void DDFModule::Rewind(vsi_l_offset nOffset)
621 :
622 : {
623 0 : if (nOffset == static_cast<vsi_l_offset>(-1))
624 0 : nOffset = nFirstRecordOffset;
625 :
626 0 : if (fpDDF == nullptr)
627 0 : return;
628 :
629 0 : if (VSIFSeekL(fpDDF, nOffset, SEEK_SET) < 0)
630 0 : return;
631 :
632 0 : if (nOffset == nFirstRecordOffset && poRecord != nullptr)
633 0 : poRecord->Clear();
634 : }
|