1 /******************************************************************************
2  *
3  * Project:  ISO 8211 Access
4  * Purpose:  Implements the DDFField 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: ddffield.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:28  dsr
34  * Rename/Import to OpenCPN
35  *
36  * Revision 1.16  2003/07/03 15:38:46  warmerda
37  * some write capabilities added
38  *
39  * Revision 1.15  2001/08/30 21:08:19  warmerda
40  * expand tabs
41  *
42  * Revision 1.14  2001/08/30 02:14:50  warmerda
43  * Provide for control of max number of field instances dumped via environment.
44  *
45  * Revision 1.13  2001/08/27 19:09:00  warmerda
46  * added GetInstanceData() method on DDFField
47  *
48  * Revision 1.12  2001/07/18 04:51:57  warmerda
49  *
50  * Revision 1.11  2000/09/19 14:09:11  warmerda
51  * fixed dump of binary info
52  *
53  * Revision 1.10  2000/06/13 13:38:39  warmerda
54  * Improved reporting of binary data in Dump method.
55  * Fixed GetRepeatCount() so that short field data can be properly detected.
56  *
57  * Revision 1.9  1999/11/18 19:03:04  warmerda
58  * expanded tabs
59  *
60  * Revision 1.8  1999/11/03 14:04:57  warmerda
61  * Made use of GetSubfieldData() with repeated fixed length
62  * fields much more efficient.
63  *
64  * Revision 1.7  1999/09/21 16:25:32  warmerda
65  * Fixed bug with repeating variable length fields and running out of data.
66  *
67  * Revision 1.6  1999/06/23 02:14:08  warmerda
68  * added support for variable width repeaters to GetRepeatCount()
69  *
70  * Revision 1.5  1999/06/01 19:10:38  warmerda
71  * don't assert on variable length repeating fields
72  *
73  * Revision 1.4  1999/05/07 14:10:49  warmerda
74  * added maxbytes to getsubfielddata()
75  *
76  * Revision 1.3  1999/05/06 15:39:26  warmerda
77  * avoid printing non-ASCII characters
78  *
79  * Revision 1.2  1999/04/27 22:09:50  warmerda
80  * updated docs
81  *
82  * Revision 1.1  1999/04/27 18:45:05  warmerda
83  * New
84  *
85  */
86 #include <algorithm>
87 #include "iso8211.h"
88 #include "gdal/cpl_conv.h"
89 
90 // Note, we implement no constructor for this class to make instantiation
91 // cheaper.  It is required that the Initialize() be called before anything
92 // else.
93 
94 /************************************************************************/
95 /*                             Initialize()                             */
96 /************************************************************************/
97 
Initialize(DDFFieldDefn * poDefnIn,const char * pachDataIn,int nDataSizeIn)98 void DDFField::Initialize( DDFFieldDefn *poDefnIn, const char * pachDataIn,
99                            int nDataSizeIn )
100 
101 {
102     pachData = pachDataIn;
103     nDataSize = nDataSizeIn;
104     poDefn = poDefnIn;
105 }
106 
107 /************************************************************************/
108 /*                                Dump()                                */
109 /************************************************************************/
110 
111 /**
112  * Write out field contents to debugging file.
113  *
114  * A variety of information about this field, and all it's
115  * subfields is written to the given debugging file handle.  Note that
116  * field definition information (ala DDFFieldDefn) isn't written.
117  *
118  * @param fp The standard io file handle to write to.  ie. stderr
119  */
120 
Dump(FILE * fp)121 void DDFField::Dump( FILE * fp )
122 
123 {
124     int         nMaxRepeat = 8;
125 
126     if( getenv("DDF_MAXDUMP") != NULL )
127         nMaxRepeat = atoi(getenv("DDF_MAXDUMP"));
128 
129     fprintf( fp, "  DDFField:\n" );
130     fprintf( fp, "      Tag = `%s'\n", poDefn->GetName() );
131     fprintf( fp, "      DataSize = %d\n", nDataSize );
132 
133     fprintf( fp, "      Data = \n" );
134 
135     int il = 0;
136     for( int i = 0; i < std::min(nDataSize,1000); i++ )
137     {
138         //if( pachData[i] < 32 || pachData[i] > 126 )
139             fprintf( fp, "\\%02X", ((unsigned char *) pachData)[i] );
140         //else
141             //fprintf( fp, "%c", pachData[i] );
142             il ++;
143             if( il == 16){
144                 fprintf( fp, "\n" );
145                 il = 0;
146             }
147 
148     }
149 
150     if( nDataSize > 1000 )
151         fprintf( fp, "..." );
152     fprintf( fp, "'\n" );
153 
154 /* -------------------------------------------------------------------- */
155 /*      dump the data of the subfields.                                 */
156 /* -------------------------------------------------------------------- */
157     int         iOffset = 0, nLoopCount;
158 
159     for( nLoopCount = 0; nLoopCount < GetRepeatCount(); nLoopCount++ )
160     {
161         if( nLoopCount > nMaxRepeat )
162         {
163             fprintf( fp, "      ...\n" );
164             break;
165         }
166 
167         for( int i = 0; i < poDefn->GetSubfieldCount(); i++ )
168         {
169             int         nBytesConsumed;
170 
171             poDefn->GetSubfield(i)->DumpData( pachData + iOffset,
172                                               nDataSize - iOffset, fp );
173 
174             poDefn->GetSubfield(i)->GetDataLength( pachData + iOffset,
175                                                    nDataSize - iOffset,
176                                                    &nBytesConsumed );
177 
178             iOffset += nBytesConsumed;
179         }
180     }
181 }
182 
183 /************************************************************************/
184 /*                          GetSubfieldData()                           */
185 /************************************************************************/
186 
187 /**
188  * Fetch raw data pointer for a particular subfield of this field.
189  *
190  * The passed DDFSubfieldDefn (poSFDefn) should be acquired from the
191  * DDFFieldDefn corresponding with this field.  This is normally done
192  * once before reading any records.  This method involves a series of
193  * calls to DDFSubfield::GetDataLength() in order to track through the
194  * DDFField data to that belonging to the requested subfield.  This can
195  * be relatively expensive.<p>
196  *
197  * @param poSFDefn The definition of the subfield for which the raw
198  * data pointer is desired.
199  * @param pnMaxBytes The maximum number of bytes that can be accessed from
200  * the returned data pointer is placed in this int, unless it is NULL.
201  * @param iSubfieldIndex The instance of this subfield to fetch.  Use zero
202  * (the default) for the first instance.
203  *
204  * @return A pointer into the DDFField's data that belongs to the subfield.
205  * This returned pointer is invalidated by the next record read
206  * (DDFRecord::ReadRecord()) and the returned pointer should not be freed
207  * by the application.
208  */
209 
GetSubfieldData(DDFSubfieldDefn * poSFDefn,int * pnMaxBytes,int iSubfieldIndex)210 const char *DDFField::GetSubfieldData( DDFSubfieldDefn *poSFDefn,
211                                        int *pnMaxBytes, int iSubfieldIndex )
212 
213 {
214     int         iOffset = 0;
215 
216     if( poSFDefn == NULL )
217         return NULL;
218 
219     if( iSubfieldIndex > 0 && poDefn->GetFixedWidth() > 0 )
220     {
221         iOffset = poDefn->GetFixedWidth() * iSubfieldIndex;
222         iSubfieldIndex = 0;
223     }
224 
225     while( iSubfieldIndex >= 0 )
226     {
227         for( int iSF = 0; iSF < poDefn->GetSubfieldCount(); iSF++ )
228         {
229             int nBytesConsumed;
230             DDFSubfieldDefn * poThisSFDefn = poDefn->GetSubfield( iSF );
231 
232             if( poThisSFDefn == poSFDefn && iSubfieldIndex == 0 )
233             {
234                 if( pnMaxBytes != NULL )
235                     *pnMaxBytes = nDataSize - iOffset;
236 
237                 return pachData + iOffset;
238             }
239 
240             poThisSFDefn->GetDataLength( pachData+iOffset, nDataSize - iOffset,
241                                          &nBytesConsumed);
242             iOffset += nBytesConsumed;
243         }
244 
245         iSubfieldIndex--;
246     }
247 
248     // We didn't find our target subfield or instance!
249     return NULL;
250 }
251 
252 /************************************************************************/
253 /*                           GetRepeatCount()                           */
254 /************************************************************************/
255 
256 /**
257  * How many times do the subfields of this record repeat?  This
258  * will always be one for non-repeating fields.
259  *
260  * @return The number of times that the subfields of this record occur
261  * in this record.  This will be one for non-repeating fields.
262  *
263  * @see <a href="example.html">8211view example program</a>
264  * for demonstation of handling repeated fields properly.
265  */
266 
GetRepeatCount()267 int DDFField::GetRepeatCount()
268 
269 {
270     if( !poDefn->IsRepeating() )
271         return 1;
272 
273 /* -------------------------------------------------------------------- */
274 /*      The occurance count depends on how many copies of this          */
275 /*      field's list of subfields can fit into the data space.          */
276 /* -------------------------------------------------------------------- */
277     if( poDefn->GetFixedWidth() )
278     {
279         return nDataSize / poDefn->GetFixedWidth();
280     }
281 
282 /* -------------------------------------------------------------------- */
283 /*      Note that it may be legal to have repeating variable width      */
284 /*      subfields, but I don't have any samples, so I ignore it for     */
285 /*      now.                                                            */
286 /*                                                                      */
287 /*      The file data/cape_royal_AZ_DEM/1183XREF.DDF has a repeating    */
288 /*      variable length field, but the count is one, so it isn't        */
289 /*      much value for testing.                                         */
290 /* -------------------------------------------------------------------- */
291     int         iOffset = 0, iRepeatCount = 1;
292 
293     while( TRUE )
294     {
295         for( int iSF = 0; iSF < poDefn->GetSubfieldCount(); iSF++ )
296         {
297             int nBytesConsumed;
298             DDFSubfieldDefn * poThisSFDefn = poDefn->GetSubfield( iSF );
299 
300             if( poThisSFDefn->GetWidth() > nDataSize - iOffset )
301                 nBytesConsumed = poThisSFDefn->GetWidth();
302             else
303                 poThisSFDefn->GetDataLength( pachData+iOffset,
304                                              nDataSize - iOffset,
305                                              &nBytesConsumed);
306 
307             iOffset += nBytesConsumed;
308             if( iOffset > nDataSize )
309                 return iRepeatCount - 1;
310         }
311 
312         if( iOffset > nDataSize - 2 )
313             return iRepeatCount;
314 
315         iRepeatCount++;
316     }
317 }
318 
319 /************************************************************************/
320 /*                          GetInstanceData()                           */
321 /************************************************************************/
322 
323 /**
324  * Get field instance data and size.
325  *
326  * The returned data pointer and size values are suitable for use with
327  * DDFRecord::SetFieldRaw().
328  *
329  * @param nInstance a value from 0 to GetRepeatCount()-1.
330  * @param pnInstanceSize a location to put the size (in bytes) of the
331  * field instance data returned.  This size will include the unit terminator
332  * (if any), but not the field terminator.  This size pointer may be NULL
333  * if not needed.
334  *
335  * @return the data pointer, or NULL on error.
336  */
337 
GetInstanceData(int nInstance,int * pnInstanceSize)338 const char *DDFField::GetInstanceData( int nInstance,
339                                        int *pnInstanceSize )
340 
341 {
342     int nRepeatCount = GetRepeatCount();
343     const char *pachWrkData;
344 
345     if( nInstance < 0 || nInstance >= nRepeatCount )
346         return NULL;
347 
348 /* -------------------------------------------------------------------- */
349 /*      Special case for fields without subfields (like "0001").  We    */
350 /*      don't currently handle repeating simple fields.                 */
351 /* -------------------------------------------------------------------- */
352     if( poDefn->GetSubfieldCount() == 0 )
353     {
354         pachWrkData = GetData();
355         if( pnInstanceSize != 0 )
356             *pnInstanceSize = GetDataSize();
357         return pachWrkData;
358     }
359 
360 /* -------------------------------------------------------------------- */
361 /*      Get a pointer to the start of the existing data for this        */
362 /*      iteration of the field.                                         */
363 /* -------------------------------------------------------------------- */
364     int         nBytesRemaining1 = 0, nBytesRemaining2 = 0;
365     DDFSubfieldDefn *poFirstSubfield;
366 
367     poFirstSubfield = poDefn->GetSubfield(0);
368 
369     pachWrkData = GetSubfieldData(poFirstSubfield, &nBytesRemaining1,
370                                nInstance);
371 
372 /* -------------------------------------------------------------------- */
373 /*      Figure out the size of the entire field instance, including     */
374 /*      unit terminators, but not any trailing field terminator.        */
375 /* -------------------------------------------------------------------- */
376     if( pnInstanceSize != NULL )
377     {
378         DDFSubfieldDefn *poLastSubfield;
379         int              nLastSubfieldWidth;
380         const char          *pachLastData;
381 
382         poLastSubfield = poDefn->GetSubfield(poDefn->GetSubfieldCount()-1);
383 
384         pachLastData = GetSubfieldData( poLastSubfield, &nBytesRemaining2,
385                                         nInstance );
386         poLastSubfield->GetDataLength( pachLastData, nBytesRemaining2,
387                                        &nLastSubfieldWidth );
388 
389         if((pachLastData[nLastSubfieldWidth-1] == 0) && (pachLastData[nLastSubfieldWidth - 2] == DDF_FIELD_TERMINATOR))
390             nLastSubfieldWidth -= 2;
391 
392         *pnInstanceSize =
393             nBytesRemaining1 - (nBytesRemaining2 - nLastSubfieldWidth);
394     }
395 
396     return pachWrkData;
397 }
398