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