Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: ISO 8211 Access
4 : * Purpose: Implements the DDFSubfieldDefn class.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 1999, Frank Warmerdam
9 : * Copyright (c) 2011-2013, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "cpl_port.h"
15 : #include "iso8211.h"
16 :
17 : #include <cstdio>
18 : #include <cstdlib>
19 : #include <cstring>
20 :
21 : #include <algorithm>
22 :
23 : #include "cpl_conv.h"
24 : #include "cpl_error.h"
25 : #include "cpl_string.h"
26 :
27 : /************************************************************************/
28 : /* DDFSubfieldDefn() */
29 : /************************************************************************/
30 :
31 : DDFSubfieldDefn::DDFSubfieldDefn() = default;
32 :
33 : /************************************************************************/
34 : /* ~DDFSubfieldDefn() */
35 : /************************************************************************/
36 :
37 : DDFSubfieldDefn::~DDFSubfieldDefn() = default;
38 :
39 : /************************************************************************/
40 : /* SetName() */
41 : /************************************************************************/
42 :
43 8056 : void DDFSubfieldDefn::SetName(const char *pszNewName)
44 :
45 : {
46 8056 : osName = pszNewName;
47 8056 : while (!osName.empty() && osName.back() == ' ')
48 0 : osName.pop_back();
49 8056 : }
50 :
51 : /************************************************************************/
52 : /* SetFormat() */
53 : /* */
54 : /* While interpreting the format string we don't support: */
55 : /* */
56 : /* o Passing an explicit terminator for variable length field. */
57 : /* o 'X' for unused data ... this should really be filtered */
58 : /* out by DDFFieldDefn::ApplyFormats(), but isn't. */
59 : /* o 'B' bitstrings that aren't a multiple of eight. */
60 : /************************************************************************/
61 :
62 8056 : int DDFSubfieldDefn::SetFormat(const char *pszFormat)
63 :
64 : {
65 8056 : osFormatString = pszFormat;
66 :
67 : /* -------------------------------------------------------------------- */
68 : /* These values will likely be used. */
69 : /* -------------------------------------------------------------------- */
70 8056 : if (osFormatString.size() >= 2 && osFormatString[1] == '(')
71 : {
72 2536 : nFormatWidth = atoi(osFormatString.c_str() + 2);
73 2536 : if (nFormatWidth < 0)
74 : {
75 0 : CPLError(CE_Failure, CPLE_AppDefined, "Format width %s is invalid.",
76 0 : osFormatString.c_str() + 2);
77 0 : return FALSE;
78 : }
79 2536 : bIsVariable = nFormatWidth == 0;
80 : }
81 : else
82 5520 : bIsVariable = TRUE;
83 :
84 : /* -------------------------------------------------------------------- */
85 : /* Interpret the format string. */
86 : /* -------------------------------------------------------------------- */
87 8056 : switch (osFormatString[0])
88 : {
89 1781 : case 'A':
90 : case 'C': // It isn't clear to me how this is different than 'A'
91 1781 : eType = DDFString;
92 1781 : break;
93 :
94 328 : case 'R':
95 328 : eType = DDFFloat;
96 328 : break;
97 :
98 1151 : case 'I':
99 : case 'S':
100 1151 : eType = DDFInt;
101 1151 : break;
102 :
103 4796 : case 'B':
104 : case 'b':
105 : // Is the width expressed in bits? (is it a bitstring)
106 4796 : bIsVariable = FALSE;
107 4796 : if (osFormatString[1] == '\0')
108 0 : return FALSE;
109 :
110 4796 : if (osFormatString[1] == '(')
111 : {
112 192 : nFormatWidth = atoi(osFormatString.c_str() + 2);
113 192 : if (nFormatWidth < 0 || nFormatWidth % 8 != 0)
114 : {
115 0 : CPLError(CE_Failure, CPLE_AppDefined,
116 : "Format width %s is invalid.",
117 0 : osFormatString.c_str() + 2);
118 0 : return FALSE;
119 : }
120 :
121 192 : nFormatWidth = nFormatWidth / 8;
122 192 : eBinaryFormat = SInt; // good default, works for SDTS.
123 :
124 192 : if (nFormatWidth < 5)
125 0 : eType = DDFInt;
126 : else
127 192 : eType = DDFBinaryString;
128 : }
129 :
130 : // or do we have a binary type indicator? (is it binary)
131 : else
132 : {
133 4604 : if (osFormatString[1] < '0' || osFormatString[1] > '5')
134 : {
135 0 : CPLError(CE_Failure, CPLE_AppDefined,
136 : "Binary format = %c is invalid.",
137 0 : osFormatString[1]);
138 0 : return FALSE;
139 : }
140 4604 : eBinaryFormat =
141 4604 : static_cast<DDFBinaryFormat>(osFormatString[1] - '0');
142 4604 : nFormatWidth = atoi(osFormatString.c_str() + 2);
143 4604 : if (nFormatWidth < 0)
144 : {
145 0 : CPLError(CE_Failure, CPLE_AppDefined,
146 : "Format width %s is invalid.",
147 0 : osFormatString.c_str() + 2);
148 0 : return FALSE;
149 : }
150 :
151 4604 : if (eBinaryFormat == SInt || eBinaryFormat == UInt)
152 4604 : eType = DDFInt;
153 : else
154 0 : eType = DDFFloat;
155 : }
156 4796 : break;
157 :
158 0 : case 'X':
159 : // 'X' is extra space, and should not be directly assigned to a
160 : // subfield ... I have not encountered it in use yet though.
161 0 : CPLError(CE_Failure, CPLE_AppDefined,
162 0 : "Format type of `%c' not supported.\n", osFormatString[0]);
163 :
164 0 : return FALSE;
165 :
166 0 : default:
167 0 : CPLError(CE_Failure, CPLE_AppDefined,
168 : "Format type of `%c' not recognised.\n",
169 0 : osFormatString[0]);
170 :
171 0 : return FALSE;
172 : }
173 :
174 8056 : return TRUE;
175 : }
176 :
177 : /************************************************************************/
178 : /* Dump() */
179 : /************************************************************************/
180 :
181 : /**
182 : * Write out subfield definition info to debugging file.
183 : *
184 : * A variety of information about this field definition is written to the
185 : * give debugging file handle.
186 : *
187 : * @param fp The standard IO file handle to write to. i.e. stderr
188 : */
189 :
190 0 : void DDFSubfieldDefn::Dump(FILE *fp) const
191 :
192 : {
193 0 : fprintf(fp, " DDFSubfieldDefn:\n");
194 0 : fprintf(fp, " Label = `%s'\n", osName.c_str());
195 0 : fprintf(fp, " FormatString = `%s'\n", osFormatString.c_str());
196 0 : }
197 :
198 : /************************************************************************/
199 : /* GetDataLength() */
200 : /* */
201 : /* This method will scan for the end of a variable field. */
202 : /************************************************************************/
203 :
204 : /**
205 : * Scan for the end of variable length data. Given a pointer to the data
206 : * for this subfield (from within a DDFRecord) this method will return the
207 : * number of bytes which are data for this subfield. The number of bytes
208 : * consumed as part of this field can also be fetched. This number may
209 : * be one longer than the length if there is a terminator character
210 : * used.<p>
211 : *
212 : * This method is mainly for internal use, or for applications which
213 : * want the raw binary data to interpret themselves. Otherwise use one
214 : * of ExtractStringData(), ExtractIntData() or ExtractFloatData().
215 : *
216 : * @param pachSourceData The pointer to the raw data for this field. This
217 : * may have come from DDFRecord::GetData(), taking into account skip factors
218 : * over previous subfields data.
219 : * @param nMaxBytes The maximum number of bytes that are accessible after
220 : * pachSourceData.
221 : * @param pnConsumedBytes Pointer to an integer into which the number of
222 : * bytes consumed by this field should be written. May be NULL to ignore.
223 : *
224 : * @return The number of bytes at pachSourceData which are actual data for
225 : * this record (not including unit, or field terminator).
226 : */
227 :
228 768456 : int DDFSubfieldDefn::GetDataLength(const char *pachSourceData, int nMaxBytes,
229 : int *pnConsumedBytes) const
230 :
231 : {
232 768456 : if (!bIsVariable)
233 : {
234 651403 : if (nFormatWidth > nMaxBytes)
235 : {
236 0 : CPLError(CE_Warning, CPLE_AppDefined,
237 : "Only %d bytes available for subfield %s with\n"
238 : "format string %s ... returning shortened data.",
239 : nMaxBytes, osName.c_str(), osFormatString.c_str());
240 :
241 0 : if (pnConsumedBytes != nullptr)
242 0 : *pnConsumedBytes = nMaxBytes;
243 :
244 0 : return nMaxBytes;
245 : }
246 : else
247 : {
248 651403 : if (pnConsumedBytes != nullptr)
249 651082 : *pnConsumedBytes = nFormatWidth;
250 :
251 651403 : return nFormatWidth;
252 : }
253 : }
254 : else
255 : {
256 117053 : int nLength = 0;
257 117053 : int bAsciiField = TRUE;
258 117053 : int extraConsumedBytes = 0;
259 :
260 : /* We only check for the field terminator because of some buggy
261 : * datasets with missing format terminators. However, we have found
262 : * the field terminator and unit terminators are legal characters
263 : * within the fields of some extended datasets (such as JP34NC94.000).
264 : * So we don't check for the field terminator and unit terminators as
265 : * a single byte if the field appears to be multi-byte which we
266 : * establish by checking for the buffer ending with 0x1e 0x00 (a
267 : * two byte field terminator).
268 : *
269 : * In the case of S57, the subfield ATVL of the NATF field can be
270 : * encoded in lexical level 2 (see S57 specification, Edition 3.1,
271 : * paragraph 2.4 and 2.5). In that case the Unit Terminator and Field
272 : * Terminator are followed by the NULL character.
273 : * A better fix would be to read the NALL tag in the DSSI to check
274 : * that the lexical level is 2, instead of relying on the value of
275 : * the first byte as we are doing - but that is not information
276 : * that is available at the libiso8211 level (bug #1526)
277 : */
278 :
279 : // If the whole field ends with 0x1e 0x00 then we assume this
280 : // field is a double byte character set.
281 117053 : if (nMaxBytes > 1 &&
282 117053 : (pachSourceData[nMaxBytes - 2] == chFormatDelimiter ||
283 1041 : pachSourceData[nMaxBytes - 2] == DDF_FIELD_TERMINATOR) &&
284 116014 : pachSourceData[nMaxBytes - 1] == 0x00)
285 2 : bAsciiField = FALSE;
286 :
287 : // if( !bAsciiField )
288 : // CPLDebug( "ISO8211", "Non-ASCII field detected." );
289 :
290 887465 : while (nLength < nMaxBytes)
291 : {
292 887465 : if (bAsciiField)
293 : {
294 887205 : if (pachSourceData[nLength] == chFormatDelimiter ||
295 770154 : pachSourceData[nLength] == DDF_FIELD_TERMINATOR)
296 : break;
297 : }
298 : else
299 : {
300 260 : if (nLength > 0 &&
301 258 : (pachSourceData[nLength - 1] == chFormatDelimiter ||
302 254 : pachSourceData[nLength - 1] == DDF_FIELD_TERMINATOR) &&
303 4 : pachSourceData[nLength] == 0)
304 : {
305 : // Suck up the field terminator if one follows
306 : // or else it will be interpreted as a new subfield.
307 : // This is a pretty ugly counter-intuitive hack!
308 2 : if (nLength + 1 < nMaxBytes &&
309 2 : pachSourceData[nLength + 1] == DDF_FIELD_TERMINATOR)
310 2 : extraConsumedBytes++;
311 2 : break;
312 : }
313 : }
314 :
315 770412 : nLength++;
316 : }
317 :
318 117053 : if (pnConsumedBytes != nullptr)
319 : {
320 93766 : if (nMaxBytes == 0)
321 0 : *pnConsumedBytes = nLength + extraConsumedBytes;
322 : else
323 93766 : *pnConsumedBytes = nLength + extraConsumedBytes + 1;
324 : }
325 :
326 117053 : return nLength;
327 : }
328 : }
329 :
330 : /************************************************************************/
331 : /* ExtractStringData() */
332 : /************************************************************************/
333 :
334 : /**
335 : * Extract a zero terminated string containing the data for this subfield.
336 : * Given a pointer to the data
337 : * for this subfield (from within a DDFRecord) this method will return the
338 : * data for this subfield. The number of bytes
339 : * consumed as part of this field can also be fetched. This number may
340 : * be one longer than the string length if there is a terminator character
341 : * used.<p>
342 : *
343 : * This function will return the raw binary data of a subfield for
344 : * types other than DDFString, including data past zero chars. This is
345 : * the standard way of extracting DDFBinaryString subfields for instance.<p>
346 : *
347 : * CAUTION: this method is not thread safe as it updates mutable member
348 : * variables.
349 : *
350 : * @param pachSourceData The pointer to the raw data for this field. This
351 : * may have come from DDFRecord::GetData(), taking into account skip factors
352 : * over previous subfields data.
353 : * @param nMaxBytes The maximum number of bytes that are accessible after
354 : * pachSourceData.
355 : * @param pnConsumedBytes Pointer to an integer into which the number of
356 : * bytes consumed by this field should be written. May be NULL to ignore.
357 : * This is used as a skip factor to increment pachSourceData to point to the
358 : * next subfields data.
359 : *
360 : * @return A pointer to a buffer containing the data for this field. The
361 : * returned pointer is to an internal buffer which is invalidated on the
362 : * next ExtractStringData() call on this DDFSubfieldDefn(). It should not
363 : * be freed by the application.
364 : *
365 : * @see ExtractIntData(), ExtractFloatData()
366 : */
367 :
368 24423 : const char *DDFSubfieldDefn::ExtractStringData(const char *pachSourceData,
369 : int nMaxBytes,
370 : int *pnConsumedBytes) const
371 :
372 : {
373 : const int nLength =
374 24423 : GetDataLength(pachSourceData, nMaxBytes, pnConsumedBytes);
375 :
376 24423 : osBuffer.assign(pachSourceData, pachSourceData + nLength);
377 :
378 24423 : return osBuffer.c_str();
379 : }
380 :
381 : /************************************************************************/
382 : /* ExtractFloatData() */
383 : /************************************************************************/
384 :
385 : /**
386 : * Extract a subfield value as a float. Given a pointer to the data
387 : * for this subfield (from within a DDFRecord) this method will return the
388 : * floating point data for this subfield. The number of bytes
389 : * consumed as part of this field can also be fetched. This method may be
390 : * called for any type of subfield, and will return zero if the subfield is
391 : * not numeric.
392 : *
393 : * @param pachSourceData The pointer to the raw data for this field. This
394 : * may have come from DDFRecord::GetData(), taking into account skip factors
395 : * over previous subfields data.
396 : * @param nMaxBytes The maximum number of bytes that are accessible after
397 : * pachSourceData.
398 : * @param pnConsumedBytes Pointer to an integer into which the number of
399 : * bytes consumed by this field should be written. May be NULL to ignore.
400 : * This is used as a skip factor to increment pachSourceData to point to the
401 : * next subfields data.
402 : *
403 : * @return The subfield's numeric value (or zero if it isn't numeric).
404 : *
405 : * @see ExtractIntData(), ExtractStringData()
406 : */
407 :
408 120 : double DDFSubfieldDefn::ExtractFloatData(const char *pachSourceData,
409 : int nMaxBytes,
410 : int *pnConsumedBytes) const
411 :
412 : {
413 120 : switch (osFormatString[0])
414 : {
415 120 : case 'A':
416 : case 'I':
417 : case 'R':
418 : case 'S':
419 : case 'C':
420 120 : return CPLAtof(
421 120 : ExtractStringData(pachSourceData, nMaxBytes, pnConsumedBytes));
422 :
423 0 : case 'B':
424 : case 'b':
425 : {
426 : unsigned char abyData[8];
427 0 : void *pabyData = abyData;
428 :
429 0 : if (nFormatWidth > nMaxBytes)
430 : {
431 0 : CPLError(CE_Warning, CPLE_AppDefined,
432 : "Attempt to extract float subfield %s with format %s\n"
433 : "failed as only %d bytes available. Using zero.",
434 : osName.c_str(), osFormatString.c_str(), nMaxBytes);
435 0 : return 0;
436 : }
437 0 : if (nFormatWidth > static_cast<int>(sizeof(abyData)))
438 : {
439 0 : CPLError(CE_Failure, CPLE_AppDefined,
440 0 : "Format width %d too large", nFormatWidth);
441 0 : return 0;
442 : }
443 :
444 0 : if (pnConsumedBytes != nullptr)
445 0 : *pnConsumedBytes = nFormatWidth;
446 :
447 : // Byte swap the data if it isn't in machine native format.
448 : // In any event we copy it into our buffer to ensure it is
449 : // word aligned.
450 : #ifdef CPL_LSB
451 0 : if (osFormatString[0] == 'B')
452 : #else
453 : if (osFormatString[0] == 'b')
454 : #endif
455 : {
456 0 : for (int i = 0; i < nFormatWidth; i++)
457 0 : abyData[nFormatWidth - i - 1] = pachSourceData[i];
458 : }
459 : else
460 : {
461 0 : memcpy(abyData, pachSourceData, nFormatWidth);
462 : }
463 :
464 : // Interpret the bytes of data.
465 0 : switch (eBinaryFormat)
466 : {
467 0 : case UInt:
468 0 : if (nFormatWidth == 1)
469 0 : return abyData[0];
470 0 : else if (nFormatWidth == 2)
471 0 : return *(static_cast<GUInt16 *>(pabyData));
472 0 : else if (nFormatWidth == 4)
473 0 : return *(static_cast<GUInt32 *>(pabyData));
474 : else
475 : {
476 : // CPLAssert( false );
477 0 : return 0.0;
478 : }
479 :
480 0 : case SInt:
481 0 : if (nFormatWidth == 1)
482 0 : return *(static_cast<signed char *>(pabyData));
483 0 : else if (nFormatWidth == 2)
484 0 : return *(static_cast<GInt16 *>(pabyData));
485 0 : else if (nFormatWidth == 4)
486 0 : return *(static_cast<GInt32 *>(pabyData));
487 : else
488 : {
489 : // CPLAssert( false );
490 0 : return 0.0;
491 : }
492 :
493 0 : case FloatReal:
494 0 : if (nFormatWidth == 4)
495 : return static_cast<double>(
496 0 : *(static_cast<float *>(pabyData)));
497 0 : else if (nFormatWidth == 8)
498 0 : return *(static_cast<double *>(pabyData));
499 : else
500 : {
501 : // CPLAssert( false );
502 0 : return 0.0;
503 : }
504 :
505 0 : case NotBinary:
506 : case FPReal:
507 : case FloatComplex:
508 : // CPLAssert( false );
509 0 : return 0.0;
510 : }
511 0 : break;
512 : // end of 'b'/'B' case.
513 : }
514 :
515 0 : default:
516 : // CPLAssert( false );
517 0 : return 0.0;
518 : }
519 :
520 : // CPLAssert( false );
521 0 : return 0.0;
522 : }
523 :
524 : /************************************************************************/
525 : /* ExtractIntData() */
526 : /************************************************************************/
527 :
528 : /**
529 : * Extract a subfield value as an integer. Given a pointer to the data
530 : * for this subfield (from within a DDFRecord) this method will return the
531 : * int data for this subfield. The number of bytes
532 : * consumed as part of this field can also be fetched. This method may be
533 : * called for any type of subfield, and will return zero if the subfield is
534 : * not numeric.
535 : *
536 : * @param pachSourceData The pointer to the raw data for this field. This
537 : * may have come from DDFRecord::GetData(), taking into account skip factors
538 : * over previous subfields data.
539 : * @param nMaxBytes The maximum number of bytes that are accessible after
540 : * pachSourceData.
541 : * @param pnConsumedBytes Pointer to an integer into which the number of
542 : * bytes consumed by this field should be written. May be NULL to ignore.
543 : * This is used as a skip factor to increment pachSourceData to point to the
544 : * next subfields data.
545 : *
546 : * @return The subfield's numeric value (or zero if it isn't numeric).
547 : *
548 : * @see ExtractFloatData(), ExtractStringData()
549 : */
550 :
551 633063 : int DDFSubfieldDefn::ExtractIntData(const char *pachSourceData, int nMaxBytes,
552 : int *pnConsumedBytes) const
553 :
554 : {
555 633063 : switch (osFormatString[0])
556 : {
557 427 : case 'A':
558 : case 'I':
559 : case 'R':
560 : case 'S':
561 : case 'C':
562 427 : return atoi(
563 427 : ExtractStringData(pachSourceData, nMaxBytes, pnConsumedBytes));
564 :
565 632636 : case 'B':
566 : case 'b':
567 : {
568 : unsigned char abyData[8];
569 632636 : void *pabyData = abyData;
570 :
571 632636 : if (nFormatWidth > nMaxBytes ||
572 632636 : nFormatWidth >= static_cast<int>(sizeof(abyData)))
573 : {
574 0 : CPLError(
575 : CE_Warning, CPLE_AppDefined,
576 : "Attempt to extract int subfield %s with format %s\n"
577 : "failed as only %d bytes available. Using zero.",
578 : osName.c_str(), osFormatString.c_str(),
579 0 : std::min(nMaxBytes, static_cast<int>(sizeof(abyData))));
580 632636 : return 0;
581 : }
582 :
583 632636 : if (pnConsumedBytes != nullptr)
584 336102 : *pnConsumedBytes = nFormatWidth;
585 :
586 : // Byte swap the data if it isn't in machine native format.
587 : // In any event we copy it into our buffer to ensure it is
588 : // word aligned.
589 : #ifdef CPL_LSB
590 632636 : if (osFormatString[0] == 'B')
591 : #else
592 : if (osFormatString[0] == 'b')
593 : #endif
594 : {
595 0 : for (int i = 0; i < nFormatWidth; i++)
596 0 : abyData[nFormatWidth - i - 1] = pachSourceData[i];
597 : }
598 : else
599 : {
600 632636 : memcpy(abyData, pachSourceData, nFormatWidth);
601 : }
602 :
603 : // Interpret the bytes of data.
604 632636 : switch (eBinaryFormat)
605 : {
606 172288 : case UInt:
607 172288 : if (nFormatWidth == 4)
608 : return static_cast<int>(
609 43702 : *(static_cast<GUInt32 *>(pabyData)));
610 128586 : else if (nFormatWidth == 1)
611 51667 : return abyData[0];
612 76919 : else if (nFormatWidth == 2)
613 76919 : return *(static_cast<GUInt16 *>(pabyData));
614 : else
615 : {
616 : // CPLAssert( false );
617 0 : return 0;
618 : }
619 :
620 460348 : case SInt:
621 460348 : if (nFormatWidth == 4)
622 460348 : return *(static_cast<GInt32 *>(pabyData));
623 0 : else if (nFormatWidth == 1)
624 0 : return *(static_cast<signed char *>(pabyData));
625 0 : else if (nFormatWidth == 2)
626 0 : return *(static_cast<GInt16 *>(pabyData));
627 : else
628 : {
629 : // CPLAssert( false );
630 0 : return 0;
631 : }
632 :
633 0 : case FloatReal:
634 0 : if (nFormatWidth == 4)
635 : return static_cast<int>(
636 0 : *(static_cast<float *>(pabyData)));
637 0 : else if (nFormatWidth == 8)
638 : return static_cast<int>(
639 0 : *(static_cast<double *>(pabyData)));
640 : else
641 : {
642 : // CPLAssert( false );
643 0 : return 0;
644 : }
645 :
646 0 : case NotBinary:
647 : case FPReal:
648 : case FloatComplex:
649 : // CPLAssert( false );
650 0 : return 0;
651 : }
652 0 : break;
653 : // end of 'b'/'B' case.
654 : }
655 :
656 0 : default:
657 : // CPLAssert( false );
658 0 : return 0;
659 : }
660 :
661 : // CPLAssert( false );
662 0 : return 0;
663 : }
664 :
665 : /************************************************************************/
666 : /* DumpData() */
667 : /* */
668 : /* Dump the instance data for this subfield from a data */
669 : /* record. This fits into the output dump stream of a DDFField. */
670 : /************************************************************************/
671 :
672 : /**
673 : * Dump subfield value to debugging file.
674 : *
675 : * @param pachData Pointer to data for this subfield.
676 : * @param nMaxBytes Maximum number of bytes available in pachData.
677 : * @param fp File to write report to.
678 : */
679 :
680 0 : void DDFSubfieldDefn::DumpData(const char *pachData, int nMaxBytes,
681 : FILE *fp) const
682 :
683 : {
684 0 : if (nMaxBytes < 0)
685 : {
686 0 : fprintf(fp, " Subfield `%s' = {invalid length}\n", osName.c_str());
687 0 : return;
688 : }
689 0 : if (eType == DDFFloat)
690 0 : fprintf(fp, " Subfield `%s' = %f\n", osName.c_str(),
691 : ExtractFloatData(pachData, nMaxBytes, nullptr));
692 0 : else if (eType == DDFInt)
693 0 : fprintf(fp, " Subfield `%s' = %d\n", osName.c_str(),
694 : ExtractIntData(pachData, nMaxBytes, nullptr));
695 0 : else if (eType == DDFBinaryString)
696 : {
697 0 : int nBytes = 0;
698 : const GByte *pabyBString = reinterpret_cast<const GByte *>(
699 0 : ExtractStringData(pachData, nMaxBytes, &nBytes));
700 :
701 0 : fprintf(fp, " Subfield `%s' = 0x", osName.c_str());
702 0 : for (int i = 0; i < std::min(nBytes, 24); i++)
703 0 : fprintf(fp, "%02X", pabyBString[i]);
704 :
705 0 : if (nBytes > 24)
706 0 : fprintf(fp, "%s", "...");
707 :
708 0 : fprintf(fp, "\n");
709 : }
710 : else
711 0 : fprintf(fp, " Subfield `%s' = `%s'\n", osName.c_str(),
712 : ExtractStringData(pachData, nMaxBytes, nullptr));
713 : }
714 :
715 : /************************************************************************/
716 : /* GetDefaultValue() */
717 : /************************************************************************/
718 :
719 : /**
720 : * Get default data.
721 : *
722 : * Returns the default subfield data contents for this subfield definition.
723 : * For variable length numbers this will normally be "0<unit-terminator>".
724 : * For variable length strings it will be "<unit-terminator>". For fixed
725 : * length numbers it is zero filled. For fixed length strings it is space
726 : * filled. For binary numbers it is binary zero filled.
727 : *
728 : * @param pachData the buffer into which the returned default will be placed.
729 : * May be NULL if just querying default size.
730 : * @param nBytesAvailable the size of pachData in bytes.
731 : * @param pnBytesUsed will receive the size of the subfield default data in
732 : * bytes.
733 : *
734 : * @return TRUE on success or FALSE on failure or if the passed buffer is too
735 : * small to hold the default.
736 : */
737 :
738 6862 : int DDFSubfieldDefn::GetDefaultValue(char *pachData, int nBytesAvailable,
739 : int *pnBytesUsed) const
740 :
741 : {
742 : int nDefaultSize;
743 :
744 6862 : if (!bIsVariable)
745 6462 : nDefaultSize = nFormatWidth;
746 : else
747 400 : nDefaultSize = 1;
748 :
749 6862 : if (pnBytesUsed != nullptr)
750 6862 : *pnBytesUsed = nDefaultSize;
751 :
752 6862 : if (pachData == nullptr)
753 3431 : return TRUE;
754 :
755 3431 : if (nBytesAvailable < nDefaultSize)
756 0 : return FALSE;
757 :
758 3431 : if (bIsVariable)
759 : {
760 200 : pachData[0] = DDF_UNIT_TERMINATOR;
761 : }
762 : else
763 : {
764 : char chFillChar;
765 3231 : if (GetBinaryFormat() == NotBinary)
766 : {
767 57 : if (GetType() == DDFInt || GetType() == DDFFloat)
768 19 : chFillChar = '0'; /* ASCII zero intended */
769 : else
770 38 : chFillChar = ' ';
771 : }
772 : else
773 3174 : chFillChar = 0;
774 3231 : memset(pachData, chFillChar, nDefaultSize);
775 : }
776 :
777 3431 : return TRUE;
778 : }
779 :
780 : /************************************************************************/
781 : /* FormatStringValue() */
782 : /************************************************************************/
783 :
784 : /**
785 : * Format string subfield value.
786 : *
787 : * Returns a buffer with the passed in string value reformatted in a way
788 : * suitable for storage in a DDFField for this subfield.
789 : */
790 :
791 908 : int DDFSubfieldDefn::FormatStringValue(char *pachData, int nBytesAvailable,
792 : int *pnBytesUsed, const char *pszValue,
793 : int nValueLength) const
794 :
795 : {
796 : int nSize;
797 :
798 908 : if (nValueLength == -1)
799 438 : nValueLength = static_cast<int>(strlen(pszValue));
800 :
801 908 : if (bIsVariable)
802 : {
803 326 : nSize = nValueLength + 1;
804 : }
805 : else
806 : {
807 582 : nSize = nFormatWidth;
808 : }
809 :
810 908 : if (pnBytesUsed != nullptr)
811 454 : *pnBytesUsed = nSize;
812 :
813 908 : if (pachData == nullptr)
814 454 : return TRUE;
815 :
816 454 : if (nBytesAvailable < nSize)
817 0 : return FALSE;
818 :
819 454 : if (bIsVariable)
820 : {
821 163 : strncpy(pachData, pszValue, nSize - 1);
822 163 : pachData[nSize - 1] = DDF_UNIT_TERMINATOR;
823 : }
824 : else
825 : {
826 291 : if (GetBinaryFormat() == NotBinary)
827 : {
828 56 : memset(pachData, ' ', nSize);
829 : // cppcheck-suppress redundantCopy
830 56 : memcpy(pachData, pszValue, std::min(nValueLength, nSize));
831 : }
832 : else
833 : {
834 235 : memset(pachData, 0, nSize);
835 : // cppcheck-suppress redundantCopy
836 235 : memcpy(pachData, pszValue, std::min(nValueLength, nSize));
837 : }
838 : }
839 :
840 454 : return TRUE;
841 : }
842 :
843 : /************************************************************************/
844 : /* FormatIntValue() */
845 : /************************************************************************/
846 :
847 : /**
848 : * Format int subfield value.
849 : *
850 : * Returns a buffer with the passed in int value reformatted in a way
851 : * suitable for storage in a DDFField for this subfield.
852 : */
853 :
854 5360 : int DDFSubfieldDefn::FormatIntValue(char *pachData, int nBytesAvailable,
855 : int *pnBytesUsed, int nNewValue) const
856 :
857 : {
858 : int nSize;
859 : char szWork[30];
860 :
861 5360 : snprintf(szWork, sizeof(szWork), "%d", nNewValue);
862 :
863 5360 : if (bIsVariable)
864 : {
865 0 : nSize = static_cast<int>(strlen(szWork)) + 1;
866 : }
867 : else
868 : {
869 5360 : nSize = nFormatWidth;
870 :
871 5360 : if (GetBinaryFormat() == NotBinary &&
872 0 : static_cast<int>(strlen(szWork)) > nSize)
873 0 : return FALSE;
874 : }
875 :
876 5360 : if (pnBytesUsed != nullptr)
877 2680 : *pnBytesUsed = nSize;
878 :
879 5360 : if (pachData == nullptr)
880 2680 : return TRUE;
881 :
882 2680 : if (nBytesAvailable < nSize)
883 0 : return FALSE;
884 :
885 2680 : if (bIsVariable)
886 : {
887 0 : memcpy(pachData, szWork, nSize - 1);
888 0 : pachData[nSize - 1] = DDF_UNIT_TERMINATOR;
889 : }
890 : else
891 : {
892 2680 : GUInt32 nMask = 0xff;
893 :
894 2680 : switch (GetBinaryFormat())
895 : {
896 0 : case NotBinary:
897 : {
898 0 : constexpr char chFillChar = '0'; /* ASCII zero intended */
899 0 : const int nZeroFillCount =
900 0 : nSize - static_cast<int>(strlen(szWork));
901 0 : for (int i = 0; i < nZeroFillCount; ++i)
902 0 : pachData[i] = chFillChar;
903 0 : memcpy(pachData + nZeroFillCount, szWork, strlen(szWork));
904 0 : break;
905 : }
906 :
907 2680 : case UInt:
908 : case SInt:
909 7761 : for (int i = 0; i < nFormatWidth; i++)
910 : {
911 : int iOut;
912 :
913 : // big endian required?
914 5081 : if (osFormatString[0] == 'B')
915 0 : iOut = nFormatWidth - i - 1;
916 : else
917 5081 : iOut = i;
918 :
919 5081 : pachData[iOut] =
920 5081 : static_cast<char>((nNewValue & nMask) >> (i * 8));
921 5081 : nMask <<= 8;
922 : }
923 2680 : break;
924 :
925 0 : case FloatReal:
926 0 : CPLAssert(false);
927 : break;
928 :
929 0 : default:
930 0 : CPLAssert(false);
931 : break;
932 : }
933 : }
934 :
935 2680 : return TRUE;
936 : }
937 :
938 : /************************************************************************/
939 : /* FormatFloatValue() */
940 : /************************************************************************/
941 :
942 : /**
943 : * Format float subfield value.
944 : *
945 : * Returns a buffer with the passed in float value reformatted in a way
946 : * suitable for storage in a DDFField for this subfield.
947 : */
948 :
949 2 : int DDFSubfieldDefn::FormatFloatValue(char *pachData, int nBytesAvailable,
950 : int *pnBytesUsed, double dfNewValue) const
951 :
952 : {
953 : int nSize;
954 : char szWork[120];
955 :
956 2 : CPLsnprintf(szWork, sizeof(szWork), "%.16g", dfNewValue);
957 :
958 2 : if (bIsVariable)
959 : {
960 0 : nSize = static_cast<int>(strlen(szWork)) + 1;
961 : }
962 : else
963 : {
964 2 : nSize = nFormatWidth;
965 :
966 4 : if (GetBinaryFormat() == NotBinary &&
967 2 : static_cast<int>(strlen(szWork)) > nSize)
968 0 : return FALSE;
969 : }
970 :
971 2 : if (pnBytesUsed != nullptr)
972 1 : *pnBytesUsed = nSize;
973 :
974 2 : if (pachData == nullptr)
975 1 : return TRUE;
976 :
977 1 : if (nBytesAvailable < nSize)
978 0 : return FALSE;
979 :
980 1 : if (bIsVariable)
981 : {
982 0 : memcpy(pachData, szWork, nSize - 1);
983 0 : pachData[nSize - 1] = DDF_UNIT_TERMINATOR;
984 : }
985 : else
986 : {
987 1 : if (GetBinaryFormat() == NotBinary)
988 : {
989 1 : constexpr char chFillChar = '0'; /* ASCII zero intended */
990 1 : const int nZeroFillCount = nSize - static_cast<int>(strlen(szWork));
991 4 : for (int i = 0; i < nZeroFillCount; ++i)
992 3 : pachData[i] = chFillChar;
993 1 : memcpy(pachData + nZeroFillCount, szWork, strlen(szWork));
994 : }
995 : else
996 : {
997 0 : CPLAssert(false);
998 : /* implement me */
999 : }
1000 : }
1001 :
1002 1 : return TRUE;
1003 : }
|