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