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