root/trunk/gdal/frmts/iso8211/ddfrecord.cpp

Revision 15632, 71.8 kB (checked in by warmerdam, 4 weeks ago)

corrections to handle double byte attributes better (#1526)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1 /******************************************************************************
2  * $Id$
3  *
4  * Project:  ISO 8211 Access
5  * Purpose:  Implements the DDFRecord class.
6  * Author:   Frank Warmerdam, warmerdam@pobox.com
7  *
8  ******************************************************************************
9  * Copyright (c) 1999, Frank Warmerdam
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a
12  * copy of this software and associated documentation files (the "Software"),
13  * to deal in the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15  * and/or sell copies of the Software, and to permit persons to whom the
16  * Software is furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included
19  * in all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27  * DEALINGS IN THE SOFTWARE.
28  ****************************************************************************/
29
30 #include "iso8211.h"
31 #include "cpl_conv.h"
32
33 CPL_CVSID("$Id$");
34
35 static const size_t nLeaderSize = 24;
36
37 /************************************************************************/
38 /*                             DDFRecord()                              */
39 /************************************************************************/
40
41 DDFRecord::DDFRecord( DDFModule * poModuleIn )
42
43 {
44     poModule = poModuleIn;
45
46     nReuseHeader = FALSE;
47
48     nFieldOffset = 0;
49
50     nDataSize = 0;
51     pachData = NULL;
52
53     nFieldCount = 0;
54     paoFields = NULL;
55
56     bIsClone = FALSE;
57
58     _sizeFieldTag = 4;
59     _sizeFieldPos = 0;
60     _sizeFieldLength = 0;
61 }
62
63 /************************************************************************/
64 /*                             ~DDFRecord()                             */
65 /************************************************************************/
66
67 DDFRecord::~DDFRecord()
68
69 {
70     Clear();
71
72     if( bIsClone )
73         poModule->RemoveCloneRecord( this );
74 }
75
76 /************************************************************************/
77 /*                                Dump()                                */
78 /************************************************************************/
79
80 /**
81  * Write out record contents to debugging file.
82  *
83  * A variety of information about this record, and all it's fields and
84  * subfields is written to the given debugging file handle.  Note that
85  * field definition information (ala DDFFieldDefn) isn't written.
86  *
87  * @param fp The standard io file handle to write to.  ie. stderr
88  */
89
90 void DDFRecord::Dump( FILE * fp )
91
92 {
93     fprintf( fp, "DDFRecord:\n" );
94     fprintf( fp, "    nReuseHeader = %d\n", nReuseHeader );
95     fprintf( fp, "    nDataSize = %d\n", nDataSize );
96     fprintf( fp,
97              "    _sizeFieldLength=%d, _sizeFieldPos=%d, _sizeFieldTag=%d\n",
98              _sizeFieldLength, _sizeFieldPos, _sizeFieldTag );
99
100     for( int i = 0; i < nFieldCount; i++ )
101     {
102         paoFields[i].Dump( fp );
103     }
104 }
105
106 /************************************************************************/
107 /*                                Read()                                */
108 /*                                                                      */
109 /*      Read a record of data from the file, and parse the header to    */
110 /*      build a field list for the record (or reuse the existing one    */
111 /*      if reusing headers).  It is expected that the file pointer      */
112 /*      will be positioned at the beginning of a data record.  It is    */
113 /*      the DDFModule's responsibility to do so.                        */
114 /*                                                                      */
115 /*      This method should only be called by the DDFModule class.       */
116 /************************************************************************/
117
118 int DDFRecord::Read()
119
120 {
121 /* -------------------------------------------------------------------- */
122 /*      Redefine the record on the basis of the header if needed.       */
123 /*      As a side effect this will read the data for the record as well.*/
124 /* -------------------------------------------------------------------- */
125     if( !nReuseHeader )
126     {
127         return( ReadHeader() );
128     }
129
130 /* -------------------------------------------------------------------- */
131 /*      Otherwise we read just the data and carefully overlay it on     */
132 /*      the previous records data without disturbing the rest of the    */
133 /*      record.                                                         */
134 /* -------------------------------------------------------------------- */
135     size_t      nReadBytes;
136
137     nReadBytes = VSIFReadL( pachData + nFieldOffset, 1,
138                             nDataSize - nFieldOffset,
139                             poModule->GetFP() );
140     if( nReadBytes != (size_t) (nDataSize - nFieldOffset)
141         && nReadBytes == 0
142         && VSIFEofL( poModule->GetFP() ) )
143     {
144         return FALSE;
145     }
146     else if( nReadBytes != (size_t) (nDataSize - nFieldOffset) )
147     {
148         CPLError( CE_Failure, CPLE_FileIO,
149                   "Data record is short on DDF file.\n" );
150        
151         return FALSE;
152     }
153
154     // notdef: eventually we may have to do something at this point to
155     // notify the DDFField's that their data values have changed.
156     
157     return TRUE;
158 }
159
160 /************************************************************************/
161 /*                               Write()                                */
162 /************************************************************************/
163
164 /**
165  * Write record out to module.
166  *
167  * This method writes the current record to the module to which it is
168  * attached.  Normally this would be at the end of the file, and only used
169  * for modules newly created with DDFModule::Create().  Rewriting existing
170  * records is not supported at this time.  Calling Write() multiple times
171  * on a DDFRecord will result it multiple copies being written at the end of
172  * the module.
173  *
174  * @return TRUE on success or FALSE on failure.
175  */
176
177 int DDFRecord::Write()
178
179 {
180     if( !ResetDirectory() )
181         return FALSE;
182    
183 /* -------------------------------------------------------------------- */
184 /*      Prepare leader.                                                 */
185 /* -------------------------------------------------------------------- */
186     char szLeader[nLeaderSize+1];
187
188     memset( szLeader, ' ', nLeaderSize );
189
190     sprintf( szLeader+0, "%05d", (int) (nDataSize + nLeaderSize) );
191     szLeader[5] = ' ';
192     szLeader[6] = 'D';
193    
194     sprintf( szLeader + 12, "%05d", (int) (nFieldOffset + nLeaderSize) );
195     szLeader[17] = ' ';
196
197     szLeader[20] = (char) ('0' + _sizeFieldLength);
198     szLeader[21] = (char) ('0' + _sizeFieldPos);
199     szLeader[22] = '0';
200     szLeader[23] = (char) ('0' + _sizeFieldTag);
201
202     /* notdef: lots of stuff missing */
203
204 /* -------------------------------------------------------------------- */
205 /*      Write the leader.                                               */
206 /* -------------------------------------------------------------------- */
207     VSIFWriteL( szLeader, nLeaderSize, 1, poModule->GetFP() );
208
209 /* -------------------------------------------------------------------- */
210 /*      Write the remainder of the record.                              */
211 /* -------------------------------------------------------------------- */
212     VSIFWriteL( pachData, nDataSize, 1, poModule->GetFP() );
213    
214     return TRUE;
215 }
216
217 /************************************************************************/
218 /*                               Clear()                                */
219 /*                                                                      */
220 /*      Clear any information associated with the last header in        */
221 /*      preparation for reading a new header.                           */
222 /************************************************************************/
223
224 void DDFRecord::Clear()
225
226 {
227     if( paoFields != NULL )
228         delete[] paoFields;
229
230     paoFields = NULL;
231     nFieldCount = 0;
232
233     if( pachData != NULL )
234         CPLFree( pachData );
235
236     pachData = NULL;
237     nDataSize = 0;
238     nReuseHeader = FALSE;
239 }
240
241 /************************************************************************/
242 /*                             ReadHeader()                             */
243 /*                                                                      */
244 /*      This perform the header reading and parsing job for the         */
245 /*      Read() method.  It reads the header, and builds a field         */
246 /*      list.                                                           */
247 /************************************************************************/
248
249 int DDFRecord::ReadHeader()
250
251 {
252 /* -------------------------------------------------------------------- */
253 /*      Clear any existing information.                                 */
254 /* -------------------------------------------------------------------- */
255     Clear();
256    
257 /* -------------------------------------------------------------------- */
258 /*      Read the 24 byte leader.                                        */
259 /* -------------------------------------------------------------------- */
260     char        achLeader[nLeaderSize];
261     int         nReadBytes;
262
263     nReadBytes = VSIFReadL(achLeader,1,nLeaderSize,poModule->GetFP());
264     if( nReadBytes == 0 && VSIFEofL( poModule->GetFP() ) )
265     {
266         return FALSE;
267     }
268     else if( nReadBytes != (int) nLeaderSize )
269     {
270         CPLError( CE_Failure, CPLE_FileIO,
271                   "Leader is short on DDF file." );
272        
273         return FALSE;
274     }
275
276 /* -------------------------------------------------------------------- */
277 /*      Extract information from leader.                                */
278 /* -------------------------------------------------------------------- */
279     int         _recLength, _fieldAreaStart;
280     char        _leaderIden;
281    
282     _recLength                    = DDFScanInt( achLeader+0, 5 );
283     _leaderIden                   = achLeader[6];
284     _fieldAreaStart               = DDFScanInt(achLeader+12,5);
285    
286     _sizeFieldLength = achLeader[20] - '0';
287     _sizeFieldPos = achLeader[21] - '0';
288     _sizeFieldTag = achLeader[23] - '0';
289
290     if( _sizeFieldLength < 0 || _sizeFieldLength > 9
291         || _sizeFieldPos < 0 || _sizeFieldPos > 9
292         || _sizeFieldTag < 0 || _sizeFieldTag > 9 )
293     {
294         CPLError( CE_Failure, CPLE_AppDefined,
295                   "ISO8211 record leader appears to be corrupt." );
296         return FALSE;
297     }
298
299     if( _leaderIden == 'R' )
300         nReuseHeader = TRUE;
301
302     nFieldOffset = _fieldAreaStart - nLeaderSize;
303
304 /* -------------------------------------------------------------------- */
305 /*      Is there anything seemly screwy about this record?              */
306 /* -------------------------------------------------------------------- */
307     if(( _recLength < 24 || _recLength > 100000000
308          || _fieldAreaStart < 24 || _fieldAreaStart > 100000 )
309        && (_recLength != 0))
310     {
311         CPLError( CE_Failure, CPLE_FileIO,
312                   "Data record appears to be corrupt on DDF file.\n"
313                   " -- ensure that the files were uncompressed without modifying\n"
314                   "carriage return/linefeeds (by default WINZIP does this)." );
315        
316         return FALSE;
317     }
318
319 /* ==================================================================== */
320 /*      Handle the normal case with the record length available.        */
321 /* ==================================================================== */
322     if(_recLength != 0) {
323 /* -------------------------------------------------------------------- */
324 /*      Read the remainder of the record.                               */
325 /* -------------------------------------------------------------------- */
326         nDataSize = _recLength - nLeaderSize;
327         pachData = (char *) CPLMalloc(nDataSize);
328
329         if( VSIFReadL( pachData, 1, nDataSize, poModule->GetFP()) !=
330             (size_t) nDataSize )
331         {
332             CPLError( CE_Failure, CPLE_FileIO,
333                       "Data record is short on DDF file." );
334          
335             return FALSE;
336         }
337
338 /* -------------------------------------------------------------------- */
339 /*      If we don't find a field terminator at the end of the record    */
340 /*      we will read extra bytes till we get to it.                     */
341 /* -------------------------------------------------------------------- */
342         while( pachData[nDataSize-1] != DDF_FIELD_TERMINATOR
343                && (nDataSize == 0 || pachData[nDataSize-2] != DDF_FIELD_TERMINATOR) )
344         {
345             nDataSize++;
346             pachData = (char *) CPLRealloc(pachData,nDataSize);
347            
348             if( VSIFReadL( pachData + nDataSize - 1, 1, 1, poModule->GetFP() )
349                 != 1 )
350             {
351                 CPLError( CE_Failure, CPLE_FileIO,
352                           "Data record is short on DDF file." );
353                
354                 return FALSE;
355             }
356             CPLDebug( "ISO8211",
357                       "Didn't find field terminator, read one more byte." );
358         }
359
360 /* -------------------------------------------------------------------- */
361 /*      Loop over the directory entries, making a pass counting them.   */
362 /* -------------------------------------------------------------------- */
363         int         i;
364         int         nFieldEntryWidth;
365      
366         nFieldEntryWidth = _sizeFieldLength + _sizeFieldPos + _sizeFieldTag;
367         nFieldCount = 0;
368         for( i = 0; i < nDataSize; i += nFieldEntryWidth )
369         {
370             if( pachData[i] == DDF_FIELD_TERMINATOR )
371                 break;
372          
373             nFieldCount++;
374         }
375    
376 /* -------------------------------------------------------------------- */
377 /*      Allocate, and read field definitions.                           */
378 /* -------------------------------------------------------------------- */
379         paoFields = new DDFField[nFieldCount];
380    
381         for( i = 0; i < nFieldCount; i++ )
382         {
383             char    szTag[128];
384             int     nEntryOffset = i*nFieldEntryWidth;
385             int     nFieldLength, nFieldPos;
386          
387 /* -------------------------------------------------------------------- */
388 /*      Read the position information and tag.                          */
389 /* -------------------------------------------------------------------- */
390             strncpy( szTag, pachData+nEntryOffset, _sizeFieldTag );
391             szTag[_sizeFieldTag] = '\0';
392          
393             nEntryOffset += _sizeFieldTag;
394             nFieldLength = DDFScanInt( pachData+nEntryOffset, _sizeFieldLength );
395          
396             nEntryOffset += _sizeFieldLength;
397             nFieldPos = DDFScanInt( pachData+nEntryOffset, _sizeFieldPos );
398          
399 /* -------------------------------------------------------------------- */
400 /*      Find the corresponding field in the module directory.           */
401 /* -------------------------------------------------------------------- */
402             DDFFieldDefn    *poFieldDefn = poModule->FindFieldDefn( szTag );
403          
404             if( poFieldDefn == NULL )
405             {
406                 CPLError( CE_Failure, CPLE_AppDefined,
407                           "Undefined field `%s' encountered in data record.",
408                           szTag );
409                 return FALSE;
410             }
411
412 /* -------------------------------------------------------------------- */
413 /*      Assign info the DDFField.                                       */
414 /* -------------------------------------------------------------------- */
415             paoFields[i].Initialize( poFieldDefn,
416                                      pachData + _fieldAreaStart + nFieldPos - nLeaderSize,
417                                      nFieldLength );
418         }
419      
420         return TRUE;
421     }
422 /* ==================================================================== */
423 /*      Handle the exceptional case where the record length is          */
424 /*      zero.  In this case we have to read all the data based on       */
425 /*      the size of data items as per ISO8211 spec Annex C, 1.5.1.      */
426 /*                                                                      */
427 /*      See Bugzilla bug 181 and test with file US4CN21M.000.           */
428 /* ==================================================================== */
429     else {
430         CPLDebug( "ISO8211",
431                   "Record with zero length, use variant (C.1.5.1) logic." );
432
433         /* ----------------------------------------------------------------- */
434         /*   _recLength == 0, handle the large record.                       */
435         /*                                                                   */
436         /*   Read the remainder of the record.                               */
437         /* ----------------------------------------------------------------- */
438         nDataSize = 0;
439         pachData = NULL;
440
441         /* ----------------------------------------------------------------- */
442         /*   Loop over the directory entries, making a pass counting them.   */
443         /* ----------------------------------------------------------------- */
444         int nFieldEntryWidth = _sizeFieldLength + _sizeFieldPos + _sizeFieldTag;
445         nFieldCount = 0;
446         int i=0;
447         char *tmpBuf = (char*)VSIMalloc(nFieldEntryWidth);
448
449         if( tmpBuf == NULL )
450         {
451             CPLError( CE_Failure, CPLE_OutOfMemory,
452                       "Attempt to allocate %d byte ISO8211 record buffer failed.",
453                       nFieldEntryWidth );
454             return FALSE;
455         }
456      
457         // while we're not at the end, store this entry,
458         // and keep on reading...
459         do {
460             // read an Entry:
461             if(nFieldEntryWidth !=
462                (int) VSIFReadL(tmpBuf, 1, nFieldEntryWidth, poModule->GetFP())) {
463                 CPLError(CE_Failure, CPLE_FileIO,
464                          "Data record is short on DDF file.");
465                 return FALSE;
466             }
467      
468             // move this temp buffer into more permanent storage:
469             char *newBuf = (char*)CPLMalloc(nDataSize+nFieldEntryWidth);
470             if(pachData!=NULL) {
471                 memcpy(newBuf, pachData, nDataSize);
472                 CPLFree(pachData);
473             }
474             memcpy(&newBuf[nDataSize], tmpBuf, nFieldEntryWidth);
475             pachData = newBuf;
476             nDataSize += nFieldEntryWidth;
477
478             if(DDF_FIELD_TERMINATOR != tmpBuf[0]) {
479                 nFieldCount++;
480             }
481         }
482         while(DDF_FIELD_TERMINATOR != tmpBuf[0]);
483
484         // --------------------------------------------------------------------
485         // Now, rewind a little.  Only the TERMINATOR should have been read
486         // --------------------------------------------------------------------
487         int rewindSize = nFieldEntryWidth - 1;
488         FILE *fp = poModule->GetFP();
489         vsi_l_offset pos = VSIFTellL(fp) - rewindSize;
490         VSIFSeekL(fp, pos, SEEK_SET);
491         nDataSize -= rewindSize;
492
493         // --------------------------------------------------------------------
494         // Okay, now let's populate the heck out of pachData...
495         // --------------------------------------------------------------------
496         for(i=0; i<nFieldCount; i++) {
497             int nEntryOffset = (i*nFieldEntryWidth) + _sizeFieldTag;
498             int nFieldLength = DDFScanInt(pachData + nEntryOffset,
499                                           _sizeFieldLength);
500             char *tmpBuf = (char*)CPLMalloc(nFieldLength);
501
502             // read an Entry:
503             if(nFieldLength !=
504                (int) VSIFReadL(tmpBuf, 1, nFieldLength, poModule->GetFP())) {
505                 CPLError(CE_Failure, CPLE_FileIO,
506                          "Data record is short on DDF file.");
507                 return FALSE;
508             }
509      
510             // move this temp buffer into more permanent storage:
511             char *newBuf = (char*)CPLMalloc(nDataSize+nFieldLength);
512             memcpy(newBuf, pachData, nDataSize);
513             CPLFree(pachData);
514             memcpy(&newBuf[nDataSize], tmpBuf, nFieldLength);
515             CPLFree(tmpBuf);
516             pachData = newBuf;
517             nDataSize += nFieldLength;
518         }
519    
520         /* ----------------------------------------------------------------- */
521         /*     Allocate, and read field definitions.                         */
522         /* ----------------------------------------------------------------- */
523         paoFields = new DDFField[nFieldCount];
524      
525         for( i = 0; i < nFieldCount; i++ )
526         {
527             char    szTag[128];
528             int     nEntryOffset = i*nFieldEntryWidth;
529             int     nFieldLength, nFieldPos;
530          
531             /* ------------------------------------------------------------- */
532             /* Read the position information and tag.                        */
533             /* ------------------------------------------------------------- */
534             strncpy( szTag, pachData+nEntryOffset, _sizeFieldTag );
535             szTag[_sizeFieldTag] = '\0';
536          
537             nEntryOffset += _sizeFieldTag;
538             nFieldLength = DDFScanInt( pachData+nEntryOffset, _sizeFieldLength );
539          
540             nEntryOffset += _sizeFieldLength;
541             nFieldPos = DDFScanInt( pachData+nEntryOffset, _sizeFieldPos );
542          
543             /* ------------------------------------------------------------- */
544             /* Find the corresponding field in the module directory.         */
545             /* ------------------------------------------------------------- */
546             DDFFieldDefn    *poFieldDefn = poModule->FindFieldDefn( szTag );
547          
548             if( poFieldDefn == NULL )
549             {
550                 CPLError( CE_Failure, CPLE_AppDefined,
551                           "Undefined field `%s' encountered in data record.",
552                           szTag );
553                 return FALSE;
554             }
555
556             /* ------------------------------------------------------------- */
557             /* Assign info the DDFField.                                     */
558             /* ------------------------------------------------------------- */
559
560             paoFields[i].Initialize( poFieldDefn,
561                                      pachData + _fieldAreaStart
562                                      + nFieldPos - nLeaderSize,
563                                      nFieldLength );
564         }
565      
566         return TRUE;
567     }
568 }
569
570 /************************************************************************/
571 /*                             FindField()                              */
572 /************************************************************************/
573
574 /**
575  * Find the named field within this record.
576  *
577  * @param pszName The name of the field to fetch.  The comparison is
578  * case insensitive.
579  * @param iFieldIndex The instance of this field to fetch.  Use zero (the
580  * default) for the first instance.
581  *
582  * @return Pointer to the requested DDFField.  This pointer is to an
583  * internal object, and should not be freed.  It remains valid until
584  * the next record read.
585  */
586
587 DDFField * DDFRecord::FindField( const char * pszName, int iFieldIndex )
588
589 {
590     for( int i = 0; i < nFieldCount; i++ )
591     {
592         if( EQUAL(paoFields[i].GetFieldDefn()->GetName(),pszName) )
593         {
594             if( iFieldIndex == 0 )
595                 return paoFields + i;
596             else
597                 iFieldIndex--;
598         }
599     }
600
601     return NULL;
602 }
603
604 /************************************************************************/
605 /*                              GetField()                              */
606 /************************************************************************/
607
608 /**
609  * Fetch field object based on index.
610  *
611  * @param i The index of the field to fetch.  Between 0 and GetFieldCount()-1.
612  *
613  * @return A DDFField pointer, or NULL if the index is out of range.
614  */
615
616 DDFField *DDFRecord::GetField( int i )
617
618 {
619     if( i < 0 || i >= nFieldCount )
620         return NULL;
621     else
622         return paoFields + i;
623 }
624
625 /************************************************************************/
626 /*                           GetIntSubfield()                           */
627 /************************************************************************/
628
629 /**
630  * Fetch value of a subfield as an integer.  This is a convenience
631  * function for fetching a subfield of a field within this record.
632  *
633  * @param pszField The name of the field containing the subfield.
634  * @param iFieldIndex The instance of this field within the record.  Use
635  * zero for the first instance of this field.
636  * @param pszSubfield The name of the subfield within the selected field.
637  * @param iSubfieldIndex The instance of this subfield within the record.
638  * Use zero for the first instance.
639  * @param pnSuccess Pointer to an int which will be set to TRUE if the fetch
640  * succeeds, or FALSE if it fails.  Use NULL if you don't want to check
641  * success.
642  * @return The value of the subfield, or zero if it failed for some reason.
643  */
644
645 int DDFRecord::GetIntSubfield( const char * pszField, int iFieldIndex,
646                                const char * pszSubfield, int iSubfieldIndex,
647                                int * pnSuccess )
648
649 {
650     DDFField    *poField;
651     int         nDummyErr;
652
653     if( pnSuccess == NULL )
654         pnSuccess = &nDummyErr;
655
656     *pnSuccess = FALSE;
657            
658 /* -------------------------------------------------------------------- */
659 /*      Fetch the field. If this fails, return zero.                    */
660 /* -------------------------------------------------------------------- */
661     poField = FindField( pszField, iFieldIndex );
662     if( poField == NULL )
663         return 0;
664
665 /* -------------------------------------------------------------------- */
666 /*      Get the subfield definition                                     */
667 /* -------------------------------------------------------------------- */
668     DDFSubfieldDefn     *poSFDefn;
669
670     poSFDefn = poField->GetFieldDefn()->FindSubfieldDefn( pszSubfield );
671     if( poSFDefn == NULL )
672         return 0;
673
674 /* -------------------------------------------------------------------- */
675 /*      Get a pointer to the data.                                      */
676 /* -------------------------------------------------------------------- */
677     int         nBytesRemaining;
678    
679     const char *pachData = poField->GetSubfieldData(poSFDefn,
680                                                     &nBytesRemaining,
681                                                     iSubfieldIndex);
682
683 /* -------------------------------------------------------------------- */
684 /*      Return the extracted value.                                     */
685 /* -------------------------------------------------------------------- */
686     *pnSuccess = TRUE;
687
688     return( poSFDefn->ExtractIntData( pachData, nBytesRemaining, NULL ) );
689 }
690
691 /************************************************************************/
692 /*                          GetFloatSubfield()                          */
693 /************************************************************************/
694
695 /**
696  * Fetch value of a subfield as a float (double).  This is a convenience
697  * function for fetching a subfield of a field within this record.
698  *
699  * @param pszField The name of the field containing the subfield.
700  * @param iFieldIndex The instance of this field within the record.  Use
701  * zero for the first instance of this field.
702  * @param pszSubfield The name of the subfield within the selected field.
703  * @param iSubfieldIndex The instance of this subfield within the record.
704  * Use zero for the first instance.
705  * @param pnSuccess Pointer to an int which will be set to TRUE if the fetch
706  * succeeds, or FALSE if it fails.  Use NULL if you don't want to check
707  * success.
708  * @return The value of the subfield, or zero if it failed for some reason.
709  */
710
711 double DDFRecord::GetFloatSubfield( const char * pszField, int iFieldIndex,
712                                  const char * pszSubfield, int iSubfieldIndex,
713                                     int * pnSuccess )
714
715 {
716     DDFField    *poField;
717     int         nDummyErr;
718
719     if( pnSuccess == NULL )
720         pnSuccess = &nDummyErr;
721
722     *pnSuccess = FALSE;
723            
724 /* -------------------------------------------------------------------- */
725 /*      Fetch the field. If this fails, return zero.                    */
726 /* -------------------------------------------------------------------- */
727     poField = FindField( pszField, iFieldIndex );
728     if( poField == NULL )
729         return 0;
730
731 /* -------------------------------------------------------------------- */
732 /*      Get the subfield definition                                     */
733 /* -------------------------------------------------------------------- */
734     DDFSubfieldDefn     *poSFDefn;
735
736     poSFDefn = poField->GetFieldDefn()->FindSubfieldDefn( pszSubfield );
737     if( poSFDefn == NULL )
738         return 0;
739
740 /* -------------------------------------------------------------------- */
741 /*      Get a pointer to the data.                                      */
742 /* -------------------------------------------------------------------- */
743     int         nBytesRemaining;
744    
745     const char *pachData = poField->GetSubfieldData(poSFDefn,
746                                                     &nBytesRemaining,
747                                                     iSubfieldIndex);
748
749 /* -------------------------------------------------------------------- */
750 /*      Return the extracted value.                                     */
751 /* -------------------------------------------------------------------- */
752     *pnSuccess = TRUE;
753
754     return( poSFDefn->ExtractFloatData( pachData, nBytesRemaining, NULL ) );
755 }
756
757 /************************************************************************/
758 /*                         GetStringSubfield()                          */
759 /************************************************************************/
760
761 /**
762  * Fetch value of a subfield as a string.  This is a convenience
763  * function for fetching a subfield of a field within this record.
764  *
765  * @param pszField The name of the field containing the subfield.
766  * @param iFieldIndex The instance of this field within the record.  Use
767  * zero for the first instance of this field.
768  * @param pszSubfield The name of the subfield within the selected field.
769  * @param iSubfieldIndex The instance of this subfield within the record.
770  * Use zero for the first instance.
771  * @param pnSuccess Pointer to an int which will be set to TRUE if the fetch
772  * succeeds, or FALSE if it fails.  Use NULL if you don't want to check
773  * success.
774  * @return The value of the subfield, or NULL if it failed for some reason.
775  * The returned pointer is to internal data and should not be modified or
776  * freed by the application.
777  */
778
779 const char *
780 DDFRecord::GetStringSubfield( const char * pszField, int iFieldIndex,
781                               const char * pszSubfield, int iSubfieldIndex,
782                               int * pnSuccess )
783
784 {
785     DDFField    *poField;
786     int         nDummyErr;
787
788     if( pnSuccess == NULL )
789         pnSuccess = &nDummyErr;
790
791     *pnSuccess = FALSE;
792            
793 /* -------------------------------------------------------------------- */
794 /*      Fetch the field. If this fails, return zero.                    */
795 /* -------------------------------------------------------------------- */
796     poField = FindField( pszField, iFieldIndex );
797     if( poField == NULL )
798         return NULL;
799
800 /* -------------------------------------------------------------------- */
801 /*      Get the subfield definition                                     */
802 /* -------------------------------------------------------------------- */
803     DDFSubfieldDefn     *poSFDefn;
804
805     poSFDefn = poField->GetFieldDefn()->FindSubfieldDefn( pszSubfield );
806     if( poSFDefn == NULL )
807         return NULL;
808
809 /* -------------------------------------------------------------------- */
810 /*      Get a pointer to the data.                                      */
811 /* -------------------------------------------------------------------- */
812     int         nBytesRemaining;
813    
814     const char *pachData = poField->GetSubfieldData(poSFDefn,
815                                                     &nBytesRemaining,
816                                                     iSubfieldIndex);
817
818 /* -------------------------------------------------------------------- */
819 /*      Return the extracted value.                                     */
820 /* -------------------------------------------------------------------- */
821     *pnSuccess = TRUE;
822
823     return( poSFDefn->ExtractStringData( pachData, nBytesRemaining, NULL ) );
824 }
825
826 /************************************************************************/
827 /*                               Clone()                                */
828 /************************************************************************/
829
830 /**
831  * Make a copy of a record.
832  *
833  * This method is used to make a copy of a record that will become (mostly)
834  * the properly of application.  However, it is automatically destroyed if
835  * the DDFModule it was created relative to is destroyed, as it's field
836  * and subfield definitions relate to that DDFModule.  However, it does
837  * persist even when the record returned by DDFModule::ReadRecord() is
838  * invalidated, such as when reading a new record.  This allows an application
839  * to cache whole DDFRecords.
840  *
841  * @return A new copy of the DDFRecord.  This can be delete'd by the
842  * application when no longer needed, otherwise it will be cleaned up when