1 /******************************************************************************
2  *
3  * Project:  ISO 8211 Access
4  * Purpose:  Implements the DDFSubfieldDefn class.
5  * Author:   Frank Warmerdam, warmerda@home.com
6  *
7  ******************************************************************************
8  * Copyright (c) 1999, Frank Warmerdam
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a
11  * copy of this software and associated documentation files (the "Software"),
12  * to deal in the Software without restriction, including without limitation
13  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14  * and/or sell copies of the Software, and to permit persons to whom the
15  * Software is furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included
18  * in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26  * DEALINGS IN THE SOFTWARE.
27  ******************************************************************************
28  *
29  * $Log: ddfsubfielddefn.cpp,v $
30  * Revision 1.1.1.1  2006/08/21 05:52:20  dsr
31  * Initial import as opencpn, GNU Automake compliant.
32  *
33  * Revision 1.1.1.1  2006/04/19 03:23:29  dsr
34  * Rename/Import to OpenCPN
35  *
36  * Revision 1.13  2004/01/06 19:07:14  warmerda
37  * Added braces within complex case in switch for HP/UX compatibility.
38  *
39  * Revision 1.12  2003/12/15 20:24:58  warmerda
40  * expand tabs
41  *
42  * Revision 1.11  2003/11/12 21:22:14  warmerda
43  * fixed some docs
44  *
45  * Revision 1.10  2003/09/05 19:13:45  warmerda
46  * added format support for binary ints
47  *
48  * Revision 1.9  2003/09/03 20:36:26  warmerda
49  * added subfield writing support
50  *
51  * Revision 1.8  2001/07/18 04:51:57  warmerda
52  *
53  * Revision 1.7  2000/09/19 14:09:34  warmerda
54  * avoid checking for field terminators in multi-byte strings
55  *
56  * Revision 1.6  2000/06/13 13:39:27  warmerda
57  * added warnings, and better handlng of short data for subfields
58  *
59  * Revision 1.5  1999/11/18 19:03:04  warmerda
60  * expanded tabs
61  *
62  * Revision 1.4  1999/05/10 17:36:23  warmerda
63  * Strip trailing spaces off subfield names.
64  *
65  * Revision 1.3  1999/05/06 15:15:07  warmerda
66  * Removed extra break;
67  *
68  * Revision 1.2  1999/05/06 14:25:43  warmerda
69  * added DDFBinaryString, and a bit of optimization
70  *
71  * Revision 1.1  1999/04/27 18:45:05  warmerda
72  * New
73  *
74  */
75 
76 #include <algorithm>
77 #include "gdal/cpl_conv.h"
78 #include "iso8211.h"
79 
80 /************************************************************************/
81 /*                          DDFSubfieldDefn()                           */
82 /************************************************************************/
83 
DDFSubfieldDefn()84 DDFSubfieldDefn::DDFSubfieldDefn()
85 
86 {
87     pszName = NULL;
88 
89     bIsVariable = TRUE;
90     nFormatWidth = 0;
91     chFormatDelimeter = DDF_UNIT_TERMINATOR;
92     eBinaryFormat = NotBinary;
93     eType = DDFString;
94 
95     pszFormatString = CPLStrdup("");
96 
97     nMaxBufChars = 0;
98     pachBuffer = NULL;
99 }
100 
101 /************************************************************************/
102 /*                          ~DDFSubfieldDefn()                          */
103 /************************************************************************/
104 
~DDFSubfieldDefn()105 DDFSubfieldDefn::~DDFSubfieldDefn()
106 
107 {
108     CPLFree( pszName );
109     CPLFree( pszFormatString );
110     CPLFree( pachBuffer );
111 }
112 
113 /************************************************************************/
114 /*                              SetName()                               */
115 /************************************************************************/
116 
SetName(const char * pszNewName)117 void DDFSubfieldDefn::SetName( const char * pszNewName )
118 
119 {
120     int         i;
121 
122     CPLFree( pszName );
123 
124     pszName = CPLStrdup( pszNewName );
125 
126     for( i = strlen(pszName)-1; i > 0 && pszName[i] == ' '; i-- )
127         pszName[i] = '\0';
128 }
129 
130 /************************************************************************/
131 /*                             SetFormat()                              */
132 /*                                                                      */
133 /*      While interpreting the format string we don't support:          */
134 /*                                                                      */
135 /*       o Passing an explicit terminator for variable length field.    */
136 /*       o 'X' for unused data ... this should really be filtered       */
137 /*         out by DDFFieldDefn::ApplyFormats(), but isn't.              */
138 /*       o 'B' bitstrings that aren't a multiple of eight.              */
139 /************************************************************************/
140 
SetFormat(const char * pszFormat)141 int DDFSubfieldDefn::SetFormat( const char * pszFormat )
142 
143 {
144     CPLFree( pszFormatString );
145     pszFormatString = CPLStrdup( pszFormat );
146 
147 /* -------------------------------------------------------------------- */
148 /*      These values will likely be used.                               */
149 /* -------------------------------------------------------------------- */
150     if( pszFormatString[1] == '(' )
151     {
152         nFormatWidth = atoi(pszFormatString+2);
153         bIsVariable = nFormatWidth == 0;
154     }
155     else
156         bIsVariable = TRUE;
157 
158 /* -------------------------------------------------------------------- */
159 /*      Interpret the format string.                                    */
160 /* -------------------------------------------------------------------- */
161     switch( pszFormatString[0] )
162     {
163       case 'A':
164       case 'C':         // It isn't clear to me how this is different than 'A'
165         eType = DDFString;
166         break;
167 
168       case 'R':
169         eType = DDFFloat;
170         break;
171 
172       case 'I':
173       case 'S':
174         eType = DDFInt;
175         break;
176 
177       case 'B':
178       case 'b':
179         // Is the width expressed in bits? (is it a bitstring)
180         bIsVariable = FALSE;
181         if( pszFormatString[1] == '(' )
182         {
183             CPLAssert( atoi(pszFormatString+2) % 8 == 0 );
184 
185             nFormatWidth = atoi(pszFormatString+2) / 8;
186             eBinaryFormat = SInt; // good default, works for SDTS.
187 
188             if( nFormatWidth < 5 )
189                 eType = DDFInt;
190             else
191                 eType = DDFBinaryString;
192         }
193 
194         // or do we have a binary type indicator? (is it binary)
195         else
196         {
197             eBinaryFormat = (DDFBinaryFormat) (pszFormatString[1] - '0');
198             nFormatWidth = atoi(pszFormatString+2);
199 
200             if( eBinaryFormat == SInt || eBinaryFormat == UInt )
201                 eType = DDFInt;
202             else
203                 eType = DDFFloat;
204         }
205         break;
206 
207       case 'X':
208         // 'X' is extra space, and shouldn't be directly assigned to a
209         // subfield ... I haven't encountered it in use yet though.
210         CPLError( CE_Failure, CPLE_AppDefined,
211                   "Format type of `%c' not supported.\n",
212                   pszFormatString[0] );
213 
214         CPLAssert( FALSE );
215         return FALSE;
216 
217       default:
218         CPLError( CE_Failure, CPLE_AppDefined,
219                   "Format type of `%c' not recognised.\n",
220                   pszFormatString[0] );
221 
222         CPLAssert( FALSE );
223         return FALSE;
224     }
225 
226     return TRUE;
227 }
228 
229 /************************************************************************/
230 /*                                Dump()                                */
231 /************************************************************************/
232 
233 /**
234  * Write out subfield definition info to debugging file.
235  *
236  * A variety of information about this field definition is written to the
237  * give debugging file handle.
238  *
239  * @param fp The standard io file handle to write to.  ie. stderr
240  */
241 
Dump(FILE * fp)242 void DDFSubfieldDefn::Dump( FILE * fp )
243 
244 {
245     fprintf( fp, "    DDFSubfieldDefn:\n" );
246     fprintf( fp, "        Label = `%s'\n", pszName );
247     fprintf( fp, "        FormatString = `%s'\n", pszFormatString );
248 }
249 
250 /************************************************************************/
251 /*                           GetDataLength()                            */
252 /*                                                                      */
253 /*      This method will scan for the end of a variable field.          */
254 /************************************************************************/
255 
256 /**
257  * Scan for the end of variable length data.  Given a pointer to the data
258  * for this subfield (from within a DDFRecord) this method will return the
259  * number of bytes which are data for this subfield.  The number of bytes
260  * consumed as part of this field can also be fetched.  This number may
261  * be one longer than the length if there is a terminator character
262  * used.<p>
263  *
264  * This method is mainly for internal use, or for applications which
265  * want the raw binary data to interpret themselves.  Otherwise use one
266  * of ExtractStringData(), ExtractIntData() or ExtractFloatData().
267  *
268  * @param pachSourceData The pointer to the raw data for this field.  This
269  * may have come from DDFRecord::GetData(), taking into account skip factors
270  * over previous subfields data.
271  * @param nMaxBytes The maximum number of bytes that are accessable after
272  * pachSourceData.
273  * @param pnConsumedBytes Pointer to an integer into which the number of
274  * bytes consumed by this field should be written.  May be NULL to ignore.
275  *
276  * @return The number of bytes at pachSourceData which are actual data for
277  * this record (not including unit, or field terminator).
278  */
279 
GetDataLength(const char * pachSourceData,int nMaxBytes,int * pnConsumedBytes)280 int DDFSubfieldDefn::GetDataLength( const char * pachSourceData,
281                                     int nMaxBytes, int * pnConsumedBytes )
282 
283 {
284     if( !bIsVariable )
285     {
286         if( nFormatWidth > nMaxBytes )
287         {
288             CPLError( CE_Warning, CPLE_AppDefined,
289                       "Only %d bytes available for subfield %s with\n"
290                       "format string %s ... returning shortened data.",
291                       nMaxBytes, pszName, pszFormatString );
292 
293             if( pnConsumedBytes != NULL )
294                 *pnConsumedBytes = nMaxBytes;
295 
296             return nMaxBytes;
297         }
298         else
299         {
300             if( pnConsumedBytes != NULL )
301                 *pnConsumedBytes = nFormatWidth;
302 
303             return nFormatWidth;
304         }
305     }
306 #if 0
307     else
308     {
309         int     nLength = 0;
310         int     bCheckFieldTerminator = TRUE;
311 
312         /* We only check for the field terminator because of some buggy
313          * datasets with missing format terminators.  However, we have found
314          * the field terminator is a legal character within the fields of
315          * some extended datasets (such as JP34NC94.000).  So we don't check
316          * for the field terminator if the field appears to be multi-byte
317          * which we established by the first character being out of the
318          * ASCII printable range (32-127).
319          */
320 
321         if( pachSourceData[0] < 32 || pachSourceData[0] >= 127 )
322             bCheckFieldTerminator = FALSE;
323 
324         while( nLength < nMaxBytes
325                && pachSourceData[nLength] != chFormatDelimeter )
326         {
327             if( bCheckFieldTerminator
328                 && pachSourceData[nLength] == DDF_FIELD_TERMINATOR )
329                 break;
330 
331             nLength++;
332         }
333 
334         if( pnConsumedBytes != NULL )
335         {
336             if( nMaxBytes == 0 )
337                 *pnConsumedBytes = nLength;
338             else
339                 *pnConsumedBytes = nLength+1;
340         }
341 
342         return nLength;
343     }
344 #else
345     // This improved UTF-16 detection code comes from GDAL V110
346     else
347     {
348         int     nLength = 0;
349         int     bAsciiField = TRUE;
350         int     extraConsumedBytes = 0;
351 
352         /* We only check for the field terminator because of some buggy
353          * datasets with missing format terminators.  However, we have found
354          * the field terminator and unit terminators are legal characters
355          * within the fields of some extended datasets (such as JP34NC94.000).
356          * So we don't check for the field terminator and unit terminators as
357          * a single byte if the field appears to be multi-byte which we
358          * establish by checking for the buffer ending with 0x1e 0x00 (a
359          * two byte field terminator).
360          *
361          * In the case of S57, the subfield ATVL of the NATF field can be
362          * encoded in lexical level 2 (see S57 specification, Edition 3.1,
363          * paragraph 2.4 and 2.5). In that case the Unit Terminator and Field
364          * Terminator are followed by the NULL character.
365          * A better fix would be to read the NALL tag in the DSSI to check
366          * that the lexical level is 2, instead of relying on the value of
367          * the first byte as we are doing - but that is not information
368          * that is available at the libiso8211 level (bug #1526)
369          */
370 
371         // If the whole field ends with 0x1e 0x00 then we assume this
372         // field is a double byte character set.
373         if( nMaxBytes > 1
374             && (pachSourceData[nMaxBytes-2] == chFormatDelimeter
375                 || pachSourceData[nMaxBytes-2] == DDF_FIELD_TERMINATOR)
376             && pachSourceData[nMaxBytes-1] == 0x00 )
377             bAsciiField = FALSE;
378 
379 //        if( !bAsciiField )
380 //            CPLDebug( "ISO8211", "Non-ASCII field detected." );
381 
382         while( nLength < nMaxBytes)
383         {
384             if (bAsciiField)
385             {
386                 if (pachSourceData[nLength] == chFormatDelimeter ||
387                     pachSourceData[nLength] == DDF_FIELD_TERMINATOR)
388                     break;
389             }
390             else
391             {
392                 if (nLength > 0
393                     && (pachSourceData[nLength-1] == chFormatDelimeter
394                         || pachSourceData[nLength-1] == DDF_FIELD_TERMINATOR)
395                     && pachSourceData[nLength] == 0)
396                 {
397                     // Suck up the field terminator if one follows
398                     // or else it will be interpreted as a new subfield.
399                     // This is a pretty ugly counter-intuitive hack!
400                     if (nLength+1 < nMaxBytes &&  pachSourceData[nLength+1] == DDF_FIELD_TERMINATOR)
401                         extraConsumedBytes++;
402                     if (nLength+2 < nMaxBytes &&  pachSourceData[nLength+2] == 0)
403                         extraConsumedBytes++;
404 
405 
406                     break;
407                 }
408             }
409 
410             nLength++;
411         }
412 
413         if( pnConsumedBytes != NULL )
414         {
415             if( nMaxBytes == 0 )
416                 *pnConsumedBytes = nLength + extraConsumedBytes;
417             else
418                 *pnConsumedBytes = nLength + extraConsumedBytes + 1;
419         }
420 
421         return nLength;
422     }
423 #endif
424 }
425 
426 /************************************************************************/
427 /*                         ExtractStringData()                          */
428 /************************************************************************/
429 
430 /**
431  * Extract a zero terminated string containing the data for this subfield.
432  * Given a pointer to the data
433  * for this subfield (from within a DDFRecord) this method will return the
434  * data for this subfield.  The number of bytes
435  * consumed as part of this field can also be fetched.  This number may
436  * be one longer than the string length if there is a terminator character
437  * used.<p>
438  *
439  * This function will return the raw binary data of a subfield for
440  * types other than DDFString, including data past zero chars.  This is
441  * the standard way of extracting DDFBinaryString subfields for instance.<p>
442  *
443  * @param pachSourceData The pointer to the raw data for this field.  This
444  * may have come from DDFRecord::GetData(), taking into account skip factors
445  * over previous subfields data.
446  * @param nMaxBytes The maximum number of bytes that are accessable after
447  * pachSourceData.
448  * @param pnConsumedBytes Pointer to an integer into which the number of
449  * bytes consumed by this field should be written.  May be NULL to ignore.
450  * This is used as a skip factor to increment pachSourceData to point to the
451  * next subfields data.
452  *
453  * @return A pointer to a buffer containing the data for this field.  The
454  * returned pointer is to an internal buffer which is invalidated on the
455  * next ExtractStringData() call on this DDFSubfieldDefn().  It should not
456  * be freed by the application.
457  *
458  * @see ExtractIntData(), ExtractFloatData()
459  */
460 
461 const char *
ExtractStringData(const char * pachSourceData,int nMaxBytes,int * pnConsumedBytes)462 DDFSubfieldDefn::ExtractStringData( const char * pachSourceData,
463                                     int nMaxBytes, int * pnConsumedBytes )
464 
465 {
466     int         nLength = GetDataLength( pachSourceData, nMaxBytes,
467                                          pnConsumedBytes );
468 
469 /* -------------------------------------------------------------------- */
470 /*      Do we need to grow the buffer.                                  */
471 /* -------------------------------------------------------------------- */
472     if( nMaxBufChars < nLength+1 )
473     {
474         CPLFree( pachBuffer );
475 
476         nMaxBufChars = nLength+1;
477         pachBuffer = (char *) CPLMalloc(nMaxBufChars);
478     }
479 
480 /* -------------------------------------------------------------------- */
481 /*      Copy the data to the buffer.  We use memcpy() so that it        */
482 /*      will work for binary data.                                      */
483 /* -------------------------------------------------------------------- */
484     memcpy( pachBuffer, pachSourceData, nLength );
485     pachBuffer[nLength] = '\0';
486 
487     return pachBuffer;
488 }
489 
490 /************************************************************************/
491 /*                          ExtractFloatData()                          */
492 /************************************************************************/
493 
494 /**
495  * Extract a subfield value as a float.  Given a pointer to the data
496  * for this subfield (from within a DDFRecord) this method will return the
497  * floating point data for this subfield.  The number of bytes
498  * consumed as part of this field can also be fetched.  This method may be
499  * called for any type of subfield, and will return zero if the subfield is
500  * not numeric.
501  *
502  * @param pachSourceData The pointer to the raw data for this field.  This
503  * may have come from DDFRecord::GetData(), taking into account skip factors
504  * over previous subfields data.
505  * @param nMaxBytes The maximum number of bytes that are accessable after
506  * pachSourceData.
507  * @param pnConsumedBytes Pointer to an integer into which the number of
508  * bytes consumed by this field should be written.  May be NULL to ignore.
509  * This is used as a skip factor to increment pachSourceData to point to the
510  * next subfields data.
511  *
512  * @return The subfield's numeric value (or zero if it isn't numeric).
513  *
514  * @see ExtractIntData(), ExtractStringData()
515  */
516 
517 double
ExtractFloatData(const char * pachSourceData,int nMaxBytes,int * pnConsumedBytes)518 DDFSubfieldDefn::ExtractFloatData( const char * pachSourceData,
519                                    int nMaxBytes, int * pnConsumedBytes )
520 
521 {
522     switch( pszFormatString[0] )
523     {
524       case 'A':
525       case 'I':
526       case 'R':
527       case 'S':
528       case 'C':
529         return atof(ExtractStringData(pachSourceData, nMaxBytes,
530                                       pnConsumedBytes));
531 
532       case 'B':
533       case 'b':
534       {
535           unsigned char   abyData[8] = {0,0,0,0,0,0,0,0};
536 
537           CPLAssert( nFormatWidth <= nMaxBytes );
538           if( pnConsumedBytes != NULL )
539               *pnConsumedBytes = nFormatWidth;
540 
541           // Byte swap the data if it isn't in machine native format.
542           // In any event we copy it into our buffer to ensure it is
543           // word aligned.
544 #ifdef CPL_LSB
545           if( pszFormatString[0] == 'B' )
546 #else
547               if( pszFormatString[0] == 'b' )
548 #endif
549               {
550                   for( int i = 0; i < nFormatWidth; i++ )
551                       abyData[nFormatWidth-i-1] = pachSourceData[i];
552               }
553               else
554               {
555                   memcpy( abyData, pachSourceData, nFormatWidth );
556               }
557 
558           // Interpret the bytes of data.
559           switch( eBinaryFormat )
560           {
561             case UInt:
562               if( nFormatWidth == 1 )
563                   return( abyData[0] );
564               else if( nFormatWidth == 2 )
565                   return( *((GUInt16 *) abyData) );
566               else if( nFormatWidth == 4 )
567                   return( *((GUInt32 *) abyData) );
568               else
569               {
570                   CPLAssert( FALSE );
571                   return 0.0;
572               }
573 
574             case SInt:
575               if( nFormatWidth == 1 )
576                   return( *((signed char *) abyData) );
577               else if( nFormatWidth == 2 )
578                   return( *((GInt16 *) abyData) );
579               else if( nFormatWidth == 4 )
580                   return( *((GInt32 *) abyData) );
581               else
582               {
583                   CPLAssert( FALSE );
584                   return 0.0;
585               }
586 
587             case FloatReal:
588               if( nFormatWidth == 4 )
589                   return( *((float *) abyData) );
590               else if( nFormatWidth == 8 )
591                   return( *((double *) abyData) );
592               else
593               {
594                   CPLAssert( FALSE );
595                   return 0.0;
596               }
597 
598             case NotBinary:
599             case FPReal:
600             case FloatComplex:
601               CPLAssert( FALSE );
602               return 0.0;
603           }
604           break;
605           // end of 'b'/'B' case.
606       }
607 
608       default:
609         CPLAssert( FALSE );
610         return 0.0;
611     }
612 
613     CPLAssert( FALSE );
614     return 0.0;
615 }
616 
617 /************************************************************************/
618 /*                           ExtractIntData()                           */
619 /************************************************************************/
620 
621 /**
622  * Extract a subfield value as an integer.  Given a pointer to the data
623  * for this subfield (from within a DDFRecord) this method will return the
624  * int data for this subfield.  The number of bytes
625  * consumed as part of this field can also be fetched.  This method may be
626  * called for any type of subfield, and will return zero if the subfield is
627  * not numeric.
628  *
629  * @param pachSourceData The pointer to the raw data for this field.  This
630  * may have come from DDFRecord::GetData(), taking into account skip factors
631  * over previous subfields data.
632  * @param nMaxBytes The maximum number of bytes that are accessable after
633  * pachSourceData.
634  * @param pnConsumedBytes Pointer to an integer into which the number of
635  * bytes consumed by this field should be written.  May be NULL to ignore.
636  * This is used as a skip factor to increment pachSourceData to point to the
637  * next subfields data.
638  *
639  * @return The subfield's numeric value (or zero if it isn't numeric).
640  *
641  * @see ExtractFloatData(), ExtractStringData()
642  */
643 
644 int
ExtractIntData(const char * pachSourceData,int nMaxBytes,int * pnConsumedBytes)645 DDFSubfieldDefn::ExtractIntData( const char * pachSourceData,
646                                  int nMaxBytes, int * pnConsumedBytes )
647 
648 {
649     switch( pszFormatString[0] )
650     {
651       case 'A':
652       case 'I':
653       case 'R':
654       case 'S':
655       case 'C':
656         return atoi(ExtractStringData(pachSourceData, nMaxBytes,
657                                       pnConsumedBytes));
658 
659       case 'B':
660       case 'b':
661       {
662           unsigned char   abyData[8] = {0,0,0,0,0,0,0,0};
663 
664           if( nFormatWidth > nMaxBytes )
665           {
666               CPLError( CE_Warning, CPLE_AppDefined,
667                         "Attempt to extract int subfield %s with format %s\n"
668                         "failed as only %d bytes available.  Using zero.",
669                         pszName, pszFormatString, nMaxBytes );
670               return 0;
671           }
672 
673           if( pnConsumedBytes != NULL )
674               *pnConsumedBytes = nFormatWidth;
675 
676           // Byte swap the data if it isn't in machine native format.
677           // In any event we copy it into our buffer to ensure it is
678           // word aligned.
679 #ifdef CPL_LSB
680           if( pszFormatString[0] == 'B' )
681 #else
682               if( pszFormatString[0] == 'b' )
683 #endif
684               {
685                   for( int i = 0; i < nFormatWidth; i++ )
686                       abyData[nFormatWidth-i-1] = pachSourceData[i];
687               }
688               else
689               {
690                   memcpy( abyData, pachSourceData, nFormatWidth );
691               }
692 
693           // Interpret the bytes of data.
694           switch( eBinaryFormat )
695           {
696             case UInt:
697               if( nFormatWidth == 4 )
698                   return( (int) *((GUInt32 *) abyData) );
699               else if( nFormatWidth == 1 )
700                   return( abyData[0] );
701               else if( nFormatWidth == 2 )
702                   return( *((GUInt16 *) abyData) );
703               else
704               {
705                   CPLAssert( FALSE );
706                   return 0;
707               }
708 
709             case SInt:
710               if( nFormatWidth == 4 )
711                   return( *((GInt32 *) abyData) );
712               else if( nFormatWidth == 1 )
713                   return( *((signed char *) abyData) );
714               else if( nFormatWidth == 2 )
715                   return( *((GInt16 *) abyData) );
716               else
717               {
718                   CPLAssert( FALSE );
719                   return 0;
720               }
721 
722             case FloatReal:
723               if( nFormatWidth == 4 )
724                   return( (int) *((float *) abyData) );
725               else if( nFormatWidth == 8 )
726                   return( (int) *((double *) abyData) );
727               else
728               {
729                   CPLAssert( FALSE );
730                   return 0;
731               }
732 
733             case NotBinary:
734             case FPReal:
735             case FloatComplex:
736               CPLAssert( FALSE );
737               return 0;
738           }
739           break;
740           // end of 'b'/'B' case.
741       }
742 
743       default:
744         CPLAssert( FALSE );
745         return 0;
746     }
747 
748     CPLAssert( FALSE );
749     return 0;
750 }
751 
752 /************************************************************************/
753 /*                              DumpData()                              */
754 /*                                                                      */
755 /*      Dump the instance data for this subfield from a data            */
756 /*      record.  This fits into the output dump stream of a DDFField.   */
757 /************************************************************************/
758 
759 /**
760  * Dump subfield value to debugging file.
761  *
762  * @param pachData Pointer to data for this subfield.
763  * @param nMaxBytes Maximum number of bytes available in pachData.
764  * @param fp File to write report to.
765  */
766 
DumpData(const char * pachData,int nMaxBytes,FILE * fp)767 void DDFSubfieldDefn::DumpData( const char * pachData, int nMaxBytes,
768                                 FILE * fp )
769 
770 {
771     if( eType == DDFFloat )
772         fprintf( fp, "      Subfield `%s' = %f\n",
773                  pszName,
774                  ExtractFloatData( pachData, nMaxBytes, NULL ) );
775     else if( eType == DDFInt )
776         fprintf( fp, "      Subfield `%s' = %d\n",
777                  pszName,
778                  ExtractIntData( pachData, nMaxBytes, NULL ) );
779     else if( eType == DDFBinaryString )
780     {
781         int     nBytes, i;
782         GByte   *pabyBString = (GByte *) ExtractStringData( pachData, nMaxBytes, &nBytes );
783 
784         fprintf( fp, "      Subfield `%s' = 0x", pszName );
785         for( i = 0; i < std::min(nBytes,24); i++ )
786             fprintf( fp, "%02X", pabyBString[i] );
787 
788         if( nBytes > 24 )
789             fprintf( fp, "%s", "..." );
790 
791         fprintf( fp, "\n" );
792     }
793     else
794         fprintf( fp, "      Subfield `%s' = `%s'\n",
795                  pszName,
796                  ExtractStringData( pachData, nMaxBytes, NULL ) );
797 }
798 
799 /************************************************************************/
800 /*                          GetDefaultValue()                           */
801 /************************************************************************/
802 
803 /**
804  * Get default data.
805  *
806  * Returns the default subfield data contents for this subfield definition.
807  * For variable length numbers this will normally be "0<unit-terminator>".
808  * For variable length strings it will be "<unit-terminator>".  For fixed
809  * length numbers it is zero filled.  For fixed length strings it is space
810  * filled.  For binary numbers it is binary zero filled.
811  *
812  * @param pachData the buffer into which the returned default will be placed.
813  * May be NULL if just querying default size.
814  * @param nBytesAvailable the size of pachData in bytes.
815  * @param pnBytesUsed will receive the size of the subfield default data in
816  * bytes.
817  *
818  * @return TRUE on success or FALSE on failure or if the passed buffer is too
819  * small to hold the default.
820  */
821 
GetDefaultValue(char * pachData,int nBytesAvailable,int * pnBytesUsed,bool b_isUTF16)822 int DDFSubfieldDefn::GetDefaultValue( char *pachData, int nBytesAvailable,
823                                       int *pnBytesUsed, bool b_isUTF16 )
824 
825 {
826     int nDefaultSize;
827 
828     if( !bIsVariable )
829         nDefaultSize = nFormatWidth;
830     else if(b_isUTF16 )
831         nDefaultSize = 2;
832     else
833         nDefaultSize = 1;
834 
835     if( pnBytesUsed != NULL )
836         *pnBytesUsed = nDefaultSize;
837 
838     if( pachData == NULL )
839         return TRUE;
840 
841     if( nBytesAvailable < nDefaultSize )
842         return FALSE;
843 
844     if( bIsVariable )
845     {
846         pachData[0] = DDF_UNIT_TERMINATOR;
847         if(b_isUTF16)
848             pachData[1] = 0;
849     }
850     else
851     {
852         if( GetBinaryFormat() == NotBinary )
853         {
854             if( GetType() == DDFInt || GetType() == DDFFloat )
855                 memset( pachData, '0', nDefaultSize );
856             else
857                 memset( pachData, ' ', nDefaultSize );
858         }
859         else
860             memset( pachData, 0, nDefaultSize );
861     }
862 
863     return TRUE;
864 }
865 
866 /************************************************************************/
867 /*                         FormatStringValue()                          */
868 /************************************************************************/
869 
870 /**
871  * Format string subfield value.
872  *
873  * Returns a buffer with the passed in string value reformatted in a way
874  * suitable for storage in a DDFField for this subfield.
875  */
876 
FormatStringValue(char * pachData,int nBytesAvailable,int * pnBytesUsed,const char * pszValue,int nValueLength)877 int DDFSubfieldDefn::FormatStringValue( char *pachData, int nBytesAvailable,
878                                         int *pnBytesUsed,
879                                         const char *pszValue,
880                                         int nValueLength )
881 
882 {
883     int nSize;
884 
885     if( nValueLength == -1 )
886         nValueLength = strlen(pszValue);
887 
888     if( bIsVariable )
889     {
890         nSize = nValueLength + 1;
891     }
892     else
893     {
894         nSize = nFormatWidth;
895     }
896 
897     if( pnBytesUsed != NULL )
898         *pnBytesUsed = nSize;
899 
900     if( pachData == NULL )
901         return TRUE;
902 
903     if( nBytesAvailable < nSize )
904         return FALSE;
905 
906     if( bIsVariable )
907     {
908         strncpy( pachData, pszValue, nSize-1 );
909         pachData[nSize-1] = DDF_UNIT_TERMINATOR;
910     }
911     else
912     {
913         if( GetBinaryFormat() == NotBinary )
914         {
915             memset( pachData, ' ', nSize );
916             memcpy( pachData, pszValue, MIN(nValueLength,nSize) );
917         }
918         else
919         {
920             memset( pachData, 0, nSize );
921             memcpy( pachData, pszValue, MIN(nValueLength,nSize) );
922         }
923     }
924 
925     return TRUE;
926 }
927 
928 /************************************************************************/
929 /*                           FormatIntValue()                           */
930 /************************************************************************/
931 
932 /**
933  * Format int subfield value.
934  *
935  * Returns a buffer with the passed in int value reformatted in a way
936  * suitable for storage in a DDFField for this subfield.
937  */
938 
FormatIntValue(char * pachData,int nBytesAvailable,int * pnBytesUsed,int nNewValue)939 int DDFSubfieldDefn::FormatIntValue( char *pachData, int nBytesAvailable,
940                                      int *pnBytesUsed, int nNewValue )
941 
942 {
943 #pragma GCC diagnostic push
944 #if defined(__GNUC__) && __GNUC__ >= 8
945 #pragma GCC diagnostic ignored "-Wstringop-truncation"
946 #pragma GCC diagnostic ignored "-Wstringop-overflow"
947 #endif
948 
949     int nSize;
950     char szWork[30];
951 
952     sprintf( szWork, "%d", nNewValue );
953 
954     if( bIsVariable )
955     {
956         nSize = strlen(szWork) + 1;
957     }
958     else
959     {
960         nSize = nFormatWidth;
961 
962         if( GetBinaryFormat() == NotBinary && (int) strlen(szWork) > nSize )
963             return FALSE;
964     }
965 
966     if( pnBytesUsed != NULL )
967         *pnBytesUsed = nSize;
968 
969     if( pachData == NULL )
970         return TRUE;
971 
972     if( nBytesAvailable < nSize )
973         return FALSE;
974 
975     if( bIsVariable )
976     {
977         strncpy( pachData, szWork, nSize-1 );
978         pachData[nSize-1] = DDF_UNIT_TERMINATOR;
979     }
980     else
981     {
982         GUInt32 nMask = 0xff;
983         int i;
984 
985         switch( GetBinaryFormat() )
986         {
987           case NotBinary:
988             memset( pachData, '0', nSize );
989             strncpy( pachData + nSize - strlen(szWork), szWork,
990                      strlen(szWork) );
991             break;
992 
993           case UInt:
994           case SInt:
995             for( i = 0; i < nFormatWidth; i++ )
996             {
997                 int iOut;
998 
999                 // big endian required?
1000                 if( pszFormatString[0] == 'B' )
1001                     iOut = nFormatWidth - i - 1;
1002                 else
1003                     iOut = i;
1004 
1005                 pachData[iOut] = (nNewValue & nMask) >> (i*8);
1006                 nMask *= 256;
1007             }
1008             break;
1009 
1010           case FloatReal:
1011             CPLAssert( FALSE );
1012             break;
1013 
1014           default:
1015             CPLAssert( FALSE );
1016             break;
1017         }
1018     }
1019 
1020     return TRUE;
1021 #pragma GCC diagnostic pop
1022 }
1023 
1024 /************************************************************************/
1025 /*                          FormatFloatValue()                          */
1026 /************************************************************************/
1027 
1028 /**
1029  * Format float subfield value.
1030  *
1031  * Returns a buffer with the passed in float value reformatted in a way
1032  * suitable for storage in a DDFField for this subfield.
1033  */
1034 
FormatFloatValue(char * pachData,int nBytesAvailable,int * pnBytesUsed,double dfNewValue)1035 int DDFSubfieldDefn::FormatFloatValue( char *pachData, int nBytesAvailable,
1036                                        int *pnBytesUsed, double dfNewValue )
1037 
1038 {
1039 #pragma GCC diagnostic push
1040 #if defined(__GNUC__) && __GNUC__ >= 8
1041 #pragma GCC diagnostic ignored "-Wstringop-truncation"
1042 #pragma GCC diagnostic ignored "-Wstringop-overflow"
1043 #endif
1044     int nSize;
1045     char szWork[120];
1046 
1047     sprintf( szWork, "%.16g", dfNewValue );
1048 
1049     if( bIsVariable )
1050     {
1051         nSize = strlen(szWork) + 1;
1052     }
1053     else
1054     {
1055         nSize = nFormatWidth;
1056 
1057         if( GetBinaryFormat() == NotBinary && (int) strlen(szWork) > nSize )
1058             return FALSE;
1059     }
1060 
1061     if( pnBytesUsed != NULL )
1062         *pnBytesUsed = nSize;
1063 
1064     if( pachData == NULL )
1065         return TRUE;
1066 
1067     if( nBytesAvailable < nSize )
1068         return FALSE;
1069 
1070     if( bIsVariable )
1071     {
1072         strncpy( pachData, szWork, nSize-1 );
1073         pachData[nSize-1] = DDF_UNIT_TERMINATOR;
1074     }
1075     else
1076     {
1077         if( GetBinaryFormat() == NotBinary )
1078         {
1079             memset( pachData, '0', nSize );
1080             strncpy( pachData + nSize - strlen(szWork), szWork,
1081                      strlen(szWork) );
1082         }
1083         else
1084         {
1085             CPLAssert( FALSE );
1086             /* implement me */
1087         }
1088     }
1089 
1090     return TRUE;
1091 #pragma GCC diagnostic pop
1092 }
1093