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 105337 : HFAField::HFAField()
65 : : nBytes(0), nItemCount(0), chPointer('\0'), chItemType('\0'),
66 : pszItemObjectType(nullptr), poItemObjectType(nullptr),
67 105337 : papszEnumNames(nullptr), pszFieldName(nullptr)
68 : {
69 105337 : memset(szNumberString, 0, sizeof(szNumberString));
70 105337 : }
71 :
72 : /************************************************************************/
73 : /* ~HFAField() */
74 : /************************************************************************/
75 :
76 210674 : HFAField::~HFAField()
77 :
78 : {
79 105337 : CPLFree(pszItemObjectType);
80 105337 : CSLDestroy(papszEnumNames);
81 105337 : CPLFree(pszFieldName);
82 105337 : }
83 :
84 : /************************************************************************/
85 : /* Initialize() */
86 : /************************************************************************/
87 :
88 105337 : const char *HFAField::Initialize(const char *pszInput)
89 :
90 : {
91 : // Read the number.
92 105337 : nItemCount = atoi(pszInput);
93 105337 : if (nItemCount < 0)
94 0 : return nullptr;
95 :
96 212915 : while (*pszInput != '\0' && *pszInput != ':')
97 107578 : pszInput++;
98 :
99 105337 : if (*pszInput == '\0')
100 0 : return nullptr;
101 :
102 105337 : pszInput++;
103 :
104 : // Is this a pointer?
105 105337 : if (*pszInput == 'p' || *pszInput == '*')
106 24169 : chPointer = *(pszInput++);
107 :
108 : // Get the general type.
109 105337 : if (*pszInput == '\0')
110 0 : return nullptr;
111 :
112 105337 : chItemType = *(pszInput++);
113 :
114 105337 : 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 105336 : int i = 0; // TODO: Describe why i needs to span chItemType blocks.
123 :
124 105336 : if (chItemType == 'o')
125 : {
126 171030 : for (i = 0; pszInput[i] != '\0' && pszInput[i] != ','; i++)
127 : {
128 : }
129 12585 : if (pszInput[i] == '\0')
130 0 : return nullptr;
131 :
132 12585 : pszItemObjectType = static_cast<char *>(CPLMalloc(i + 1));
133 12585 : strncpy(pszItemObjectType, pszInput, i);
134 12585 : pszItemObjectType[i] = '\0';
135 :
136 12585 : 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 105336 : if (chItemType == 'x' && *pszInput == '{')
146 : {
147 2862 : int nBraceDepth = 1;
148 2862 : pszInput++;
149 :
150 : // Skip past the definition.
151 93318 : while (nBraceDepth > 0 && *pszInput != '\0')
152 : {
153 90456 : if (*pszInput == '{')
154 1433 : nBraceDepth++;
155 89023 : else if (*pszInput == '}')
156 4295 : nBraceDepth--;
157 :
158 90456 : pszInput++;
159 : }
160 2862 : if (*pszInput == '\0')
161 0 : return nullptr;
162 :
163 2862 : chItemType = 'o';
164 :
165 : // Find the comma terminating the type name.
166 36516 : for (i = 0; pszInput[i] != '\0' && pszInput[i] != ','; i++)
167 : {
168 : }
169 2862 : if (pszInput[i] == '\0')
170 0 : return nullptr;
171 :
172 2862 : pszItemObjectType = static_cast<char *>(CPLMalloc(i + 1));
173 2862 : strncpy(pszItemObjectType, pszInput, i);
174 2862 : pszItemObjectType[i] = '\0';
175 :
176 2862 : pszInput += i + 1;
177 : }
178 :
179 : // If this is an enumeration we have to extract all the
180 : // enumeration values.
181 105336 : if (chItemType == 'e')
182 : {
183 14377 : const int nEnumCount = atoi(pszInput);
184 :
185 14377 : if (nEnumCount < 0 || nEnumCount > 100000)
186 0 : return nullptr;
187 :
188 14377 : pszInput = strchr(pszInput, ':');
189 14377 : if (pszInput == nullptr)
190 0 : return nullptr;
191 :
192 14377 : pszInput++;
193 :
194 14377 : papszEnumNames =
195 14377 : static_cast<char **>(VSICalloc(sizeof(char *), nEnumCount + 1));
196 14377 : if (papszEnumNames == nullptr)
197 0 : return nullptr;
198 :
199 81369 : for (int iEnum = 0; iEnum < nEnumCount; iEnum++)
200 : {
201 648178 : for (i = 0; pszInput[i] != '\0' && pszInput[i] != ','; i++)
202 : {
203 : }
204 :
205 66992 : if (pszInput[i] != ',')
206 0 : return nullptr;
207 :
208 66992 : char *pszToken = static_cast<char *>(CPLMalloc(i + 1));
209 66992 : strncpy(pszToken, pszInput, i);
210 66992 : pszToken[i] = '\0';
211 :
212 66992 : papszEnumNames[iEnum] = pszToken;
213 :
214 66992 : pszInput += i + 1;
215 : }
216 : }
217 :
218 : // Extract the field name.
219 1017650 : for (i = 0; pszInput[i] != '\0' && pszInput[i] != ','; i++)
220 : {
221 : }
222 105336 : if (pszInput[i] == '\0')
223 0 : return nullptr;
224 :
225 105336 : pszFieldName = static_cast<char *>(CPLMalloc(i + 1));
226 105336 : strncpy(pszFieldName, pszInput, i);
227 105336 : pszFieldName[i] = '\0';
228 :
229 105336 : pszInput += i + 1;
230 :
231 105336 : return pszInput;
232 : }
233 :
234 : /************************************************************************/
235 : /* CompleteDefn() */
236 : /* */
237 : /* Establish size, and pointers to component types. */
238 : /************************************************************************/
239 :
240 105330 : 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 105330 : if (pszItemObjectType != nullptr)
246 15441 : poItemObjectType = poDict->FindType(pszItemObjectType);
247 :
248 : // Figure out the size.
249 105330 : if (chPointer == 'p')
250 : {
251 16609 : nBytes = -1; // We can't know the instance size.
252 : }
253 88721 : else if (poItemObjectType != nullptr)
254 : {
255 11755 : if (!poItemObjectType->CompleteDefn(poDict))
256 2 : return false;
257 11753 : if (poItemObjectType->nBytes == -1)
258 9548 : nBytes = -1;
259 2205 : else if (poItemObjectType->nBytes != 0 &&
260 2205 : nItemCount > INT_MAX / poItemObjectType->nBytes)
261 0 : nBytes = -1;
262 : else
263 2205 : nBytes = poItemObjectType->nBytes * nItemCount;
264 :
265 : // TODO(schwehr): What does the 8 represent?
266 11753 : if (chPointer == '*' && nBytes != -1)
267 : {
268 2205 : if (nBytes > INT_MAX - 8)
269 0 : nBytes = -1;
270 : else
271 2205 : nBytes += 8; // Count, and offset.
272 : }
273 : }
274 : else
275 : {
276 76966 : const int nItemSize = poDict->GetItemSize(chItemType);
277 76966 : if (nItemSize != 0 && nItemCount > INT_MAX / nItemSize)
278 3933 : nBytes = -1;
279 : else
280 73033 : nBytes = nItemSize * nItemCount;
281 : }
282 105328 : 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 14924 : 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 14924 : if (chPointer != '\0')
399 : {
400 7681 : 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 7681 : if (chItemType == 'b')
406 : {
407 53 : nCount = 1;
408 : }
409 : // Set the size from string length.
410 7628 : else if (chReqType == 's' && (chItemType == 'c' || chItemType == 'C'))
411 : {
412 1766 : if (pValue != nullptr)
413 1459 : 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 5862 : nCount = nIndexValue + 1;
419 : }
420 :
421 : // TODO(schwehr): What does the 8 represent?
422 7681 : 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 7681 : GUInt32 nOffset = 0;
433 7681 : memcpy(&nOffset, pabyData, 4);
434 : HFAStandard(4, &nOffset);
435 7681 : if (nOffset < nCount)
436 : {
437 5933 : nOffset = nCount;
438 : HFAStandard(4, &nOffset);
439 5933 : memcpy(pabyData, &nOffset, 4);
440 : }
441 :
442 7681 : if (pValue == nullptr)
443 307 : nOffset = 0;
444 : else
445 7374 : nOffset = nDataOffset + 8;
446 : HFAStandard(4, &nOffset);
447 7681 : memcpy(pabyData + 4, &nOffset, 4);
448 :
449 7681 : pabyData += 8;
450 :
451 7681 : nDataOffset += 8;
452 7681 : nDataSize -= 8;
453 : }
454 :
455 : // Pointers to char or uchar arrays requested as strings are
456 : // handled as a special case.
457 14924 : if ((chItemType == 'c' || chItemType == 'C') && chReqType == 's')
458 : {
459 1766 : int nBytesToCopy = 0;
460 :
461 1766 : if (nBytes == -1)
462 : {
463 1766 : if (pValue != nullptr)
464 1459 : nBytesToCopy = static_cast<int>(strlen((char *)pValue) + 1);
465 : }
466 : else
467 : {
468 0 : nBytesToCopy = nBytes;
469 : }
470 :
471 1766 : 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 1766 : memset(pabyData, 0, nBytesToCopy);
481 :
482 1766 : if (pValue != nullptr)
483 1459 : strncpy((char *)pabyData, (char *)pValue, nBytesToCopy);
484 :
485 1766 : return CE_None;
486 : }
487 :
488 : // Translate the passed type into different representations.
489 13158 : int nIntValue = 0;
490 13158 : double dfDoubleValue = 0.0;
491 :
492 13158 : if (chReqType == 's')
493 : {
494 2114 : CPLAssert(pValue != nullptr);
495 2114 : nIntValue = atoi((char *)pValue);
496 2114 : dfDoubleValue = CPLAtof((char *)pValue);
497 : }
498 11044 : else if (chReqType == 'd')
499 : {
500 7463 : CPLAssert(pValue != nullptr);
501 7463 : dfDoubleValue = *((double *)pValue);
502 7463 : if (dfDoubleValue > INT_MAX)
503 0 : nIntValue = INT_MAX;
504 7463 : else if (dfDoubleValue < INT_MIN)
505 1 : nIntValue = INT_MIN;
506 7462 : else if (std::isfinite(dfDoubleValue))
507 7461 : nIntValue = static_cast<int>(dfDoubleValue);
508 : }
509 3581 : else if (chReqType == 'i')
510 : {
511 3581 : CPLAssert(pValue != nullptr);
512 3581 : nIntValue = *((int *)pValue);
513 3581 : 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 13158 : 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 1507 : case 'e':
555 : case 's':
556 : {
557 1507 : if (chItemType == 'e' && chReqType == 's')
558 : {
559 926 : CPLAssert(pValue != nullptr);
560 926 : nIntValue = CSLFindString(papszEnumNames, (char *)pValue);
561 926 : 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 1507 : 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 1507 : unsigned short nNumber = static_cast<unsigned short>(nIntValue);
582 : // TODO(schwehr): What is this 2?
583 : HFAStandard(2, &nNumber);
584 1507 : memcpy(pabyData + nIndexValue * 2, &nNumber, 2);
585 : }
586 1507 : 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 2314 : case 't':
608 : case 'l':
609 : {
610 2314 : 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 2314 : GUInt32 nNumber = nIntValue;
620 : // TODO(schwehr): What is this 4?
621 : HFAStandard(4, &nNumber);
622 2314 : memcpy(pabyData + nIndexValue * 4, &nNumber, 4);
623 : }
624 2314 : break;
625 :
626 607 : case 'L':
627 : {
628 607 : 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 607 : GInt32 nNumber = nIntValue;
638 : HFAStandard(4, &nNumber);
639 607 : memcpy(pabyData + nIndexValue * 4, &nNumber, 4);
640 : }
641 607 : 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 5658 : case 'd':
663 : {
664 5658 : 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 5658 : double dfNumber = dfDoubleValue;
674 : HFAStandard(8, &dfNumber);
675 5658 : memcpy(pabyData + nIndexValue * 8, &dfNumber, 8);
676 : }
677 5658 : break;
678 :
679 53 : case 'b':
680 : {
681 : // Extract existing rows, columns, and datatype.
682 53 : GInt32 nRows = 1; // TODO(schwehr): Why init to 1 instead of 0?
683 53 : memcpy(&nRows, pabyData, 4);
684 : HFAStandard(4, &nRows);
685 :
686 53 : GInt32 nColumns = 1; // TODO(schwehr): Why init to 1 instead of 0?
687 53 : memcpy(&nColumns, pabyData + 4, 4);
688 : HFAStandard(4, &nColumns);
689 :
690 53 : GInt16 nBaseItemType = 0;
691 53 : 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 53 : if (nIndexValue == -3)
698 12 : nBaseItemType = static_cast<GInt16>(nIntValue);
699 41 : else if (nIndexValue == -2)
700 12 : nColumns = nIntValue;
701 29 : else if (nIndexValue == -1)
702 12 : nRows = nIntValue;
703 :
704 53 : 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 52 : memcpy(pabyData, &nRows, 4);
710 : HFAStandard(4, &nColumns);
711 52 : memcpy(pabyData + 4, &nColumns, 4);
712 : HFAStandard(2, &nBaseItemType);
713 52 : memcpy(pabyData + 8, &nBaseItemType, 2);
714 : HFAStandard(2, &nBaseItemType); // Swap back for our use.
715 :
716 52 : if (nBaseItemType < EPT_MIN || nBaseItemType > EPT_MAX)
717 0 : return CE_Failure;
718 52 : const EPTType eBaseItemType = static_cast<EPTType>(nBaseItemType);
719 :
720 : // We ignore the 2 byte objecttype value.
721 :
722 52 : nDataSize -= 12;
723 :
724 52 : if (nIndexValue >= 0)
725 : {
726 32 : if ((nIndexValue + 1) *
727 16 : (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 16 : if (eBaseItemType == EPT_f64)
738 : {
739 16 : double dfNumber = dfDoubleValue;
740 :
741 : HFAStandard(8, &dfNumber);
742 16 : 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 52 : break;
762 :
763 3019 : case 'o':
764 3019 : if (poItemObjectType != nullptr)
765 : {
766 3019 : int nExtraOffset = 0;
767 :
768 3019 : if (poItemObjectType->nBytes > 0)
769 : {
770 1251 : if (nIndexValue != 0 &&
771 49 : poItemObjectType->nBytes > INT_MAX / nIndexValue)
772 : {
773 0 : return CE_Failure;
774 : }
775 1251 : nExtraOffset = poItemObjectType->nBytes * nIndexValue;
776 : }
777 : else
778 : {
779 1777 : 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 3019 : if (nExtraOffset >= nDataSize)
799 1 : return CE_Failure;
800 :
801 3018 : if (pszField != nullptr && strlen(pszField) > 0)
802 : {
803 6036 : return poItemObjectType->SetInstValue(
804 3018 : pszField, pabyData + nExtraOffset,
805 3018 : nDataOffset + nExtraOffset, nDataSize - nExtraOffset,
806 3018 : 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 10138 : 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 46505 : 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 46505 : const int nInstItemCount = GetInstCount(pabyData, nDataSize);
841 :
842 46505 : 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 46505 : if (nIndexValue < 0 || nIndexValue >= nInstItemCount)
848 : {
849 566 : if (chItemType == 'b' && nIndexValue >= -3 && nIndexValue < 0)
850 : /* ok - special index values */;
851 : else
852 566 : return false;
853 : }
854 :
855 : // If this field contains a pointer, then we will adjust the
856 : // data offset relative to it.
857 45939 : if (chPointer != '\0')
858 : {
859 19187 : if (nDataSize < 8)
860 : {
861 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
862 0 : return false;
863 : }
864 :
865 19187 : GUInt32 nOffset = 0;
866 19187 : 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 19187 : pabyData += 8;
882 19187 : nDataOffset += 8;
883 19187 : nDataSize -= 8;
884 : }
885 :
886 : // Pointers to char or uchar arrays requested as strings are
887 : // handled as a special case.
888 45939 : if ((chItemType == 'c' || chItemType == 'C') && chReqType == 's')
889 : {
890 2018 : *((GByte **)pReqReturn) = pabyData;
891 2018 : if (pnRemainingDataSize)
892 212 : *pnRemainingDataSize = nDataSize;
893 2018 : return pabyData != nullptr;
894 : }
895 :
896 : // Handle by type.
897 43921 : char *pszStringRet = nullptr;
898 43921 : int nIntRet = 0;
899 43921 : double dfDoubleRet = 0.0;
900 43921 : GByte *pabyRawData = nullptr;
901 :
902 43921 : 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 8816 : case 'e':
916 : case 's':
917 : {
918 8816 : if (nIndexValue * 2 + 2 > nDataSize)
919 : {
920 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
921 0 : return false;
922 : }
923 8816 : unsigned short nNumber = 0;
924 8816 : memcpy(&nNumber, pabyData + nIndexValue * 2, 2);
925 : HFAStandard(2, &nNumber);
926 8816 : nIntRet = nNumber;
927 8816 : dfDoubleRet = nIntRet;
928 :
929 17632 : if (chItemType == 'e' &&
930 8816 : nNumber < static_cast<unsigned>(CSLCount(papszEnumNames)))
931 : {
932 8815 : pszStringRet = papszEnumNames[nNumber];
933 : }
934 : }
935 8816 : 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 10613 : case 't':
953 : case 'l':
954 : {
955 10613 : if (nIndexValue * 4 + 4 > nDataSize)
956 : {
957 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
958 0 : return false;
959 : }
960 10613 : GUInt32 nNumber = 0;
961 10613 : memcpy(&nNumber, pabyData + nIndexValue * 4, 4);
962 : HFAStandard(4, &nNumber);
963 10613 : nIntRet = nNumber;
964 10613 : dfDoubleRet = nIntRet;
965 : }
966 10613 : break;
967 :
968 3070 : case 'L':
969 : {
970 3070 : if (nIndexValue * 4 + 4 > nDataSize)
971 : {
972 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
973 0 : return false;
974 : }
975 3070 : GInt32 nNumber = 0;
976 : // TODO(schwehr): What is 4?
977 3070 : memcpy(&nNumber, pabyData + nIndexValue * 4, 4);
978 : HFAStandard(4, &nNumber);
979 3070 : nIntRet = nNumber;
980 3070 : dfDoubleRet = nIntRet;
981 : }
982 3070 : 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 7341 : case 'd':
1011 : {
1012 7341 : if (nIndexValue * 8 + 8 > nDataSize)
1013 : {
1014 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
1015 0 : return false;
1016 : }
1017 7341 : double dfNumber = 0;
1018 7341 : memcpy(&dfNumber, pabyData + nIndexValue * 8, 8);
1019 : HFAStandard(8, &dfNumber);
1020 7341 : dfDoubleRet = dfNumber;
1021 7341 : if (chReqType == 'i')
1022 : {
1023 0 : if (dfNumber > std::numeric_limits<int>::max() ||
1024 0 : dfNumber < std::numeric_limits<int>::min() ||
1025 0 : std::isnan(dfNumber))
1026 : {
1027 0 : CPLError(CE_Failure, CPLE_AppDefined,
1028 : "Too large for int: %f", dfNumber);
1029 0 : return false;
1030 : }
1031 0 : nIntRet = static_cast<int>(dfNumber);
1032 : }
1033 : }
1034 7341 : break;
1035 :
1036 135 : case 'b':
1037 : {
1038 135 : if (nDataSize < 12)
1039 0 : return false;
1040 :
1041 135 : GInt32 nRows = 0;
1042 135 : memcpy(&nRows, pabyData, 4);
1043 : HFAStandard(4, &nRows);
1044 :
1045 135 : GInt32 nColumns = 0;
1046 135 : memcpy(&nColumns, pabyData + 4, 4);
1047 : HFAStandard(4, &nColumns);
1048 :
1049 135 : GInt16 nBaseItemType = 0;
1050 135 : memcpy(&nBaseItemType, pabyData + 8, 2);
1051 : HFAStandard(2, &nBaseItemType);
1052 : // We ignore the 2 byte objecttype value.
1053 :
1054 135 : if (nIndexValue < -3 || nRows <= 0 || nColumns <= 0 ||
1055 135 : nRows > INT_MAX / nColumns || nIndexValue >= nRows * nColumns)
1056 0 : return false;
1057 :
1058 135 : pabyData += 12;
1059 135 : nDataSize -= 12;
1060 :
1061 135 : if (nIndexValue == -3)
1062 : {
1063 0 : dfDoubleRet = nBaseItemType;
1064 0 : nIntRet = nBaseItemType;
1065 : }
1066 135 : else if (nIndexValue == -2)
1067 : {
1068 0 : dfDoubleRet = nColumns;
1069 0 : nIntRet = nColumns;
1070 : }
1071 135 : else if (nIndexValue == -1)
1072 : {
1073 0 : dfDoubleRet = nRows;
1074 0 : nIntRet = nRows;
1075 : }
1076 135 : else if (nBaseItemType == EPT_u1)
1077 : {
1078 : // TODO(schwehr): What are these constants like 8 and 0x7?
1079 0 : if (nIndexValue * 8 >= nDataSize)
1080 : {
1081 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
1082 0 : return false;
1083 : }
1084 :
1085 0 : if (pabyData[nIndexValue >> 3] & (1 << (nIndexValue & 0x7)))
1086 : {
1087 0 : dfDoubleRet = 1;
1088 0 : nIntRet = 1;
1089 : }
1090 : else
1091 : {
1092 0 : dfDoubleRet = 0.0;
1093 0 : nIntRet = 0;
1094 : }
1095 : }
1096 135 : else if (nBaseItemType == EPT_u2)
1097 : {
1098 0 : const int nBitOffset = nIndexValue & 0x3;
1099 0 : const int nByteOffset = nIndexValue >> 2;
1100 :
1101 0 : if (nByteOffset >= nDataSize)
1102 : {
1103 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
1104 0 : return false;
1105 : }
1106 :
1107 0 : const int nMask = 0x3;
1108 0 : nIntRet = (pabyData[nByteOffset] >> nBitOffset) & nMask;
1109 0 : dfDoubleRet = nIntRet;
1110 : }
1111 135 : else if (nBaseItemType == EPT_u4)
1112 : {
1113 0 : const int nBitOffset = nIndexValue & 0x7;
1114 0 : const int nByteOffset = nIndexValue >> 3;
1115 :
1116 0 : if (nByteOffset >= nDataSize)
1117 : {
1118 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
1119 0 : return false;
1120 : }
1121 :
1122 0 : const int nMask = 0x7;
1123 0 : nIntRet = (pabyData[nByteOffset] >> nBitOffset) & nMask;
1124 0 : dfDoubleRet = nIntRet;
1125 : }
1126 135 : else if (nBaseItemType == EPT_u8)
1127 : {
1128 18 : if (nIndexValue >= nDataSize)
1129 : {
1130 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
1131 0 : return false;
1132 : }
1133 18 : dfDoubleRet = pabyData[nIndexValue];
1134 18 : nIntRet = pabyData[nIndexValue];
1135 : }
1136 117 : else if (nBaseItemType == EPT_s8)
1137 : {
1138 0 : if (nIndexValue >= nDataSize)
1139 : {
1140 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
1141 0 : return false;
1142 : }
1143 0 : dfDoubleRet = ((signed char *)pabyData)[nIndexValue];
1144 0 : nIntRet = ((signed char *)pabyData)[nIndexValue];
1145 : }
1146 117 : else if (nBaseItemType == EPT_s16)
1147 : {
1148 0 : if (nIndexValue * 2 + 2 > nDataSize)
1149 : {
1150 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
1151 0 : return false;
1152 : }
1153 0 : GInt16 nValue = 0;
1154 0 : memcpy(&nValue, pabyData + 2 * nIndexValue, 2);
1155 : HFAStandard(2, &nValue);
1156 :
1157 0 : dfDoubleRet = nValue;
1158 0 : nIntRet = nValue;
1159 : }
1160 117 : else if (nBaseItemType == EPT_u16)
1161 : {
1162 0 : if (nIndexValue * 2 + 2 > nDataSize)
1163 : {
1164 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
1165 0 : return false;
1166 : }
1167 0 : GUInt16 nValue = 0;
1168 0 : memcpy(&nValue, pabyData + 2 * nIndexValue, 2);
1169 : HFAStandard(2, &nValue);
1170 :
1171 0 : dfDoubleRet = nValue;
1172 0 : nIntRet = nValue;
1173 : }
1174 117 : else if (nBaseItemType == EPT_s32)
1175 : {
1176 0 : if (nIndexValue * 4 + 4 > nDataSize)
1177 : {
1178 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
1179 0 : return false;
1180 : }
1181 0 : GInt32 nValue = 0;
1182 0 : memcpy(&nValue, pabyData + 4 * nIndexValue, 4);
1183 : HFAStandard(4, &nValue);
1184 :
1185 0 : dfDoubleRet = nValue;
1186 0 : nIntRet = nValue;
1187 : }
1188 117 : else if (nBaseItemType == EPT_u32)
1189 : {
1190 0 : if (nIndexValue * 4 + 4 > nDataSize)
1191 : {
1192 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
1193 0 : return false;
1194 : }
1195 0 : GUInt32 nValue = 0;
1196 0 : memcpy(&nValue, pabyData + 4 * nIndexValue, 4);
1197 : HFAStandard(4, &nValue);
1198 :
1199 0 : dfDoubleRet = nValue;
1200 0 : nIntRet = nValue;
1201 : }
1202 117 : else if (nBaseItemType == EPT_f32)
1203 : {
1204 0 : if (nIndexValue * 4 + 4 > nDataSize)
1205 : {
1206 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
1207 0 : return false;
1208 : }
1209 0 : float fValue = 0.0f;
1210 0 : memcpy(&fValue, pabyData + 4 * nIndexValue, 4);
1211 : HFAStandard(4, &fValue);
1212 :
1213 0 : dfDoubleRet = fValue;
1214 0 : nIntRet = FloatToIntClamp(fValue);
1215 : }
1216 117 : else if (nBaseItemType == EPT_f64)
1217 : {
1218 117 : if (nIndexValue * 8 + 8 > nDataSize)
1219 : {
1220 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
1221 0 : return false;
1222 : }
1223 117 : double dfValue = 0.0;
1224 117 : memcpy(&dfValue, pabyData + 8 * nIndexValue, 8);
1225 : HFAStandard(8, &dfValue);
1226 :
1227 117 : dfDoubleRet = dfValue;
1228 117 : if (chReqType == 'i')
1229 : {
1230 0 : const int nMax = std::numeric_limits<int>::max();
1231 0 : const int nMin = std::numeric_limits<int>::min();
1232 0 : if (dfDoubleRet >= nMax)
1233 : {
1234 0 : nIntRet = nMax;
1235 : }
1236 0 : else if (dfDoubleRet <= nMin)
1237 : {
1238 0 : nIntRet = nMin;
1239 : }
1240 0 : else if (std::isnan(dfDoubleRet))
1241 : {
1242 0 : CPLError(CE_Warning, CPLE_AppDefined,
1243 : "NaN converted to INT_MAX.");
1244 0 : nIntRet = nMax;
1245 : }
1246 : else
1247 : {
1248 0 : nIntRet = static_cast<int>(dfDoubleRet);
1249 : }
1250 : }
1251 : }
1252 : else
1253 : {
1254 0 : CPLError(CE_Failure, CPLE_AppDefined,
1255 : "Unknown base item type: %d", nBaseItemType);
1256 0 : return false;
1257 : }
1258 : }
1259 135 : break;
1260 :
1261 13946 : case 'o':
1262 13946 : if (poItemObjectType != nullptr)
1263 : {
1264 13946 : int nExtraOffset = 0;
1265 :
1266 13946 : if (poItemObjectType->nBytes > 0)
1267 : {
1268 11396 : if (nIndexValue != 0 &&
1269 9264 : poItemObjectType->nBytes > INT_MAX / nIndexValue)
1270 : // TODO(schwehr): Why was this CE_Failure when the
1271 : // others are false?
1272 0 : return false;
1273 11396 : nExtraOffset = poItemObjectType->nBytes * nIndexValue;
1274 : }
1275 : else
1276 : {
1277 2567 : for (int iIndexCounter = 0; iIndexCounter < nIndexValue &&
1278 : nExtraOffset < nDataSize;
1279 : iIndexCounter++)
1280 : {
1281 17 : std::set<HFAField *> oVisitedFields;
1282 34 : const int nInc = poItemObjectType->GetInstBytes(
1283 17 : pabyData + nExtraOffset, nDataSize - nExtraOffset,
1284 : oVisitedFields);
1285 17 : if (nInc <= 0 || nExtraOffset > INT_MAX - nInc)
1286 : {
1287 0 : CPLError(CE_Failure, CPLE_AppDefined,
1288 : "Invalid return value");
1289 : // TODO(schwehr): Verify this false is okay.
1290 0 : return false;
1291 : }
1292 :
1293 17 : nExtraOffset += nInc;
1294 : }
1295 : }
1296 :
1297 13946 : if (nExtraOffset >= nDataSize)
1298 0 : return false;
1299 :
1300 13946 : pabyRawData = pabyData + nExtraOffset;
1301 :
1302 13946 : if (pszField != nullptr && strlen(pszField) > 0)
1303 : {
1304 27892 : return poItemObjectType->ExtractInstValue(
1305 13946 : pszField, pabyRawData, nDataOffset + nExtraOffset,
1306 : nDataSize - nExtraOffset, chReqType, pReqReturn,
1307 13946 : pnRemainingDataSize);
1308 : }
1309 : }
1310 : else
1311 : {
1312 : // E. Rouault: not completely sure about this, but helps avoid
1313 : // DoS timeouts in cases like
1314 : // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=1806
1315 0 : return false;
1316 : }
1317 0 : break;
1318 :
1319 0 : default:
1320 0 : return false;
1321 : break;
1322 : }
1323 :
1324 : // Return the appropriate representation.
1325 29975 : if (chReqType == 's')
1326 : {
1327 1102 : if (pszStringRet == nullptr)
1328 : {
1329 : // HFAEntry:: BuildEntryFromMIFObject() expects to have always 8
1330 : // bytes before the data. In normal situations, it should not go
1331 : // here, but that can happen if the file is corrupted so reserve the
1332 : // first 8 bytes before the string to contain null bytes.
1333 39 : memset(szNumberString, 0, 8);
1334 39 : CPLsnprintf(szNumberString + 8, sizeof(szNumberString) - 8, "%.14g",
1335 : dfDoubleRet);
1336 39 : pszStringRet = szNumberString + 8;
1337 : }
1338 :
1339 1102 : *((char **)pReqReturn) = pszStringRet;
1340 1102 : return true;
1341 : }
1342 28873 : else if (chReqType == 'd')
1343 : {
1344 7437 : *((double *)pReqReturn) = dfDoubleRet;
1345 7437 : return true;
1346 : }
1347 21436 : else if (chReqType == 'i')
1348 : {
1349 21436 : *((int *)pReqReturn) = nIntRet;
1350 21436 : return true;
1351 : }
1352 0 : else if (chReqType == 'p')
1353 : {
1354 0 : *((GByte **)pReqReturn) = pabyRawData;
1355 0 : return true;
1356 : }
1357 : else
1358 : {
1359 0 : CPLAssert(false);
1360 : return false;
1361 : }
1362 : }
1363 :
1364 : /************************************************************************/
1365 : /* GetInstBytes() */
1366 : /* */
1367 : /* Get the number of bytes in a particular instance of a */
1368 : /* field. This will normally be the fixed internal nBytes */
1369 : /* value, but for pointer objects will include the variable */
1370 : /* portion. */
1371 : /************************************************************************/
1372 :
1373 168738 : int HFAField::GetInstBytes(GByte *pabyData, int nDataSize,
1374 : std::set<HFAField *> &oVisitedFields)
1375 :
1376 : {
1377 168738 : if (oVisitedFields.find(this) != oVisitedFields.end())
1378 : {
1379 0 : CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
1380 0 : return -1;
1381 : }
1382 :
1383 168738 : if (nBytes > -1)
1384 135344 : return nBytes;
1385 :
1386 33394 : int nCount = 1;
1387 33394 : int nInstBytes = 0;
1388 :
1389 33394 : if (chPointer != '\0')
1390 : {
1391 30962 : if (nDataSize < 4)
1392 : {
1393 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
1394 0 : return -1;
1395 : }
1396 :
1397 30962 : memcpy(&nCount, pabyData, 4);
1398 : HFAStandard(4, &nCount);
1399 :
1400 30962 : pabyData += 8;
1401 30962 : nInstBytes += 8;
1402 : }
1403 :
1404 33394 : if (chItemType == 'b' && nCount != 0) // BASEDATA
1405 : {
1406 515 : if (nDataSize - nInstBytes < 4 + 4 + 2)
1407 : {
1408 0 : CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small");
1409 0 : return -1;
1410 : }
1411 :
1412 515 : GInt32 nRows = 0;
1413 515 : memcpy(&nRows, pabyData, 4);
1414 : HFAStandard(4, &nRows);
1415 515 : GInt32 nColumns = 0;
1416 515 : memcpy(&nColumns, pabyData + 4, 4);
1417 : HFAStandard(4, &nColumns);
1418 515 : GInt16 nBaseItemType = 0;
1419 515 : memcpy(&nBaseItemType, pabyData + 8, 2);
1420 : HFAStandard(2, &nBaseItemType);
1421 515 : if (nBaseItemType < EPT_MIN || nBaseItemType > EPT_MAX)
1422 0 : return -1;
1423 :
1424 515 : EPTType eBaseItemType = static_cast<EPTType>(nBaseItemType);
1425 :
1426 515 : nInstBytes += 12;
1427 :
1428 515 : if (nRows < 0 || nColumns < 0)
1429 0 : return -1;
1430 515 : if (nColumns != 0 && nRows > INT_MAX / nColumns)
1431 0 : return -1;
1432 1022 : if (nRows != 0 &&
1433 507 : ((HFAGetDataTypeBits(eBaseItemType) + 7) / 8) > INT_MAX / nRows)
1434 0 : return -1;
1435 1022 : if (nColumns != 0 &&
1436 507 : ((HFAGetDataTypeBits(eBaseItemType) + 7) / 8) * nRows >
1437 507 : INT_MAX / nColumns)
1438 0 : return -1;
1439 515 : if (((HFAGetDataTypeBits(eBaseItemType) + 7) / 8) * nRows * nColumns >
1440 515 : INT_MAX - nInstBytes)
1441 0 : return -1;
1442 :
1443 515 : nInstBytes +=
1444 515 : ((HFAGetDataTypeBits(eBaseItemType) + 7) / 8) * nRows * nColumns;
1445 : }
1446 32879 : else if (poItemObjectType == nullptr)
1447 : {
1448 49434 : if (nCount != 0 &&
1449 20000 : HFADictionary::GetItemSize(chItemType) > INT_MAX / nCount)
1450 0 : return -1;
1451 29434 : if (nCount * HFADictionary::GetItemSize(chItemType) >
1452 29434 : INT_MAX - nInstBytes)
1453 0 : return -1;
1454 29434 : nInstBytes += nCount * HFADictionary::GetItemSize(chItemType);
1455 : }
1456 : else
1457 : {
1458 3445 : oVisitedFields.insert(this);
1459 5931 : for (int i = 0; i < nCount && nInstBytes < nDataSize && nInstBytes >= 0;
1460 : i++)
1461 : {
1462 2486 : const int nThisBytes = poItemObjectType->GetInstBytes(
1463 : pabyData, nDataSize - nInstBytes, oVisitedFields);
1464 2486 : if (nThisBytes <= 0 || nInstBytes > INT_MAX - nThisBytes)
1465 : {
1466 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid return value");
1467 0 : return -1;
1468 : }
1469 :
1470 2486 : nInstBytes += nThisBytes;
1471 2486 : pabyData += nThisBytes;
1472 : }
1473 3445 : oVisitedFields.erase(this);
1474 : }
1475 :
1476 33394 : return nInstBytes;
1477 : }
1478 :
1479 : /************************************************************************/
1480 : /* GetInstCount() */
1481 : /* */
1482 : /* Get the count for a particular instance of a field. This */
1483 : /* will normally be the built in value, but for variable fields */
1484 : /* this is extracted from the data itself. */
1485 : /************************************************************************/
1486 :
1487 47439 : int HFAField::GetInstCount(GByte *pabyData, int nDataSize) const
1488 :
1489 : {
1490 47439 : if (chPointer == '\0')
1491 27415 : return nItemCount;
1492 :
1493 20024 : if (chItemType == 'b')
1494 : {
1495 213 : if (nDataSize < 20)
1496 0 : return 0;
1497 :
1498 213 : GInt32 nRows = 0;
1499 213 : memcpy(&nRows, pabyData + 8, 4);
1500 : HFAStandard(4, &nRows);
1501 213 : GInt32 nColumns = 0;
1502 213 : memcpy(&nColumns, pabyData + 12, 4);
1503 : HFAStandard(4, &nColumns);
1504 :
1505 213 : if (nRows < 0 || nColumns < 0)
1506 0 : return 0;
1507 213 : if (nColumns != 0 && nRows > INT_MAX / nColumns)
1508 0 : return 0;
1509 :
1510 213 : return nRows * nColumns;
1511 : }
1512 :
1513 19811 : if (nDataSize < 4)
1514 0 : return 0;
1515 :
1516 19811 : GInt32 nCount = 0;
1517 19811 : memcpy(&nCount, pabyData, 4);
1518 : HFAStandard(4, &nCount);
1519 19811 : return nCount;
1520 : }
1521 :
1522 : /************************************************************************/
1523 : /* DumpInstValue() */
1524 : /************************************************************************/
1525 :
1526 0 : void HFAField::DumpInstValue(FILE *fpOut, GByte *pabyData, GUInt32 nDataOffset,
1527 : int nDataSize, const char *pszPrefix)
1528 :
1529 : {
1530 0 : const int nEntries = GetInstCount(pabyData, nDataSize);
1531 :
1532 : // Special case for arrays of chars or uchars which are printed
1533 : // as a string.
1534 0 : if ((chItemType == 'c' || chItemType == 'C') && nEntries > 0)
1535 : {
1536 0 : void *pReturn = nullptr;
1537 0 : if (ExtractInstValue(nullptr, 0, pabyData, nDataOffset, nDataSize, 's',
1538 : &pReturn))
1539 0 : CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "%s%s = `%s'\n", pszPrefix,
1540 : pszFieldName,
1541 : static_cast<char *>(pReturn)));
1542 : else
1543 0 : CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "%s%s = (access failed)\n",
1544 : pszPrefix, pszFieldName));
1545 :
1546 0 : return;
1547 : }
1548 :
1549 : // For BASEDATA objects, we want to first dump their dimension and type.
1550 0 : if (chItemType == 'b')
1551 : {
1552 0 : int nDataType = 0;
1553 0 : const bool bSuccess = ExtractInstValue(
1554 : nullptr, -3, pabyData, nDataOffset, nDataSize, 'i', &nDataType);
1555 0 : if (bSuccess)
1556 : {
1557 0 : int nColumns = 0;
1558 0 : ExtractInstValue(nullptr, -2, pabyData, nDataOffset, nDataSize, 'i',
1559 : &nColumns);
1560 0 : int nRows = 0;
1561 0 : ExtractInstValue(nullptr, -1, pabyData, nDataOffset, nDataSize, 'i',
1562 : &nRows);
1563 0 : CPL_IGNORE_RET_VAL(VSIFPrintf(
1564 : fpOut, "%sBASEDATA(%s): %dx%d of %s\n", pszPrefix, pszFieldName,
1565 : nColumns, nRows,
1566 0 : (nDataType >= EPT_MIN && nDataType <= EPT_MAX)
1567 0 : ? HFAGetDataTypeName(static_cast<EPTType>(nDataType))
1568 : : "invalid type"));
1569 : }
1570 : else
1571 : {
1572 0 : CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "%sBASEDATA(%s): empty\n",
1573 : pszPrefix, pszFieldName));
1574 : }
1575 : }
1576 :
1577 : // Dump each entry in the field array.
1578 0 : void *pReturn = nullptr;
1579 :
1580 0 : const int nMaxEntry = std::min(MAX_ENTRY_REPORT, nEntries);
1581 0 : for (int iEntry = 0; iEntry < nMaxEntry; iEntry++)
1582 : {
1583 0 : if (nEntries == 1)
1584 0 : CPL_IGNORE_RET_VAL(
1585 0 : VSIFPrintf(fpOut, "%s%s = ", pszPrefix, pszFieldName));
1586 : else
1587 0 : CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "%s%s[%d] = ", pszPrefix,
1588 : pszFieldName, iEntry));
1589 :
1590 0 : switch (chItemType)
1591 : {
1592 0 : case 'f':
1593 : case 'd':
1594 : {
1595 0 : double dfValue = 0.0;
1596 0 : if (ExtractInstValue(nullptr, iEntry, pabyData, nDataOffset,
1597 : nDataSize, 'd', &dfValue))
1598 0 : CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "%f\n", dfValue));
1599 : else
1600 0 : CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "(access failed)\n"));
1601 : }
1602 0 : break;
1603 :
1604 0 : case 'b':
1605 : {
1606 0 : double dfValue = 0.0;
1607 :
1608 0 : if (ExtractInstValue(nullptr, iEntry, pabyData, nDataOffset,
1609 : nDataSize, 'd', &dfValue))
1610 0 : CPL_IGNORE_RET_VAL(
1611 0 : VSIFPrintf(fpOut, "%s%.15g\n", pszPrefix, dfValue));
1612 : else
1613 0 : CPL_IGNORE_RET_VAL(
1614 0 : VSIFPrintf(fpOut, "%s(access failed)\n", pszPrefix));
1615 : }
1616 0 : break;
1617 :
1618 0 : case 'e':
1619 0 : if (ExtractInstValue(nullptr, iEntry, pabyData, nDataOffset,
1620 : nDataSize, 's', &pReturn))
1621 0 : CPL_IGNORE_RET_VAL(
1622 0 : VSIFPrintf(fpOut, "%s\n", (char *)pReturn));
1623 : else
1624 0 : CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "(access failed)\n"));
1625 0 : break;
1626 :
1627 0 : case 'o':
1628 0 : if (!ExtractInstValue(nullptr, iEntry, pabyData, nDataOffset,
1629 : nDataSize, 'p', &pReturn))
1630 : {
1631 0 : CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "(access failed)\n"));
1632 : }
1633 : else
1634 : {
1635 0 : CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "\n"));
1636 :
1637 0 : const int nByteOffset =
1638 0 : static_cast<int>(((GByte *)pReturn) - pabyData);
1639 :
1640 0 : char szLongFieldName[256] = {};
1641 0 : snprintf(szLongFieldName, sizeof(szLongFieldName), "%s ",
1642 : pszPrefix);
1643 :
1644 0 : if (poItemObjectType)
1645 0 : poItemObjectType->DumpInstValue(
1646 0 : fpOut, pabyData + nByteOffset,
1647 0 : nDataOffset + nByteOffset, nDataSize - nByteOffset,
1648 : szLongFieldName);
1649 : }
1650 0 : break;
1651 :
1652 0 : default:
1653 : {
1654 0 : GInt32 nIntValue = 0;
1655 :
1656 0 : if (ExtractInstValue(nullptr, iEntry, pabyData, nDataOffset,
1657 : nDataSize, 'i', &nIntValue))
1658 0 : CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "%d\n", nIntValue));
1659 : else
1660 0 : CPL_IGNORE_RET_VAL(VSIFPrintf(fpOut, "(access failed)\n"));
1661 : }
1662 0 : break;
1663 : }
1664 : }
1665 :
1666 0 : if (nEntries > MAX_ENTRY_REPORT)
1667 0 : CPL_IGNORE_RET_VAL(VSIFPrintf(
1668 : fpOut, "%s ... remaining instances omitted ...\n", pszPrefix));
1669 :
1670 0 : if (nEntries == 0)
1671 0 : CPL_IGNORE_RET_VAL(
1672 0 : VSIFPrintf(fpOut, "%s%s = (no values)\n", pszPrefix, pszFieldName));
1673 : }
|