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 : * Copyright (c) 2026, Even Rouault
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "cpl_port.h"
15 : #include "iso8211.h"
16 :
17 : #include <cstdio>
18 : #include <cstdlib>
19 :
20 : #include <algorithm>
21 :
22 : #include "cpl_conv.h"
23 : #include "cpl_enumerate.h"
24 :
25 : // Note, we implement no constructor for this class to make instantiation
26 : // cheaper. It is required that the Initialize() be called before anything
27 : // else.
28 :
29 : /************************************************************************/
30 : /* Initialize() */
31 : /************************************************************************/
32 :
33 292437 : bool DDFField::Initialize(const DDFFieldDefn *poDefnIn, const char *pachDataIn,
34 : int nDataSizeIn, bool bInitializeParts)
35 :
36 : {
37 292437 : pachData = pachDataIn;
38 292437 : nDataSize = nDataSizeIn;
39 292437 : poDefn = poDefnIn;
40 :
41 292437 : return bInitializeParts ? InitializeParts() : true;
42 : }
43 :
44 : /************************************************************************/
45 : /* InitializeParts() */
46 : /************************************************************************/
47 :
48 271491 : bool DDFField::InitializeParts()
49 : {
50 271491 : const bool bCreateParts = apoFieldParts.empty();
51 271491 : const size_t nDefnPartsCount = poDefn->GetParts().size();
52 271491 : CPLAssert(bCreateParts || apoFieldParts.size() == nDefnPartsCount);
53 :
54 271491 : int iOffset = 0;
55 33468 : for (const auto &[iPart, poFieldDefnPart] :
56 304959 : cpl::enumerate(poDefn->GetParts()))
57 : {
58 16734 : if (iOffset > nDataSize)
59 : {
60 0 : CPLError(CE_Failure, CPLE_AppDefined,
61 : "Not enough bytes for part %d of field %s",
62 0 : static_cast<int>(iPart), poDefn->GetName());
63 0 : return false;
64 : }
65 16734 : const int iOffsetBefore = iOffset;
66 16734 : if (nDataSize > 0)
67 : {
68 16734 : if (iPart + 1 < nDefnPartsCount)
69 : {
70 102470 : for (const auto &poThisSFDefn : poFieldDefnPart->GetSubfields())
71 : {
72 94103 : int nBytesConsumed = 0;
73 94103 : poThisSFDefn->GetDataLength(pachData + iOffset,
74 94103 : nDataSize - iOffset,
75 : &nBytesConsumed);
76 :
77 94103 : iOffset += nBytesConsumed;
78 : }
79 : }
80 : else
81 : {
82 8367 : iOffset = nDataSize;
83 8367 : if (pachData[nDataSize - 1] == DDF_FIELD_TERMINATOR)
84 8367 : --iOffset;
85 : }
86 : }
87 :
88 16734 : if (bCreateParts)
89 : {
90 7560 : auto poFieldPart = std::make_unique<DDFField>();
91 7560 : poFieldPart->Initialize(poFieldDefnPart.get(),
92 3780 : pachData + iOffsetBefore,
93 : iOffset - iOffsetBefore, false);
94 3780 : apoFieldParts.push_back(std::move(poFieldPart));
95 : }
96 : else
97 : {
98 25908 : apoFieldParts[iPart]->Initialize(poFieldDefnPart.get(),
99 12954 : pachData + iOffsetBefore,
100 : iOffset - iOffsetBefore, false);
101 : }
102 : }
103 :
104 271491 : return true;
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 its
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. i.e. stderr
119 : */
120 :
121 0 : void DDFField::Dump(FILE *fp, int nNestingLevel) const
122 :
123 : {
124 0 : std::string osIndent;
125 0 : for (int i = 0; i < nNestingLevel; ++i)
126 0 : osIndent += " ";
127 :
128 : #define Print(...) \
129 : do \
130 : { \
131 : fprintf(fp, "%s", osIndent.c_str()); \
132 : fprintf(fp, __VA_ARGS__); \
133 : } while (0)
134 :
135 0 : int nMaxRepeat = 8;
136 :
137 0 : const char *pszDDF_MAXDUMP = getenv("DDF_MAXDUMP");
138 0 : if (pszDDF_MAXDUMP != nullptr)
139 0 : nMaxRepeat = atoi(pszDDF_MAXDUMP);
140 :
141 0 : Print("DDFField:\n");
142 0 : Print(" Tag = `%s'\n", poDefn->GetName());
143 0 : Print(" DataSize = %d\n", nDataSize);
144 :
145 0 : if (!apoFieldParts.empty())
146 : {
147 0 : for (const auto &poPart : apoFieldParts)
148 : {
149 0 : poPart->Dump(fp, nNestingLevel + 1);
150 : }
151 0 : return;
152 : }
153 :
154 0 : Print(" Data = `");
155 0 : for (int i = 0; i < std::min(nDataSize, 40); i++)
156 : {
157 0 : if (pachData[i] < 32 || pachData[i] > 126)
158 0 : fprintf(fp, "\\%02X",
159 0 : reinterpret_cast<const unsigned char *>(pachData)[i]);
160 : else
161 0 : fprintf(fp, "%c", pachData[i]);
162 : }
163 :
164 0 : if (nDataSize > 40)
165 0 : fprintf(fp, "...");
166 0 : fprintf(fp, "'\n");
167 :
168 : /* -------------------------------------------------------------------- */
169 : /* dump the data of the subfields. */
170 : /* -------------------------------------------------------------------- */
171 0 : int iOffset = 0;
172 0 : const int nRepeatCount = GetRepeatCount();
173 0 : for (int nLoopCount = 0; nLoopCount < nRepeatCount; nLoopCount++)
174 : {
175 0 : if (nLoopCount > nMaxRepeat)
176 : {
177 0 : Print(" ...\n");
178 0 : break;
179 : }
180 :
181 0 : for (const auto &poThisSFDefn : poDefn->GetSubfields())
182 : {
183 0 : poThisSFDefn->DumpData(pachData + iOffset, nDataSize - iOffset, fp);
184 :
185 0 : int nBytesConsumed = 0;
186 0 : poThisSFDefn->GetDataLength(pachData + iOffset, nDataSize - iOffset,
187 : &nBytesConsumed);
188 :
189 0 : iOffset += nBytesConsumed;
190 : }
191 : }
192 : }
193 :
194 : /************************************************************************/
195 : /* GetSubfieldData() */
196 : /************************************************************************/
197 :
198 : /**
199 : * Fetch raw data pointer for a particular subfield of this field.
200 : *
201 : * The passed DDFSubfieldDefn (poSFDefn) should be acquired from the
202 : * DDFFieldDefn corresponding with this field. This is normally done
203 : * once before reading any records. This method involves a series of
204 : * calls to DDFSubfield::GetDataLength() in order to track through the
205 : * DDFField data to that belonging to the requested subfield. This can
206 : * be relatively expensive.<p>
207 : *
208 : * @param poSFDefn The definition of the subfield for which the raw
209 : * data pointer is desired.
210 : * @param pnMaxBytes The maximum number of bytes that can be accessed from
211 : * the returned data pointer is placed in this int, unless it is NULL.
212 : * @param iSubfieldIndex The instance of this subfield to fetch. Use zero
213 : * (the default) for the first instance.
214 : *
215 : * @return A pointer into the DDFField's data that belongs to the subfield.
216 : * This returned pointer is invalidated by the next record read
217 : * (DDFRecord::ReadRecord()) and the returned pointer should not be freed
218 : * by the application.
219 : */
220 :
221 1002180 : const char *DDFField::GetSubfieldData(const DDFSubfieldDefn *poSFDefn,
222 : int *pnMaxBytes, int iSubfieldIndex) const
223 :
224 : {
225 1002180 : if (poSFDefn == nullptr)
226 0 : return nullptr;
227 :
228 1002180 : int iOffset = 0;
229 1002180 : if (iSubfieldIndex > 0 && poDefn->GetFixedWidth() > 0)
230 : {
231 389371 : iOffset = poDefn->GetFixedWidth() * iSubfieldIndex;
232 389371 : iSubfieldIndex = 0;
233 : }
234 :
235 1203230 : while (iSubfieldIndex >= 0)
236 : {
237 2774340 : for (const auto &poThisSFDefn : poDefn->GetSubfields())
238 : {
239 2573300 : if (nDataSize <= iOffset)
240 : {
241 0 : CPLError(CE_Failure, CPLE_AppDefined,
242 : "Invalid data size for subfield %s of %s",
243 0 : poThisSFDefn->GetName(), poDefn->GetName());
244 1002180 : return nullptr;
245 : }
246 :
247 2573300 : if (poThisSFDefn.get() == poSFDefn && iSubfieldIndex == 0)
248 : {
249 1002180 : if (pnMaxBytes != nullptr)
250 1002180 : *pnMaxBytes = nDataSize - iOffset;
251 :
252 1002180 : return pachData + iOffset;
253 : }
254 :
255 1571110 : int nBytesConsumed = 0;
256 1571110 : poThisSFDefn->GetDataLength(pachData + iOffset, nDataSize - iOffset,
257 : &nBytesConsumed);
258 1571110 : iOffset += nBytesConsumed;
259 : }
260 :
261 201043 : iSubfieldIndex--;
262 : }
263 :
264 : // We didn't find our target subfield or instance!
265 0 : return nullptr;
266 : }
267 :
268 : /************************************************************************/
269 : /* GetRepeatCount() */
270 : /************************************************************************/
271 :
272 : /**
273 : * How many times do the subfields of this record repeat? This
274 : * will always be one for non-repeating fields.
275 : *
276 : * @return The number of times that the subfields of this record occur
277 : * in this record. This will be one for non-repeating fields.
278 : *
279 : * @see <a href="example.html">8211view example program</a>
280 : * for a demonstration of handling repeated fields properly.
281 : */
282 :
283 125715 : int DDFField::GetRepeatCount() const
284 :
285 : {
286 125715 : if (!apoFieldParts.empty())
287 982 : return 0;
288 :
289 124733 : if (!poDefn->IsRepeating())
290 10150 : return 1;
291 :
292 : /* -------------------------------------------------------------------- */
293 : /* The occurrence count depends on how many copies of this */
294 : /* field's list of subfields can fit into the data space. */
295 : /* -------------------------------------------------------------------- */
296 114583 : if (poDefn->GetFixedWidth())
297 : {
298 95443 : return nDataSize / poDefn->GetFixedWidth();
299 : }
300 :
301 : /* -------------------------------------------------------------------- */
302 : /* Note that it may be legal to have repeating variable width */
303 : /* subfields, but I don't have any samples, so I ignore it for */
304 : /* now. */
305 : /* */
306 : /* The file data/cape_royal_AZ_DEM/1183XREF.DDF has a repeating */
307 : /* variable length field, but the count is one, so it isn't */
308 : /* much value for testing. */
309 : /* -------------------------------------------------------------------- */
310 19140 : int iOffset = 0;
311 19140 : int iRepeatCount = 1;
312 :
313 : while (true)
314 : {
315 58165 : const int iOffsetBefore = iOffset;
316 193252 : for (const auto &poThisSFDefn : poDefn->GetSubfields())
317 : {
318 136458 : int nBytesConsumed = 0;
319 136458 : if (poThisSFDefn->GetWidth() > nDataSize - iOffset)
320 1371 : nBytesConsumed = poThisSFDefn->GetWidth();
321 : else
322 135087 : poThisSFDefn->GetDataLength(
323 135087 : pachData + iOffset, nDataSize - iOffset, &nBytesConsumed);
324 :
325 136458 : iOffset += nBytesConsumed;
326 136458 : if (iOffset > nDataSize)
327 1371 : return iRepeatCount - 1;
328 : }
329 56794 : if (iOffset == iOffsetBefore)
330 : {
331 : // Should probably emit error
332 0 : return iRepeatCount - 1;
333 : }
334 :
335 56794 : if (iOffset > nDataSize - 2)
336 17769 : return iRepeatCount;
337 :
338 39025 : iRepeatCount++;
339 39025 : }
340 : }
341 :
342 : /************************************************************************/
343 : /* GetInstanceData() */
344 : /************************************************************************/
345 :
346 : /**
347 : * Get field instance data and size.
348 : *
349 : * The returned data pointer and size values are suitable for use with
350 : * DDFRecord::SetFieldRaw().
351 : *
352 : * @param nInstance a value from 0 to GetRepeatCount()-1.
353 : * @param pnInstanceSize a location to put the size (in bytes) of the
354 : * field instance data returned. This size will include the unit terminator
355 : * (if any), but not the field terminator. This size pointer may be NULL
356 : * if not needed.
357 : *
358 : * @return the data pointer, or NULL on error.
359 : */
360 :
361 8927 : const char *DDFField::GetInstanceData(int nInstance, int *pnInstanceSize)
362 :
363 : {
364 8927 : const int nRepeatCount = GetRepeatCount();
365 8927 : if (!apoFieldParts.empty() && nInstance == 0)
366 : {
367 491 : const char *pachWrkData = GetData();
368 491 : if (pnInstanceSize != nullptr)
369 491 : *pnInstanceSize = GetDataSize();
370 491 : return pachWrkData;
371 : }
372 :
373 8436 : if (nInstance < 0 || nInstance >= nRepeatCount)
374 0 : return nullptr;
375 :
376 : /* -------------------------------------------------------------------- */
377 : /* Special case for fields without subfields (like "0001"). We */
378 : /* don't currently handle repeating simple fields. */
379 : /* -------------------------------------------------------------------- */
380 8436 : if (poDefn->GetSubfieldCount() == 0)
381 : {
382 0 : const char *pachWrkData = GetData();
383 0 : if (pnInstanceSize != nullptr)
384 0 : *pnInstanceSize = GetDataSize();
385 0 : return pachWrkData;
386 : }
387 :
388 : /* -------------------------------------------------------------------- */
389 : /* Get a pointer to the start of the existing data for this */
390 : /* iteration of the field. */
391 : /* -------------------------------------------------------------------- */
392 8436 : int nBytesRemaining1 = 0;
393 8436 : int nBytesRemaining2 = 0;
394 : const DDFSubfieldDefn *poFirstSubfield =
395 8436 : poDefn->GetSubfields().front().get();
396 :
397 : const char *pachWrkData =
398 8436 : GetSubfieldData(poFirstSubfield, &nBytesRemaining1, nInstance);
399 8436 : if (pachWrkData == nullptr)
400 0 : return nullptr;
401 :
402 : /* -------------------------------------------------------------------- */
403 : /* Figure out the size of the entire field instance, including */
404 : /* unit terminators, but not any trailing field terminator. */
405 : /* -------------------------------------------------------------------- */
406 8436 : if (pnInstanceSize != nullptr)
407 : {
408 : const DDFSubfieldDefn *poLastSubfield =
409 124 : poDefn->GetSubfields().back().get();
410 :
411 : const char *pachLastData =
412 124 : GetSubfieldData(poLastSubfield, &nBytesRemaining2, nInstance);
413 124 : if (pachLastData == nullptr)
414 0 : return nullptr;
415 :
416 124 : int nLastSubfieldWidth = 0;
417 124 : poLastSubfield->GetDataLength(pachLastData, nBytesRemaining2,
418 : &nLastSubfieldWidth);
419 :
420 124 : *pnInstanceSize =
421 124 : nBytesRemaining1 - (nBytesRemaining2 - nLastSubfieldWidth);
422 : }
423 :
424 8436 : return pachWrkData;
425 : }
|