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