Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: Erdas Imagine (.img) Translator
4 : * Purpose: Implementation of the HFAField class for managing information
5 : * about one field in a HFA dictionary type. Managed by HFAType.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 1999, Intergraph Corporation
10 : * Copyright (c) 2009-2011, Even Rouault <even dot rouault at spatialys.com>
11 : *
12 : * SPDX-License-Identifier: MIT
13 : ****************************************************************************/
14 :
15 : #include "cpl_port.h"
16 : #include "hfa_p.h"
17 :
18 : #include <cerrno>
19 : #include <climits>
20 : #include <cstddef>
21 : #include <cstdio>
22 : #include <cstring>
23 : #include <algorithm>
24 : #include <cmath>
25 : #include <limits>
26 : #include <vector>
27 :
28 : #include "cpl_conv.h"
29 : #include "cpl_error.h"
30 : #include "cpl_string.h"
31 : #include "cpl_vsi.h"
32 :
33 : constexpr int MAX_ENTRY_REPORT = 16;
34 :
35 : namespace
36 : {
37 :
38 0 : int FloatToIntClamp(float fValue)
39 : {
40 0 : if (std::isnan(fValue))
41 0 : return 0;
42 0 : if (fValue >= static_cast<float>(std::numeric_limits<int>::max()))
43 0 : return std::numeric_limits<int>::max();
44 0 : if (fValue <= static_cast<float>(std::numeric_limits<int>::min()))
45 0 : return std::numeric_limits<int>::min();
46 0 : return static_cast<int>(fValue);
47 : }
48 :
49 : } // namespace
50 :
51 : /************************************************************************/
52 : /* ==================================================================== */
53 : /* HFAField */
54 : /* ==================================================================== */
55 : /************************************************************************/
56 :
57 : /************************************************************************/
58 : /* HFAField() */
59 : /************************************************************************/
60 :
61 105337 : HFAField::HFAField()
62 : : nBytes(0), nItemCount(0), chPointer('\0'), chItemType('\0'),
63 : pszItemObjectType(nullptr), poItemObjectType(nullptr),
64 105337 : papszEnumNames(nullptr), pszFieldName(nullptr)
65 : {
66 105337 : memset(szNumberString, 0, sizeof(szNumberString));
67 105337 : }
68 :
69 : /************************************************************************/
70 : /* ~HFAField() */
71 : /************************************************************************/
72 :
73 210674 : HFAField::~HFAField()
74 :
75 : {
76 105337 : CPLFree(pszItemObjectType);
77 105337 : CSLDestroy(papszEnumNames);
78 105337 : CPLFree(pszFieldName);
79 105337 : }
80 :
81 : /************************************************************************/
82 : /* Initialize() */
83 : /************************************************************************/
84 :
85 105337 : const char *HFAField::Initialize(const char *pszInput)
86 :
87 : {
88 : // Read the number.
89 105337 : nItemCount = atoi(pszInput);
90 105337 : if (nItemCount < 0)
91 0 : return nullptr;
92 :
93 212915 : while (*pszInput != '\0' && *pszInput != ':')
94 107578 : pszInput++;
95 :
96 105337 : if (*pszInput == '\0')
97 0 : return nullptr;
98 :
99 105337 : pszInput++;
100 :
101 : // Is this a pointer?
102 105337 : if (*pszInput == 'p' || *pszInput == '*')
103 24169 : chPointer = *(pszInput++);
104 :
105 : // Get the general type.
106 105337 : if (*pszInput == '\0')
107 0 : return nullptr;
108 :
109 105337 : chItemType = *(pszInput++);
110 :
111 105337 : if (strchr("124cCesStlLfdmMbox", chItemType) == nullptr)
112 : {
113 1 : CPLError(CE_Failure, CPLE_AppDefined, "Unrecognized item type: %c",
114 1 : chItemType);
115 1 : return nullptr;
116 : }
117 :
118 : // If this is an object, we extract the type of the object.
119 105336 : int i = 0; // TODO: Describe why i needs to span chItemType blocks.
120 :
121 105336 : if (chItemType == 'o')
122 : {
123 171030 : for (i = 0; pszInput[i] != '\0' && pszInput[i] != ','; i++)
124 : {
125 : }
126 12585 : if (pszInput[i] == '\0')
127 0 : return nullptr;
128 :
129 12585 : pszItemObjectType = static_cast<char *>(CPLMalloc(i + 1));
130 12585 : strncpy(pszItemObjectType, pszInput, i);
131 12585 : pszItemObjectType[i] = '\0';
132 :
133 12585 : pszInput += i + 1;
134 : }
135 :
136 : // If this is an inline object, we need to skip past the
137 : // definition, and then extract the object class name.
138 : //
139 : // We ignore the actual definition, so if the object type isn't
140 : // already defined, things will not work properly. See the
141 : // file lceugr250_00_pct.aux for an example of inline defs.
142 105336 : if (chItemType == 'x' && *pszInput == '{')
143 : {
144 2862 : int nBraceDepth = 1;
145 2862 : pszInput++;
146 :
147 : // Skip past the definition.
148 93318 : while (nBraceDepth > 0 && *pszInput != '\0')
149 : {
150 90456 : if (*pszInput == '{')
151 1433 : nBraceDepth++;
152 89023 : else if (*pszInput == '}')
153 4295 : nBraceDepth--;
154 :
155 90456 : pszInput++;
156 : }
157 2862 : if (*pszInput == '\0')
158 0 : return nullptr;
159 :
160 2862 : chItemType = 'o';
161 :
162 : // Find the comma terminating the type name.
163 36516 : for (i = 0; pszInput[i] != '\0' && pszInput[i] != ','; i++)
164 : {
165 : }
166 2862 : if (pszInput[i] == '\0')
167 0 : return nullptr;
168 :
169 2862 : pszItemObjectType = static_cast<char *>(CPLMalloc(i + 1));
170 2862 : strncpy(pszItemObjectType, pszInput, i);
171 2862 : pszItemObjectType[i] = '\0';
172 :
173 2862 : pszInput += i + 1;
174 : }
175 :
176 : // If this is an enumeration we have to extract all the
177 : // enumeration values.
178 105336 : if (chItemType == 'e')
179 : {
180 14377 : const int nEnumCount = atoi(pszInput);
181 :
182 14377 : if (nEnumCount < 0 || nEnumCount > 100000)
183 0 : return nullptr;
184 :
185 14377 : pszInput = strchr(pszInput, ':');
186 14377 : if (pszInput == nullptr)
187 0 : return nullptr;
188 :
189 14377 : pszInput++;
190 :
191 14377 : papszEnumNames =
192 14377 : static_cast<char **>(VSICalloc(sizeof(char *), nEnumCount + 1));
193 14377 : if (papszEnumNames == nullptr)
194 0 : return nullptr;
195 :
196 81369 : for (int iEnum = 0; iEnum < nEnumCount; iEnum++)
197 : {
198 648178 : for (i = 0; pszInput[i] != '\0' && pszInput[i] != ','; i++)
199 : {
200 : }
201 :
202 66992 : if (pszInput[i] != ',')
203 0 : return nullptr;
204 :
205 66992 : char *pszToken = static_cast<char *>(CPLMalloc(i + 1));
206 66992 : strncpy(pszToken, pszInput, i);
207 66992 : pszToken[i] = '\0';
208 :
209 66992 : papszEnumNames[iEnum] = pszToken;
210 :
211 66992 : pszInput += i + 1;
212 : }
213 : }
214 :
215 : // Extract the field name.
216 1017650 : for (i = 0; pszInput[i] != '\0' && pszInput[i] != ','; i++)
217 : {
218 : }
219 105336 : if (pszInput[i] == '\0')
220 0 : return nullptr;
221 :
222 105336 : pszFieldName = static_cast<char *>(CPLMalloc(i + 1));
223 105336 : strncpy(pszFieldName, pszInput, i);
224 105336 : pszFieldName[i] = '\0';
225 :
226 105336 : pszInput += i + 1;
227 :
228 105336 : return pszInput;
229 : }
230 :
231 : /************************************************************************/
232 : /* CompleteDefn() */
233 : /* */
234 : /* Establish size, and pointers to component types. */
235 : /************************************************************************/
236 :
237 105330 : bool HFAField::CompleteDefn(HFADictionary *poDict)
238 :
239 : {
240 : // Get a reference to the type object if we have a type name
241 : // for this field (not a built in).
242 105330 : if (pszItemObjectType != nullptr)
243 15441 : poItemObjectType = poDict->FindType(pszItemObjectType);
244 :
245 : // Figure out the size.
246 105330 : if (chPointer == 'p')
247 : {
248 16609 : nBytes = -1; // We can't know the instance size.
249 : }
250 88721 : else if (poItemObjectType != nullptr)
251 : {
252 11755 : if (!poItemObjectType->CompleteDefn(poDict))
253 2 : return false;
254 11753 : if (poItemObjectType->nBytes == -1)
255 9548 : nBytes = -1;
256 2205 : else if (poItemObjectType->nBytes != 0 &&
257 2205 : nItemCount > INT_MAX / poItemObjectType->nBytes)
258 0 : nBytes = -1;
259 : else
260 2205 : nBytes = poItemObjectType->nBytes * nItemCount;
261 :
262 : // TODO(schwehr): What does the 8 represent?
263 11753 : if (chPointer == '*' && nBytes != -1)
264 : {
265 2205 : if (nBytes > INT_MAX - 8)
266 0 : nBytes = -1;
267 : else
268 2205 : nBytes += 8; // Count, and offset.
269 : }
270 : }
271 : else
272 : {
273 76966 : const int nItemSize = poDict->GetItemSize(chItemType);
274 76966 : if (nItemSize != 0 && nItemCount > INT_MAX / nItemSize)
275 3933 : nBytes = -1;
276 : else
277 73033 : nBytes = nItemSize * nItemCount;
278 : }
279 105328 : return true;
280 : }
281 :
282 : /************************************************************************/
283 : /* Dump() */
284 : /************************************************************************/
285 :
286 0 : void HFAField::Dump(FILE *fp)
287 :
288 : {
289 : const char *pszTypeName;
290 :
291 0 : switch (chItemType)
292 : {
293 0 : case '1':
294 0 : pszTypeName = "U1";
295 0 : break;
296 :
297 0 : case '2':
298 0 : pszTypeName = "U2";
299 0 : break;
300 :
301 0 : case '4':
302 0 : pszTypeName = "U4";
303 0 : break;
304 :
305 0 : case 'c':
306 0 : pszTypeName = "UCHAR";
307 0 : break;
308 :
309 0 : case 'C':
310 0 : pszTypeName = "CHAR";
311 0 : break;
312 :
313 0 : case 'e':
314 0 : pszTypeName = "ENUM";
315 0 : break;
316 :
317 0 : case 's':
318 0 : pszTypeName = "USHORT";
319 0 : break;
320 :
321 0 : case 'S':
322 0 : pszTypeName = "SHORT";
323 0 : break;
324 :
325 0 : case 't':
326 0 : pszTypeName = "TIME";
327 0 : break;
328 :
329 0 : case 'l':
330 0 : pszTypeName = "ULONG";
331 0 : break;
332 :
333 0 : case 'L':
334 0 : pszTypeName = "LONG";
335 0 : break;
336 :
337 0 : case 'f':
338 0 : pszTypeName = "FLOAT";
339 0 : break;
340 :
341 0 : case 'd':
342 0 : pszTypeName = "DOUBLE";
343 0 : break;
344 :
345 0 : case 'm':
346 0 : pszTypeName = "COMPLEX";
347 0 : break;
348 :
349 0 : case 'M':
350 0 : pszTypeName = "DCOMPLEX";
351 0 : break;
352 :
353 0 : case 'b':
354 0 : pszTypeName = "BASEDATA";
355 0 : break;
356 :
357 0 : case 'o':
358 0 : pszTypeName = pszItemObjectType;
359 0 : break;
360 :
361 0 : case 'x':
362 0 : pszTypeName = "InlineType";
363 0 : break;
364 :
365 0 : default:
366 0 : CPLAssert(false);
367 : pszTypeName = "Unknown";
368 : }
369 :
370 0 : CPL_IGNORE_RET_VAL(VSIFPrintf(fp, " %-19s %c %s[%d];\n", pszTypeName,
371 0 : chPointer ? chPointer : ' ', pszFieldName,
372 : nItemCount));
373 :
374 0 : if (papszEnumNames != nullptr)
375 : {
376 0 : for (int i = 0; papszEnumNames[i] != nullptr; i++)
377 : {
378 0 : CPL_IGNORE_RET_VAL(
379 0 : VSIFPrintf(fp, " %s=%d\n", papszEnumNames[i], i));
380 : }
381 : }
382 0 : }
383 :
384 : /************************************************************************/
385 : /* SetInstValue() */
386 : /************************************************************************/
387 :
388 14924 : CPLErr HFAField::SetInstValue(const char *pszField, int nIndexValue,
389 : GByte *pabyData, GUInt32 nDataOffset,
390 : int nDataSize, char chReqType, void *pValue)
391 :
392 : {
393 : // If this field contains a pointer, then we will adjust the
394 : // data offset relative to it.
395 14924 : if (chPointer != '\0')
396 : {
397 7681 : GUInt32 nCount = 0;
398 :
399 : // The count returned for BASEDATA's are the contents,
400 : // but here we really want to mark it as one BASEDATA instance
401 : // (see #2144).
402 7681 : if (chItemType == 'b')
403 : {
404 53 : nCount = 1;
405 : }
406 : // Set the size from string length.
407 7628 : else if (chReqType == 's' && (chItemType == 'c' || chItemType == 'C'))
408 : {
409 1766 : if (pValue != nullptr)
410 1459 : nCount = static_cast<GUInt32>(strlen((char *)pValue) + 1);
411 : }
412 : // Set size based on index. Assumes in-order setting of array.
413 : else
414 : {
415 5862 : nCount = nIndexValue + 1;
416 : }
417 :
418 : // TODO(schwehr): What does the 8 represent?
419 7681 : if (static_cast<int>(nCount) + 8 > nDataSize)
420 : {
421 0 : CPLError(CE_Failure, CPLE_AppDefined,
422 : "Attempt to extend field %s in node past end of data, "
423 : "not currently supported.",
424 : pszField);
425 0 : return CE_Failure;
426 : }
427 :
428 : // We will update the object count iff we are writing beyond the end.
429 7681 : GUInt32 nOffset = 0;
430 7681 : memcpy(&nOffset, pabyData, 4);
431 : HFAStandard(4, &nOffset);
432 7681 : if (nOffset < nCount)
433 : {
434 5933 : nOffset = nCount;
435 : HFAStandard(4, &nOffset);
436 5933 : memcpy(pabyData, &nOffset, 4);
437 : }
438 :
439 7681 : if (pValue == nullptr)
440 307 : nOffset = 0;
441 : else
442 7374 : nOffset = nDataOffset + 8;
443 : HFAStandard(4, &nOffset);
444 7681 : memcpy(pabyData + 4, &nOffset, 4);
445 :
446 7681 : pabyData += 8;
447 :
448 7681 : nDataOffset += 8;
449 7681 : nDataSize -= 8;
450 : }
451 :
452 : // Pointers to char or uchar arrays requested as strings are
453 : // handled as a special case.
454 14924 : if ((chItemType == 'c' || chItemType == 'C') && chReqType == 's')
455 : {
456 1766 : int nBytesToCopy = 0;
457 :
458 1766 : if (nBytes == -1)
459 : {
460 1766 : if (pValue != nullptr)
461 1459 : nBytesToCopy = static_cast<int>(strlen((char *)pValue) + 1);
462 : }
463 : else
464 : {
465 0 : nBytesToCopy = nBytes;
466 : }
467 :
468 1766 : if (nBytesToCopy > nDataSize)
469 : {
470 0 : CPLError(CE_Failure, CPLE_AppDefined,
471 : "Attempt to extend field %s in node past end of data "
472 : "not currently supported.",
473 : pszField);
474 0 : return CE_Failure;
475 : }
476 :
477 1766 : memset(pabyData, 0, nBytesToCopy);
478 :
479 1766 : if (pValue != nullptr)
480 1459 : strncpy((char *)pabyData, (char *)pValue, nBytesToCopy);
481 :
482 1766 : return CE_None;
483 : }
484 :
485 : // Translate the passed type into different representations.
486 13158 : int nIntValue = 0;
487 13158 : double dfDoubleValue = 0.0;
488 :
489 13158 : if (chReqType == 's')
490 : {
491 2114 : CPLAssert(pValue != nullptr);
492 2114 : nIntValue = atoi((char *)pValue);
493 2114 : dfDoubleValue = CPLAtof((char *)pValue);
494 : }
495 11044 : else if (chReqType == 'd')
496 : {
497 7463 : CPLAssert(pValue != nullptr);
498 7463 : dfDoubleValue = *((double *)pValue);
499 7463 : if (dfDoubleValue > INT_MAX)
500 0 : nIntValue = INT_MAX;
501 7463 : else if (dfDoubleValue < INT_MIN)
502 1 : nIntValue = INT_MIN;
503 7462 : else if (std::isfinite(dfDoubleValue))
504 7461 : nIntValue = static_cast<int>(dfDoubleValue);
505 : }
506 3581 : else if (chReqType == 'i')
507 : {
508 3581 : CPLAssert(pValue != nullptr);
509 3581 : nIntValue = *((int *)pValue);
510 3581 : dfDoubleValue = nIntValue;
511 : }
512 0 : else if (chReqType == 'p')
513 : {
514 0 : CPLError(
515 : CE_Failure, CPLE_NotSupported,
516 : "HFAField::SetInstValue() not supported yet for pointer values.");
517 :
518 0 : return CE_Failure;
519 : }
520 : else
521 : {
522 0 : CPLAssert(false);
523 : return CE_Failure;
524 : }
525 :
526 : // Handle by type.
527 13158 : switch (chItemType)
528 : {
529 0 : case 'c':
530 : case 'C':
531 0 : if (nIndexValue + 1 > nDataSize)
532 : {
533 0 : CPLError(CE_Failure, CPLE_AppDefined,
534 : "Attempt to extend field %s in node past end of data, "
535 : "not currently supported.",
536 : pszField);
537 0 : return CE_Failure;
538 : }
539 :
540 0 : if (chReqType == 's')
541 : {
542 0 : CPLAssert(pValue != nullptr);
543 0 : pabyData[nIndexValue] = ((char *)pValue)[0];
544 : }
545 : else
546 : {
547 0 : pabyData[nIndexValue] = static_cast<char>(nIntValue);
548 : }
549 0 : break;
550 :
551 1507 : case 'e':
552 : case 's':
553 : {
554 1507 : if (chItemType == 'e' && chReqType == 's')
555 : {
556 926 : CPLAssert(pValue != nullptr);
557 926 : nIntValue = CSLFindString(papszEnumNames, (char *)pValue);
558 926 : if (nIntValue == -1)
559 : {
560 0 : CPLError(CE_Failure, CPLE_AppDefined,
561 : "Attempt to set enumerated field with unknown"
562 : " value `%s'.",
563 : (char *)pValue);
564 0 : return CE_Failure;
565 : }
566 : }
567 :
568 1507 : if (nIndexValue * 2 + 2 > nDataSize)
569 : {
570 0 : CPLError(CE_Failure, CPLE_AppDefined,
571 : "Attempt to extend field %s in node past end of data, "
572 : "not currently supported.",
573 : pszField);
574 0 : return CE_Failure;
575 : }
576 :
577 : // TODO(schwehr): Warn on clamping.
578 1507 : unsigned short nNumber = static_cast<unsigned short>(nIntValue);
579 : // TODO(schwehr): What is this 2?
580 : HFAStandard(2, &nNumber);
581 1507 : memcpy(pabyData + nIndexValue * 2, &nNumber, 2);
582 : }
583 1507 : break;
584 :
585 0 : case 'S':
586 : {
587 0 : if (nIndexValue * 2 + 2 > nDataSize)
588 : {
589 0 : CPLError(CE_Failure, CPLE_AppDefined,
590 : "Attempt to extend field %s in node past end of data, "
591 : "not currently supported.",
592 : pszField);
593 0 : return CE_Failure;
594 : }
595 :
596 : // TODO(schwehr): Warn on clamping.
597 0 : short nNumber = static_cast<short>(nIntValue);
598 : // TODO(schwehr): What is this 2?
599 : HFAStandard(2, &nNumber);
600 0 : memcpy(pabyData + nIndexValue * 2, &nNumber, 2);
601 : }
602 0 : break;
603 :
604 2314 : case 't':
605 : case 'l':
606 : {
607 2314 : if (nIndexValue * 4 + 4 > nDataSize)
608 : {
609 0 : CPLError(CE_Failure, CPLE_AppDefined,
610 : "Attempt to extend field %s in node past end of data, "
611 : "not currently supported.",
612 : pszField);
613 0 : return CE_Failure;
614 : }
615 :
616 2314 : GUInt32 nNumber = nIntValue;
617 : // TODO(schwehr): What is this 4?
618 : HFAStandard(4, &nNumber);
619 2314 : memcpy(pabyData + nIndexValue * 4, &nNumber, 4);
620 : }
621 2314 : break;
622 :
623 607 : case 'L':
624 : {
625 607 : if (nIndexValue * 4 + 4 > nDataSize)
626 : {
627 0 : CPLError(CE_Failure, CPLE_AppDefined,
628 : "Attempt to extend field %s in node past end of data, "
629 : "not currently supported.",
630 : pszField);
631 0 : return CE_Failure;
632 : }
633 :
634 607 : GInt32 nNumber = nIntValue;
635 : HFAStandard(4, &nNumber);
636 607 : memcpy(pabyData + nIndexValue * 4, &nNumber, 4);
637 : }
638 607 : break;
639 :
640 0 : case 'f':
641 : {
642 0 : if (nIndexValue * 4 + 4 > nDataSize)
643 : {
644 0 : CPLError(CE_Failure, CPLE_AppDefined,
645 : "Attempt to extend field %s in node past end of data, "
646 : "not currently supported.",
647 : pszField);
648 0 : return CE_Failure;
649 : }
650 :
651 : // TODO(schwehr): Warn on clamping.
652 0 : float fNumber = static_cast<float>(dfDoubleValue);
653 : // TODO(schwehr): 4 == sizeof(float)?
654 : HFAStandard(4, &fNumber);
655 0 : memcpy(pabyData + nIndexValue * 4, &fNumber, 4);
656 : }
657 0 : break;
658 :
659 5658 : case 'd':
660 : {
661 5658 : if (nIndexValue * 8 + 8 > nDataSize)
662 : {
663 0 : CPLError(CE_Failure, CPLE_AppDefined,
664 : "Attempt to extend field %s in node past end of data, "
665 : "not currently supported.",
666 : pszField);
667 0 : return CE_Failure;
668 : }
669 :
670 5658 : double dfNumber = dfDoubleValue;
671 : HFAStandard(8, &dfNumber);
672 5658 : memcpy(pabyData + nIndexValue * 8, &dfNumber, 8);
673 : }
674 5658 : break;
675 :
676 53 : case 'b':
677 : {
678 : // Extract existing rows, columns, and datatype.
679 53 : GInt32 nRows = 1; // TODO(schwehr): Why init to 1 instead of 0?
680 53 : memcpy(&nRows, pabyData, 4);
681 : HFAStandard(4, &nRows);
682 :
683 53 : GInt32 nColumns = 1; // TODO(schwehr): Why init to 1 instead of 0?
684 53 : memcpy(&nColumns, pabyData + 4, 4);
685 : HFAStandard(4, &nColumns);
686 :
687 53 : GInt16 nBaseItemType = 0;
688 53 : memcpy(&nBaseItemType, pabyData + 8, 2);
689 : HFAStandard(2, &nBaseItemType);
690 :
691 : // Are we using special index values to update the rows, columns
692 : // or type?
693 :
694 53 : if (nIndexValue == -3)
695 12 : nBaseItemType = static_cast<GInt16>(nIntValue);
696 41 : else if (nIndexValue == -2)
697 12 : nColumns = nIntValue;
698 29 : else if (nIndexValue == -1)
699 12 : nRows = nIntValue;
700 :
701 53 : if (nIndexValue < -3 || nIndexValue >= nRows * nColumns)
702 1 : return CE_Failure;
703 :
704 : // Write back the rows, columns and basedatatype.
705 : HFAStandard(4, &nRows);
706 52 : memcpy(pabyData, &nRows, 4);
707 : HFAStandard(4, &nColumns);
708 52 : memcpy(pabyData + 4, &nColumns, 4);
709 : HFAStandard(2, &nBaseItemType);
710 52 : memcpy(pabyData + 8, &nBaseItemType, 2);
711 : HFAStandard(2, &nBaseItemType); // Swap back for our use.
712 :
713 52 : if (nBaseItemType < EPT_MIN || nBaseItemType > EPT_MAX)
714 0 : return CE_Failure;
715 52 : const EPTType eBaseItemType = static_cast<EPTType>(nBaseItemType);
716 :
717 : // We ignore the 2 byte objecttype value.
718 :
719 52 : nDataSize -= 12;
720 :
721 52 : if (nIndexValue >= 0)
722 : {
723 32 : if ((nIndexValue + 1) *
724 16 : (HFAGetDataTypeBits(eBaseItemType) / 8) >
725 : nDataSize)
726 : {
727 0 : CPLError(CE_Failure, CPLE_AppDefined,
728 : "Attempt to extend field %s in node past end of "
729 : "data, not currently supported.",
730 : pszField);
731 0 : return CE_Failure;
732 : }
733 :
734 16 : if (eBaseItemType == EPT_f64)
735 : {
736 16 : double dfNumber = dfDoubleValue;
737 :
738 : HFAStandard(8, &dfNumber);
739 16 : memcpy(pabyData + 12 + nIndexValue * 8, &dfNumber, 8);
740 : }
741 0 : else if (eBaseItemType == EPT_u8)
742 : {
743 : // TODO(schwehr): Warn on clamping.
744 0 : unsigned char nNumber =
745 0 : static_cast<unsigned char>(dfDoubleValue);
746 0 : memcpy(pabyData + 12 + nIndexValue, &nNumber, 1);
747 : }
748 : else
749 : {
750 0 : CPLError(CE_Failure, CPLE_AppDefined,
751 : "Setting basedata field %s with type %s "
752 : "not currently supported.",
753 : pszField, HFAGetDataTypeName(eBaseItemType));
754 0 : return CE_Failure;
755 : }
756 : }
757 : }
758 52 : break;
759 :
760 3019 : case 'o':
761 3019 : if (poItemObjectType != nullptr)
762 : {
763 3019 : int nExtraOffset = 0;
764 :
765 3019 : if (poItemObjectType->nBytes > 0)
766 : {
767 1251 : if (nIndexValue != 0 &&
768 49 : poItemObjectType->nBytes > INT_MAX / nIndexValue)
769 : {
770 0 : return CE_Failure;
771 : }
772 1251 : nExtraOffset = poItemObjectType->nBytes * nIndexValue;
773 : }
774 : else
775 : {
776 1777 : for (int iIndexCounter = 0; iIndexCounter < nIndexValue &&
777 : nExtraOffset < nDataSize;
778 : iIndexCounter++)
779 : {
780 9 : std::set<HFAField *> oVisitedFields;
781 18 : const int nInc = poItemObjectType->GetInstBytes(
782 9 : pabyData + nExtraOffset, nDataSize - nExtraOffset,
783 : oVisitedFields);
784 9 : if (nInc <= 0 || nExtraOffset > INT_MAX - nInc)
785 : {
786 0 : CPLError(CE_Failure, CPLE_AppDefined,
787 : "Invalid return value");
788 0 : return CE_Failure;
789 : }
790 :
791 9 : nExtraOffset += nInc;
792 : }
793 : }
794 :
795 3019 : if (nExtraOffset >= nDataSize)
796 1 : return CE_Failure;
797 :
798 3018 : if (pszField != nullptr && strlen(pszField) > 0)
799 : {
800 6036 : return poItemObjectType->SetInstValue(
801 3018 : pszField, pabyData + nExtraOffset,
802 3018 : nDataOffset + nExtraOffset, nDataSize - nExtraOffset,
803 3018 : chReqType, pValue);
804 : }
805 : else
806 : {
807 0 : CPLAssert(false);
808 : return CE_Failure;
809 : }
810 : }
811 0 : break;
812 :
813 0 : default:
814 0 : CPLAssert(false);
815 : return CE_Failure;
816 : break;
817 : }
818 :
819 10138 : return CE_None;
820 : }
821 :
822 : /************************************************************************/
823 : /* ExtractInstValue() */
824 : /* */
825 : /* Extract the value of an instance of a field. */
826 : /* */
827 : /* pszField should be NULL if this field is not a */
828 : /* substructure. */
829 : /************************************************************************/
830 :
831 46505 : bool HFAField::ExtractInstValue(const char *pszField, int nIndexValue,
832 : GByte *pabyData, GUInt32 nDataOffset,
833 : int nDataSize, char chReqType, void *pReqReturn,
834 : int *pnRemainingDataSize)
835 :
836 : {
837 46505 : const int nInstItemCount = GetInstCount(pabyData, nDataSize);
838 :
839 46505 : if (pnRemainingDataSize)
840 424 : *pnRemainingDataSize = -1;
841 :
842 : // Check the index value is valid.
843 : // Eventually this will have to account for variable fields.
844 46505 : if (nIndexValue < 0 || nIndexValue >= nInstItemCount)
845 : {
846 566 : if (chItemType == 'b' && nIndexValue >= -3 && nIndexValue < 0)
847 : /* ok - special index values */;
848 : else
849 566 : return false;
850 : }
851 :
852 : // If this field contains a pointer, then we will adjust the
853 : // data offset relative to it.
854 45939 : if (chPointer != '\0')
855 : {
856 19187 : if (nDataSize < 8)
857 : {
858 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
859 0 : return false;
860 : }
861 :
862 19187 : GUInt32 nOffset = 0;
863 19187 : memcpy(&nOffset, pabyData + 4, 4);
864 : HFAStandard(4, &nOffset);
865 :
866 : #if DEBUG_VERBOSE
867 : if (nOffset != static_cast<GUInt32>(nDataOffset + 8))
868 : {
869 : // TODO(schwehr): Debug why this is happening.
870 : CPLError(CE_Warning, CPLE_AppDefined,
871 : "ExtractInstValue: "
872 : "%s.%s points at %d, not %d as expected",
873 : pszFieldName, pszField ? pszField : "", nOffset,
874 : nDataOffset + 8);
875 : }
876 : #endif
877 :
878 19187 : pabyData += 8;
879 19187 : nDataOffset += 8;
880 19187 : nDataSize -= 8;
881 : }
882 :
883 : // Pointers to char or uchar arrays requested as strings are
884 : // handled as a special case.
885 45939 : if ((chItemType == 'c' || chItemType == 'C') && chReqType == 's')
886 : {
887 2018 : *((GByte **)pReqReturn) = pabyData;
888 2018 : if (pnRemainingDataSize)
889 212 : *pnRemainingDataSize = nDataSize;
890 2018 : return pabyData != nullptr;
891 : }
892 :
893 : // Handle by type.
894 43921 : char *pszStringRet = nullptr;
895 43921 : int nIntRet = 0;
896 43921 : double dfDoubleRet = 0.0;
897 43921 : GByte *pabyRawData = nullptr;
898 :
899 43921 : switch (chItemType)
900 : {
901 0 : case 'c':
902 : case 'C':
903 0 : if (nIndexValue >= nDataSize)
904 : {
905 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
906 0 : return false;
907 : }
908 0 : nIntRet = pabyData[nIndexValue];
909 0 : dfDoubleRet = nIntRet;
910 0 : break;
911 :
912 8816 : case 'e':
913 : case 's':
914 : {
915 8816 : if (nIndexValue * 2 + 2 > nDataSize)
916 : {
917 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
918 0 : return false;
919 : }
920 8816 : unsigned short nNumber = 0;
921 8816 : memcpy(&nNumber, pabyData + nIndexValue * 2, 2);
922 : HFAStandard(2, &nNumber);
923 8816 : nIntRet = nNumber;
924 8816 : dfDoubleRet = nIntRet;
925 :
926 17632 : if (chItemType == 'e' &&
927 8816 : nNumber < static_cast<unsigned>(CSLCount(papszEnumNames)))
928 : {
929 8815 : pszStringRet = papszEnumNames[nNumber];
930 : }
931 : }
932 8816 : break;
933 :
934 0 : case 'S':
935 : {
936 0 : if (nIndexValue * 2 + 2 > nDataSize)
937 : {
938 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
939 0 : return false;
940 : }
941 0 : short nNumber = 0;
942 0 : memcpy(&nNumber, pabyData + nIndexValue * 2, 2);
943 : HFAStandard(2, &nNumber);
944 0 : nIntRet = nNumber;
945 0 : dfDoubleRet = nIntRet;
946 : }
947 0 : break;
948 :
949 10613 : case 't':
950 : case 'l':
951 : {
952 10613 : if (nIndexValue * 4 + 4 > nDataSize)
953 : {
954 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
955 0 : return false;
956 : }
957 10613 : GUInt32 nNumber = 0;
958 10613 : memcpy(&nNumber, pabyData + nIndexValue * 4, 4);
959 : HFAStandard(4, &nNumber);
960 10613 : nIntRet = nNumber;
961 10613 : dfDoubleRet = nIntRet;
962 : }
963 10613 : break;
964 :
965 3070 : case 'L':
966 : {
967 3070 : if (nIndexValue * 4 + 4 > nDataSize)
968 : {
969 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
970 0 : return false;
971 : }
972 3070 : GInt32 nNumber = 0;
973 : // TODO(schwehr): What is 4?
974 3070 : memcpy(&nNumber, pabyData + nIndexValue * 4, 4);
975 : HFAStandard(4, &nNumber);
976 3070 : nIntRet = nNumber;
977 3070 : dfDoubleRet = nIntRet;
978 : }
979 3070 : break;
980 :
981 0 : case 'f':
982 : {
983 0 : if (nIndexValue * 4 + 4 > nDataSize)
984 : {
985 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
986 0 : return false;
987 : }
988 0 : float fNumber = 0.0f;
989 : // TODO(schwehr): What is 4?
990 0 : memcpy(&fNumber, pabyData + nIndexValue * 4, 4);
991 : HFAStandard(4, &fNumber);
992 0 : if (static_cast<double>(fNumber) >
993 0 : std::numeric_limits<int>::max() ||
994 0 : static_cast<double>(fNumber) <
995 0 : std::numeric_limits<int>::min() ||
996 0 : std::isnan(fNumber))
997 : {
998 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too large for int: %f",
999 : fNumber);
1000 0 : return false;
1001 : }
1002 0 : dfDoubleRet = fNumber;
1003 0 : nIntRet = static_cast<int>(fNumber);
1004 : }
1005 0 : break;
1006 :
1007 7341 : case 'd':
1008 : {
1009 7341 : if (nIndexValue * 8 + 8 > nDataSize)
1010 : {
1011 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
1012 0 : return false;
1013 : }
1014 7341 : double dfNumber = 0;
1015 7341 : memcpy(&dfNumber, pabyData + nIndexValue * 8, 8);
1016 : HFAStandard(8, &dfNumber);
1017 7341 : dfDoubleRet = dfNumber;
1018 7341 : if (chReqType == 'i')
1019 : {
1020 0 : if (dfNumber > std::numeric_limits<int>::max() ||
1021 0 : dfNumber < std::numeric_limits<int>::min() ||
1022 0 : std::isnan(dfNumber))
1023 : {
1024 0 : CPLError(CE_Failure, CPLE_AppDefined,
1025 : "Too large for int: %f", dfNumber);
1026 0 : return false;
1027 : }
1028 0 : nIntRet = static_cast<int>(dfNumber);
1029 : }
1030 : }
1031 7341 : break;
1032 :
1033 135 : case 'b':
1034 : {
1035 135 : if (nDataSize < 12)
1036 0 : return false;
1037 :
1038 135 : GInt32 nRows = 0;
1039 135 : memcpy(&nRows, pabyData, 4);
1040 : HFAStandard(4, &nRows);
1041 :
1042 135 : GInt32 nColumns = 0;
1043 135 : memcpy(&nColumns, pabyData + 4, 4);
1044 : HFAStandard(4, &nColumns);
1045 :
1046 135 : GInt16 nBaseItemType = 0;
1047 135 : memcpy(&nBaseItemType, pabyData + 8, 2);
1048 : HFAStandard(2, &nBaseItemType);
1049 : // We ignore the 2 byte objecttype value.
1050 :
1051 135 : if (nIndexValue < -3 || nRows <= 0 || nColumns <= 0 ||
1052 135 : nRows > INT_MAX / nColumns || nIndexValue >= nRows * nColumns)
1053 0 : return false;
1054 :
1055 135 : pabyData += 12;
1056 135 : nDataSize -= 12;
1057 :
1058 135 : if (nIndexValue == -3)
1059 : {
1060 0 : dfDoubleRet = nBaseItemType;
1061 0 : nIntRet = nBaseItemType;
1062 : }
1063 135 : else if (nIndexValue == -2)
1064 : {
1065 0 : dfDoubleRet = nColumns;
1066 0 : nIntRet = nColumns;
1067 : }
1068 135 : else if (nIndexValue == -1)
1069 : {
1070 0 : dfDoubleRet = nRows;
1071 0 : nIntRet = nRows;
1072 : }
1073 135 : else if (nBaseItemType == EPT_u1)
1074 : {
1075 : // TODO(schwehr): What are these constants like 8 and 0x7?
1076 0 : if (nIndexValue * 8 >= nDataSize)
1077 : {
1078 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
1079 0 : return false;
1080 : }
1081 :
1082 0 : if (pabyData[nIndexValue >> 3] & (1 << (nIndexValue & 0x7)))
1083 : {
1084 0 : dfDoubleRet = 1;
1085 0 : nIntRet = 1;
1086 : }
1087 : else
1088 : {
1089 0 : dfDoubleRet = 0.0;
1090 0 : nIntRet = 0;
1091 : }
1092 : }
1093 135 : else if (nBaseItemType == EPT_u2)
1094 : {
1095 0 : const int nBitOffset = nIndexValue & 0x3;
1096 0 : const int nByteOffset = nIndexValue >> 2;
1097 :
1098 0 : if (nByteOffset >= nDataSize)
1099 : {
1100 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
1101 0 : return false;
1102 : }
1103 :
1104 0 : const int nMask = 0x3;
1105 0 : nIntRet = (pabyData[nByteOffset] >> nBitOffset) & nMask;
1106 0 : dfDoubleRet = nIntRet;
1107 : }
1108 135 : else if (nBaseItemType == EPT_u4)
1109 : {
1110 0 : const int nBitOffset = nIndexValue & 0x7;
1111 0 : const int nByteOffset = nIndexValue >> 3;
1112 :
1113 0 : if (nByteOffset >= nDataSize)
1114 : {
1115 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
1116 0 : return false;
1117 : }
1118 :
1119 0 : const int nMask = 0x7;
1120 0 : nIntRet = (pabyData[nByteOffset] >> nBitOffset) & nMask;
1121 0 : dfDoubleRet = nIntRet;
1122 : }
1123 135 : else if (nBaseItemType == EPT_u8)
1124 : {
1125 18 : if (nIndexValue >= nDataSize)
1126 : {
1127 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
1128 0 : return false;
1129 : }
1130 18 : dfDoubleRet = pabyData[nIndexValue];
1131 18 : nIntRet = pabyData[nIndexValue];
1132 : }
1133 117 : else if (nBaseItemType == EPT_s8)
1134 : {
1135 0 : if (nIndexValue >= nDataSize)
1136 : {
1137 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
1138 0 : return false;
1139 : }
1140 0 : dfDoubleRet = ((signed char *)pabyData)[nIndexValue];
1141 0 : nIntRet = ((signed char *)pabyData)[nIndexValue];
1142 : }
1143 117 : else if (nBaseItemType == EPT_s16)
1144 : {
1145 0 : if (nIndexValue * 2 + 2 > nDataSize)
1146 : {
1147 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
1148 0 : return false;
1149 : }
1150 0 : GInt16 nValue = 0;
1151 0 : memcpy(&nValue, pabyData + 2 * nIndexValue, 2);
1152 : HFAStandard(2, &nValue);
1153 :
1154 0 : dfDoubleRet = nValue;
1155 0 : nIntRet = nValue;
1156 : }
1157 117 : else if (nBaseItemType == EPT_u16)
1158 : {
1159 0 : if (nIndexValue * 2 + 2 > nDataSize)
1160 : {
1161 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
1162 0 : return false;
1163 : }
1164 0 : GUInt16 nValue = 0;
1165 0 : memcpy(&nValue, pabyData + 2 * nIndexValue, 2);
1166 : HFAStandard(2, &nValue);
1167 :
1168 0 : dfDoubleRet = nValue;
1169 0 : nIntRet = nValue;
1170 : }
1171 117 : else if (nBaseItemType == EPT_s32)
1172 : {
1173 0 : if (nIndexValue * 4 + 4 > nDataSize)
1174 : {
1175 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
1176 0 : return false;
1177 : }
1178 0 : GInt32 nValue = 0;
1179 0 : memcpy(&nValue, pabyData + 4 * nIndexValue, 4);
1180 : HFAStandard(4, &nValue);
1181 :
1182 0 : dfDoubleRet = nValue;
1183 0 : nIntRet = nValue;
1184 : }
1185 117 : else if (nBaseItemType == EPT_u32)
1186 : {
1187 0 : if (nIndexValue * 4 + 4 > nDataSize)
1188 : {
1189 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
1190 0 : return false;
1191 : }
1192 0 : GUInt32 nValue = 0;
1193 0 : memcpy(&nValue, pabyData + 4 * nIndexValue, 4);
1194 : HFAStandard(4, &nValue);
1195 :
1196 0 : dfDoubleRet = nValue;
1197 0 : nIntRet = nValue;
1198 : }
1199 117 : else if (nBaseItemType == EPT_f32)
1200 : {
1201 0 : if (nIndexValue * 4 + 4 > nDataSize)
1202 : {
1203 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
1204 0 : return false;
1205 : }
1206 0 : float fValue = 0.0f;
1207 0 : memcpy(&fValue, pabyData + 4 * nIndexValue, 4);
1208 : HFAStandard(4, &fValue);
1209 :
1210 0 : dfDoubleRet = fValue;
1211 0 : nIntRet = FloatToIntClamp(fValue);
1212 : }
1213 117 : else if (nBaseItemType == EPT_f64)
1214 : {
1215 117 : if (nIndexValue * 8 + 8 > nDataSize)
1216 : {
1217 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
1218 0 : return false;
1219 : }
1220 117 : double dfValue = 0.0;
1221 117 : memcpy(&dfValue, pabyData + 8 * nIndexValue, 8);
1222 : HFAStandard(8, &dfValue);
1223 :
1224 117 : dfDoubleRet = dfValue;
1225 117 : if (chReqType == 'i')
1226 : {
1227 0 : const int nMax = std::numeric_limits<int>::max();
1228 0 : const int nMin = std::numeric_limits<int>::min();
1229 0 : if (dfDoubleRet >= nMax)
1230 : {
1231 0 : nIntRet = nMax;
1232 : }
1233 0 : else if (dfDoubleRet <= nMin)
1234 : {
1235 0 : nIntRet = nMin;
1236 : }
1237 0 : else if (std::isnan(dfDoubleRet))
1238 : {
1239 0 : CPLError(CE_Warning, CPLE_AppDefined,
1240 : "NaN converted to INT_MAX.");
1241 0 : nIntRet = nMax;
1242 : }
1243 : else
1244 : {
1245 0 : nIntRet = static_cast<int>(dfDoubleRet);
1246 : }
1247 : }
1248 : }
1249 : else
1250 : {
1251 0 : CPLError(CE_Failure, CPLE_AppDefined,
1252 : "Unknown base item type: %d", nBaseItemType);
1253 0 : return false;
1254 : }
1255 : }
1256 135 : break;
1257 :
1258 13946 : case 'o':
1259 13946 : if (poItemObjectType != nullptr)
1260 : {
1261 13946 : int nExtraOffset = 0;
1262 :
1263 13946 : if (poItemObjectType->nBytes > 0)
1264 : {
1265 11396 : if (nIndexValue != 0 &&
1266 9264 : poItemObjectType->nBytes > INT_MAX / nIndexValue)
1267 : // TODO(schwehr): Why was this CE_Failure when the
1268 : // others are false?
1269 0 : return false;
1270 11396 : nExtraOffset = poItemObjectType->nBytes * nIndexValue;
1271 : }
1272 : else
1273 : {
1274 2567 : for (int iIndexCounter = 0; iIndexCounter < nIndexValue &&
1275 : nExtraOffset < nDataSize;
1276 : iIndexCounter++)
1277 : {
1278 17 : std::set<HFAField *> oVisitedFields;
1279 34 : const int nInc = poItemObjectType->GetInstBytes(
1280 17 : pabyData + nExtraOffset, nDataSize - nExtraOffset,
1281 : oVisitedFields);
1282 17 : if (nInc <= 0 || nExtraOffset > INT_MAX - nInc)
1283 : {
1284 0 : CPLError(CE_Failure, CPLE_AppDefined,
1285 : "Invalid return value");
1286 : // TODO(schwehr): Verify this false is okay.
1287 0 : return false;
1288 : }
1289 :
1290 17 : nExtraOffset += nInc;
1291 : }
1292 : }
1293 :
1294 13946 : if (nExtraOffset >= nDataSize)
1295 0 : return false;
1296 :
1297 13946 : pabyRawData = pabyData + nExtraOffset;
1298 :
1299 13946 : if (pszField != nullptr && strlen(pszField) > 0)
1300 : {
1301 27892 : return poItemObjectType->ExtractInstValue(
1302 13946 : pszField, pabyRawData, nDataOffset + nExtraOffset,
1303 : nDataSize - nExtraOffset, chReqType, pReqReturn,
1304 13946 : pnRemainingDataSize);
1305 : }
1306 : }
1307 : else
1308 : {
1309 : // E. Rouault: not completely sure about this, but helps avoid
1310 : // DoS timeouts in cases like
1311 : // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=1806
1312 0 : return false;
1313 : }
1314 0 : break;
1315 :
1316 0 : default:
1317 0 : return false;
1318 : break;
1319 : }
1320 :
1321 : // Return the appropriate representation.
1322 29975 : if (chReqType == 's')
1323 : {
1324 1102 : if (pszStringRet == nullptr)
1325 : {
1326 : // HFAEntry:: BuildEntryFromMIFObject() expects to have always 8
1327 : // bytes before the data. In normal situations, it should not go
1328 : // here, but that can happen if the file is corrupted so reserve the
1329 : // first 8 bytes before the string to contain null bytes.
1330 39 : memset(szNumberString, 0, 8);
1331 39 : CPLsnprintf(szNumberString + 8, sizeof(szNumberString) - 8, "%.14g",
1332 : dfDoubleRet);
1333 39 : pszStringRet = szNumberString + 8;
1334 : }
1335 :
1336 1102 : *((char **)pReqReturn) = pszStringRet;
1337 1102 : return true;
1338 : }
1339 28873 : else if (chReqType == 'd')
1340 : {
1341 7437 : *((double *)pReqReturn) = dfDoubleRet;
1342 7437 : return true;
1343 : }
1344 21436 : else if (chReqType == 'i')
1345 : {
1346 21436 : *((int *)pReqReturn) = nIntRet;
1347 21436 : return true;
1348 : }
1349 0 : else if (chReqType == 'p')
1350 : {
1351 0 : *((GByte **)pReqReturn) = pabyRawData;
1352 0 : return true;
1353 : }
1354 : else
1355 : {
1356 0 : CPLAssert(false);
1357 : return false;
1358 : }
1359 : }
1360 :
1361 : /************************************************************************/
1362 : /* GetInstBytes() */
1363 : /* */
1364 : /* Get the number of bytes in a particular instance of a */
1365 : /* field. This will normally be the fixed internal nBytes */
1366 : /* value, but for pointer objects will include the variable */
1367 : /* portion. */
1368 : /************************************************************************/
1369 :
1370 168738 : int HFAField::GetInstBytes(GByte *pabyData, int nDataSize,
1371 : std::set<HFAField *> &oVisitedFields)
1372 :
1373 : {
1374 168738 : if (oVisitedFields.find(this) != oVisitedFields.end())
1375 : {
1376 0 : CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
1377 0 : return -1;
1378 : }
1379 :
1380 168738 : if (nBytes > -1)
1381 135344 : return nBytes;
1382 :
1383 33394 : int nCount = 1;
1384 33394 : int nInstBytes = 0;
1385 :
1386 33394 : if (chPointer != '\0')
1387 : {
1388 30962 : if (nDataSize < 4)
1389 : {
1390 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
1391 0 : return -1;
1392 : }
1393 :
1394 30962 : memcpy(&nCount, pabyData, 4);
1395 : HFAStandard(4, &nCount);
1396 :
1397 30962 : pabyData += 8;
1398 30962 : nInstBytes += 8;
1399 : }
1400 :
1401 33394 : if (chItemType == 'b' && nCount != 0) // BASEDATA
1402 : {
1403 515 : if (nDataSize - nInstBytes < 4 + 4 + 2)
1404 : {
1405 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
1406 0 : return -1;
1407 : }
1408 :
1409 515 : GInt32 nRows = 0;
1410 515 : memcpy(&nRows, pabyData, 4);
1411 : HFAStandard(4, &nRows);
1412 515 : GInt32 nColumns = 0;
1413 515 : memcpy(&nColumns, pabyData + 4, 4);
1414 : HFAStandard(4, &nColumns);
1415 515 : GInt16 nBaseItemType = 0;
1416 515 : memcpy(&nBaseItemType, pabyData + 8, 2);
1417 : HFAStandard(2, &nBaseItemType);
1418 515 : if (nBaseItemType < EPT_MIN || nBaseItemType > EPT_MAX)
1419 0 : return -1;
1420 :
1421 515 : EPTType eBaseItemType = static_cast<EPTType>(nBaseItemType);
1422 :
1423 515 : nInstBytes += 12;
1424 :
1425 515 : if (nRows < 0 || nColumns < 0)
1426 0 : return -1;
1427 515 : if (nColumns != 0 && nRows > INT_MAX / nColumns)
1428 0 : return -1;
1429 1022 : if (nRows != 0 &&
1430 507 : ((HFAGetDataTypeBits(eBaseItemType) + 7) / 8) > INT_MAX / nRows)
1431 0 : return -1;
1432 1022 : if (nColumns != 0 &&
1433 507 : ((HFAGetDataTypeBits(eBaseItemType) + 7) / 8) * nRows >
1434 507 : INT_MAX / nColumns)
1435 0 : return -1;
1436 515 : if (((HFAGetDataTypeBits(eBaseItemType) + 7) / 8) * nRows * nColumns >
1437 515 : INT_MAX - nInstBytes)
1438 0 : return -1;
1439 :
1440 515 : nInstBytes +=
1441 515 : ((HFAGetDataTypeBits(eBaseItemType) + 7) / 8) * nRows * nColumns;
1442 : }
1443 32879 : else if (poItemObjectType == nullptr)
1444 : {
1445 49434 : if (nCount != 0 &&
1446 20000 : HFADictionary::GetItemSize(chItemType) > INT_MAX / nCount)
1447 0 : return -1;
1448 29434 : if (nCount * HFADictionary::GetItemSize(chItemType) >
1449 29434 : INT_MAX - nInstBytes)
1450 0 : return -1;
1451 29434 : nInstBytes += nCount * HFADictionary::GetItemSize(chItemType);
1452 : }
1453 : else
1454 : {
1455 3445 : oVisitedFields.insert(this);
1456 5931 : for (int i = 0; i < nCount && nInstBytes < nDataSize && nInstBytes >= 0;
1457 : i++)
1458 : {
1459 2486 : const int nThisBytes = poItemObjectType->GetInstBytes(
1460 : pabyData, nDataSize - nInstBytes, oVisitedFields);
1461 2486 : if (nThisBytes <= 0 || nInstBytes > INT_MAX - nThisBytes)
1462 : {
1463 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid return value");
1464 0 : return -1;
1465 : }
1466 :
1467 2486 : nInstBytes += nThisBytes;
1468 2486 : pabyData += nThisBytes;
1469 : }
1470 3445 : oVisitedFields.erase(this);
1471 : }
1472 :
1473 33394 : return nInstBytes;
1474 : }
1475 :
1476 : /************************************************************************/
1477 : /* GetInstCount() */
1478 : /* */
1479 : /* Get the count for a particular instance of a field. This */
1480 : /* will normally be the built in value, but for variable fields */
1481 : /* this is extracted from the data itself. */
1482 : /************************************************************************/
1483 :
1484 47439 : int HFAField::GetInstCount(GByte *pabyData, int nDataSize) const
1485 :
1486 : {
1487 47439 : if (chPointer == '\0')
1488 27415 : return nItemCount;
1489 :
1490 20024 : if (chItemType == 'b')
1491 : {
1492 213 : if (nDataSize < 20)
1493 0 : return 0;
1494 :
1495 213 : GInt32 nRows = 0;
1496 213 : memcpy(&nRows, pabyData + 8, 4);
1497 : HFAStandard(4, &nRows);
1498 213 : GInt32 nColumns = 0;
1499 213 : memcpy(&nColumns, pabyData + 12, 4);
1500 : HFAStandard(4, &nColumns);
1501 :
1502 213 : if (nRows < 0 || nColumns < 0)
1503 0 : return 0;
1504 213 : if (nColumns != 0 && nRows > INT_MAX / nColumns)
1505 0 : return 0;
1506 :
1507 213 : return nRows * nColumns;
1508 : }
1509 :
1510 19811 : if (nDataSize < 4)
1511 0 : return 0;
1512 :
1513 19811 : GInt32 nCount = 0;
1514 19811 : memcpy(&nCount, pabyData, 4);
1515 : HFAStandard(4, &nCount);
1516 19811 : return nCount;
1517 : }
1518 :
1519 : /************************************************************************/
1520 : /* DumpInstValue() */
1521 : /************************************************************************/
1522 :
1523 0 : void HFAField::DumpInstValue(FILE *fpOut, GByte *pabyData, GUInt32 nDataOffset,
1524 : int nDataSize, const char *pszPrefix)
1525 :
1526 : {
1527 0 : const int nEntries = GetInstCount(pabyData, nDataSize);
1528 :
1529 : // Special case for arrays of chars or uchars which are printed
1530 : // as a string.
1531 0 : if ((chItemType == 'c' || chItemType == 'C') && nEntries > 0)
1532 : {
1533 0 : void *pReturn = nullptr;
1534 0 : if (ExtractInstValue(nullptr, 0, pabyData, nDataOffset, nDataSize, 's',
1535 : &pReturn))
1536 0 : CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "%s%s = `%s'\n", pszPrefix,
1537 : pszFieldName,
1538 : static_cast<char *>(pReturn)));
1539 : else
1540 0 : CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "%s%s = (access failed)\n",
1541 : pszPrefix, pszFieldName));
1542 :
1543 0 : return;
1544 : }
1545 :
1546 : // For BASEDATA objects, we want to first dump their dimension and type.
1547 0 : if (chItemType == 'b')
1548 : {
1549 0 : int nDataType = 0;
1550 0 : const bool bSuccess = ExtractInstValue(
1551 : nullptr, -3, pabyData, nDataOffset, nDataSize, 'i', &nDataType);
1552 0 : if (bSuccess)
1553 : {
1554 0 : int nColumns = 0;
1555 0 : ExtractInstValue(nullptr, -2, pabyData, nDataOffset, nDataSize, 'i',
1556 : &nColumns);
1557 0 : int nRows = 0;
1558 0 : ExtractInstValue(nullptr, -1, pabyData, nDataOffset, nDataSize, 'i',
1559 : &nRows);
1560 0 : CPL_IGNORE_RET_VAL(VSIFPrintf(
1561 : fpOut, "%sBASEDATA(%s): %dx%d of %s\n", pszPrefix, pszFieldName,
1562 : nColumns, nRows,
1563 0 : (nDataType >= EPT_MIN && nDataType <= EPT_MAX)
1564 0 : ? HFAGetDataTypeName(static_cast<EPTType>(nDataType))
1565 : : "invalid type"));
1566 : }
1567 : else
1568 : {
1569 0 : CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "%sBASEDATA(%s): empty\n",
1570 : pszPrefix, pszFieldName));
1571 : }
1572 : }
1573 :
1574 : // Dump each entry in the field array.
1575 0 : void *pReturn = nullptr;
1576 :
1577 0 : const int nMaxEntry = std::min(MAX_ENTRY_REPORT, nEntries);
1578 0 : for (int iEntry = 0; iEntry < nMaxEntry; iEntry++)
1579 : {
1580 0 : if (nEntries == 1)
1581 0 : CPL_IGNORE_RET_VAL(
1582 0 : VSIFPrintf(fpOut, "%s%s = ", pszPrefix, pszFieldName));
1583 : else
1584 0 : CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "%s%s[%d] = ", pszPrefix,
1585 : pszFieldName, iEntry));
1586 :
1587 0 : switch (chItemType)
1588 : {
1589 0 : case 'f':
1590 : case 'd':
1591 : {
1592 0 : double dfValue = 0.0;
1593 0 : if (ExtractInstValue(nullptr, iEntry, pabyData, nDataOffset,
1594 : nDataSize, 'd', &dfValue))
1595 0 : CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "%f\n", dfValue));
1596 : else
1597 0 : CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "(access failed)\n"));
1598 : }
1599 0 : break;
1600 :
1601 0 : case 'b':
1602 : {
1603 0 : double dfValue = 0.0;
1604 :
1605 0 : if (ExtractInstValue(nullptr, iEntry, pabyData, nDataOffset,
1606 : nDataSize, 'd', &dfValue))
1607 0 : CPL_IGNORE_RET_VAL(
1608 0 : VSIFPrintf(fpOut, "%s%.15g\n", pszPrefix, dfValue));
1609 : else
1610 0 : CPL_IGNORE_RET_VAL(
1611 0 : VSIFPrintf(fpOut, "%s(access failed)\n", pszPrefix));
1612 : }
1613 0 : break;
1614 :
1615 0 : case 'e':
1616 0 : if (ExtractInstValue(nullptr, iEntry, pabyData, nDataOffset,
1617 : nDataSize, 's', &pReturn))
1618 0 : CPL_IGNORE_RET_VAL(
1619 0 : VSIFPrintf(fpOut, "%s\n", (char *)pReturn));
1620 : else
1621 0 : CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "(access failed)\n"));
1622 0 : break;
1623 :
1624 0 : case 'o':
1625 0 : if (!ExtractInstValue(nullptr, iEntry, pabyData, nDataOffset,
1626 : nDataSize, 'p', &pReturn))
1627 : {
1628 0 : CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "(access failed)\n"));
1629 : }
1630 : else
1631 : {
1632 0 : CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "\n"));
1633 :
1634 0 : const int nByteOffset =
1635 0 : static_cast<int>(((GByte *)pReturn) - pabyData);
1636 :
1637 0 : char szLongFieldName[256] = {};
1638 0 : snprintf(szLongFieldName, sizeof(szLongFieldName), "%s ",
1639 : pszPrefix);
1640 :
1641 0 : if (poItemObjectType)
1642 0 : poItemObjectType->DumpInstValue(
1643 0 : fpOut, pabyData + nByteOffset,
1644 0 : nDataOffset + nByteOffset, nDataSize - nByteOffset,
1645 : szLongFieldName);
1646 : }
1647 0 : break;
1648 :
1649 0 : default:
1650 : {
1651 0 : GInt32 nIntValue = 0;
1652 :
1653 0 : if (ExtractInstValue(nullptr, iEntry, pabyData, nDataOffset,
1654 : nDataSize, 'i', &nIntValue))
1655 0 : CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "%d\n", nIntValue));
1656 : else
1657 0 : CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "(access failed)\n"));
1658 : }
1659 0 : break;
1660 : }
1661 : }
1662 :
1663 0 : if (nEntries > MAX_ENTRY_REPORT)
1664 0 : CPL_IGNORE_RET_VAL(VSIFPrintf(
1665 : fpOut, "%s ... remaining instances omitted ...\n", pszPrefix));
1666 :
1667 0 : if (nEntries == 0)
1668 0 : CPL_IGNORE_RET_VAL(
1669 0 : VSIFPrintf(fpOut, "%s%s = (no values)\n", pszPrefix, pszFieldName));
1670 : }
|