Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: ISO 8211 Access
4 : * Purpose: Implements the DDFRecord class.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 1999, Frank Warmerdam
9 : * Copyright (c) 2010-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 <cstddef>
18 : #include <cstdio>
19 : #include <cstring>
20 :
21 : #include "cpl_conv.h"
22 : #include "cpl_error.h"
23 : #include "cpl_vsi.h"
24 :
25 : constexpr int nLeaderSize = 24;
26 :
27 : /************************************************************************/
28 : /* DDFRecord() */
29 : /************************************************************************/
30 :
31 27973 : DDFRecord::DDFRecord(DDFModule *poModuleIn)
32 27973 : : poModule(poModuleIn), _sizeFieldTag(poModuleIn->GetSizeFieldTag())
33 : {
34 27973 : }
35 :
36 : /************************************************************************/
37 : /* ~DDFRecord() */
38 : /************************************************************************/
39 :
40 27973 : DDFRecord::~DDFRecord()
41 :
42 : {
43 27973 : Clear();
44 27973 : }
45 :
46 : /************************************************************************/
47 : /* Dump() */
48 : /************************************************************************/
49 :
50 : /**
51 : * Write out record contents to debugging file.
52 : *
53 : * A variety of information about this record, and all its fields and
54 : * subfields is written to the given debugging file handle. Note that
55 : * field definition information (ala DDFFieldDefn) isn't written.
56 : *
57 : * @param fp The standard IO file handle to write to. i.e. stderr
58 : */
59 :
60 0 : void DDFRecord::Dump(FILE *fp) const
61 :
62 : {
63 0 : fprintf(fp, "DDFRecord:\n");
64 0 : fprintf(fp, " bReuseHeader = %d\n", bReuseHeader);
65 0 : fprintf(fp, " nDataSize = %d\n", GetDataSize());
66 0 : fprintf(fp, " _sizeFieldLength=%d, _sizeFieldPos=%d, _sizeFieldTag=%d\n",
67 0 : _sizeFieldLength, _sizeFieldPos, _sizeFieldTag);
68 :
69 0 : for (const auto &oField : aoFields)
70 : {
71 0 : oField.Dump(fp);
72 : }
73 0 : }
74 :
75 : /************************************************************************/
76 : /* Read() */
77 : /* */
78 : /* Read a record of data from the file, and parse the header to */
79 : /* build a field list for the record (or reuse the existing one */
80 : /* if reusing headers). It is expected that the file pointer */
81 : /* will be positioned at the beginning of a data record. It is */
82 : /* the DDFModule's responsibility to do so. */
83 : /* */
84 : /* This method should only be called by the DDFModule class. */
85 : /************************************************************************/
86 :
87 28036 : int DDFRecord::Read()
88 :
89 : {
90 : /* -------------------------------------------------------------------- */
91 : /* Redefine the record on the basis of the header if needed. */
92 : /* As a side effect this will read the data for the record as well.*/
93 : /* -------------------------------------------------------------------- */
94 28036 : if (!bReuseHeader)
95 : {
96 28036 : return ReadHeader();
97 : }
98 0 : if (nFieldOffset < 0)
99 0 : return FALSE;
100 :
101 : /* -------------------------------------------------------------------- */
102 : /* Otherwise we read just the data and carefully overlay it on */
103 : /* the previous records data without disturbing the rest of the */
104 : /* record. */
105 : /* -------------------------------------------------------------------- */
106 : size_t nReadBytes;
107 :
108 0 : CPLAssert(nFieldOffset <= static_cast<int>(osData.size()));
109 0 : nReadBytes = VSIFReadL(osData.data() + nFieldOffset, 1,
110 0 : osData.size() - nFieldOffset, poModule->GetFP());
111 0 : if (nReadBytes != osData.size() - nFieldOffset && nReadBytes == 0 &&
112 0 : VSIFEofL(poModule->GetFP()))
113 : {
114 0 : return FALSE;
115 : }
116 0 : else if (nReadBytes != osData.size() - nFieldOffset)
117 : {
118 0 : CPLError(CE_Failure, CPLE_FileIO, "Data record is short on DDF file.");
119 :
120 0 : return FALSE;
121 : }
122 :
123 : // notdef: eventually we may have to do something at this point to
124 : // notify the DDFField's that their data values have changed.
125 :
126 0 : return TRUE;
127 : }
128 :
129 : /************************************************************************/
130 : /* Write() */
131 : /************************************************************************/
132 :
133 : /**
134 : * Write record out to module.
135 : *
136 : * This method writes the current record to the module to which it is
137 : * attached. Normally this would be at the end of the file, and only used
138 : * for modules newly created with DDFModule::Create(). Rewriting existing
139 : * records is not supported at this time. Calling Write() multiple times
140 : * on a DDFRecord will result it multiple copies being written at the end of
141 : * the module.
142 : *
143 : * @return TRUE on success or FALSE on failure.
144 : */
145 :
146 224 : int DDFRecord::Write()
147 :
148 : {
149 224 : ResetDirectory();
150 :
151 : /* -------------------------------------------------------------------- */
152 : /* Prepare leader. */
153 : /* -------------------------------------------------------------------- */
154 : char szLeader[nLeaderSize + 1];
155 :
156 224 : memset(szLeader, ' ', nLeaderSize);
157 :
158 224 : snprintf(szLeader + 0, sizeof(szLeader) - 0, "%05d",
159 224 : static_cast<int>(osData.size() + nLeaderSize));
160 224 : szLeader[5] = ' ';
161 224 : szLeader[6] = 'D';
162 :
163 224 : snprintf(szLeader + 12, sizeof(szLeader) - 12, "%05d",
164 224 : static_cast<int>(nFieldOffset + nLeaderSize));
165 224 : szLeader[17] = ' ';
166 :
167 224 : szLeader[20] = static_cast<char>('0' + _sizeFieldLength);
168 224 : szLeader[21] = static_cast<char>('0' + _sizeFieldPos);
169 224 : szLeader[22] = '0';
170 224 : szLeader[23] = static_cast<char>('0' + _sizeFieldTag);
171 :
172 : /* notdef: lots of stuff missing */
173 :
174 : /* -------------------------------------------------------------------- */
175 : /* Write the leader. */
176 : /* -------------------------------------------------------------------- */
177 224 : int bRet = VSIFWriteL(szLeader, nLeaderSize, 1, poModule->GetFP()) > 0;
178 :
179 : /* -------------------------------------------------------------------- */
180 : /* Write the remainder of the record. */
181 : /* -------------------------------------------------------------------- */
182 224 : bRet &= VSIFWriteL(osData.data(), osData.size(), 1, poModule->GetFP()) > 0;
183 :
184 224 : return bRet ? TRUE : FALSE;
185 : }
186 :
187 : /************************************************************************/
188 : /* Clear() */
189 : /* */
190 : /* Clear any information associated with the last header in */
191 : /* preparation for reading a new header. */
192 : /************************************************************************/
193 :
194 56009 : void DDFRecord::Clear()
195 :
196 : {
197 56009 : aoFields.clear();
198 56009 : osData.clear();
199 56009 : bReuseHeader = FALSE;
200 56009 : }
201 :
202 : /************************************************************************/
203 : /* ReadHeader() */
204 : /* */
205 : /* This perform the header reading and parsing job for the */
206 : /* Read() method. It reads the header, and builds a field */
207 : /* list. */
208 : /************************************************************************/
209 :
210 28036 : int DDFRecord::ReadHeader()
211 :
212 : {
213 : /* -------------------------------------------------------------------- */
214 : /* Clear any existing information. */
215 : /* -------------------------------------------------------------------- */
216 28036 : Clear();
217 :
218 : /* -------------------------------------------------------------------- */
219 : /* Read the 24 byte leader. */
220 : /* -------------------------------------------------------------------- */
221 : char achLeader[nLeaderSize];
222 : int nReadBytes;
223 :
224 28036 : nReadBytes = static_cast<int>(
225 28036 : VSIFReadL(achLeader, 1, nLeaderSize, poModule->GetFP()));
226 28036 : if (nReadBytes == 0 && VSIFEofL(poModule->GetFP()))
227 : {
228 74 : nFieldOffset = -1;
229 74 : return FALSE;
230 : }
231 : // The ASRP and USRP specifications mentions that 0x5E / ^ character can be
232 : // used as a padding byte so that the file size is a multiple of 8192.
233 27962 : else if (achLeader[0] == '^')
234 : {
235 8 : nFieldOffset = -1;
236 8 : return FALSE;
237 : }
238 27954 : else if (nReadBytes != static_cast<int>(nLeaderSize))
239 : {
240 0 : CPLError(CE_Failure, CPLE_FileIO, "Leader is short on DDF file.");
241 0 : nFieldOffset = -1;
242 0 : return FALSE;
243 : }
244 :
245 : /* -------------------------------------------------------------------- */
246 : /* Extract information from leader. */
247 : /* -------------------------------------------------------------------- */
248 : int _recLength, _fieldAreaStart;
249 : char _leaderIden;
250 :
251 27954 : _recLength = DDFScanInt(achLeader + 0, 5);
252 27954 : _leaderIden = achLeader[6];
253 27954 : _fieldAreaStart = DDFScanInt(achLeader + 12, 5);
254 :
255 27954 : _sizeFieldLength = achLeader[20] - '0';
256 27954 : _sizeFieldPos = achLeader[21] - '0';
257 27954 : _sizeFieldTag = achLeader[23] - '0';
258 :
259 27954 : if (_sizeFieldLength <= 0 || _sizeFieldLength > 9 || _sizeFieldPos <= 0 ||
260 27954 : _sizeFieldPos > 9 || _sizeFieldTag <= 0 || _sizeFieldTag > 9)
261 : {
262 0 : CPLError(CE_Failure, CPLE_AppDefined,
263 : "ISO8211 record leader appears to be corrupt.");
264 0 : nFieldOffset = -1;
265 0 : return FALSE;
266 : }
267 :
268 27954 : if (_leaderIden == 'R')
269 0 : bReuseHeader = TRUE;
270 :
271 27954 : nFieldOffset = _fieldAreaStart - nLeaderSize;
272 :
273 : /* -------------------------------------------------------------------- */
274 : /* Is there anything seemly screwy about this record? */
275 : /* -------------------------------------------------------------------- */
276 27954 : if (((_recLength <= 24 || _recLength > 100000000) && (_recLength != 0)) ||
277 27954 : _fieldAreaStart < 24 || _fieldAreaStart > 100000)
278 : {
279 0 : CPLError(
280 : CE_Failure, CPLE_FileIO,
281 : "Data record appears to be corrupt on DDF file.\n"
282 : " -- ensure that the files were uncompressed without modifying\n"
283 : "carriage return/linefeeds (by default WINZIP does this).");
284 0 : nFieldOffset = -1;
285 0 : return FALSE;
286 : }
287 :
288 : /* ==================================================================== */
289 : /* Handle the normal case with the record length available. */
290 : /* ==================================================================== */
291 27954 : if (_recLength != 0)
292 : {
293 : /* --------------------------------------------------------------------
294 : */
295 : /* Read the remainder of the record. */
296 : /* --------------------------------------------------------------------
297 : */
298 27953 : int nDataSize = _recLength - nLeaderSize;
299 27953 : osData.resize(nDataSize);
300 :
301 55906 : if (VSIFReadL(osData.data(), 1, osData.size(), poModule->GetFP()) !=
302 27953 : osData.size())
303 : {
304 0 : CPLError(CE_Failure, CPLE_FileIO,
305 : "Data record is short on DDF file.");
306 0 : nFieldOffset = -1;
307 0 : return FALSE;
308 : }
309 :
310 : /* --------------------------------------------------------------------
311 : */
312 : /* If we don't find a field terminator at the end of the record */
313 : /* we will read extra bytes till we get to it. */
314 : /* --------------------------------------------------------------------
315 : */
316 27955 : while (!osData.empty() && osData.back() != DDF_FIELD_TERMINATOR &&
317 1 : (osData.size() < 2 ||
318 1 : osData[osData.size() - 2] != DDF_FIELD_TERMINATOR))
319 : {
320 0 : osData.resize(osData.size() + 1);
321 :
322 0 : if (VSIFReadL(&(osData.back()), 1, 1, poModule->GetFP()) != 1)
323 : {
324 0 : CPLError(CE_Failure, CPLE_FileIO,
325 : "Data record is short on DDF file.");
326 0 : nFieldOffset = -1;
327 0 : return FALSE;
328 : }
329 : static bool bFirstTime = true;
330 0 : if (bFirstTime)
331 : {
332 0 : bFirstTime = false;
333 0 : CPLDebug("ISO8211",
334 : "Didn't find field terminator, read one more byte.");
335 : }
336 : }
337 :
338 27953 : if (nFieldOffset >= static_cast<int>(osData.size()))
339 : {
340 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
341 : "nFieldOffset < static_cast<int>(osData.size())");
342 0 : nFieldOffset = -1;
343 0 : return FALSE;
344 : }
345 :
346 : /* --------------------------------------------------------------------
347 : */
348 : /* Loop over the directory entries, making a pass counting them. */
349 : /* --------------------------------------------------------------------
350 : */
351 : int i;
352 : int nFieldEntryWidth;
353 :
354 27953 : nFieldEntryWidth = _sizeFieldLength + _sizeFieldPos + _sizeFieldTag;
355 27953 : if (nFieldEntryWidth <= 0)
356 : {
357 0 : CPLError(CE_Failure, CPLE_FileIO, "Invalid entry width = %d",
358 : nFieldEntryWidth);
359 0 : nFieldOffset = -1;
360 0 : return FALSE;
361 : }
362 :
363 27953 : int nFieldCount = 0;
364 138414 : for (i = 0; i + nFieldEntryWidth <= static_cast<int>(osData.size());
365 110461 : i += nFieldEntryWidth)
366 : {
367 138414 : if (osData[i] == DDF_FIELD_TERMINATOR)
368 27953 : break;
369 :
370 110461 : nFieldCount++;
371 : }
372 :
373 : /* --------------------------------------------------------------------
374 : */
375 : /* Allocate, and read field definitions. */
376 : /* --------------------------------------------------------------------
377 : */
378 27953 : aoFields.resize(nFieldCount);
379 :
380 138414 : for (i = 0; i < nFieldCount; i++)
381 : {
382 : char szTag[128];
383 110461 : int nEntryOffset = i * nFieldEntryWidth;
384 : int nFieldLength, nFieldPos;
385 :
386 : /* --------------------------------------------------------------------
387 : */
388 : /* Read the position information and tag. */
389 : /* --------------------------------------------------------------------
390 : */
391 110461 : strncpy(szTag, osData.c_str() + nEntryOffset, _sizeFieldTag);
392 110461 : szTag[_sizeFieldTag] = '\0';
393 :
394 110461 : nEntryOffset += _sizeFieldTag;
395 : nFieldLength =
396 110461 : DDFScanInt(osData.c_str() + nEntryOffset, _sizeFieldLength);
397 :
398 110461 : nEntryOffset += _sizeFieldLength;
399 : nFieldPos =
400 110461 : DDFScanInt(osData.c_str() + nEntryOffset, _sizeFieldPos);
401 :
402 : /* --------------------------------------------------------------------
403 : */
404 : /* Find the corresponding field in the module directory. */
405 : /* --------------------------------------------------------------------
406 : */
407 110461 : DDFFieldDefn *poFieldDefn = poModule->FindFieldDefn(szTag);
408 :
409 110461 : if (poFieldDefn == nullptr || nFieldLength < 0 || nFieldPos < 0)
410 : {
411 0 : CPLError(CE_Failure, CPLE_AppDefined,
412 : "Undefined field `%s' encountered in data record.",
413 : szTag);
414 0 : return FALSE;
415 : }
416 :
417 220922 : if (_fieldAreaStart + nFieldPos - nLeaderSize < 0 ||
418 110461 : static_cast<int>(osData.size()) -
419 110461 : (_fieldAreaStart + nFieldPos - nLeaderSize) <
420 : nFieldLength)
421 : {
422 0 : CPLError(CE_Failure, CPLE_AppDefined,
423 : "Not enough byte to initialize field `%s'.", szTag);
424 0 : nFieldOffset = -1;
425 0 : return FALSE;
426 : }
427 :
428 : /* --------------------------------------------------------------------
429 : */
430 : /* Assign info the DDFField. */
431 : /* --------------------------------------------------------------------
432 : */
433 110461 : aoFields[i].Initialize(poFieldDefn,
434 110461 : osData.c_str() + _fieldAreaStart +
435 110461 : nFieldPos - nLeaderSize,
436 : nFieldLength);
437 : }
438 :
439 27953 : return TRUE;
440 : }
441 : /* ==================================================================== */
442 : /* Handle the exceptional case where the record length is */
443 : /* zero. In this case we have to read all the data based on */
444 : /* the size of data items as per ISO8211 spec Annex C, 1.5.1. */
445 : /* */
446 : /* See Bugzilla bug 181 and test with file US4CN21M.000. */
447 : /* ==================================================================== */
448 : else
449 : {
450 1 : CPLDebug("ISO8211",
451 : "Record with zero length, use variant (C.1.5.1) logic.");
452 :
453 : /* ----------------------------------------------------------------- */
454 : /* _recLength == 0, handle the large record. */
455 : /* */
456 : /* Read the remainder of the record. */
457 : /* ----------------------------------------------------------------- */
458 1 : osData.clear();
459 :
460 : /* ----------------------------------------------------------------- */
461 : /* Loop over the directory entries, making a pass counting them. */
462 : /* ----------------------------------------------------------------- */
463 1 : int nFieldEntryWidth = _sizeFieldLength + _sizeFieldPos + _sizeFieldTag;
464 1 : int nFieldCount = 0;
465 1 : int i = 0;
466 :
467 1 : if (nFieldEntryWidth == 0)
468 : {
469 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
470 : "Invalid record buffer size : %d.", nFieldEntryWidth);
471 0 : nFieldOffset = -1;
472 0 : return FALSE;
473 : }
474 :
475 2 : std::string osEntry;
476 1 : osEntry.resize(nFieldEntryWidth);
477 :
478 : // while we're not at the end, store this entry,
479 : // and keep on reading...
480 2 : do
481 : {
482 : // read an Entry:
483 3 : if (nFieldEntryWidth !=
484 3 : static_cast<int>(VSIFReadL(osEntry.data(), 1, nFieldEntryWidth,
485 3 : poModule->GetFP())))
486 : {
487 0 : CPLError(CE_Failure, CPLE_FileIO,
488 : "Data record is short on DDF file.");
489 0 : nFieldOffset = -1;
490 0 : return FALSE;
491 : }
492 :
493 3 : osData.append(osEntry.c_str(), nFieldEntryWidth);
494 :
495 3 : if (DDF_FIELD_TERMINATOR != osEntry[0])
496 : {
497 2 : nFieldCount++;
498 2 : if (nFieldCount == 1000)
499 : {
500 0 : CPLError(CE_Failure, CPLE_FileIO,
501 : "Too many fields in DDF file.");
502 0 : nFieldOffset = -1;
503 0 : return FALSE;
504 : }
505 : }
506 3 : } while (DDF_FIELD_TERMINATOR != osEntry[0]);
507 :
508 : // --------------------------------------------------------------------
509 : // Now, rewind a little. Only the TERMINATOR should have been read
510 : // --------------------------------------------------------------------
511 1 : int rewindSize = nFieldEntryWidth - 1;
512 1 : VSILFILE *fp = poModule->GetFP();
513 1 : vsi_l_offset pos = VSIFTellL(fp) - rewindSize;
514 1 : if (VSIFSeekL(fp, pos, SEEK_SET) < 0)
515 0 : return FALSE;
516 1 : osData.resize(osData.size() - rewindSize);
517 :
518 : // --------------------------------------------------------------------
519 : // Okay, now let's populate the heck out of osData...
520 : // --------------------------------------------------------------------
521 3 : for (i = 0; i < nFieldCount; i++)
522 : {
523 2 : int nEntryOffset = (i * nFieldEntryWidth) + _sizeFieldTag;
524 : int nFieldLength =
525 2 : DDFScanInt(osData.c_str() + nEntryOffset, _sizeFieldLength);
526 2 : if (nFieldLength < 0)
527 : {
528 0 : nFieldOffset = -1;
529 0 : return FALSE;
530 : }
531 :
532 2 : osEntry.resize(nFieldLength);
533 :
534 : // read an Entry:
535 2 : if (nFieldLength !=
536 2 : static_cast<int>(VSIFReadL(osEntry.data(), 1, nFieldLength,
537 2 : poModule->GetFP())))
538 : {
539 0 : CPLError(CE_Failure, CPLE_FileIO,
540 : "Data record is short on DDF file.");
541 0 : nFieldOffset = -1;
542 0 : return FALSE;
543 : }
544 :
545 : // move this temp buffer into more permanent storage:
546 2 : osData.append(osEntry.data(), nFieldLength);
547 : }
548 :
549 1 : if (nFieldOffset >= static_cast<int>(osData.size()))
550 : {
551 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
552 : "nFieldOffset < static_cast<int>(osData.size())");
553 0 : nFieldOffset = -1;
554 0 : return FALSE;
555 : }
556 :
557 : /* ----------------------------------------------------------------- */
558 : /* Allocate, and read field definitions. */
559 : /* ----------------------------------------------------------------- */
560 1 : aoFields.resize(nFieldCount);
561 :
562 3 : for (i = 0; i < nFieldCount; i++)
563 : {
564 : char szTag[128];
565 2 : int nEntryOffset = i * nFieldEntryWidth;
566 : int nFieldLength, nFieldPos;
567 :
568 : /* ------------------------------------------------------------- */
569 : /* Read the position information and tag. */
570 : /* ------------------------------------------------------------- */
571 2 : strncpy(szTag, osData.c_str() + nEntryOffset, _sizeFieldTag);
572 2 : szTag[_sizeFieldTag] = '\0';
573 :
574 2 : nEntryOffset += _sizeFieldTag;
575 : nFieldLength =
576 2 : DDFScanInt(osData.c_str() + nEntryOffset, _sizeFieldLength);
577 :
578 2 : nEntryOffset += _sizeFieldLength;
579 : nFieldPos =
580 2 : DDFScanInt(osData.c_str() + nEntryOffset, _sizeFieldPos);
581 :
582 : /* ------------------------------------------------------------- */
583 : /* Find the corresponding field in the module directory. */
584 : /* ------------------------------------------------------------- */
585 2 : DDFFieldDefn *poFieldDefn = poModule->FindFieldDefn(szTag);
586 :
587 2 : if (poFieldDefn == nullptr || nFieldLength < 0 || nFieldPos < 0)
588 : {
589 0 : CPLError(CE_Failure, CPLE_AppDefined,
590 : "Undefined field `%s' encountered in data record.",
591 : szTag);
592 0 : nFieldOffset = -1;
593 0 : return FALSE;
594 : }
595 :
596 4 : if (_fieldAreaStart + nFieldPos - nLeaderSize < 0 ||
597 2 : static_cast<int>(osData.size()) -
598 2 : (_fieldAreaStart + nFieldPos - nLeaderSize) <
599 : nFieldLength)
600 : {
601 0 : CPLError(CE_Failure, CPLE_AppDefined,
602 : "Not enough byte to initialize field `%s'.", szTag);
603 0 : nFieldOffset = -1;
604 0 : return FALSE;
605 : }
606 :
607 : /* ------------------------------------------------------------- */
608 : /* Assign info the DDFField. */
609 : /* ------------------------------------------------------------- */
610 :
611 2 : aoFields[i].Initialize(poFieldDefn,
612 2 : osData.c_str() + _fieldAreaStart +
613 2 : nFieldPos - nLeaderSize,
614 : nFieldLength);
615 : }
616 :
617 1 : return TRUE;
618 : }
619 : }
620 :
621 : /************************************************************************/
622 : /* FindField() */
623 : /************************************************************************/
624 :
625 : /**
626 : * Find the named field within this record.
627 : *
628 : * @param pszName The name of the field to fetch. The comparison is
629 : * case insensitive.
630 : * @param iFieldIndex The instance of this field to fetch. Use zero (the
631 : * default) for the first instance.
632 : *
633 : * @return Pointer to the requested DDFField. This pointer is to an
634 : * internal object, and should not be freed. It remains valid until
635 : * the next record read.
636 : */
637 :
638 488771 : const DDFField *DDFRecord::FindField(const char *pszName, int iFieldIndex) const
639 :
640 : {
641 1469920 : for (const auto &oField : aoFields)
642 : {
643 1454430 : const DDFFieldDefn *poFieldDefn = oField.GetFieldDefn();
644 1454430 : if (poFieldDefn && EQUAL(poFieldDefn->GetName(), pszName))
645 : {
646 473302 : if (iFieldIndex == 0)
647 473280 : return &oField;
648 : else
649 22 : iFieldIndex--;
650 : }
651 : }
652 :
653 15491 : return nullptr;
654 : }
655 :
656 : /************************************************************************/
657 : /* GetField() */
658 : /************************************************************************/
659 :
660 : /**
661 : * Fetch field object based on index.
662 : *
663 : * @param i The index of the field to fetch. Between 0 and GetFieldCount()-1.
664 : *
665 : * @return A DDFField pointer, or NULL if the index is out of range.
666 : */
667 :
668 214045 : const DDFField *DDFRecord::GetField(int i) const
669 :
670 : {
671 214045 : if (i < 0 || static_cast<size_t>(i) >= aoFields.size())
672 0 : return nullptr;
673 : else
674 214045 : return &(aoFields[i]);
675 : }
676 :
677 : /************************************************************************/
678 : /* GetIntSubfield() */
679 : /************************************************************************/
680 :
681 : /**
682 : * Fetch value of a subfield as an integer. This is a convenience
683 : * function for fetching a subfield of a field within this record.
684 : *
685 : * @param pszField The name of the field containing the subfield.
686 : * @param iFieldIndex The instance of this field within the record. Use
687 : * zero for the first instance of this field.
688 : * @param pszSubfield The name of the subfield within the selected field.
689 : * @param iSubfieldIndex The instance of this subfield within the record.
690 : * Use zero for the first instance.
691 : * @param pnSuccess Pointer to an int which will be set to TRUE if the fetch
692 : * succeeds, or FALSE if it fails. Use NULL if you don't want to check
693 : * success.
694 : * @return The value of the subfield, or zero if it failed for some reason.
695 : */
696 :
697 320922 : int DDFRecord::GetIntSubfield(const char *pszField, int iFieldIndex,
698 : const char *pszSubfield, int iSubfieldIndex,
699 : int *pnSuccess) const
700 :
701 : {
702 320922 : int nDummyErr = FALSE;
703 :
704 320922 : if (pnSuccess == nullptr)
705 273854 : pnSuccess = &nDummyErr;
706 :
707 320922 : *pnSuccess = FALSE;
708 :
709 : /* -------------------------------------------------------------------- */
710 : /* Fetch the field. If this fails, return zero. */
711 : /* -------------------------------------------------------------------- */
712 320922 : const DDFField *poField = FindField(pszField, iFieldIndex);
713 320922 : if (poField == nullptr)
714 33 : return 0;
715 :
716 : /* -------------------------------------------------------------------- */
717 : /* Get the subfield definition */
718 : /* -------------------------------------------------------------------- */
719 : const DDFSubfieldDefn *poSFDefn =
720 320889 : poField->GetFieldDefn()->FindSubfieldDefn(pszSubfield);
721 320889 : if (poSFDefn == nullptr)
722 32 : return 0;
723 :
724 : /* -------------------------------------------------------------------- */
725 : /* Get a pointer to the data. */
726 : /* -------------------------------------------------------------------- */
727 : int nBytesRemaining;
728 :
729 : const char *l_pachData =
730 320857 : poField->GetSubfieldData(poSFDefn, &nBytesRemaining, iSubfieldIndex);
731 320857 : if (l_pachData == nullptr)
732 0 : return 0;
733 :
734 : /* -------------------------------------------------------------------- */
735 : /* Return the extracted value. */
736 : /* */
737 : /* Assume an error has occurred if no bytes are consumed. */
738 : /* -------------------------------------------------------------------- */
739 320857 : int nConsumedBytes = 0;
740 : int nResult =
741 320857 : poSFDefn->ExtractIntData(l_pachData, nBytesRemaining, &nConsumedBytes);
742 :
743 320857 : if (nConsumedBytes > 0)
744 320857 : *pnSuccess = TRUE;
745 :
746 320857 : return nResult;
747 : }
748 :
749 : /************************************************************************/
750 : /* GetFloatSubfield() */
751 : /************************************************************************/
752 :
753 : /**
754 : * Fetch value of a subfield as a float (double). This is a convenience
755 : * function for fetching a subfield of a field within this record.
756 : *
757 : * @param pszField The name of the field containing the subfield.
758 : * @param iFieldIndex The instance of this field within the record. Use
759 : * zero for the first instance of this field.
760 : * @param pszSubfield The name of the subfield within the selected field.
761 : * @param iSubfieldIndex The instance of this subfield within the record.
762 : * Use zero for the first instance.
763 : * @param pnSuccess Pointer to an int which will be set to TRUE if the fetch
764 : * succeeds, or FALSE if it fails. Use NULL if you don't want to check
765 : * success.
766 : * @return The value of the subfield, or zero if it failed for some reason.
767 : */
768 :
769 120 : double DDFRecord::GetFloatSubfield(const char *pszField, int iFieldIndex,
770 : const char *pszSubfield, int iSubfieldIndex,
771 : int *pnSuccess) const
772 :
773 : {
774 120 : int nDummyErr = FALSE;
775 :
776 120 : if (pnSuccess == nullptr)
777 84 : pnSuccess = &nDummyErr;
778 :
779 120 : *pnSuccess = FALSE;
780 :
781 : /* -------------------------------------------------------------------- */
782 : /* Fetch the field. If this fails, return zero. */
783 : /* -------------------------------------------------------------------- */
784 120 : const DDFField *poField = FindField(pszField, iFieldIndex);
785 120 : if (poField == nullptr)
786 0 : return 0;
787 :
788 : /* -------------------------------------------------------------------- */
789 : /* Get the subfield definition */
790 : /* -------------------------------------------------------------------- */
791 : const DDFSubfieldDefn *poSFDefn =
792 120 : poField->GetFieldDefn()->FindSubfieldDefn(pszSubfield);
793 120 : if (poSFDefn == nullptr)
794 2 : return 0;
795 :
796 : /* -------------------------------------------------------------------- */
797 : /* Get a pointer to the data. */
798 : /* -------------------------------------------------------------------- */
799 : int nBytesRemaining;
800 :
801 : const char *l_pachData =
802 118 : poField->GetSubfieldData(poSFDefn, &nBytesRemaining, iSubfieldIndex);
803 118 : if (l_pachData == nullptr)
804 0 : return 0;
805 :
806 : /* -------------------------------------------------------------------- */
807 : /* Return the extracted value. */
808 : /* -------------------------------------------------------------------- */
809 118 : int nConsumedBytes = 0;
810 118 : double dfResult = poSFDefn->ExtractFloatData(l_pachData, nBytesRemaining,
811 : &nConsumedBytes);
812 :
813 118 : if (nConsumedBytes > 0)
814 118 : *pnSuccess = TRUE;
815 :
816 118 : return dfResult;
817 : }
818 :
819 : /************************************************************************/
820 : /* GetStringSubfield() */
821 : /************************************************************************/
822 :
823 : /**
824 : * Fetch value of a subfield as a string. This is a convenience
825 : * function for fetching a subfield of a field within this record.
826 : *
827 : * @param pszField The name of the field containing the subfield.
828 : * @param iFieldIndex The instance of this field within the record. Use
829 : * zero for the first instance of this field.
830 : * @param pszSubfield The name of the subfield within the selected field.
831 : * @param iSubfieldIndex The instance of this subfield within the record.
832 : * Use zero for the first instance.
833 : * @param pnSuccess Pointer to an int which will be set to TRUE if the fetch
834 : * succeeds, or FALSE if it fails. Use NULL if you don't want to check
835 : * success.
836 : * @return The value of the subfield, or NULL if it failed for some reason.
837 : * The returned pointer is to internal data and should not be modified or
838 : * freed by the application.
839 : */
840 :
841 23544 : const char *DDFRecord::GetStringSubfield(const char *pszField, int iFieldIndex,
842 : const char *pszSubfield,
843 : int iSubfieldIndex,
844 : int *pnSuccess) const
845 :
846 : {
847 23544 : int nDummyErr = FALSE;
848 :
849 23544 : if (pnSuccess == nullptr)
850 23493 : pnSuccess = &nDummyErr;
851 :
852 23544 : *pnSuccess = FALSE;
853 :
854 : /* -------------------------------------------------------------------- */
855 : /* Fetch the field. If this fails, return zero. */
856 : /* -------------------------------------------------------------------- */
857 23544 : const DDFField *poField = FindField(pszField, iFieldIndex);
858 23544 : if (poField == nullptr)
859 0 : return nullptr;
860 :
861 : /* -------------------------------------------------------------------- */
862 : /* Get the subfield definition */
863 : /* -------------------------------------------------------------------- */
864 : const DDFSubfieldDefn *poSFDefn =
865 23544 : poField->GetFieldDefn()->FindSubfieldDefn(pszSubfield);
866 23544 : if (poSFDefn == nullptr)
867 52 : return nullptr;
868 :
869 : /* -------------------------------------------------------------------- */
870 : /* Get a pointer to the data. */
871 : /* -------------------------------------------------------------------- */
872 : int nBytesRemaining;
873 :
874 : const char *l_pachData =
875 23492 : poField->GetSubfieldData(poSFDefn, &nBytesRemaining, iSubfieldIndex);
876 23492 : if (l_pachData == nullptr)
877 0 : return nullptr;
878 :
879 : /* -------------------------------------------------------------------- */
880 : /* Return the extracted value. */
881 : /* -------------------------------------------------------------------- */
882 23492 : *pnSuccess = TRUE;
883 :
884 23492 : return poSFDefn->ExtractStringData(l_pachData, nBytesRemaining, nullptr);
885 : }
886 :
887 : /************************************************************************/
888 : /* Clone() */
889 : /************************************************************************/
890 :
891 : /**
892 : * Make a copy of a record.
893 : *
894 : * This method is used to make a copy of a record that will become (mostly)
895 : * the properly of application.
896 : *
897 : * @return A new copy of the DDFRecord. Its lifetime must not extend the one
898 : * of the DDFModule of the original record, unless TransferTo() is called.
899 : */
900 :
901 27647 : std::unique_ptr<DDFRecord> DDFRecord::Clone() const
902 :
903 : {
904 27647 : auto poNR = std::make_unique<DDFRecord>(poModule);
905 :
906 27647 : poNR->bReuseHeader = false;
907 27647 : poNR->nFieldOffset = nFieldOffset;
908 :
909 27647 : poNR->osData = osData;
910 :
911 27647 : poNR->aoFields.resize(aoFields.size());
912 136924 : for (size_t i = 0; i < aoFields.size(); i++)
913 : {
914 : int nOffset;
915 :
916 109277 : nOffset = static_cast<int>(aoFields[i].GetData() - osData.c_str());
917 327831 : poNR->aoFields[i].Initialize(aoFields[i].GetFieldDefn(),
918 109277 : poNR->osData.c_str() + nOffset,
919 109277 : aoFields[i].GetDataSize());
920 : }
921 :
922 27647 : return poNR;
923 : }
924 :
925 : /************************************************************************/
926 : /* TransferTo() */
927 : /************************************************************************/
928 :
929 : /**
930 : * Transfer this record to another module.
931 : *
932 : * All DDFFieldDefn
933 : * references are transcribed onto the new module based on field names.
934 : * If any fields don't have a similarly named field on the target module
935 : * the operation will fail. No validation of field types and properties
936 : * is done, but this operation is intended only to be used between
937 : * modules with matching definitions of all affected fields.
938 : *
939 : * @param poTargetModule the module to which the record should be transferred
940 : *
941 : * @return true on success
942 : */
943 :
944 28 : bool DDFRecord::TransferTo(DDFModule *poTargetModule)
945 :
946 : {
947 : /* -------------------------------------------------------------------- */
948 : /* Verify that all fields have a corresponding field definition */
949 : /* on the target module. */
950 : /* -------------------------------------------------------------------- */
951 137 : for (const auto &oField : aoFields)
952 : {
953 109 : const DDFFieldDefn *poDefn = oField.GetFieldDefn();
954 :
955 109 : if (poTargetModule->FindFieldDefn(poDefn->GetName()) == nullptr)
956 : {
957 0 : CPLError(CE_Failure, CPLE_AppDefined,
958 : "Cannot find field definition %s in target module",
959 : poDefn->GetName());
960 0 : return false;
961 : }
962 :
963 : //TODO? check equality between source and target field definitions
964 : }
965 :
966 : /* -------------------------------------------------------------------- */
967 : /* Update all internal information to reference other module. */
968 : /* -------------------------------------------------------------------- */
969 137 : for (auto &oField : aoFields)
970 : {
971 : DDFFieldDefn *poDefn =
972 109 : poTargetModule->FindFieldDefn(oField.GetFieldDefn()->GetName());
973 :
974 109 : oField.Initialize(poDefn, oField.GetData(), oField.GetDataSize());
975 : }
976 :
977 28 : poModule = poTargetModule;
978 :
979 28 : return true;
980 : }
981 :
982 : /************************************************************************/
983 : /* DeleteField() */
984 : /************************************************************************/
985 :
986 : /**
987 : * Delete a field instance from a record.
988 : *
989 : * Remove a field from this record, cleaning up the data
990 : * portion and repacking the fields list. We don't try to
991 : * reallocate the data area of the record to be smaller.
992 : *
993 : * NOTE: This method doesn't actually remove the header
994 : * information for this field from the record tag list yet.
995 : * This should be added if the resulting record is even to be
996 : * written back to disk!
997 : *
998 : * @param poTarget the field instance on this record to delete.
999 : *
1000 : * @return TRUE on success, or FALSE on failure. Failure can occur if
1001 : * poTarget isn't really a field on this record.
1002 : */
1003 :
1004 0 : int DDFRecord::DeleteField(DDFField *poTarget)
1005 :
1006 : {
1007 : int iTarget;
1008 :
1009 : /* -------------------------------------------------------------------- */
1010 : /* Find which field we are to delete. */
1011 : /* -------------------------------------------------------------------- */
1012 0 : for (iTarget = 0; iTarget < GetFieldCount(); iTarget++)
1013 : {
1014 0 : if (aoFields.data() + iTarget == poTarget)
1015 0 : break;
1016 : }
1017 :
1018 0 : if (iTarget == GetFieldCount())
1019 0 : return FALSE;
1020 :
1021 : /* -------------------------------------------------------------------- */
1022 : /* Change the target fields data size to zero. This takes care */
1023 : /* of repacking the data array, and updating all the following */
1024 : /* field data pointers. */
1025 : /* -------------------------------------------------------------------- */
1026 0 : ResizeField(poTarget, 0);
1027 :
1028 : /* -------------------------------------------------------------------- */
1029 : /* remove the target field, moving down all the other fields */
1030 : /* one step in the field list. */
1031 : /* -------------------------------------------------------------------- */
1032 0 : aoFields.erase(aoFields.begin() + iTarget);
1033 :
1034 0 : return TRUE;
1035 : }
1036 :
1037 : /************************************************************************/
1038 : /* ResizeField() */
1039 : /************************************************************************/
1040 :
1041 : /**
1042 : * Alter field data size within record.
1043 : *
1044 : * This method will rearrange a DDFRecord altering the amount of space
1045 : * reserved for one of the existing fields. All following fields will
1046 : * be shifted accordingly. This includes updating the DDFField infos,
1047 : * and actually moving stuff within the data array after reallocating
1048 : * to the desired size.
1049 : *
1050 : * @param poField the field to alter.
1051 : * @param nNewDataSize the number of data bytes to be reserved for the field.
1052 : *
1053 : * @return TRUE on success or FALSE on failure.
1054 : */
1055 :
1056 1243 : int DDFRecord::ResizeField(DDFField *poField, int nNewDataSize)
1057 :
1058 : {
1059 : /* -------------------------------------------------------------------- */
1060 : /* Find which field we are to resize. */
1061 : /* -------------------------------------------------------------------- */
1062 : int iTarget;
1063 3527 : for (iTarget = 0; iTarget < GetFieldCount(); iTarget++)
1064 : {
1065 3527 : if (aoFields.data() + iTarget == poField)
1066 1243 : break;
1067 : }
1068 :
1069 1243 : if (iTarget == GetFieldCount())
1070 0 : return FALSE;
1071 :
1072 : /* -------------------------------------------------------------------- */
1073 : /* How much data needs to be shifted up or down after this field? */
1074 : /* -------------------------------------------------------------------- */
1075 : const int nBytesToMove =
1076 1243 : static_cast<int>(osData.size()) -
1077 1243 : static_cast<int>(poField->GetData() + poField->GetDataSize() -
1078 1243 : osData.data());
1079 :
1080 : /* -------------------------------------------------------------------- */
1081 : /* Store field offsets */
1082 : /* -------------------------------------------------------------------- */
1083 1243 : std::vector<int> anOffsets;
1084 4771 : for (auto &oField : aoFields)
1085 3528 : anOffsets.push_back(static_cast<int>(oField.GetData() - osData.data()));
1086 :
1087 : /* -------------------------------------------------------------------- */
1088 : /* Reallocate the data buffer accordingly. */
1089 : /* -------------------------------------------------------------------- */
1090 : // Don't realloc things smaller ... we will cut off some data.
1091 1243 : const int nBytesToAdd = nNewDataSize - poField->GetDataSize();
1092 1243 : if (nBytesToAdd > 0)
1093 : {
1094 1168 : osData.resize(osData.size() + nBytesToAdd);
1095 : }
1096 :
1097 : /* -------------------------------------------------------------------- */
1098 : /* Update fields to point into newly allocated buffer. */
1099 : /* -------------------------------------------------------------------- */
1100 4771 : for (size_t i = 0; i < aoFields.size(); ++i)
1101 : {
1102 3528 : auto &oField = aoFields[i];
1103 3528 : oField.Initialize(oField.GetFieldDefn(), osData.c_str() + anOffsets[i],
1104 : oField.GetDataSize());
1105 : }
1106 :
1107 : /* -------------------------------------------------------------------- */
1108 : /* Shift the data beyond this field up or down as needed. */
1109 : /* -------------------------------------------------------------------- */
1110 1243 : if (nBytesToMove > 0)
1111 3 : memmove(const_cast<char *>(poField->GetData()) +
1112 1 : poField->GetDataSize() + nBytesToAdd,
1113 1 : poField->GetData() + poField->GetDataSize(), nBytesToMove);
1114 :
1115 : /* -------------------------------------------------------------------- */
1116 : /* Update the target fields info. */
1117 : /* -------------------------------------------------------------------- */
1118 1243 : poField->Initialize(poField->GetFieldDefn(), poField->GetData(),
1119 1243 : poField->GetDataSize() + nBytesToAdd);
1120 :
1121 : /* -------------------------------------------------------------------- */
1122 : /* Shift all following fields down, and update their data */
1123 : /* locations. */
1124 : /* -------------------------------------------------------------------- */
1125 1243 : if (nBytesToAdd < 0)
1126 : {
1127 2 : for (int i = iTarget + 1; i < GetFieldCount(); i++)
1128 : {
1129 1 : const char *pszOldDataLocation = aoFields[i].GetData();
1130 :
1131 3 : aoFields[i].Initialize(aoFields[i].GetFieldDefn(),
1132 1 : pszOldDataLocation + nBytesToAdd,
1133 1 : aoFields[i].GetDataSize());
1134 : }
1135 :
1136 1 : osData.resize(osData.size() - (-nBytesToAdd));
1137 : }
1138 : else
1139 : {
1140 1242 : for (int i = GetFieldCount() - 1; i > iTarget; i--)
1141 : {
1142 0 : const char *pszOldDataLocation = aoFields[i].GetData();
1143 :
1144 0 : aoFields[i].Initialize(aoFields[i].GetFieldDefn(),
1145 0 : pszOldDataLocation + nBytesToAdd,
1146 0 : aoFields[i].GetDataSize());
1147 : }
1148 : }
1149 :
1150 1243 : return TRUE;
1151 : }
1152 :
1153 : /************************************************************************/
1154 : /* AddField() */
1155 : /************************************************************************/
1156 :
1157 : /**
1158 : * Add a new field to record.
1159 : *
1160 : * Add a new zero sized field to the record. The new field is always
1161 : * added at the end of the record.
1162 : *
1163 : * NOTE: This method doesn't currently update the header information for
1164 : * the record to include the field information for this field, so the
1165 : * resulting record image isn't suitable for writing to disk. However,
1166 : * everything else about the record state should be updated properly to
1167 : * reflect the new field.
1168 : *
1169 : * @param poDefn the definition of the field to be added.
1170 : *
1171 : * @return the field object on success, or NULL on failure.
1172 : */
1173 :
1174 796 : DDFField *DDFRecord::AddField(DDFFieldDefn *poDefn)
1175 :
1176 : {
1177 : /* -------------------------------------------------------------------- */
1178 : /* Reallocate the fields array larger by one, and initialize */
1179 : /* the new field. */
1180 : /* -------------------------------------------------------------------- */
1181 796 : aoFields.resize(aoFields.size() + 1);
1182 :
1183 : /* -------------------------------------------------------------------- */
1184 : /* Initialize the new field properly. */
1185 : /* -------------------------------------------------------------------- */
1186 796 : if (aoFields.size() == 1)
1187 : {
1188 224 : aoFields[0].Initialize(poDefn, GetData(), 0);
1189 : }
1190 : else
1191 : {
1192 572 : aoFields.back().Initialize(
1193 : poDefn,
1194 572 : aoFields[GetFieldCount() - 2].GetData() +
1195 572 : aoFields[GetFieldCount() - 2].GetDataSize(),
1196 : 0);
1197 : }
1198 :
1199 : /* -------------------------------------------------------------------- */
1200 : /* Initialize field. */
1201 : /* -------------------------------------------------------------------- */
1202 796 : CreateDefaultFieldInstance(aoFields.data() + GetFieldCount() - 1, 0);
1203 :
1204 796 : return &(aoFields.back());
1205 : }
1206 :
1207 : /************************************************************************/
1208 : /* SetFieldRaw() */
1209 : /************************************************************************/
1210 :
1211 : /**
1212 : * Set the raw contents of a field instance.
1213 : *
1214 : * @param poField the field to set data within.
1215 : * @param iIndexWithinField The instance of this field to replace. Must
1216 : * be a value between 0 and GetRepeatCount(). If GetRepeatCount() is used, a
1217 : * new instance of the field is appended.
1218 : * @param pachRawData the raw data to replace this field instance with.
1219 : * @param nRawDataSize the number of bytes pointed to by pachRawData.
1220 : *
1221 : * @return TRUE on success or FALSE on failure.
1222 : */
1223 :
1224 1122 : int DDFRecord::SetFieldRaw(DDFField *poField, int iIndexWithinField,
1225 : const char *pachRawData, int nRawDataSize)
1226 :
1227 : {
1228 : int iTarget, nRepeatCount;
1229 :
1230 : /* -------------------------------------------------------------------- */
1231 : /* Find which field we are to update. */
1232 : /* -------------------------------------------------------------------- */
1233 3216 : for (iTarget = 0; iTarget < GetFieldCount(); iTarget++)
1234 : {
1235 3216 : if (aoFields.data() + iTarget == poField)
1236 1122 : break;
1237 : }
1238 :
1239 1122 : if (iTarget == GetFieldCount())
1240 0 : return FALSE;
1241 :
1242 1122 : nRepeatCount = poField->GetRepeatCount();
1243 :
1244 1122 : if (iIndexWithinField < 0 || iIndexWithinField > nRepeatCount)
1245 0 : return FALSE;
1246 :
1247 : /* -------------------------------------------------------------------- */
1248 : /* Are we adding an instance? This is easier and different */
1249 : /* than replacing an existing instance. */
1250 : /* -------------------------------------------------------------------- */
1251 1757 : if (iIndexWithinField == nRepeatCount ||
1252 635 : !poField->GetFieldDefn()->IsRepeating())
1253 : {
1254 999 : if (!poField->GetFieldDefn()->IsRepeating() && iIndexWithinField != 0)
1255 0 : return FALSE;
1256 :
1257 999 : int nOldSize = poField->GetDataSize();
1258 999 : if (nOldSize == 0)
1259 796 : nOldSize++; // for added DDF_FIELD_TERMINATOR.
1260 :
1261 999 : if (!ResizeField(poField, nOldSize + nRawDataSize))
1262 0 : return FALSE;
1263 :
1264 999 : char *pachFieldData = const_cast<char *>(poField->GetData());
1265 999 : memcpy(pachFieldData + nOldSize - 1, pachRawData, nRawDataSize);
1266 999 : pachFieldData[nOldSize + nRawDataSize - 1] = DDF_FIELD_TERMINATOR;
1267 :
1268 999 : return TRUE;
1269 : }
1270 :
1271 : /* -------------------------------------------------------------------- */
1272 : /* Get a pointer to the start of the existing data for this */
1273 : /* iteration of the field. */
1274 : /* -------------------------------------------------------------------- */
1275 123 : const char *pachWrkData = nullptr;
1276 123 : int nInstanceSize = 0;
1277 :
1278 : // We special case this to avoid a lot of warnings when initializing
1279 : // the field the first time.
1280 123 : if (poField->GetDataSize() == 0)
1281 : {
1282 0 : pachWrkData = poField->GetData();
1283 : }
1284 : else
1285 : {
1286 : pachWrkData =
1287 123 : poField->GetInstanceData(iIndexWithinField, &nInstanceSize);
1288 : }
1289 :
1290 : /* -------------------------------------------------------------------- */
1291 : /* Create new image of this whole field. */
1292 : /* -------------------------------------------------------------------- */
1293 123 : int nNewFieldSize = poField->GetDataSize() - nInstanceSize + nRawDataSize;
1294 :
1295 123 : std::string osNewImage;
1296 123 : osNewImage.resize(nNewFieldSize);
1297 :
1298 123 : int nPreBytes = static_cast<int>(pachWrkData - poField->GetData());
1299 123 : int nPostBytes = poField->GetDataSize() - nPreBytes - nInstanceSize;
1300 :
1301 123 : memcpy(osNewImage.data(), poField->GetData(), nPreBytes);
1302 369 : memcpy(osNewImage.data() + nPreBytes + nRawDataSize,
1303 123 : poField->GetData() + nPreBytes + nInstanceSize, nPostBytes);
1304 123 : memcpy(osNewImage.data() + nPreBytes, pachRawData, nRawDataSize);
1305 :
1306 : /* -------------------------------------------------------------------- */
1307 : /* Resize the field to the desired new size. */
1308 : /* -------------------------------------------------------------------- */
1309 123 : ResizeField(poField, nNewFieldSize);
1310 :
1311 123 : memcpy(const_cast<char *>(poField->GetData()), osNewImage.data(),
1312 : nNewFieldSize);
1313 :
1314 123 : return TRUE;
1315 : }
1316 :
1317 : /************************************************************************/
1318 : /* UpdateFieldRaw() */
1319 : /************************************************************************/
1320 :
1321 121 : int DDFRecord::UpdateFieldRaw(DDFField *poField, int iIndexWithinField,
1322 : int nStartOffset, int nOldSize,
1323 : const char *pachRawData, int nRawDataSize)
1324 :
1325 : {
1326 : int iTarget, nRepeatCount;
1327 :
1328 : /* -------------------------------------------------------------------- */
1329 : /* Find which field we are to update. */
1330 : /* -------------------------------------------------------------------- */
1331 311 : for (iTarget = 0; iTarget < GetFieldCount(); iTarget++)
1332 : {
1333 311 : if (aoFields.data() + iTarget == poField)
1334 121 : break;
1335 : }
1336 :
1337 121 : if (iTarget == GetFieldCount())
1338 0 : return FALSE;
1339 :
1340 121 : nRepeatCount = poField->GetRepeatCount();
1341 :
1342 121 : if (iIndexWithinField < 0 || iIndexWithinField >= nRepeatCount)
1343 0 : return FALSE;
1344 :
1345 : /* -------------------------------------------------------------------- */
1346 : /* Figure out how much pre and post data there is. */
1347 : /* -------------------------------------------------------------------- */
1348 121 : int nInstanceSize = 0;
1349 :
1350 : char *pachWrkData = const_cast<char *>(
1351 121 : poField->GetInstanceData(iIndexWithinField, &nInstanceSize));
1352 : int nPreBytes =
1353 121 : static_cast<int>(pachWrkData - poField->GetData() + nStartOffset);
1354 121 : int nPostBytes = poField->GetDataSize() - nPreBytes - nOldSize;
1355 :
1356 : /* -------------------------------------------------------------------- */
1357 : /* If we aren't changing the size, just copy over the existing */
1358 : /* data. */
1359 : /* -------------------------------------------------------------------- */
1360 121 : if (nOldSize == nRawDataSize)
1361 : {
1362 0 : memcpy(pachWrkData + nStartOffset, pachRawData, nRawDataSize);
1363 0 : return TRUE;
1364 : }
1365 :
1366 : /* -------------------------------------------------------------------- */
1367 : /* If we are shrinking, move in the new data, and shuffle down */
1368 : /* the old before resizing. */
1369 : /* -------------------------------------------------------------------- */
1370 121 : char *pabyFieldData = const_cast<char *>(poField->GetData());
1371 121 : if (nRawDataSize < nOldSize)
1372 : {
1373 0 : memcpy(pabyFieldData + nPreBytes, pachRawData, nRawDataSize);
1374 0 : memmove(pabyFieldData + nPreBytes + nRawDataSize,
1375 0 : pabyFieldData + nPreBytes + nOldSize, nPostBytes);
1376 : }
1377 :
1378 : /* -------------------------------------------------------------------- */
1379 : /* Resize the whole buffer. */
1380 : /* -------------------------------------------------------------------- */
1381 121 : if (!ResizeField(poField, poField->GetDataSize() - nOldSize + nRawDataSize))
1382 0 : return FALSE;
1383 :
1384 : /* -------------------------------------------------------------------- */
1385 : /* If we growing the buffer, shuffle up the post data, and */
1386 : /* move in our new values. */
1387 : /* -------------------------------------------------------------------- */
1388 121 : pabyFieldData = const_cast<char *>(poField->GetData());
1389 121 : if (nRawDataSize >= nOldSize)
1390 : {
1391 121 : memmove(pabyFieldData + nPreBytes + nRawDataSize,
1392 121 : pabyFieldData + nPreBytes + nOldSize, nPostBytes);
1393 121 : memcpy(pabyFieldData + nPreBytes, pachRawData, nRawDataSize);
1394 : }
1395 :
1396 121 : return TRUE;
1397 : }
1398 :
1399 : /************************************************************************/
1400 : /* ResetDirectory() */
1401 : /* */
1402 : /* Re-prepares the directory information for the record. */
1403 : /************************************************************************/
1404 :
1405 224 : void DDFRecord::ResetDirectory()
1406 :
1407 : {
1408 : /* -------------------------------------------------------------------- */
1409 : /* Eventually we should try to optimize the size of offset and */
1410 : /* field length. */
1411 : /* -------------------------------------------------------------------- */
1412 :
1413 : /* -------------------------------------------------------------------- */
1414 : /* Compute how large the directory needs to be. */
1415 : /* -------------------------------------------------------------------- */
1416 : int nEntrySize, nDirSize;
1417 :
1418 224 : nEntrySize = _sizeFieldPos + _sizeFieldLength + _sizeFieldTag;
1419 224 : nDirSize = nEntrySize * GetFieldCount() + 1;
1420 :
1421 : /* -------------------------------------------------------------------- */
1422 : /* If the directory size is different than what is currently */
1423 : /* reserved for it, we must resize. */
1424 : /* -------------------------------------------------------------------- */
1425 224 : if (nDirSize != nFieldOffset)
1426 : {
1427 : const int nNewDataSize =
1428 224 : static_cast<int>(osData.size()) - nFieldOffset + nDirSize;
1429 224 : std::string osNewData;
1430 224 : osNewData.resize(nNewDataSize);
1431 224 : memcpy(osNewData.data() + nDirSize, osData.c_str() + nFieldOffset,
1432 224 : nNewDataSize - nDirSize);
1433 :
1434 1020 : for (auto &oField : aoFields)
1435 : {
1436 : int nOffset;
1437 796 : nOffset = static_cast<int>(oField.GetData() - osData.c_str() -
1438 796 : nFieldOffset + nDirSize);
1439 1592 : oField.Initialize(oField.GetFieldDefn(),
1440 796 : osNewData.c_str() + nOffset,
1441 : oField.GetDataSize());
1442 : }
1443 :
1444 224 : osData = std::move(osNewData);
1445 224 : nFieldOffset = nDirSize;
1446 : }
1447 :
1448 : /* -------------------------------------------------------------------- */
1449 : /* Now set each directory entry. */
1450 : /* -------------------------------------------------------------------- */
1451 224 : int iField = 0;
1452 1020 : for (auto &oField : aoFields)
1453 : {
1454 796 : const DDFFieldDefn *poDefn = oField.GetFieldDefn();
1455 : char szFormat[128];
1456 :
1457 796 : snprintf(szFormat, sizeof(szFormat), "%%%ds%%0%dd%%0%dd", _sizeFieldTag,
1458 : _sizeFieldLength, _sizeFieldPos);
1459 :
1460 796 : snprintf(osData.data() + nEntrySize * iField, nEntrySize + 1, szFormat,
1461 : poDefn->GetName(), oField.GetDataSize(),
1462 796 : oField.GetData() - osData.data() - nFieldOffset);
1463 796 : ++iField;
1464 : }
1465 :
1466 224 : osData[nEntrySize * GetFieldCount()] = DDF_FIELD_TERMINATOR;
1467 224 : }
1468 :
1469 : /************************************************************************/
1470 : /* CreateDefaultFieldInstance() */
1471 : /************************************************************************/
1472 :
1473 : /**
1474 : * Initialize default instance.
1475 : *
1476 : * This method is normally only used internally by the AddField() method
1477 : * to initialize the new field instance with default subfield values. It
1478 : * installs default data for one instance of the field in the record
1479 : * using the DDFFieldDefn::GetDefaultValue() method and
1480 : * DDFRecord::SetFieldRaw().
1481 : *
1482 : * @param poField the field within the record to be assign a default
1483 : * instance.
1484 : * @param iIndexWithinField the instance to set (may not have been tested with
1485 : * values other than 0).
1486 : *
1487 : * @return TRUE on success or FALSE on failure.
1488 : */
1489 :
1490 999 : int DDFRecord::CreateDefaultFieldInstance(DDFField *poField,
1491 : int iIndexWithinField)
1492 :
1493 : {
1494 999 : int nRawSize = 0;
1495 999 : char *pachRawData = poField->GetFieldDefn()->GetDefaultValue(&nRawSize);
1496 999 : if (pachRawData == nullptr)
1497 224 : return FALSE;
1498 :
1499 : const int nSuccess =
1500 775 : SetFieldRaw(poField, iIndexWithinField, pachRawData, nRawSize);
1501 :
1502 775 : CPLFree(pachRawData);
1503 :
1504 775 : return nSuccess;
1505 : }
1506 :
1507 : /************************************************************************/
1508 : /* SetStringSubfield() */
1509 : /************************************************************************/
1510 :
1511 : /**
1512 : * Set a string subfield in record.
1513 : *
1514 : * The value of a given subfield is replaced with a new string value
1515 : * formatted appropriately.
1516 : *
1517 : * @param pszField the field name to operate on.
1518 : * @param iFieldIndex the field index to operate on (zero based).
1519 : * @param pszSubfield the subfield name to operate on.
1520 : * @param iSubfieldIndex the subfield index to operate on (zero based).
1521 : * @param pszValue the new string to place in the subfield. This may be
1522 : * arbitrary binary bytes if nValueLength is specified.
1523 : * @param nValueLength the number of valid bytes in pszValue, may be -1 to
1524 : * internally fetch with strlen().
1525 : *
1526 : * @return TRUE if successful, and FALSE if not.
1527 : */
1528 :
1529 454 : int DDFRecord::SetStringSubfield(const char *pszField, int iFieldIndex,
1530 : const char *pszSubfield, int iSubfieldIndex,
1531 : const char *pszValue, int nValueLength)
1532 :
1533 : {
1534 : /* -------------------------------------------------------------------- */
1535 : /* Fetch the field. If this fails, return zero. */
1536 : /* -------------------------------------------------------------------- */
1537 454 : DDFField *poField = FindField(pszField, iFieldIndex);
1538 454 : if (poField == nullptr)
1539 0 : return FALSE;
1540 :
1541 : /* -------------------------------------------------------------------- */
1542 : /* Get the subfield definition */
1543 : /* -------------------------------------------------------------------- */
1544 : const DDFSubfieldDefn *poSFDefn =
1545 454 : poField->GetFieldDefn()->FindSubfieldDefn(pszSubfield);
1546 454 : if (poSFDefn == nullptr)
1547 0 : return FALSE;
1548 :
1549 : /* -------------------------------------------------------------------- */
1550 : /* How long will the formatted value be? */
1551 : /* -------------------------------------------------------------------- */
1552 : int nFormattedLen;
1553 :
1554 454 : if (!poSFDefn->FormatStringValue(nullptr, 0, &nFormattedLen, pszValue,
1555 : nValueLength))
1556 0 : return FALSE;
1557 :
1558 : /* -------------------------------------------------------------------- */
1559 : /* Get a pointer to the data. */
1560 : /* -------------------------------------------------------------------- */
1561 : int nMaxBytes;
1562 : char *pachSubfieldData = const_cast<char *>(
1563 454 : poField->GetSubfieldData(poSFDefn, &nMaxBytes, iSubfieldIndex));
1564 454 : if (pachSubfieldData == nullptr)
1565 0 : return FALSE;
1566 :
1567 : /* -------------------------------------------------------------------- */
1568 : /* Add new instance if we have run out of data. */
1569 : /* -------------------------------------------------------------------- */
1570 454 : if (nMaxBytes == 0 ||
1571 454 : (nMaxBytes == 1 && pachSubfieldData[0] == DDF_FIELD_TERMINATOR))
1572 : {
1573 139 : CreateDefaultFieldInstance(poField, iSubfieldIndex);
1574 :
1575 : // Refetch.
1576 : pachSubfieldData = const_cast<char *>(
1577 139 : poField->GetSubfieldData(poSFDefn, &nMaxBytes, iSubfieldIndex));
1578 139 : if (pachSubfieldData == nullptr)
1579 0 : return FALSE;
1580 : }
1581 :
1582 : /* -------------------------------------------------------------------- */
1583 : /* If the new length matches the existing length, just overlay */
1584 : /* and return. */
1585 : /* -------------------------------------------------------------------- */
1586 : int nExistingLength;
1587 :
1588 454 : poSFDefn->GetDataLength(pachSubfieldData, nMaxBytes, &nExistingLength);
1589 :
1590 454 : if (nExistingLength == nFormattedLen)
1591 : {
1592 333 : return poSFDefn->FormatStringValue(pachSubfieldData, nFormattedLen,
1593 333 : nullptr, pszValue, nValueLength);
1594 : }
1595 :
1596 : /* -------------------------------------------------------------------- */
1597 : /* We will need to resize the raw data. */
1598 : /* -------------------------------------------------------------------- */
1599 121 : int nInstanceSize = 0;
1600 :
1601 : const char *pachFieldInstData =
1602 121 : poField->GetInstanceData(iFieldIndex, &nInstanceSize);
1603 :
1604 121 : const int nStartOffset =
1605 121 : static_cast<int>(pachSubfieldData - pachFieldInstData);
1606 :
1607 242 : std::string osNewData;
1608 121 : osNewData.resize(nFormattedLen);
1609 121 : poSFDefn->FormatStringValue(osNewData.data(), nFormattedLen, nullptr,
1610 : pszValue, nValueLength);
1611 :
1612 121 : return UpdateFieldRaw(poField, iFieldIndex, nStartOffset, nExistingLength,
1613 242 : osNewData.data(), nFormattedLen);
1614 : }
1615 :
1616 : /************************************************************************/
1617 : /* SetIntSubfield() */
1618 : /************************************************************************/
1619 :
1620 : /**
1621 : * Set an integer subfield in record.
1622 : *
1623 : * The value of a given subfield is replaced with a new integer value
1624 : * formatted appropriately.
1625 : *
1626 : * @param pszField the field name to operate on.
1627 : * @param iFieldIndex the field index to operate on (zero based).
1628 : * @param pszSubfield the subfield name to operate on.
1629 : * @param iSubfieldIndex the subfield index to operate on (zero based).
1630 : * @param nNewValue the new value to place in the subfield.
1631 : *
1632 : * @return TRUE if successful, and FALSE if not.
1633 : */
1634 :
1635 2680 : int DDFRecord::SetIntSubfield(const char *pszField, int iFieldIndex,
1636 : const char *pszSubfield, int iSubfieldIndex,
1637 : int nNewValue)
1638 :
1639 : {
1640 : /* -------------------------------------------------------------------- */
1641 : /* Fetch the field. If this fails, return zero. */
1642 : /* -------------------------------------------------------------------- */
1643 2680 : DDFField *poField = FindField(pszField, iFieldIndex);
1644 2680 : if (poField == nullptr)
1645 0 : return FALSE;
1646 :
1647 : /* -------------------------------------------------------------------- */
1648 : /* Get the subfield definition */
1649 : /* -------------------------------------------------------------------- */
1650 : const DDFSubfieldDefn *poSFDefn =
1651 2680 : poField->GetFieldDefn()->FindSubfieldDefn(pszSubfield);
1652 2680 : if (poSFDefn == nullptr)
1653 0 : return FALSE;
1654 :
1655 : /* -------------------------------------------------------------------- */
1656 : /* How long will the formatted value be? */
1657 : /* -------------------------------------------------------------------- */
1658 : int nFormattedLen;
1659 :
1660 2680 : if (!poSFDefn->FormatIntValue(nullptr, 0, &nFormattedLen, nNewValue))
1661 0 : return FALSE;
1662 :
1663 : /* -------------------------------------------------------------------- */
1664 : /* Get a pointer to the data. */
1665 : /* -------------------------------------------------------------------- */
1666 : int nMaxBytes;
1667 : char *pachSubfieldData = const_cast<char *>(
1668 2680 : poField->GetSubfieldData(poSFDefn, &nMaxBytes, iSubfieldIndex));
1669 2680 : if (pachSubfieldData == nullptr)
1670 0 : return FALSE;
1671 :
1672 : /* -------------------------------------------------------------------- */
1673 : /* Add new instance if we have run out of data. */
1674 : /* -------------------------------------------------------------------- */
1675 2680 : if (nMaxBytes == 0 ||
1676 2680 : (nMaxBytes == 1 && pachSubfieldData[0] == DDF_FIELD_TERMINATOR))
1677 : {
1678 64 : CreateDefaultFieldInstance(poField, iSubfieldIndex);
1679 :
1680 : // Refetch.
1681 : pachSubfieldData = const_cast<char *>(
1682 64 : poField->GetSubfieldData(poSFDefn, &nMaxBytes, iSubfieldIndex));
1683 64 : if (pachSubfieldData == nullptr)
1684 0 : return FALSE;
1685 : }
1686 :
1687 : /* -------------------------------------------------------------------- */
1688 : /* If the new length matches the existing length, just overlay */
1689 : /* and return. */
1690 : /* -------------------------------------------------------------------- */
1691 : int nExistingLength;
1692 :
1693 2680 : poSFDefn->GetDataLength(pachSubfieldData, nMaxBytes, &nExistingLength);
1694 :
1695 2680 : if (nExistingLength == nFormattedLen)
1696 : {
1697 2680 : return poSFDefn->FormatIntValue(pachSubfieldData, nFormattedLen,
1698 2680 : nullptr, nNewValue);
1699 : }
1700 :
1701 : /* -------------------------------------------------------------------- */
1702 : /* We will need to resize the raw data. */
1703 : /* -------------------------------------------------------------------- */
1704 0 : int nInstanceSize = 0;
1705 :
1706 : const char *pachFieldInstData =
1707 0 : poField->GetInstanceData(iFieldIndex, &nInstanceSize);
1708 :
1709 0 : const int nStartOffset =
1710 0 : static_cast<int>(pachSubfieldData - pachFieldInstData);
1711 :
1712 0 : std::string osNewData;
1713 0 : osNewData.resize(nFormattedLen);
1714 0 : poSFDefn->FormatIntValue(osNewData.data(), nFormattedLen, nullptr,
1715 : nNewValue);
1716 :
1717 0 : return UpdateFieldRaw(poField, iFieldIndex, nStartOffset, nExistingLength,
1718 0 : osNewData.data(), nFormattedLen);
1719 : }
1720 :
1721 : /************************************************************************/
1722 : /* SetFloatSubfield() */
1723 : /************************************************************************/
1724 :
1725 : /**
1726 : * Set a float subfield in record.
1727 : *
1728 : * The value of a given subfield is replaced with a new float value
1729 : * formatted appropriately.
1730 : *
1731 : * @param pszField the field name to operate on.
1732 : * @param iFieldIndex the field index to operate on (zero based).
1733 : * @param pszSubfield the subfield name to operate on.
1734 : * @param iSubfieldIndex the subfield index to operate on (zero based).
1735 : * @param dfNewValue the new value to place in the subfield.
1736 : *
1737 : * @return TRUE if successful, and FALSE if not.
1738 : */
1739 :
1740 1 : int DDFRecord::SetFloatSubfield(const char *pszField, int iFieldIndex,
1741 : const char *pszSubfield, int iSubfieldIndex,
1742 : double dfNewValue)
1743 :
1744 : {
1745 : /* -------------------------------------------------------------------- */
1746 : /* Fetch the field. If this fails, return zero. */
1747 : /* -------------------------------------------------------------------- */
1748 1 : DDFField *poField = FindField(pszField, iFieldIndex);
1749 1 : if (poField == nullptr)
1750 0 : return FALSE;
1751 :
1752 : /* -------------------------------------------------------------------- */
1753 : /* Get the subfield definition */
1754 : /* -------------------------------------------------------------------- */
1755 : const DDFSubfieldDefn *poSFDefn =
1756 1 : poField->GetFieldDefn()->FindSubfieldDefn(pszSubfield);
1757 1 : if (poSFDefn == nullptr)
1758 0 : return FALSE;
1759 :
1760 : /* -------------------------------------------------------------------- */
1761 : /* How long will the formatted value be? */
1762 : /* -------------------------------------------------------------------- */
1763 : int nFormattedLen;
1764 :
1765 1 : if (!poSFDefn->FormatFloatValue(nullptr, 0, &nFormattedLen, dfNewValue))
1766 0 : return FALSE;
1767 :
1768 : /* -------------------------------------------------------------------- */
1769 : /* Get a pointer to the data. */
1770 : /* -------------------------------------------------------------------- */
1771 : int nMaxBytes;
1772 : char *pachSubfieldData = const_cast<char *>(
1773 1 : poField->GetSubfieldData(poSFDefn, &nMaxBytes, iSubfieldIndex));
1774 1 : if (pachSubfieldData == nullptr)
1775 0 : return FALSE;
1776 :
1777 : /* -------------------------------------------------------------------- */
1778 : /* Add new instance if we have run out of data. */
1779 : /* -------------------------------------------------------------------- */
1780 1 : if (nMaxBytes == 0 ||
1781 1 : (nMaxBytes == 1 && pachSubfieldData[0] == DDF_FIELD_TERMINATOR))
1782 : {
1783 0 : CreateDefaultFieldInstance(poField, iSubfieldIndex);
1784 :
1785 : // Refetch.
1786 : pachSubfieldData = const_cast<char *>(
1787 0 : poField->GetSubfieldData(poSFDefn, &nMaxBytes, iSubfieldIndex));
1788 0 : if (pachSubfieldData == nullptr)
1789 0 : return FALSE;
1790 : }
1791 :
1792 : /* -------------------------------------------------------------------- */
1793 : /* If the new length matches the existing length, just overlay */
1794 : /* and return. */
1795 : /* -------------------------------------------------------------------- */
1796 : int nExistingLength;
1797 :
1798 1 : poSFDefn->GetDataLength(pachSubfieldData, nMaxBytes, &nExistingLength);
1799 :
1800 1 : if (nExistingLength == nFormattedLen)
1801 : {
1802 1 : return poSFDefn->FormatFloatValue(pachSubfieldData, nFormattedLen,
1803 1 : nullptr, dfNewValue);
1804 : }
1805 :
1806 : /* -------------------------------------------------------------------- */
1807 : /* We will need to resize the raw data. */
1808 : /* -------------------------------------------------------------------- */
1809 0 : int nInstanceSize = 0;
1810 :
1811 : const char *pachFieldInstData =
1812 0 : poField->GetInstanceData(iFieldIndex, &nInstanceSize);
1813 :
1814 0 : const int nStartOffset =
1815 0 : static_cast<int>(pachSubfieldData - pachFieldInstData);
1816 :
1817 0 : std::string osNewData;
1818 0 : osNewData.resize(nFormattedLen);
1819 0 : poSFDefn->FormatFloatValue(osNewData.data(), nFormattedLen, nullptr,
1820 : dfNewValue);
1821 :
1822 0 : return UpdateFieldRaw(poField, iFieldIndex, nStartOffset, nExistingLength,
1823 0 : osNewData.data(), nFormattedLen);
1824 : }
|