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