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