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