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