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 226213 : void DDFField::Initialize(const DDFFieldDefn *poDefnIn, const char *pachDataIn,
32 : int nDataSizeIn)
33 :
34 : {
35 226213 : pachData = pachDataIn;
36 226213 : nDataSize = nDataSizeIn;
37 226213 : poDefn = poDefnIn;
38 226213 : }
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) const
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",
72 0 : reinterpret_cast<const unsigned char *>(pachData)[i]);
73 : else
74 0 : fprintf(fp, "%c", pachData[i]);
75 : }
76 :
77 0 : if (nDataSize > 40)
78 0 : fprintf(fp, "...");
79 0 : fprintf(fp, "'\n");
80 :
81 : /* -------------------------------------------------------------------- */
82 : /* dump the data of the subfields. */
83 : /* -------------------------------------------------------------------- */
84 0 : int iOffset = 0;
85 :
86 0 : for (int nLoopCount = 0; nLoopCount < GetRepeatCount(); nLoopCount++)
87 : {
88 0 : if (nLoopCount > nMaxRepeat)
89 : {
90 0 : fprintf(fp, " ...\n");
91 0 : break;
92 : }
93 :
94 0 : for (const auto &poThisSFDefn : poDefn->GetSubfields())
95 : {
96 0 : poThisSFDefn->DumpData(pachData + iOffset, nDataSize - iOffset, fp);
97 :
98 0 : int nBytesConsumed = 0;
99 0 : poThisSFDefn->GetDataLength(pachData + iOffset, nDataSize - iOffset,
100 : &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 787575 : const char *DDFField::GetSubfieldData(const DDFSubfieldDefn *poSFDefn,
135 : int *pnMaxBytes, int iSubfieldIndex) const
136 :
137 : {
138 787575 : if (poSFDefn == nullptr)
139 0 : return nullptr;
140 :
141 787575 : int iOffset = 0;
142 787575 : if (iSubfieldIndex > 0 && poDefn->GetFixedWidth() > 0)
143 : {
144 350886 : iOffset = poDefn->GetFixedWidth() * iSubfieldIndex;
145 350886 : iSubfieldIndex = 0;
146 : }
147 :
148 853704 : while (iSubfieldIndex >= 0)
149 : {
150 1543720 : for (const auto &poThisSFDefn : poDefn->GetSubfields())
151 : {
152 1477590 : if (nDataSize <= iOffset)
153 : {
154 0 : CPLError(CE_Failure, CPLE_AppDefined,
155 : "Invalid data size for subfield %s of %s",
156 0 : poThisSFDefn->GetName(), poDefn->GetName());
157 787575 : return nullptr;
158 : }
159 :
160 1477590 : if (poThisSFDefn.get() == poSFDefn && iSubfieldIndex == 0)
161 : {
162 787575 : if (pnMaxBytes != nullptr)
163 787575 : *pnMaxBytes = nDataSize - iOffset;
164 :
165 787575 : return pachData + iOffset;
166 : }
167 :
168 690013 : int nBytesConsumed = 0;
169 690013 : poThisSFDefn->GetDataLength(pachData + iOffset, nDataSize - iOffset,
170 : &nBytesConsumed);
171 690013 : iOffset += nBytesConsumed;
172 : }
173 :
174 66129 : iSubfieldIndex--;
175 : }
176 :
177 : // We didn't find our target subfield or instance!
178 0 : return nullptr;
179 : }
180 :
181 : /************************************************************************/
182 : /* GetRepeatCount() */
183 : /************************************************************************/
184 :
185 : /**
186 : * How many times do the subfields of this record repeat? This
187 : * will always be one for non-repeating fields.
188 : *
189 : * @return The number of times that the subfields of this record occur
190 : * in this record. This will be one for non-repeating fields.
191 : *
192 : * @see <a href="example.html">8211view example program</a>
193 : * for a demonstration of handling repeated fields properly.
194 : */
195 :
196 90727 : int DDFField::GetRepeatCount() const
197 :
198 : {
199 90727 : if (!poDefn->IsRepeating())
200 1388 : return 1;
201 :
202 : /* -------------------------------------------------------------------- */
203 : /* The occurrence count depends on how many copies of this */
204 : /* field's list of subfields can fit into the data space. */
205 : /* -------------------------------------------------------------------- */
206 89339 : if (poDefn->GetFixedWidth())
207 : {
208 81078 : return nDataSize / poDefn->GetFixedWidth();
209 : }
210 :
211 : /* -------------------------------------------------------------------- */
212 : /* Note that it may be legal to have repeating variable width */
213 : /* subfields, but I don't have any samples, so I ignore it for */
214 : /* now. */
215 : /* */
216 : /* The file data/cape_royal_AZ_DEM/1183XREF.DDF has a repeating */
217 : /* variable length field, but the count is one, so it isn't */
218 : /* much value for testing. */
219 : /* -------------------------------------------------------------------- */
220 8261 : int iOffset = 0;
221 8261 : int iRepeatCount = 1;
222 :
223 : while (true)
224 : {
225 23833 : const int iOffsetBefore = iOffset;
226 71718 : for (const auto &poThisSFDefn : poDefn->GetSubfields())
227 : {
228 48088 : int nBytesConsumed = 0;
229 48088 : if (poThisSFDefn->GetWidth() > nDataSize - iOffset)
230 203 : nBytesConsumed = poThisSFDefn->GetWidth();
231 : else
232 47885 : poThisSFDefn->GetDataLength(
233 47885 : pachData + iOffset, nDataSize - iOffset, &nBytesConsumed);
234 :
235 48088 : iOffset += nBytesConsumed;
236 48088 : if (iOffset > nDataSize)
237 203 : return iRepeatCount - 1;
238 : }
239 23630 : if (iOffset == iOffsetBefore)
240 : {
241 : // Should probably emit error
242 0 : return iRepeatCount - 1;
243 : }
244 :
245 23630 : if (iOffset > nDataSize - 2)
246 8058 : return iRepeatCount;
247 :
248 15572 : iRepeatCount++;
249 15572 : }
250 : }
251 :
252 : /************************************************************************/
253 : /* GetInstanceData() */
254 : /************************************************************************/
255 :
256 : /**
257 : * Get field instance data and size.
258 : *
259 : * The returned data pointer and size values are suitable for use with
260 : * DDFRecord::SetFieldRaw().
261 : *
262 : * @param nInstance a value from 0 to GetRepeatCount()-1.
263 : * @param pnInstanceSize a location to put the size (in bytes) of the
264 : * field instance data returned. This size will include the unit terminator
265 : * (if any), but not the field terminator. This size pointer may be NULL
266 : * if not needed.
267 : *
268 : * @return the data pointer, or NULL on error.
269 : */
270 :
271 366 : const char *DDFField::GetInstanceData(int nInstance, int *pnInstanceSize)
272 :
273 : {
274 366 : int nRepeatCount = GetRepeatCount();
275 :
276 366 : if (nInstance < 0 || nInstance >= nRepeatCount)
277 0 : return nullptr;
278 :
279 : /* -------------------------------------------------------------------- */
280 : /* Special case for fields without subfields (like "0001"). We */
281 : /* don't currently handle repeating simple fields. */
282 : /* -------------------------------------------------------------------- */
283 366 : if (poDefn->GetSubfieldCount() == 0)
284 : {
285 0 : const char *pachWrkData = GetData();
286 0 : if (pnInstanceSize != nullptr)
287 0 : *pnInstanceSize = GetDataSize();
288 0 : return pachWrkData;
289 : }
290 :
291 : /* -------------------------------------------------------------------- */
292 : /* Get a pointer to the start of the existing data for this */
293 : /* iteration of the field. */
294 : /* -------------------------------------------------------------------- */
295 366 : int nBytesRemaining1 = 0;
296 366 : int nBytesRemaining2 = 0;
297 : const DDFSubfieldDefn *poFirstSubfield =
298 366 : poDefn->GetSubfields().front().get();
299 :
300 : const char *pachWrkData =
301 366 : GetSubfieldData(poFirstSubfield, &nBytesRemaining1, nInstance);
302 366 : if (pachWrkData == nullptr)
303 0 : return nullptr;
304 :
305 : /* -------------------------------------------------------------------- */
306 : /* Figure out the size of the entire field instance, including */
307 : /* unit terminators, but not any trailing field terminator. */
308 : /* -------------------------------------------------------------------- */
309 366 : if (pnInstanceSize != nullptr)
310 : {
311 : const DDFSubfieldDefn *poLastSubfield =
312 366 : poDefn->GetSubfields().back().get();
313 :
314 : const char *pachLastData =
315 366 : GetSubfieldData(poLastSubfield, &nBytesRemaining2, nInstance);
316 366 : if (pachLastData == nullptr)
317 0 : return nullptr;
318 :
319 366 : int nLastSubfieldWidth = 0;
320 366 : poLastSubfield->GetDataLength(pachLastData, nBytesRemaining2,
321 : &nLastSubfieldWidth);
322 :
323 366 : *pnInstanceSize =
324 366 : nBytesRemaining1 - (nBytesRemaining2 - nLastSubfieldWidth);
325 : }
326 :
327 366 : return pachWrkData;
328 : }
|