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