Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: ISO 8211 Access
4 : * Purpose: Implements the DDFField class.
5 : * Author: Frank Warmerdam, warmerdam@pobox.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 : #include "cpl_port.h"
30 : #include "iso8211.h"
31 :
32 : #include <cstdio>
33 : #include <cstdlib>
34 :
35 : #include <algorithm>
36 :
37 : #include "cpl_conv.h"
38 :
39 : // Note, we implement no constructor for this class to make instantiation
40 : // cheaper. It is required that the Initialize() be called before anything
41 : // else.
42 :
43 : /************************************************************************/
44 : /* Initialize() */
45 : /************************************************************************/
46 :
47 17691 : void DDFField::Initialize(DDFFieldDefn *poDefnIn, const char *pachDataIn,
48 : int nDataSizeIn)
49 :
50 : {
51 17691 : pachData = pachDataIn;
52 17691 : nDataSize = nDataSizeIn;
53 17691 : poDefn = poDefnIn;
54 17691 : }
55 :
56 : /************************************************************************/
57 : /* Dump() */
58 : /************************************************************************/
59 :
60 : /**
61 : * Write out field contents to debugging file.
62 : *
63 : * A variety of information about this field, and all its
64 : * subfields is written to the given debugging file handle. Note that
65 : * field definition information (ala DDFFieldDefn) isn't written.
66 : *
67 : * @param fp The standard IO file handle to write to. i.e. stderr
68 : */
69 :
70 0 : void DDFField::Dump(FILE *fp)
71 :
72 : {
73 0 : int nMaxRepeat = 8;
74 :
75 0 : const char *pszDDF_MAXDUMP = getenv("DDF_MAXDUMP");
76 0 : if (pszDDF_MAXDUMP != nullptr)
77 0 : nMaxRepeat = atoi(pszDDF_MAXDUMP);
78 :
79 0 : fprintf(fp, " DDFField:\n");
80 0 : fprintf(fp, " Tag = `%s'\n", poDefn->GetName());
81 0 : fprintf(fp, " DataSize = %d\n", nDataSize);
82 :
83 0 : fprintf(fp, " Data = `");
84 0 : for (int i = 0; i < std::min(nDataSize, 40); i++)
85 : {
86 0 : if (pachData[i] < 32 || pachData[i] > 126)
87 0 : fprintf(fp, "\\%02X", ((unsigned char *)pachData)[i]);
88 : else
89 0 : fprintf(fp, "%c", pachData[i]);
90 : }
91 :
92 0 : if (nDataSize > 40)
93 0 : fprintf(fp, "...");
94 0 : fprintf(fp, "'\n");
95 :
96 : /* -------------------------------------------------------------------- */
97 : /* dump the data of the subfields. */
98 : /* -------------------------------------------------------------------- */
99 0 : int iOffset = 0;
100 :
101 0 : for (int nLoopCount = 0; nLoopCount < GetRepeatCount(); nLoopCount++)
102 : {
103 0 : if (nLoopCount > nMaxRepeat)
104 : {
105 0 : fprintf(fp, " ...\n");
106 0 : break;
107 : }
108 :
109 0 : for (int i = 0; i < poDefn->GetSubfieldCount(); i++)
110 : {
111 0 : poDefn->GetSubfield(i)->DumpData(pachData + iOffset,
112 0 : nDataSize - iOffset, fp);
113 :
114 0 : int nBytesConsumed = 0;
115 0 : poDefn->GetSubfield(i)->GetDataLength(
116 0 : pachData + iOffset, nDataSize - iOffset, &nBytesConsumed);
117 :
118 0 : iOffset += nBytesConsumed;
119 : }
120 : }
121 0 : }
122 :
123 : /************************************************************************/
124 : /* GetSubfieldData() */
125 : /************************************************************************/
126 :
127 : /**
128 : * Fetch raw data pointer for a particular subfield of this field.
129 : *
130 : * The passed DDFSubfieldDefn (poSFDefn) should be acquired from the
131 : * DDFFieldDefn corresponding with this field. This is normally done
132 : * once before reading any records. This method involves a series of
133 : * calls to DDFSubfield::GetDataLength() in order to track through the
134 : * DDFField data to that belonging to the requested subfield. This can
135 : * be relatively expensive.<p>
136 : *
137 : * @param poSFDefn The definition of the subfield for which the raw
138 : * data pointer is desired.
139 : * @param pnMaxBytes The maximum number of bytes that can be accessed from
140 : * the returned data pointer is placed in this int, unless it is NULL.
141 : * @param iSubfieldIndex The instance of this subfield to fetch. Use zero
142 : * (the default) for the first instance.
143 : *
144 : * @return A pointer into the DDFField's data that belongs to the subfield.
145 : * This returned pointer is invalidated by the next record read
146 : * (DDFRecord::ReadRecord()) and the returned pointer should not be freed
147 : * by the application.
148 : */
149 :
150 46375 : const char *DDFField::GetSubfieldData(DDFSubfieldDefn *poSFDefn,
151 : int *pnMaxBytes, int iSubfieldIndex)
152 :
153 : {
154 46375 : if (poSFDefn == nullptr)
155 0 : return nullptr;
156 :
157 46375 : int iOffset = 0;
158 46375 : if (iSubfieldIndex > 0 && poDefn->GetFixedWidth() > 0)
159 : {
160 7932 : iOffset = poDefn->GetFixedWidth() * iSubfieldIndex;
161 7932 : iSubfieldIndex = 0;
162 : }
163 :
164 47453 : while (iSubfieldIndex >= 0)
165 : {
166 133528 : for (int iSF = 0; iSF < poDefn->GetSubfieldCount(); iSF++)
167 : {
168 132450 : DDFSubfieldDefn *poThisSFDefn = poDefn->GetSubfield(iSF);
169 :
170 132450 : if (nDataSize <= iOffset)
171 : {
172 0 : CPLError(CE_Failure, CPLE_AppDefined,
173 : "Invalid data size for subfield %s of %s",
174 0 : poThisSFDefn->GetName(), poDefn->GetName());
175 46375 : return nullptr;
176 : }
177 :
178 132450 : if (poThisSFDefn == poSFDefn && iSubfieldIndex == 0)
179 : {
180 46375 : if (pnMaxBytes != nullptr)
181 46357 : *pnMaxBytes = nDataSize - iOffset;
182 :
183 46375 : return pachData + iOffset;
184 : }
185 :
186 86075 : int nBytesConsumed = 0;
187 86075 : poThisSFDefn->GetDataLength(pachData + iOffset, nDataSize - iOffset,
188 : &nBytesConsumed);
189 86075 : iOffset += nBytesConsumed;
190 : }
191 :
192 1078 : iSubfieldIndex--;
193 : }
194 :
195 : // We didn't find our target subfield or instance!
196 0 : return nullptr;
197 : }
198 :
199 : /************************************************************************/
200 : /* GetRepeatCount() */
201 : /************************************************************************/
202 :
203 : /**
204 : * How many times do the subfields of this record repeat? This
205 : * will always be one for non-repeating fields.
206 : *
207 : * @return The number of times that the subfields of this record occur
208 : * in this record. This will be one for non-repeating fields.
209 : *
210 : * @see <a href="example.html">8211view example program</a>
211 : * for a demonstration of handling repeated fields properly.
212 : */
213 :
214 6907 : int DDFField::GetRepeatCount()
215 :
216 : {
217 6907 : if (!poDefn->IsRepeating())
218 566 : return 1;
219 :
220 : /* -------------------------------------------------------------------- */
221 : /* The occurrence count depends on how many copies of this */
222 : /* field's list of subfields can fit into the data space. */
223 : /* -------------------------------------------------------------------- */
224 6341 : if (poDefn->GetFixedWidth())
225 : {
226 5183 : return nDataSize / poDefn->GetFixedWidth();
227 : }
228 :
229 : /* -------------------------------------------------------------------- */
230 : /* Note that it may be legal to have repeating variable width */
231 : /* subfields, but I don't have any samples, so I ignore it for */
232 : /* now. */
233 : /* */
234 : /* The file data/cape_royal_AZ_DEM/1183XREF.DDF has a repeating */
235 : /* variable length field, but the count is one, so it isn't */
236 : /* much value for testing. */
237 : /* -------------------------------------------------------------------- */
238 1158 : int iOffset = 0;
239 1158 : int iRepeatCount = 1;
240 :
241 : while (true)
242 : {
243 1439 : const int iOffsetBefore = iOffset;
244 4581 : for (int iSF = 0; iSF < poDefn->GetSubfieldCount(); iSF++)
245 : {
246 3305 : DDFSubfieldDefn *poThisSFDefn = poDefn->GetSubfield(iSF);
247 :
248 3305 : int nBytesConsumed = 0;
249 3305 : if (poThisSFDefn->GetWidth() > nDataSize - iOffset)
250 163 : nBytesConsumed = poThisSFDefn->GetWidth();
251 : else
252 3142 : poThisSFDefn->GetDataLength(
253 3142 : pachData + iOffset, nDataSize - iOffset, &nBytesConsumed);
254 :
255 3305 : iOffset += nBytesConsumed;
256 3305 : if (iOffset > nDataSize)
257 163 : return iRepeatCount - 1;
258 : }
259 1276 : if (iOffset == iOffsetBefore)
260 : {
261 : // Should probably emit error
262 0 : return iRepeatCount - 1;
263 : }
264 :
265 1276 : if (iOffset > nDataSize - 2)
266 995 : return iRepeatCount;
267 :
268 281 : iRepeatCount++;
269 281 : }
270 : }
271 :
272 : /************************************************************************/
273 : /* GetInstanceData() */
274 : /************************************************************************/
275 :
276 : /**
277 : * Get field instance data and size.
278 : *
279 : * The returned data pointer and size values are suitable for use with
280 : * DDFRecord::SetFieldRaw().
281 : *
282 : * @param nInstance a value from 0 to GetRepeatCount()-1.
283 : * @param pnInstanceSize a location to put the size (in bytes) of the
284 : * field instance data returned. This size will include the unit terminator
285 : * (if any), but not the field terminator. This size pointer may be NULL
286 : * if not needed.
287 : *
288 : * @return the data pointer, or NULL on error.
289 : */
290 :
291 257 : const char *DDFField::GetInstanceData(int nInstance, int *pnInstanceSize)
292 :
293 : {
294 257 : int nRepeatCount = GetRepeatCount();
295 :
296 257 : if (nInstance < 0 || nInstance >= nRepeatCount)
297 0 : return nullptr;
298 :
299 : /* -------------------------------------------------------------------- */
300 : /* Special case for fields without subfields (like "0001"). We */
301 : /* don't currently handle repeating simple fields. */
302 : /* -------------------------------------------------------------------- */
303 257 : if (poDefn->GetSubfieldCount() == 0)
304 : {
305 0 : const char *pachWrkData = GetData();
306 0 : if (pnInstanceSize != nullptr)
307 0 : *pnInstanceSize = GetDataSize();
308 0 : return pachWrkData;
309 : }
310 :
311 : /* -------------------------------------------------------------------- */
312 : /* Get a pointer to the start of the existing data for this */
313 : /* iteration of the field. */
314 : /* -------------------------------------------------------------------- */
315 257 : int nBytesRemaining1 = 0;
316 257 : int nBytesRemaining2 = 0;
317 257 : DDFSubfieldDefn *poFirstSubfield = poDefn->GetSubfield(0);
318 :
319 : const char *pachWrkData =
320 257 : GetSubfieldData(poFirstSubfield, &nBytesRemaining1, nInstance);
321 257 : if (pachWrkData == nullptr)
322 0 : return nullptr;
323 :
324 : /* -------------------------------------------------------------------- */
325 : /* Figure out the size of the entire field instance, including */
326 : /* unit terminators, but not any trailing field terminator. */
327 : /* -------------------------------------------------------------------- */
328 257 : if (pnInstanceSize != nullptr)
329 : {
330 : DDFSubfieldDefn *poLastSubfield =
331 257 : poDefn->GetSubfield(poDefn->GetSubfieldCount() - 1);
332 :
333 : const char *pachLastData =
334 257 : GetSubfieldData(poLastSubfield, &nBytesRemaining2, nInstance);
335 257 : if (pachLastData == nullptr)
336 0 : return nullptr;
337 :
338 257 : int nLastSubfieldWidth = 0;
339 257 : poLastSubfield->GetDataLength(pachLastData, nBytesRemaining2,
340 : &nLastSubfieldWidth);
341 :
342 257 : *pnInstanceSize =
343 257 : nBytesRemaining1 - (nBytesRemaining2 - nLastSubfieldWidth);
344 : }
345 :
346 257 : return pachWrkData;
347 : }
|