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