/src/gdal/frmts/iso8211/ddfmodule.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | DDFModule::DDFModule() |
36 | 0 | : fpDDF(nullptr), bReadOnly(TRUE), nFirstRecordOffset(0), |
37 | 0 | _interchangeLevel('\0'), _inlineCodeExtensionIndicator('\0'), |
38 | 0 | _versionNumber('\0'), _appIndicator('\0'), _fieldControlLength(9), |
39 | 0 | _recLength(0), _leaderIden('L'), _fieldAreaStart(0), _sizeFieldLength(0), |
40 | 0 | _sizeFieldPos(0), _sizeFieldTag(0), nFieldDefnCount(0), |
41 | 0 | papoFieldDefns(nullptr), poRecord(nullptr), nCloneCount(0), |
42 | 0 | nMaxCloneCount(0), papoClones(nullptr) |
43 | 0 | { |
44 | 0 | strcpy(_extendedCharSet, " ! "); |
45 | 0 | } |
46 | | |
47 | | /************************************************************************/ |
48 | | /* ~DDFModule() */ |
49 | | /************************************************************************/ |
50 | | |
51 | | /** |
52 | | * The destructor. |
53 | | */ |
54 | | |
55 | | DDFModule::~DDFModule() |
56 | | |
57 | 0 | { |
58 | 0 | Close(); |
59 | 0 | } |
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 | | void DDFModule::Close() |
73 | | |
74 | 0 | { |
75 | | /* -------------------------------------------------------------------- */ |
76 | | /* Close the file. */ |
77 | | /* -------------------------------------------------------------------- */ |
78 | 0 | if (fpDDF != nullptr) |
79 | 0 | { |
80 | 0 | CPL_IGNORE_RET_VAL(VSIFCloseL(fpDDF)); |
81 | 0 | fpDDF = nullptr; |
82 | 0 | } |
83 | | |
84 | | /* -------------------------------------------------------------------- */ |
85 | | /* Cleanup the working record. */ |
86 | | /* -------------------------------------------------------------------- */ |
87 | 0 | if (poRecord != nullptr) |
88 | 0 | { |
89 | 0 | delete poRecord; |
90 | 0 | poRecord = nullptr; |
91 | 0 | } |
92 | | |
93 | | /* -------------------------------------------------------------------- */ |
94 | | /* Cleanup the clones. */ |
95 | | /* -------------------------------------------------------------------- */ |
96 | 0 | for (int i = 0; i < nCloneCount; i++) |
97 | 0 | { |
98 | 0 | papoClones[i]->RemoveIsCloneFlag(); |
99 | 0 | delete papoClones[i]; |
100 | 0 | } |
101 | 0 | nCloneCount = 0; |
102 | 0 | nMaxCloneCount = 0; |
103 | 0 | CPLFree(papoClones); |
104 | 0 | papoClones = nullptr; |
105 | | |
106 | | /* -------------------------------------------------------------------- */ |
107 | | /* Cleanup the field definitions. */ |
108 | | /* -------------------------------------------------------------------- */ |
109 | 0 | for (int i = 0; i < nFieldDefnCount; i++) |
110 | 0 | delete papoFieldDefns[i]; |
111 | 0 | CPLFree(papoFieldDefns); |
112 | 0 | papoFieldDefns = nullptr; |
113 | 0 | nFieldDefnCount = 0; |
114 | 0 | } |
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 | | int DDFModule::Open(const char *pszFilename, int bFailQuietly) |
138 | | |
139 | 0 | { |
140 | 0 | constexpr int nLeaderSize = 24; |
141 | | |
142 | | /* -------------------------------------------------------------------- */ |
143 | | /* Close the existing file if there is one. */ |
144 | | /* -------------------------------------------------------------------- */ |
145 | 0 | if (fpDDF != nullptr) |
146 | 0 | Close(); |
147 | | |
148 | | /* -------------------------------------------------------------------- */ |
149 | | /* Open the file. */ |
150 | | /* -------------------------------------------------------------------- */ |
151 | 0 | VSIStatBufL sStat; |
152 | 0 | if (VSIStatL(pszFilename, &sStat) == 0 && !VSI_ISDIR(sStat.st_mode)) |
153 | 0 | fpDDF = VSIFOpenL(pszFilename, "rb"); |
154 | |
|
155 | 0 | if (fpDDF == nullptr) |
156 | 0 | { |
157 | 0 | if (!bFailQuietly) |
158 | 0 | CPLError(CE_Failure, CPLE_OpenFailed, |
159 | 0 | "Unable to open DDF file `%s'.", pszFilename); |
160 | 0 | return FALSE; |
161 | 0 | } |
162 | | |
163 | | /* -------------------------------------------------------------------- */ |
164 | | /* Read the 24 byte leader. */ |
165 | | /* -------------------------------------------------------------------- */ |
166 | 0 | char achLeader[nLeaderSize]; |
167 | |
|
168 | 0 | if ((int)VSIFReadL(achLeader, 1, nLeaderSize, fpDDF) != nLeaderSize) |
169 | 0 | { |
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 | 0 | "Leader is short on DDF file `%s'.", pszFilename); |
176 | |
|
177 | 0 | return FALSE; |
178 | 0 | } |
179 | | |
180 | | /* -------------------------------------------------------------------- */ |
181 | | /* Verify that this appears to be a valid DDF file. */ |
182 | | /* -------------------------------------------------------------------- */ |
183 | 0 | int i, bValid = TRUE; |
184 | |
|
185 | 0 | for (i = 0; i < nLeaderSize; i++) |
186 | 0 | { |
187 | 0 | if (achLeader[i] < 32 || achLeader[i] > 126) |
188 | 0 | bValid = FALSE; |
189 | 0 | } |
190 | |
|
191 | 0 | if (achLeader[5] != '1' && achLeader[5] != '2' && achLeader[5] != '3') |
192 | 0 | bValid = FALSE; |
193 | |
|
194 | 0 | if (achLeader[6] != 'L') |
195 | 0 | bValid = FALSE; |
196 | 0 | if (achLeader[8] != '1' && achLeader[8] != ' ') |
197 | 0 | bValid = FALSE; |
198 | | |
199 | | /* -------------------------------------------------------------------- */ |
200 | | /* Extract information from leader. */ |
201 | | /* -------------------------------------------------------------------- */ |
202 | |
|
203 | 0 | if (bValid) |
204 | 0 | { |
205 | 0 | _recLength = DDFScanInt(achLeader + 0, 5); |
206 | 0 | _interchangeLevel = achLeader[5]; |
207 | 0 | _leaderIden = achLeader[6]; |
208 | 0 | _inlineCodeExtensionIndicator = achLeader[7]; |
209 | 0 | _versionNumber = achLeader[8]; |
210 | 0 | _appIndicator = achLeader[9]; |
211 | 0 | _fieldControlLength = DDFScanInt(achLeader + 10, 2); |
212 | 0 | _fieldAreaStart = DDFScanInt(achLeader + 12, 5); |
213 | 0 | _extendedCharSet[0] = achLeader[17]; |
214 | 0 | _extendedCharSet[1] = achLeader[18]; |
215 | 0 | _extendedCharSet[2] = achLeader[19]; |
216 | 0 | _extendedCharSet[3] = '\0'; |
217 | 0 | _sizeFieldLength = DDFScanInt(achLeader + 20, 1); |
218 | 0 | _sizeFieldPos = DDFScanInt(achLeader + 21, 1); |
219 | 0 | _sizeFieldTag = DDFScanInt(achLeader + 23, 1); |
220 | |
|
221 | 0 | if (_recLength < nLeaderSize || _fieldControlLength <= 0 || |
222 | 0 | _fieldAreaStart < 24 || _sizeFieldLength <= 0 || |
223 | 0 | _sizeFieldPos <= 0 || _sizeFieldTag <= 0) |
224 | 0 | { |
225 | 0 | bValid = FALSE; |
226 | 0 | } |
227 | 0 | } |
228 | | |
229 | | /* -------------------------------------------------------------------- */ |
230 | | /* If the header is invalid, then clean up, report the error */ |
231 | | /* and return. */ |
232 | | /* -------------------------------------------------------------------- */ |
233 | 0 | if (!bValid) |
234 | 0 | { |
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 | 0 | "File `%s' does not appear to have\n" |
241 | 0 | "a valid ISO 8211 header.\n", |
242 | 0 | pszFilename); |
243 | 0 | return FALSE; |
244 | 0 | } |
245 | | |
246 | | /* -------------------------------------------------------------------- */ |
247 | | /* Read the whole record info memory. */ |
248 | | /* -------------------------------------------------------------------- */ |
249 | 0 | char *pachRecord = (char *)CPLMalloc(_recLength); |
250 | 0 | memcpy(pachRecord, achLeader, nLeaderSize); |
251 | |
|
252 | 0 | if ((int)VSIFReadL(pachRecord + nLeaderSize, 1, _recLength - nLeaderSize, |
253 | 0 | fpDDF) != _recLength - nLeaderSize) |
254 | 0 | { |
255 | 0 | if (!bFailQuietly) |
256 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
257 | 0 | "Header record is short on DDF file `%s'.", pszFilename); |
258 | |
|
259 | 0 | CPLFree(pachRecord); |
260 | 0 | return FALSE; |
261 | 0 | } |
262 | | |
263 | | /* -------------------------------------------------------------------- */ |
264 | | /* First make a pass counting the directory entries. */ |
265 | | /* -------------------------------------------------------------------- */ |
266 | 0 | int nFieldEntryWidth, nFDCount = 0; |
267 | |
|
268 | 0 | nFieldEntryWidth = _sizeFieldLength + _sizeFieldPos + _sizeFieldTag; |
269 | |
|
270 | 0 | for (i = nLeaderSize; i + nFieldEntryWidth <= _recLength; |
271 | 0 | i += nFieldEntryWidth) |
272 | 0 | { |
273 | 0 | if (pachRecord[i] == DDF_FIELD_TERMINATOR) |
274 | 0 | break; |
275 | | |
276 | 0 | nFDCount++; |
277 | 0 | } |
278 | | |
279 | | /* -------------------------------------------------------------------- */ |
280 | | /* Allocate, and read field definitions. */ |
281 | | /* -------------------------------------------------------------------- */ |
282 | 0 | for (i = 0; i < nFDCount; i++) |
283 | 0 | { |
284 | 0 | char szTag[128]; |
285 | 0 | int nEntryOffset = nLeaderSize + i * nFieldEntryWidth; |
286 | 0 | int nFieldLength, nFieldPos; |
287 | |
|
288 | 0 | strncpy(szTag, pachRecord + nEntryOffset, _sizeFieldTag); |
289 | 0 | szTag[_sizeFieldTag] = '\0'; |
290 | |
|
291 | 0 | nEntryOffset += _sizeFieldTag; |
292 | 0 | nFieldLength = DDFScanInt(pachRecord + nEntryOffset, _sizeFieldLength); |
293 | |
|
294 | 0 | nEntryOffset += _sizeFieldLength; |
295 | 0 | nFieldPos = DDFScanInt(pachRecord + nEntryOffset, _sizeFieldPos); |
296 | |
|
297 | 0 | if (nFieldPos < 0 || nFieldPos > INT_MAX - _fieldAreaStart || |
298 | 0 | nFieldLength < |
299 | 0 | 2 || // DDFFieldDefn::Initialize() assumes at least 2 bytes |
300 | 0 | _recLength - (_fieldAreaStart + nFieldPos) < nFieldLength) |
301 | 0 | { |
302 | 0 | if (!bFailQuietly) |
303 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
304 | 0 | "Header record invalid on DDF file `%s'.", |
305 | 0 | pszFilename); |
306 | |
|
307 | 0 | CPLFree(pachRecord); |
308 | 0 | return FALSE; |
309 | 0 | } |
310 | | |
311 | 0 | DDFFieldDefn *poFDefn = new DDFFieldDefn(); |
312 | 0 | if (poFDefn->Initialize(this, szTag, nFieldLength, |
313 | 0 | pachRecord + _fieldAreaStart + nFieldPos)) |
314 | 0 | AddField(poFDefn); |
315 | 0 | else |
316 | 0 | delete poFDefn; |
317 | 0 | } |
318 | | |
319 | 0 | CPLFree(pachRecord); |
320 | | |
321 | | /* -------------------------------------------------------------------- */ |
322 | | /* Record the current file offset, the beginning of the first */ |
323 | | /* data record. */ |
324 | | /* -------------------------------------------------------------------- */ |
325 | 0 | nFirstRecordOffset = (long)VSIFTellL(fpDDF); |
326 | |
|
327 | 0 | return TRUE; |
328 | 0 | } |
329 | | |
330 | | /************************************************************************/ |
331 | | /* Initialize() */ |
332 | | /************************************************************************/ |
333 | | |
334 | | 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 | 0 | { |
341 | 0 | _interchangeLevel = chInterchangeLevel; |
342 | 0 | _leaderIden = chLeaderIden; |
343 | 0 | _inlineCodeExtensionIndicator = chCodeExtensionIndicator; |
344 | 0 | _versionNumber = chVersionNumber; |
345 | 0 | _appIndicator = chAppIndicator; |
346 | 0 | snprintf(_extendedCharSet, sizeof(_extendedCharSet), "%s", |
347 | 0 | pszExtendedCharSet); |
348 | 0 | _sizeFieldLength = nSizeFieldLength; |
349 | 0 | _sizeFieldPos = nSizeFieldPos; |
350 | 0 | _sizeFieldTag = nSizeFieldTag; |
351 | |
|
352 | 0 | return TRUE; |
353 | 0 | } |
354 | | |
355 | | /************************************************************************/ |
356 | | /* Create() */ |
357 | | /************************************************************************/ |
358 | | |
359 | | int DDFModule::Create(const char *pszFilename) |
360 | | |
361 | 0 | { |
362 | 0 | CPLAssert(fpDDF == nullptr); |
363 | | |
364 | | /* -------------------------------------------------------------------- */ |
365 | | /* Create the file on disk. */ |
366 | | /* -------------------------------------------------------------------- */ |
367 | 0 | fpDDF = VSIFOpenL(pszFilename, "wb+"); |
368 | 0 | if (fpDDF == nullptr) |
369 | 0 | { |
370 | 0 | CPLError(CE_Failure, CPLE_OpenFailed, |
371 | 0 | "Failed to create file %s, check path and permissions.", |
372 | 0 | pszFilename); |
373 | 0 | return FALSE; |
374 | 0 | } |
375 | | |
376 | 0 | bReadOnly = FALSE; |
377 | | |
378 | | /* -------------------------------------------------------------------- */ |
379 | | /* Prepare all the field definition information. */ |
380 | | /* -------------------------------------------------------------------- */ |
381 | 0 | int iField; |
382 | |
|
383 | 0 | _recLength = |
384 | 0 | 24 + |
385 | 0 | nFieldDefnCount * (_sizeFieldLength + _sizeFieldPos + _sizeFieldTag) + |
386 | 0 | 1; |
387 | |
|
388 | 0 | _fieldAreaStart = _recLength; |
389 | |
|
390 | 0 | for (iField = 0; iField < nFieldDefnCount; iField++) |
391 | 0 | { |
392 | 0 | int nLength; |
393 | |
|
394 | 0 | papoFieldDefns[iField]->GenerateDDREntry(this, nullptr, &nLength); |
395 | 0 | _recLength += nLength; |
396 | 0 | } |
397 | | |
398 | | /* -------------------------------------------------------------------- */ |
399 | | /* Setup 24 byte leader. */ |
400 | | /* -------------------------------------------------------------------- */ |
401 | 0 | char achLeader[25]; |
402 | |
|
403 | 0 | snprintf(achLeader + 0, sizeof(achLeader) - 0, "%05d", (int)_recLength); |
404 | 0 | achLeader[5] = _interchangeLevel; |
405 | 0 | achLeader[6] = _leaderIden; |
406 | 0 | achLeader[7] = _inlineCodeExtensionIndicator; |
407 | 0 | achLeader[8] = _versionNumber; |
408 | 0 | achLeader[9] = _appIndicator; |
409 | 0 | snprintf(achLeader + 10, sizeof(achLeader) - 10, "%02d", |
410 | 0 | (int)_fieldControlLength); |
411 | 0 | snprintf(achLeader + 12, sizeof(achLeader) - 12, "%05d", |
412 | 0 | (int)_fieldAreaStart); |
413 | 0 | memcpy(achLeader + 17, _extendedCharSet, 3); |
414 | 0 | snprintf(achLeader + 20, sizeof(achLeader) - 20, "%1d", |
415 | 0 | (int)_sizeFieldLength); |
416 | 0 | snprintf(achLeader + 21, sizeof(achLeader) - 21, "%1d", (int)_sizeFieldPos); |
417 | 0 | achLeader[22] = '0'; |
418 | 0 | snprintf(achLeader + 23, sizeof(achLeader) - 23, "%1d", (int)_sizeFieldTag); |
419 | 0 | int bRet = VSIFWriteL(achLeader, 24, 1, fpDDF) > 0; |
420 | | |
421 | | /* -------------------------------------------------------------------- */ |
422 | | /* Write out directory entries. */ |
423 | | /* -------------------------------------------------------------------- */ |
424 | 0 | int nOffset = 0; |
425 | 0 | for (iField = 0; iField < nFieldDefnCount; iField++) |
426 | 0 | { |
427 | 0 | char achDirEntry[255]; |
428 | 0 | char szFormat[32]; |
429 | 0 | int nLength; |
430 | |
|
431 | 0 | CPLAssert(_sizeFieldLength + _sizeFieldPos + _sizeFieldTag < |
432 | 0 | (int)sizeof(achDirEntry)); |
433 | |
|
434 | 0 | papoFieldDefns[iField]->GenerateDDREntry(this, nullptr, &nLength); |
435 | |
|
436 | 0 | CPLAssert((int)strlen(papoFieldDefns[iField]->GetName()) == |
437 | 0 | _sizeFieldTag); |
438 | 0 | snprintf(achDirEntry, sizeof(achDirEntry), "%s", |
439 | 0 | papoFieldDefns[iField]->GetName()); |
440 | 0 | snprintf(szFormat, sizeof(szFormat), "%%0%dd", (int)_sizeFieldLength); |
441 | 0 | snprintf(achDirEntry + _sizeFieldTag, |
442 | 0 | sizeof(achDirEntry) - _sizeFieldTag, szFormat, nLength); |
443 | 0 | snprintf(szFormat, sizeof(szFormat), "%%0%dd", (int)_sizeFieldPos); |
444 | 0 | snprintf(achDirEntry + _sizeFieldTag + _sizeFieldLength, |
445 | 0 | sizeof(achDirEntry) - _sizeFieldTag - _sizeFieldLength, |
446 | 0 | szFormat, nOffset); |
447 | 0 | nOffset += nLength; |
448 | |
|
449 | 0 | bRet &= VSIFWriteL(achDirEntry, |
450 | 0 | _sizeFieldLength + _sizeFieldPos + _sizeFieldTag, 1, |
451 | 0 | fpDDF) > 0; |
452 | 0 | } |
453 | |
|
454 | 0 | char chUT = DDF_FIELD_TERMINATOR; |
455 | 0 | bRet &= VSIFWriteL(&chUT, 1, 1, fpDDF) > 0; |
456 | | |
457 | | /* -------------------------------------------------------------------- */ |
458 | | /* Write out the field descriptions themselves. */ |
459 | | /* -------------------------------------------------------------------- */ |
460 | 0 | for (iField = 0; iField < nFieldDefnCount; iField++) |
461 | 0 | { |
462 | 0 | char *pachData = nullptr; |
463 | 0 | int nLength = 0; |
464 | |
|
465 | 0 | papoFieldDefns[iField]->GenerateDDREntry(this, &pachData, &nLength); |
466 | 0 | bRet &= VSIFWriteL(pachData, nLength, 1, fpDDF) > 0; |
467 | 0 | CPLFree(pachData); |
468 | 0 | } |
469 | |
|
470 | 0 | return bRet ? TRUE : FALSE; |
471 | 0 | } |
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 | | void DDFModule::Dump(FILE *fp) |
488 | | |
489 | 0 | { |
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 | 0 | { |
507 | 0 | papoFieldDefns[i]->Dump(fp); |
508 | 0 | } |
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 | | const DDFFieldDefn *DDFModule::FindFieldDefn(const char *pszFieldName) const |
530 | | |
531 | 0 | { |
532 | 0 | 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 | 0 | for (i = 0; i < nFieldDefnCount; i++) |
539 | 0 | { |
540 | 0 | const char *pszThisName = papoFieldDefns[i]->GetName(); |
541 | |
|
542 | 0 | if (*pszThisName == *pszFieldName && *pszFieldName != '\0' && |
543 | 0 | strcmp(pszFieldName + 1, pszThisName + 1) == 0) |
544 | 0 | return papoFieldDefns[i]; |
545 | 0 | } |
546 | | |
547 | | /* -------------------------------------------------------------------- */ |
548 | | /* Now do a more general check. Application code may not */ |
549 | | /* always use the correct name case. */ |
550 | | /* -------------------------------------------------------------------- */ |
551 | 0 | for (i = 0; i < nFieldDefnCount; i++) |
552 | 0 | { |
553 | 0 | if (EQUAL(pszFieldName, papoFieldDefns[i]->GetName())) |
554 | 0 | return papoFieldDefns[i]; |
555 | 0 | } |
556 | | |
557 | 0 | return nullptr; |
558 | 0 | } |
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 | | DDFRecord *DDFModule::ReadRecord() |
579 | | |
580 | 0 | { |
581 | 0 | if (poRecord == nullptr) |
582 | 0 | poRecord = new DDFRecord(this); |
583 | |
|
584 | 0 | if (poRecord->Read()) |
585 | 0 | return poRecord; |
586 | 0 | else |
587 | 0 | return nullptr; |
588 | 0 | } |
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 | | void DDFModule::AddField(DDFFieldDefn *poNewFDefn) |
605 | | |
606 | 0 | { |
607 | 0 | nFieldDefnCount++; |
608 | 0 | papoFieldDefns = (DDFFieldDefn **)CPLRealloc( |
609 | 0 | papoFieldDefns, sizeof(void *) * nFieldDefnCount); |
610 | 0 | papoFieldDefns[nFieldDefnCount - 1] = poNewFDefn; |
611 | 0 | } |
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 | | DDFFieldDefn *DDFModule::GetField(int i) |
625 | | |
626 | 0 | { |
627 | 0 | if (i < 0 || i >= nFieldDefnCount) |
628 | 0 | return nullptr; |
629 | 0 | else |
630 | 0 | return papoFieldDefns[i]; |
631 | 0 | } |
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 | | void DDFModule::AddCloneRecord(DDFRecord *poRecordIn) |
641 | | |
642 | 0 | { |
643 | | /* -------------------------------------------------------------------- */ |
644 | | /* Do we need to grow the container array? */ |
645 | | /* -------------------------------------------------------------------- */ |
646 | 0 | if (nCloneCount == nMaxCloneCount) |
647 | 0 | { |
648 | 0 | nMaxCloneCount = nCloneCount * 2 + 20; |
649 | 0 | papoClones = (DDFRecord **)CPLRealloc(papoClones, |
650 | 0 | nMaxCloneCount * sizeof(void *)); |
651 | 0 | } |
652 | | |
653 | | /* -------------------------------------------------------------------- */ |
654 | | /* Add to the list. */ |
655 | | /* -------------------------------------------------------------------- */ |
656 | 0 | papoClones[nCloneCount++] = poRecordIn; |
657 | 0 | } |
658 | | |
659 | | /************************************************************************/ |
660 | | /* RemoveCloneRecord() */ |
661 | | /************************************************************************/ |
662 | | |
663 | | void DDFModule::RemoveCloneRecord(DDFRecord *poRecordIn) |
664 | | |
665 | 0 | { |
666 | 0 | int i; |
667 | |
|
668 | 0 | for (i = 0; i < nCloneCount; i++) |
669 | 0 | { |
670 | 0 | if (papoClones[i] == poRecordIn) |
671 | 0 | { |
672 | 0 | papoClones[i] = papoClones[nCloneCount - 1]; |
673 | 0 | nCloneCount--; |
674 | 0 | return; |
675 | 0 | } |
676 | 0 | } |
677 | | |
678 | 0 | CPLAssert(false); |
679 | 0 | } |
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 | | void DDFModule::Rewind(long nOffset) |
696 | | |
697 | 0 | { |
698 | 0 | if (nOffset == -1) |
699 | 0 | nOffset = nFirstRecordOffset; |
700 | |
|
701 | 0 | if (fpDDF == nullptr) |
702 | 0 | return; |
703 | | |
704 | 0 | if (VSIFSeekL(fpDDF, nOffset, SEEK_SET) < 0) |
705 | 0 | return; |
706 | | |
707 | 0 | if (nOffset == nFirstRecordOffset && poRecord != nullptr) |
708 | 0 | poRecord->Clear(); |
709 | 0 | } |