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