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 105760 : HFAField::HFAField()
62 : : nBytes(0), nItemCount(0), chPointer('\0'), chItemType('\0'),
63 : pszItemObjectType(nullptr), poItemObjectType(nullptr),
64 105760 : papszEnumNames(nullptr), pszFieldName(nullptr)
65 : {
66 105760 : memset(szNumberString, 0, sizeof(szNumberString));
67 105760 : }
68 :
69 : /************************************************************************/
70 : /* ~HFAField() */
71 : /************************************************************************/
72 :
73 211520 : HFAField::~HFAField()
74 :
75 : {
76 105760 : CPLFree(pszItemObjectType);
77 105760 : CSLDestroy(papszEnumNames);
78 105760 : CPLFree(pszFieldName);
79 105760 : }
80 :
81 : /************************************************************************/
82 : /* Initialize() */
83 : /************************************************************************/
84 :
85 105760 : const char *HFAField::Initialize(const char *pszInput)
86 :
87 : {
88 : // Read the number.
89 105760 : nItemCount = atoi(pszInput);
90 105760 : if (nItemCount < 0)
91 0 : return nullptr;
92 :
93 213770 : while (*pszInput != '\0' && *pszInput != ':')
94 108010 : pszInput++;
95 :
96 105760 : if (*pszInput == '\0')
97 0 : return nullptr;
98 :
99 105760 : pszInput++;
100 :
101 : // Is this a pointer?
102 105760 : if (*pszInput == 'p' || *pszInput == '*')
103 24268 : chPointer = *(pszInput++);
104 :
105 : // Get the general type.
106 105760 : if (*pszInput == '\0')
107 0 : return nullptr;
108 :
109 105760 : chItemType = *(pszInput++);
110 :
111 105760 : 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 105759 : int i = 0; // TODO: Describe why i needs to span chItemType blocks.
120 :
121 105759 : if (chItemType == 'o')
122 : {
123 171723 : for (i = 0; pszInput[i] != '\0' && pszInput[i] != ','; i++)
124 : {
125 : }
126 12636 : if (pszInput[i] == '\0')
127 0 : return nullptr;
128 :
129 12636 : pszItemObjectType = static_cast<char *>(CPLMalloc(i + 1));
130 12636 : strncpy(pszItemObjectType, pszInput, i);
131 12636 : pszItemObjectType[i] = '\0';
132 :
133 12636 : 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 105759 : if (chItemType == 'x' && *pszInput == '{')
143 : {
144 2874 : int nBraceDepth = 1;
145 2874 : pszInput++;
146 :
147 : // Skip past the definition.
148 93708 : while (nBraceDepth > 0 && *pszInput != '\0')
149 : {
150 90834 : if (*pszInput == '{')
151 1439 : nBraceDepth++;
152 89395 : else if (*pszInput == '}')
153 4313 : nBraceDepth--;
154 :
155 90834 : pszInput++;
156 : }
157 2874 : if (*pszInput == '\0')
158 0 : return nullptr;
159 :
160 2874 : chItemType = 'o';
161 :
162 : // Find the comma terminating the type name.
163 36669 : for (i = 0; pszInput[i] != '\0' && pszInput[i] != ','; i++)
164 : {
165 : }
166 2874 : if (pszInput[i] == '\0')
167 0 : return nullptr;
168 :
169 2874 : pszItemObjectType = static_cast<char *>(CPLMalloc(i + 1));
170 2874 : strncpy(pszItemObjectType, pszInput, i);
171 2874 : pszItemObjectType[i] = '\0';
172 :
173 2874 : pszInput += i + 1;
174 : }
175 :
176 : // If this is an enumeration we have to extract all the
177 : // enumeration values.
178 105759 : if (chItemType == 'e')
179 : {
180 14434 : const int nEnumCount = atoi(pszInput);
181 :
182 14434 : if (nEnumCount < 0 || nEnumCount > 100000)
183 0 : return nullptr;
184 :
185 14434 : pszInput = strchr(pszInput, ':');
186 14434 : if (pszInput == nullptr)
187 0 : return nullptr;
188 :
189 14434 : pszInput++;
190 :
191 14434 : papszEnumNames =
192 14434 : static_cast<char **>(VSICalloc(sizeof(char *), nEnumCount + 1));
193 14434 : if (papszEnumNames == nullptr)
194 0 : return nullptr;
195 :
196 81681 : for (int iEnum = 0; iEnum < nEnumCount; iEnum++)
197 : {
198 650584 : for (i = 0; pszInput[i] != '\0' && pszInput[i] != ','; i++)
199 : {
200 : }
201 :
202 67247 : if (pszInput[i] != ',')
203 0 : return nullptr;
204 :
205 67247 : char *pszToken = static_cast<char *>(CPLMalloc(i + 1));
206 67247 : strncpy(pszToken, pszInput, i);
207 67247 : pszToken[i] = '\0';
208 :
209 67247 : papszEnumNames[iEnum] = pszToken;
210 :
211 67247 : pszInput += i + 1;
212 : }
213 : }
214 :
215 : // Extract the field name.
216 1021740 : for (i = 0; pszInput[i] != '\0' && pszInput[i] != ','; i++)
217 : {
218 : }
219 105759 : if (pszInput[i] == '\0')
220 0 : return nullptr;
221 :
222 105759 : pszFieldName = static_cast<char *>(CPLMalloc(i + 1));
223 105759 : strncpy(pszFieldName, pszInput, i);
224 105759 : pszFieldName[i] = '\0';
225 :
226 105759 : pszInput += i + 1;
227 :
228 105759 : return pszInput;
229 : }
230 :
231 : /************************************************************************/
232 : /* CompleteDefn() */
233 : /* */
234 : /* Establish size, and pointers to component types. */
235 : /************************************************************************/
236 :
237 105753 : 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 105753 : if (pszItemObjectType != nullptr)
243 15504 : poItemObjectType = poDict->FindType(pszItemObjectType);
244 :
245 : // Figure out the size.
246 105753 : if (chPointer == 'p')
247 : {
248 16678 : nBytes = -1; // We can't know the instance size.
249 : }
250 89075 : else if (poItemObjectType != nullptr)
251 : {
252 11803 : if (!poItemObjectType->CompleteDefn(poDict))
253 2 : return false;
254 11801 : if (poItemObjectType->nBytes == -1)
255 9587 : nBytes = -1;
256 2214 : else if (poItemObjectType->nBytes != 0 &&
257 2214 : nItemCount > INT_MAX / poItemObjectType->nBytes)
258 0 : nBytes = -1;
259 : else
260 2214 : nBytes = poItemObjectType->nBytes * nItemCount;
261 :
262 : // TODO(schwehr): What does the 8 represent?
263 11801 : if (chPointer == '*' && nBytes != -1)
264 : {
265 2214 : if (nBytes > INT_MAX - 8)
266 0 : nBytes = -1;
267 : else
268 2214 : nBytes += 8; // Count, and offset.
269 : }
270 : }
271 : else
272 : {
273 77272 : const int nItemSize = poDict->GetItemSize(chItemType);
274 77272 : if (nItemSize != 0 && nItemCount > INT_MAX / nItemSize)
275 3948 : nBytes = -1;
276 : else
277 73324 : nBytes = nItemSize * nItemCount;
278 : }
279 105751 : 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 14954 : 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 14954 : if (chPointer != '\0')
396 : {
397 7687 : 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 7687 : if (chItemType == 'b')
403 : {
404 53 : nCount = 1;
405 : }
406 : // Set the size from string length.
407 7634 : else if (chReqType == 's' && (chItemType == 'c' || chItemType == 'C'))
408 : {
409 1770 : if (pValue != nullptr)
410 1463 : 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 5864 : nCount = nIndexValue + 1;
416 : }
417 :
418 : // TODO(schwehr): What does the 8 represent?
419 7687 : 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 7687 : GUInt32 nOffset = 0;
430 7687 : memcpy(&nOffset, pabyData, 4);
431 : HFAStandard(4, &nOffset);
432 7687 : if (nOffset < nCount)
433 : {
434 5938 : nOffset = nCount;
435 : HFAStandard(4, &nOffset);
436 5938 : memcpy(pabyData, &nOffset, 4);
437 : }
438 :
439 7687 : if (pValue == nullptr)
440 307 : nOffset = 0;
441 : else
442 7380 : nOffset = nDataOffset + 8;
443 : HFAStandard(4, &nOffset);
444 7687 : memcpy(pabyData + 4, &nOffset, 4);
445 :
446 7687 : pabyData += 8;
447 :
448 7687 : nDataOffset += 8;
449 7687 : nDataSize -= 8;
450 : }
451 :
452 : // Pointers to char or uchar arrays requested as strings are
453 : // handled as a special case.
454 14954 : if ((chItemType == 'c' || chItemType == 'C') && chReqType == 's')
455 : {
456 1770 : int nBytesToCopy = 0;
457 :
458 1770 : if (nBytes == -1)
459 : {
460 1770 : if (pValue != nullptr)
461 1463 : nBytesToCopy = static_cast<int>(strlen((char *)pValue) + 1);
462 : }
463 : else
464 : {
465 0 : nBytesToCopy = nBytes;
466 : }
467 :
468 1770 : 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 1770 : memset(pabyData, 0, nBytesToCopy);
478 :
479 1770 : if (pValue != nullptr)
480 1463 : strncpy((char *)pabyData, (char *)pValue, nBytesToCopy);
481 :
482 1770 : return CE_None;
483 : }
484 :
485 : // Translate the passed type into different representations.
486 13184 : int nIntValue = 0;
487 13184 : double dfDoubleValue = 0.0;
488 :
489 13184 : if (chReqType == 's')
490 : {
491 2125 : CPLAssert(pValue != nullptr);
492 2125 : nIntValue = atoi((char *)pValue);
493 2125 : dfDoubleValue = CPLAtof((char *)pValue);
494 : }
495 11059 : 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 3596 : else if (chReqType == 'i')
507 : {
508 3596 : CPLAssert(pValue != nullptr);
509 3596 : nIntValue = *((int *)pValue);
510 3596 : 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 13184 : 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 1515 : case 'e':
552 : case 's':
553 : {
554 1515 : if (chItemType == 'e' && chReqType == 's')
555 : {
556 932 : CPLAssert(pValue != nullptr);
557 932 : nIntValue = CSLFindString(papszEnumNames, (char *)pValue);
558 932 : 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 1515 : 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 1515 : unsigned short nNumber = static_cast<unsigned short>(nIntValue);
579 : // TODO(schwehr): What is this 2?
580 : HFAStandard(2, &nNumber);
581 1515 : memcpy(pabyData + nIndexValue * 2, &nNumber, 2);
582 : }
583 1515 : 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 2325 : case 't':
605 : case 'l':
606 : {
607 2325 : 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 2325 : GUInt32 nNumber = nIntValue;
617 : // TODO(schwehr): What is this 4?
618 : HFAStandard(4, &nNumber);
619 2325 : memcpy(pabyData + nIndexValue * 4, &nNumber, 4);
620 : }
621 2325 : break;
622 :
623 609 : case 'L':
624 : {
625 609 : 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 609 : GInt32 nNumber = nIntValue;
635 : HFAStandard(4, &nNumber);
636 609 : memcpy(pabyData + nIndexValue * 4, &nNumber, 4);
637 : }
638 609 : 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 3024 : case 'o':
761 3024 : if (poItemObjectType != nullptr)
762 : {
763 3024 : int nExtraOffset = 0;
764 :
765 3024 : if (poItemObjectType->nBytes > 0)
766 : {
767 1252 : if (nIndexValue != 0 &&
768 49 : poItemObjectType->nBytes > INT_MAX / nIndexValue)
769 : {
770 0 : return CE_Failure;
771 : }
772 1252 : nExtraOffset = poItemObjectType->nBytes * nIndexValue;
773 : }
774 : else
775 : {
776 1781 : 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 3024 : if (nExtraOffset >= nDataSize)
796 1 : return CE_Failure;
797 :
798 3023 : if (pszField != nullptr && strlen(pszField) > 0)
799 : {
800 6046 : return poItemObjectType->SetInstValue(
801 3023 : pszField, pabyData + nExtraOffset,
802 3023 : nDataOffset + nExtraOffset, nDataSize - nExtraOffset,
803 3023 : 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 : }
817 :
818 10159 : return CE_None;
819 : }
820 :
821 : /************************************************************************/
822 : /* ExtractInstValue() */
823 : /* */
824 : /* Extract the value of an instance of a field. */
825 : /* */
826 : /* pszField should be NULL if this field is not a */
827 : /* substructure. */
828 : /************************************************************************/
829 :
830 46564 : bool HFAField::ExtractInstValue(const char *pszField, int nIndexValue,
831 : GByte *pabyData, GUInt32 nDataOffset,
832 : int nDataSize, char chReqType, void *pReqReturn,
833 : int *pnRemainingDataSize)
834 :
835 : {
836 46564 : const int nInstItemCount = GetInstCount(pabyData, nDataSize);
837 :
838 46564 : if (pnRemainingDataSize)
839 424 : *pnRemainingDataSize = -1;
840 :
841 : // Check the index value is valid.
842 : // Eventually this will have to account for variable fields.
843 46564 : if (nIndexValue < 0 || nIndexValue >= nInstItemCount)
844 : {
845 567 : if (chItemType == 'b' && nIndexValue >= -3 && nIndexValue < 0)
846 : /* ok - special index values */;
847 : else
848 567 : return false;
849 : }
850 :
851 : // If this field contains a pointer, then we will adjust the
852 : // data offset relative to it.
853 45997 : if (chPointer != '\0')
854 : {
855 19195 : if (nDataSize < 8)
856 : {
857 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
858 0 : return false;
859 : }
860 :
861 19195 : GUInt32 nOffset = 0;
862 19195 : memcpy(&nOffset, pabyData + 4, 4);
863 : HFAStandard(4, &nOffset);
864 :
865 : #if DEBUG_VERBOSE
866 : if (nOffset != static_cast<GUInt32>(nDataOffset + 8))
867 : {
868 : // TODO(schwehr): Debug why this is happening.
869 : CPLError(CE_Warning, CPLE_AppDefined,
870 : "ExtractInstValue: "
871 : "%s.%s points at %d, not %d as expected",
872 : pszFieldName, pszField ? pszField : "", nOffset,
873 : nDataOffset + 8);
874 : }
875 : #endif
876 :
877 19195 : pabyData += 8;
878 19195 : nDataOffset += 8;
879 19195 : nDataSize -= 8;
880 : }
881 :
882 : // Pointers to char or uchar arrays requested as strings are
883 : // handled as a special case.
884 45997 : if ((chItemType == 'c' || chItemType == 'C') && chReqType == 's')
885 : {
886 2021 : *((GByte **)pReqReturn) = pabyData;
887 2021 : if (pnRemainingDataSize)
888 212 : *pnRemainingDataSize = nDataSize;
889 2021 : return pabyData != nullptr;
890 : }
891 :
892 : // Handle by type.
893 43976 : char *pszStringRet = nullptr;
894 43976 : int nIntRet = 0;
895 43976 : double dfDoubleRet = 0.0;
896 43976 : GByte *pabyRawData = nullptr;
897 :
898 43976 : switch (chItemType)
899 : {
900 0 : case 'c':
901 : case 'C':
902 0 : if (nIndexValue >= nDataSize)
903 : {
904 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
905 0 : return false;
906 : }
907 0 : nIntRet = pabyData[nIndexValue];
908 0 : dfDoubleRet = nIntRet;
909 0 : break;
910 :
911 8830 : case 'e':
912 : case 's':
913 : {
914 8830 : if (nIndexValue * 2 + 2 > nDataSize)
915 : {
916 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
917 0 : return false;
918 : }
919 8830 : unsigned short nNumber = 0;
920 8830 : memcpy(&nNumber, pabyData + nIndexValue * 2, 2);
921 : HFAStandard(2, &nNumber);
922 8830 : nIntRet = nNumber;
923 8830 : dfDoubleRet = nIntRet;
924 :
925 17660 : if (chItemType == 'e' &&
926 8830 : nNumber < static_cast<unsigned>(CSLCount(papszEnumNames)))
927 : {
928 8829 : pszStringRet = papszEnumNames[nNumber];
929 : }
930 : }
931 8830 : break;
932 :
933 0 : case 'S':
934 : {
935 0 : if (nIndexValue * 2 + 2 > nDataSize)
936 : {
937 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
938 0 : return false;
939 : }
940 0 : short nNumber = 0;
941 0 : memcpy(&nNumber, pabyData + nIndexValue * 2, 2);
942 : HFAStandard(2, &nNumber);
943 0 : nIntRet = nNumber;
944 0 : dfDoubleRet = nIntRet;
945 : }
946 0 : break;
947 :
948 10646 : case 't':
949 : case 'l':
950 : {
951 10646 : if (nIndexValue * 4 + 4 > nDataSize)
952 : {
953 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
954 0 : return false;
955 : }
956 10646 : GUInt32 nNumber = 0;
957 10646 : memcpy(&nNumber, pabyData + nIndexValue * 4, 4);
958 : HFAStandard(4, &nNumber);
959 10646 : nIntRet = nNumber;
960 10646 : dfDoubleRet = nIntRet;
961 : }
962 10646 : break;
963 :
964 3071 : case 'L':
965 : {
966 3071 : if (nIndexValue * 4 + 4 > nDataSize)
967 : {
968 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
969 0 : return false;
970 : }
971 3071 : GInt32 nNumber = 0;
972 : // TODO(schwehr): What is 4?
973 3071 : memcpy(&nNumber, pabyData + nIndexValue * 4, 4);
974 : HFAStandard(4, &nNumber);
975 3071 : nIntRet = nNumber;
976 3071 : dfDoubleRet = nIntRet;
977 : }
978 3071 : break;
979 :
980 0 : case 'f':
981 : {
982 0 : if (nIndexValue * 4 + 4 > nDataSize)
983 : {
984 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
985 0 : return false;
986 : }
987 0 : float fNumber = 0.0f;
988 : // TODO(schwehr): What is 4?
989 0 : memcpy(&fNumber, pabyData + nIndexValue * 4, 4);
990 : HFAStandard(4, &fNumber);
991 0 : if (static_cast<double>(fNumber) >
992 0 : std::numeric_limits<int>::max() ||
993 0 : static_cast<double>(fNumber) <
994 0 : std::numeric_limits<int>::min() ||
995 0 : std::isnan(fNumber))
996 : {
997 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too large for int: %f",
998 : fNumber);
999 0 : return false;
1000 : }
1001 0 : dfDoubleRet = fNumber;
1002 0 : nIntRet = static_cast<int>(fNumber);
1003 : }
1004 0 : break;
1005 :
1006 7341 : case 'd':
1007 : {
1008 7341 : if (nIndexValue * 8 + 8 > nDataSize)
1009 : {
1010 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
1011 0 : return false;
1012 : }
1013 7341 : double dfNumber = 0;
1014 7341 : memcpy(&dfNumber, pabyData + nIndexValue * 8, 8);
1015 : HFAStandard(8, &dfNumber);
1016 7341 : dfDoubleRet = dfNumber;
1017 7341 : if (chReqType == 'i')
1018 : {
1019 0 : if (dfNumber > std::numeric_limits<int>::max() ||
1020 0 : dfNumber < std::numeric_limits<int>::min() ||
1021 0 : std::isnan(dfNumber))
1022 : {
1023 0 : CPLError(CE_Failure, CPLE_AppDefined,
1024 : "Too large for int: %f", dfNumber);
1025 0 : return false;
1026 : }
1027 0 : nIntRet = static_cast<int>(dfNumber);
1028 : }
1029 : }
1030 7341 : break;
1031 :
1032 135 : case 'b':
1033 : {
1034 135 : if (nDataSize < 12)
1035 0 : return false;
1036 :
1037 135 : GInt32 nRows = 0;
1038 135 : memcpy(&nRows, pabyData, 4);
1039 : HFAStandard(4, &nRows);
1040 :
1041 135 : GInt32 nColumns = 0;
1042 135 : memcpy(&nColumns, pabyData + 4, 4);
1043 : HFAStandard(4, &nColumns);
1044 :
1045 135 : GInt16 nBaseItemType = 0;
1046 135 : memcpy(&nBaseItemType, pabyData + 8, 2);
1047 : HFAStandard(2, &nBaseItemType);
1048 : // We ignore the 2 byte objecttype value.
1049 :
1050 135 : if (nIndexValue < -3 || nRows <= 0 || nColumns <= 0 ||
1051 135 : nRows > INT_MAX / nColumns || nIndexValue >= nRows * nColumns)
1052 0 : return false;
1053 :
1054 135 : pabyData += 12;
1055 135 : nDataSize -= 12;
1056 :
1057 135 : if (nIndexValue == -3)
1058 : {
1059 0 : dfDoubleRet = nBaseItemType;
1060 0 : nIntRet = nBaseItemType;
1061 : }
1062 135 : else if (nIndexValue == -2)
1063 : {
1064 0 : dfDoubleRet = nColumns;
1065 0 : nIntRet = nColumns;
1066 : }
1067 135 : else if (nIndexValue == -1)
1068 : {
1069 0 : dfDoubleRet = nRows;
1070 0 : nIntRet = nRows;
1071 : }
1072 135 : else if (nBaseItemType == EPT_u1)
1073 : {
1074 : // TODO(schwehr): What are these constants like 8 and 0x7?
1075 0 : if (nIndexValue * 8 >= nDataSize)
1076 : {
1077 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
1078 0 : return false;
1079 : }
1080 :
1081 0 : if (pabyData[nIndexValue >> 3] & (1 << (nIndexValue & 0x7)))
1082 : {
1083 0 : dfDoubleRet = 1;
1084 0 : nIntRet = 1;
1085 : }
1086 : else
1087 : {
1088 0 : dfDoubleRet = 0.0;
1089 0 : nIntRet = 0;
1090 : }
1091 : }
1092 135 : else if (nBaseItemType == EPT_u2)
1093 : {
1094 0 : const int nBitOffset = nIndexValue & 0x3;
1095 0 : const int nByteOffset = nIndexValue >> 2;
1096 :
1097 0 : if (nByteOffset >= nDataSize)
1098 : {
1099 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
1100 0 : return false;
1101 : }
1102 :
1103 0 : const int nMask = 0x3;
1104 0 : nIntRet = (pabyData[nByteOffset] >> nBitOffset) & nMask;
1105 0 : dfDoubleRet = nIntRet;
1106 : }
1107 135 : else if (nBaseItemType == EPT_u4)
1108 : {
1109 0 : const int nBitOffset = nIndexValue & 0x7;
1110 0 : const int nByteOffset = nIndexValue >> 3;
1111 :
1112 0 : if (nByteOffset >= nDataSize)
1113 : {
1114 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
1115 0 : return false;
1116 : }
1117 :
1118 0 : const int nMask = 0x7;
1119 0 : nIntRet = (pabyData[nByteOffset] >> nBitOffset) & nMask;
1120 0 : dfDoubleRet = nIntRet;
1121 : }
1122 135 : else if (nBaseItemType == EPT_u8)
1123 : {
1124 18 : if (nIndexValue >= nDataSize)
1125 : {
1126 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
1127 0 : return false;
1128 : }
1129 18 : dfDoubleRet = pabyData[nIndexValue];
1130 18 : nIntRet = pabyData[nIndexValue];
1131 : }
1132 117 : else if (nBaseItemType == EPT_s8)
1133 : {
1134 0 : if (nIndexValue >= nDataSize)
1135 : {
1136 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
1137 0 : return false;
1138 : }
1139 0 : dfDoubleRet = ((signed char *)pabyData)[nIndexValue];
1140 0 : nIntRet = ((signed char *)pabyData)[nIndexValue];
1141 : }
1142 117 : else if (nBaseItemType == EPT_s16)
1143 : {
1144 0 : if (nIndexValue * 2 + 2 > nDataSize)
1145 : {
1146 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
1147 0 : return false;
1148 : }
1149 0 : GInt16 nValue = 0;
1150 0 : memcpy(&nValue, pabyData + 2 * nIndexValue, 2);
1151 : HFAStandard(2, &nValue);
1152 :
1153 0 : dfDoubleRet = nValue;
1154 0 : nIntRet = nValue;
1155 : }
1156 117 : else if (nBaseItemType == EPT_u16)
1157 : {
1158 0 : if (nIndexValue * 2 + 2 > nDataSize)
1159 : {
1160 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
1161 0 : return false;
1162 : }
1163 0 : GUInt16 nValue = 0;
1164 0 : memcpy(&nValue, pabyData + 2 * nIndexValue, 2);
1165 : HFAStandard(2, &nValue);
1166 :
1167 0 : dfDoubleRet = nValue;
1168 0 : nIntRet = nValue;
1169 : }
1170 117 : else if (nBaseItemType == EPT_s32)
1171 : {
1172 0 : if (nIndexValue * 4 + 4 > nDataSize)
1173 : {
1174 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
1175 0 : return false;
1176 : }
1177 0 : GInt32 nValue = 0;
1178 0 : memcpy(&nValue, pabyData + 4 * nIndexValue, 4);
1179 : HFAStandard(4, &nValue);
1180 :
1181 0 : dfDoubleRet = nValue;
1182 0 : nIntRet = nValue;
1183 : }
1184 117 : else if (nBaseItemType == EPT_u32)
1185 : {
1186 0 : if (nIndexValue * 4 + 4 > nDataSize)
1187 : {
1188 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
1189 0 : return false;
1190 : }
1191 0 : GUInt32 nValue = 0;
1192 0 : memcpy(&nValue, pabyData + 4 * nIndexValue, 4);
1193 : HFAStandard(4, &nValue);
1194 :
1195 0 : dfDoubleRet = nValue;
1196 0 : nIntRet = nValue;
1197 : }
1198 117 : else if (nBaseItemType == EPT_f32)
1199 : {
1200 0 : if (nIndexValue * 4 + 4 > nDataSize)
1201 : {
1202 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
1203 0 : return false;
1204 : }
1205 0 : float fValue = 0.0f;
1206 0 : memcpy(&fValue, pabyData + 4 * nIndexValue, 4);
1207 : HFAStandard(4, &fValue);
1208 :
1209 0 : dfDoubleRet = fValue;
1210 0 : nIntRet = FloatToIntClamp(fValue);
1211 : }
1212 117 : else if (nBaseItemType == EPT_f64)
1213 : {
1214 117 : if (nIndexValue * 8 + 8 > nDataSize)
1215 : {
1216 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
1217 0 : return false;
1218 : }
1219 117 : double dfValue = 0.0;
1220 117 : memcpy(&dfValue, pabyData + 8 * nIndexValue, 8);
1221 : HFAStandard(8, &dfValue);
1222 :
1223 117 : dfDoubleRet = dfValue;
1224 117 : if (chReqType == 'i')
1225 : {
1226 0 : const int nMax = std::numeric_limits<int>::max();
1227 0 : const int nMin = std::numeric_limits<int>::min();
1228 0 : if (dfDoubleRet >= nMax)
1229 : {
1230 0 : nIntRet = nMax;
1231 : }
1232 0 : else if (dfDoubleRet <= nMin)
1233 : {
1234 0 : nIntRet = nMin;
1235 : }
1236 0 : else if (std::isnan(dfDoubleRet))
1237 : {
1238 0 : CPLError(CE_Warning, CPLE_AppDefined,
1239 : "NaN converted to INT_MAX.");
1240 0 : nIntRet = nMax;
1241 : }
1242 : else
1243 : {
1244 0 : nIntRet = static_cast<int>(dfDoubleRet);
1245 : }
1246 : }
1247 : }
1248 : else
1249 : {
1250 0 : CPLError(CE_Failure, CPLE_AppDefined,
1251 : "Unknown base item type: %d", nBaseItemType);
1252 0 : return false;
1253 : }
1254 : }
1255 135 : break;
1256 :
1257 13953 : case 'o':
1258 13953 : if (poItemObjectType != nullptr)
1259 : {
1260 13953 : int nExtraOffset = 0;
1261 :
1262 13953 : if (poItemObjectType->nBytes > 0)
1263 : {
1264 11400 : if (nIndexValue != 0 &&
1265 9264 : poItemObjectType->nBytes > INT_MAX / nIndexValue)
1266 : // TODO(schwehr): Why was this CE_Failure when the
1267 : // others are false?
1268 0 : return false;
1269 11400 : nExtraOffset = poItemObjectType->nBytes * nIndexValue;
1270 : }
1271 : else
1272 : {
1273 2570 : for (int iIndexCounter = 0; iIndexCounter < nIndexValue &&
1274 : nExtraOffset < nDataSize;
1275 : iIndexCounter++)
1276 : {
1277 17 : std::set<HFAField *> oVisitedFields;
1278 34 : const int nInc = poItemObjectType->GetInstBytes(
1279 17 : pabyData + nExtraOffset, nDataSize - nExtraOffset,
1280 : oVisitedFields);
1281 17 : if (nInc <= 0 || nExtraOffset > INT_MAX - nInc)
1282 : {
1283 0 : CPLError(CE_Failure, CPLE_AppDefined,
1284 : "Invalid return value");
1285 : // TODO(schwehr): Verify this false is okay.
1286 0 : return false;
1287 : }
1288 :
1289 17 : nExtraOffset += nInc;
1290 : }
1291 : }
1292 :
1293 13953 : if (nExtraOffset >= nDataSize)
1294 0 : return false;
1295 :
1296 13953 : pabyRawData = pabyData + nExtraOffset;
1297 :
1298 13953 : if (pszField != nullptr && strlen(pszField) > 0)
1299 : {
1300 27906 : return poItemObjectType->ExtractInstValue(
1301 13953 : pszField, pabyRawData, nDataOffset + nExtraOffset,
1302 : nDataSize - nExtraOffset, chReqType, pReqReturn,
1303 13953 : pnRemainingDataSize);
1304 : }
1305 : }
1306 : else
1307 : {
1308 : // E. Rouault: not completely sure about this, but helps avoid
1309 : // DoS timeouts in cases like
1310 : // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=1806
1311 0 : return false;
1312 : }
1313 0 : break;
1314 :
1315 0 : default:
1316 0 : return false;
1317 : }
1318 :
1319 : // Return the appropriate representation.
1320 30023 : if (chReqType == 's')
1321 : {
1322 1104 : if (pszStringRet == nullptr)
1323 : {
1324 : // HFAEntry:: BuildEntryFromMIFObject() expects to have always 8
1325 : // bytes before the data. In normal situations, it should not go
1326 : // here, but that can happen if the file is corrupted so reserve the
1327 : // first 8 bytes before the string to contain null bytes.
1328 39 : memset(szNumberString, 0, 8);
1329 39 : CPLsnprintf(szNumberString + 8, sizeof(szNumberString) - 8, "%.14g",
1330 : dfDoubleRet);
1331 39 : pszStringRet = szNumberString + 8;
1332 : }
1333 :
1334 1104 : *((char **)pReqReturn) = pszStringRet;
1335 1104 : return true;
1336 : }
1337 28919 : else if (chReqType == 'd')
1338 : {
1339 7437 : *((double *)pReqReturn) = dfDoubleRet;
1340 7437 : return true;
1341 : }
1342 21482 : else if (chReqType == 'i')
1343 : {
1344 21482 : *((int *)pReqReturn) = nIntRet;
1345 21482 : return true;
1346 : }
1347 0 : else if (chReqType == 'p')
1348 : {
1349 0 : *((GByte **)pReqReturn) = pabyRawData;
1350 0 : return true;
1351 : }
1352 : else
1353 : {
1354 0 : CPLAssert(false);
1355 : return false;
1356 : }
1357 : }
1358 :
1359 : /************************************************************************/
1360 : /* GetInstBytes() */
1361 : /* */
1362 : /* Get the number of bytes in a particular instance of a */
1363 : /* field. This will normally be the fixed internal nBytes */
1364 : /* value, but for pointer objects will include the variable */
1365 : /* portion. */
1366 : /************************************************************************/
1367 :
1368 168907 : int HFAField::GetInstBytes(GByte *pabyData, int nDataSize,
1369 : std::set<HFAField *> &oVisitedFields)
1370 :
1371 : {
1372 168907 : if (oVisitedFields.find(this) != oVisitedFields.end())
1373 : {
1374 0 : CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
1375 0 : return -1;
1376 : }
1377 :
1378 168907 : if (nBytes > -1)
1379 135505 : return nBytes;
1380 :
1381 33402 : int nCount = 1;
1382 33402 : int nInstBytes = 0;
1383 :
1384 33402 : if (chPointer != '\0')
1385 : {
1386 30966 : if (nDataSize < 4)
1387 : {
1388 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
1389 0 : return -1;
1390 : }
1391 :
1392 30966 : memcpy(&nCount, pabyData, 4);
1393 : HFAStandard(4, &nCount);
1394 :
1395 30966 : pabyData += 8;
1396 30966 : nInstBytes += 8;
1397 : }
1398 :
1399 33402 : if (chItemType == 'b' && nCount != 0) // BASEDATA
1400 : {
1401 515 : if (nDataSize - nInstBytes < 4 + 4 + 2)
1402 : {
1403 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
1404 0 : return -1;
1405 : }
1406 :
1407 515 : GInt32 nRows = 0;
1408 515 : memcpy(&nRows, pabyData, 4);
1409 : HFAStandard(4, &nRows);
1410 515 : GInt32 nColumns = 0;
1411 515 : memcpy(&nColumns, pabyData + 4, 4);
1412 : HFAStandard(4, &nColumns);
1413 515 : GInt16 nBaseItemType = 0;
1414 515 : memcpy(&nBaseItemType, pabyData + 8, 2);
1415 : HFAStandard(2, &nBaseItemType);
1416 515 : if (nBaseItemType < EPT_MIN || nBaseItemType > EPT_MAX)
1417 0 : return -1;
1418 :
1419 515 : EPTType eBaseItemType = static_cast<EPTType>(nBaseItemType);
1420 :
1421 515 : nInstBytes += 12;
1422 :
1423 515 : if (nRows < 0 || nColumns < 0)
1424 0 : return -1;
1425 515 : if (nColumns != 0 && nRows > INT_MAX / nColumns)
1426 0 : return -1;
1427 1022 : if (nRows != 0 &&
1428 507 : ((HFAGetDataTypeBits(eBaseItemType) + 7) / 8) > INT_MAX / nRows)
1429 0 : return -1;
1430 1022 : if (nColumns != 0 &&
1431 507 : ((HFAGetDataTypeBits(eBaseItemType) + 7) / 8) * nRows >
1432 507 : INT_MAX / nColumns)
1433 0 : return -1;
1434 515 : if (((HFAGetDataTypeBits(eBaseItemType) + 7) / 8) * nRows * nColumns >
1435 515 : INT_MAX - nInstBytes)
1436 0 : return -1;
1437 :
1438 515 : nInstBytes +=
1439 515 : ((HFAGetDataTypeBits(eBaseItemType) + 7) / 8) * nRows * nColumns;
1440 : }
1441 32887 : else if (poItemObjectType == nullptr)
1442 : {
1443 49442 : if (nCount != 0 &&
1444 20004 : HFADictionary::GetItemSize(chItemType) > INT_MAX / nCount)
1445 0 : return -1;
1446 29438 : if (nCount * HFADictionary::GetItemSize(chItemType) >
1447 29438 : INT_MAX - nInstBytes)
1448 0 : return -1;
1449 29438 : nInstBytes += nCount * HFADictionary::GetItemSize(chItemType);
1450 : }
1451 : else
1452 : {
1453 3449 : oVisitedFields.insert(this);
1454 5939 : for (int i = 0; i < nCount && nInstBytes < nDataSize && nInstBytes >= 0;
1455 : i++)
1456 : {
1457 2490 : const int nThisBytes = poItemObjectType->GetInstBytes(
1458 : pabyData, nDataSize - nInstBytes, oVisitedFields);
1459 2490 : if (nThisBytes <= 0 || nInstBytes > INT_MAX - nThisBytes)
1460 : {
1461 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid return value");
1462 0 : return -1;
1463 : }
1464 :
1465 2490 : nInstBytes += nThisBytes;
1466 2490 : pabyData += nThisBytes;
1467 : }
1468 3449 : oVisitedFields.erase(this);
1469 : }
1470 :
1471 33402 : return nInstBytes;
1472 : }
1473 :
1474 : /************************************************************************/
1475 : /* GetInstCount() */
1476 : /* */
1477 : /* Get the count for a particular instance of a field. This */
1478 : /* will normally be the built in value, but for variable fields */
1479 : /* this is extracted from the data itself. */
1480 : /************************************************************************/
1481 :
1482 47499 : int HFAField::GetInstCount(GByte *pabyData, int nDataSize) const
1483 :
1484 : {
1485 47499 : if (chPointer == '\0')
1486 27465 : return nItemCount;
1487 :
1488 20034 : if (chItemType == 'b')
1489 : {
1490 213 : if (nDataSize < 20)
1491 0 : return 0;
1492 :
1493 213 : GInt32 nRows = 0;
1494 213 : memcpy(&nRows, pabyData + 8, 4);
1495 : HFAStandard(4, &nRows);
1496 213 : GInt32 nColumns = 0;
1497 213 : memcpy(&nColumns, pabyData + 12, 4);
1498 : HFAStandard(4, &nColumns);
1499 :
1500 213 : if (nRows < 0 || nColumns < 0)
1501 0 : return 0;
1502 213 : if (nColumns != 0 && nRows > INT_MAX / nColumns)
1503 0 : return 0;
1504 :
1505 213 : return nRows * nColumns;
1506 : }
1507 :
1508 19821 : if (nDataSize < 4)
1509 0 : return 0;
1510 :
1511 19821 : GInt32 nCount = 0;
1512 19821 : memcpy(&nCount, pabyData, 4);
1513 : HFAStandard(4, &nCount);
1514 19821 : return nCount;
1515 : }
1516 :
1517 : /************************************************************************/
1518 : /* DumpInstValue() */
1519 : /************************************************************************/
1520 :
1521 0 : void HFAField::DumpInstValue(FILE *fpOut, GByte *pabyData, GUInt32 nDataOffset,
1522 : int nDataSize, const char *pszPrefix)
1523 :
1524 : {
1525 0 : const int nEntries = GetInstCount(pabyData, nDataSize);
1526 :
1527 : // Special case for arrays of chars or uchars which are printed
1528 : // as a string.
1529 0 : if ((chItemType == 'c' || chItemType == 'C') && nEntries > 0)
1530 : {
1531 0 : void *pReturn = nullptr;
1532 0 : if (ExtractInstValue(nullptr, 0, pabyData, nDataOffset, nDataSize, 's',
1533 : &pReturn))
1534 0 : CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "%s%s = `%s'\n", pszPrefix,
1535 : pszFieldName,
1536 : static_cast<char *>(pReturn)));
1537 : else
1538 0 : CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "%s%s = (access failed)\n",
1539 : pszPrefix, pszFieldName));
1540 :
1541 0 : return;
1542 : }
1543 :
1544 : // For BASEDATA objects, we want to first dump their dimension and type.
1545 0 : if (chItemType == 'b')
1546 : {
1547 0 : int nDataType = 0;
1548 0 : const bool bSuccess = ExtractInstValue(
1549 : nullptr, -3, pabyData, nDataOffset, nDataSize, 'i', &nDataType);
1550 0 : if (bSuccess)
1551 : {
1552 0 : int nColumns = 0;
1553 0 : ExtractInstValue(nullptr, -2, pabyData, nDataOffset, nDataSize, 'i',
1554 : &nColumns);
1555 0 : int nRows = 0;
1556 0 : ExtractInstValue(nullptr, -1, pabyData, nDataOffset, nDataSize, 'i',
1557 : &nRows);
1558 0 : CPL_IGNORE_RET_VAL(VSIFPrintf(
1559 : fpOut, "%sBASEDATA(%s): %dx%d of %s\n", pszPrefix, pszFieldName,
1560 : nColumns, nRows,
1561 0 : (nDataType >= EPT_MIN && nDataType <= EPT_MAX)
1562 0 : ? HFAGetDataTypeName(static_cast<EPTType>(nDataType))
1563 : : "invalid type"));
1564 : }
1565 : else
1566 : {
1567 0 : CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "%sBASEDATA(%s): empty\n",
1568 : pszPrefix, pszFieldName));
1569 : }
1570 : }
1571 :
1572 : // Dump each entry in the field array.
1573 0 : void *pReturn = nullptr;
1574 :
1575 0 : const int nMaxEntry = std::min(MAX_ENTRY_REPORT, nEntries);
1576 0 : for (int iEntry = 0; iEntry < nMaxEntry; iEntry++)
1577 : {
1578 0 : if (nEntries == 1)
1579 0 : CPL_IGNORE_RET_VAL(
1580 0 : VSIFPrintf(fpOut, "%s%s = ", pszPrefix, pszFieldName));
1581 : else
1582 0 : CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "%s%s[%d] = ", pszPrefix,
1583 : pszFieldName, iEntry));
1584 :
1585 0 : switch (chItemType)
1586 : {
1587 0 : case 'f':
1588 : case 'd':
1589 : {
1590 0 : double dfValue = 0.0;
1591 0 : if (ExtractInstValue(nullptr, iEntry, pabyData, nDataOffset,
1592 : nDataSize, 'd', &dfValue))
1593 0 : CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "%f\n", dfValue));
1594 : else
1595 0 : CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "(access failed)\n"));
1596 : }
1597 0 : break;
1598 :
1599 0 : case 'b':
1600 : {
1601 0 : double dfValue = 0.0;
1602 :
1603 0 : if (ExtractInstValue(nullptr, iEntry, pabyData, nDataOffset,
1604 : nDataSize, 'd', &dfValue))
1605 0 : CPL_IGNORE_RET_VAL(
1606 0 : VSIFPrintf(fpOut, "%s%.15g\n", pszPrefix, dfValue));
1607 : else
1608 0 : CPL_IGNORE_RET_VAL(
1609 0 : VSIFPrintf(fpOut, "%s(access failed)\n", pszPrefix));
1610 : }
1611 0 : break;
1612 :
1613 0 : case 'e':
1614 0 : if (ExtractInstValue(nullptr, iEntry, pabyData, nDataOffset,
1615 : nDataSize, 's', &pReturn))
1616 0 : CPL_IGNORE_RET_VAL(
1617 0 : VSIFPrintf(fpOut, "%s\n", (char *)pReturn));
1618 : else
1619 0 : CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "(access failed)\n"));
1620 0 : break;
1621 :
1622 0 : case 'o':
1623 0 : if (!ExtractInstValue(nullptr, iEntry, pabyData, nDataOffset,
1624 : nDataSize, 'p', &pReturn))
1625 : {
1626 0 : CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "(access failed)\n"));
1627 : }
1628 : else
1629 : {
1630 0 : CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "\n"));
1631 :
1632 0 : const int nByteOffset =
1633 0 : static_cast<int>(((GByte *)pReturn) - pabyData);
1634 :
1635 0 : char szLongFieldName[256] = {};
1636 0 : snprintf(szLongFieldName, sizeof(szLongFieldName), "%s ",
1637 : pszPrefix);
1638 :
1639 0 : if (poItemObjectType)
1640 0 : poItemObjectType->DumpInstValue(
1641 0 : fpOut, pabyData + nByteOffset,
1642 0 : nDataOffset + nByteOffset, nDataSize - nByteOffset,
1643 : szLongFieldName);
1644 : }
1645 0 : break;
1646 :
1647 0 : default:
1648 : {
1649 0 : GInt32 nIntValue = 0;
1650 :
1651 0 : if (ExtractInstValue(nullptr, iEntry, pabyData, nDataOffset,
1652 : nDataSize, 'i', &nIntValue))
1653 0 : CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "%d\n", nIntValue));
1654 : else
1655 0 : CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "(access failed)\n"));
1656 : }
1657 0 : break;
1658 : }
1659 : }
1660 :
1661 0 : if (nEntries > MAX_ENTRY_REPORT)
1662 0 : CPL_IGNORE_RET_VAL(VSIFPrintf(
1663 : fpOut, "%s ... remaining instances omitted ...\n", pszPrefix));
1664 :
1665 0 : if (nEntries == 0)
1666 0 : CPL_IGNORE_RET_VAL(
1667 0 : VSIFPrintf(fpOut, "%s%s = (no values)\n", pszPrefix, pszFieldName));
1668 : }
|