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