/src/gdal/frmts/iso8211/ddffielddefn.cpp
Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: ISO 8211 Access |
4 | | * Purpose: Implements the DDFFieldDefn 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 <algorithm> |
18 | | #include <cctype> |
19 | | #include <cstddef> |
20 | | #include <cstdio> |
21 | | #include <cstdlib> |
22 | | #include <cstring> |
23 | | |
24 | | #include "cpl_conv.h" |
25 | | #include "cpl_error.h" |
26 | | #include "cpl_string.h" |
27 | | |
28 | 41.0k | #define CPLE_DiscardedFormat 1301 |
29 | | |
30 | | /************************************************************************/ |
31 | | /* DDFFieldDefn() */ |
32 | | /************************************************************************/ |
33 | | |
34 | 419k | DDFFieldDefn::DDFFieldDefn() = default; |
35 | | |
36 | | /************************************************************************/ |
37 | | /* ~DDFFieldDefn() */ |
38 | | /************************************************************************/ |
39 | | |
40 | 419k | DDFFieldDefn::~DDFFieldDefn() = default; |
41 | | |
42 | | /************************************************************************/ |
43 | | /* AddSubfield() */ |
44 | | /************************************************************************/ |
45 | | |
46 | | void DDFFieldDefn::AddSubfield(const char *pszName, const char *pszFormat) |
47 | | |
48 | 0 | { |
49 | 0 | auto poSFDefn = std::make_unique<DDFSubfieldDefn>(); |
50 | |
|
51 | 0 | poSFDefn->SetName(pszName); |
52 | 0 | poSFDefn->SetFormat(pszFormat); |
53 | 0 | AddSubfield(std::move(poSFDefn)); |
54 | 0 | } |
55 | | |
56 | | /************************************************************************/ |
57 | | /* AddSubfield() */ |
58 | | /************************************************************************/ |
59 | | |
60 | | void DDFFieldDefn::AddSubfield(std::unique_ptr<DDFSubfieldDefn> poNewSFDefn, |
61 | | bool bDontAddToFormat) |
62 | | |
63 | 1.54M | { |
64 | 1.54M | if (bDontAddToFormat) |
65 | 1.54M | { |
66 | 1.54M | apoSubfields.push_back(std::move(poNewSFDefn)); |
67 | 1.54M | return; |
68 | 1.54M | } |
69 | | |
70 | | /* -------------------------------------------------------------------- */ |
71 | | /* Add this format to the format list. We don't bother */ |
72 | | /* aggregating formats here. */ |
73 | | /* -------------------------------------------------------------------- */ |
74 | 0 | if (_formatControls.empty()) |
75 | 0 | { |
76 | 0 | _formatControls = "()"; |
77 | 0 | } |
78 | |
|
79 | 0 | std::string osNewFormatControls = _formatControls; |
80 | 0 | osNewFormatControls.pop_back(); |
81 | 0 | if (!osNewFormatControls.empty() && osNewFormatControls.back() != '(') |
82 | 0 | osNewFormatControls += ','; |
83 | 0 | osNewFormatControls += poNewSFDefn->GetFormat(); |
84 | 0 | osNewFormatControls += ')'; |
85 | |
|
86 | 0 | _formatControls = std::move(osNewFormatControls); |
87 | | |
88 | | /* -------------------------------------------------------------------- */ |
89 | | /* Add the subfield name to the list. */ |
90 | | /* -------------------------------------------------------------------- */ |
91 | 0 | if (!_arrayDescr.empty() && |
92 | 0 | (_arrayDescr[0] != '*' || _arrayDescr.size() > 1)) |
93 | 0 | _arrayDescr += '!'; |
94 | 0 | _arrayDescr += poNewSFDefn->GetName(); |
95 | |
|
96 | 0 | apoSubfields.push_back(std::move(poNewSFDefn)); |
97 | 0 | } |
98 | | |
99 | | /************************************************************************/ |
100 | | /* Create() */ |
101 | | /* */ |
102 | | /* Initialize a new field defn from application input, instead */ |
103 | | /* of from an existing file. */ |
104 | | /************************************************************************/ |
105 | | |
106 | | int DDFFieldDefn::Create(const char *pszTagIn, const char *pszFieldName, |
107 | | const char *pszDescription, |
108 | | DDF_data_struct_code eDataStructCode, |
109 | | DDF_data_type_code eDataTypeCode, |
110 | | const char *pszFormat) |
111 | | |
112 | 0 | { |
113 | 0 | CPLAssert(osTag.empty()); |
114 | 0 | poModule = nullptr; |
115 | 0 | osTag = pszTagIn; |
116 | 0 | _fieldName = pszFieldName; |
117 | 0 | _arrayDescr = pszDescription ? pszDescription : ""; |
118 | |
|
119 | 0 | _data_struct_code = eDataStructCode; |
120 | 0 | _data_type_code = eDataTypeCode; |
121 | |
|
122 | 0 | _formatControls = pszFormat ? pszFormat : ""; |
123 | |
|
124 | 0 | bRepeatingSubfields = (pszDescription != nullptr && *pszDescription == '*'); |
125 | |
|
126 | 0 | if (!_formatControls.empty() && _data_struct_code != dsc_elementary) |
127 | 0 | { |
128 | 0 | if (!BuildSubfields()) |
129 | 0 | return false; |
130 | | |
131 | 0 | if (apoFieldParts.empty() && !ApplyFormats()) |
132 | 0 | return false; |
133 | 0 | } |
134 | | |
135 | 0 | return TRUE; |
136 | 0 | } |
137 | | |
138 | | /************************************************************************/ |
139 | | /* GenerateDDREntry() */ |
140 | | /************************************************************************/ |
141 | | |
142 | | int DDFFieldDefn::GenerateDDREntry(DDFModule *poModuleIn, char **ppachData, |
143 | | int *pnLength) |
144 | | |
145 | 0 | { |
146 | 0 | const int iFDOffset = poModuleIn->GetFieldControlLength(); |
147 | 0 | CPLAssert(iFDOffset >= 6 && iFDOffset <= 9); |
148 | 0 | *pnLength = static_cast<int>(iFDOffset + _fieldName.size() + 1 + |
149 | 0 | _arrayDescr.size() + 1); |
150 | 0 | if (!_formatControls.empty()) |
151 | 0 | { |
152 | 0 | *pnLength += static_cast<int>(_formatControls.size() + 1); |
153 | 0 | } |
154 | |
|
155 | 0 | if (ppachData == nullptr) |
156 | 0 | return TRUE; |
157 | | |
158 | 0 | *ppachData = static_cast<char *>(CPLMalloc(*pnLength + 1)); |
159 | 0 | (*ppachData)[*pnLength] = 0; |
160 | |
|
161 | 0 | if (_data_struct_code == dsc_elementary) |
162 | 0 | (*ppachData)[0] = '0'; |
163 | 0 | else if (_data_struct_code == dsc_vector) |
164 | 0 | (*ppachData)[0] = '1'; |
165 | 0 | else if (_data_struct_code == dsc_array) |
166 | 0 | (*ppachData)[0] = '2'; |
167 | 0 | else if (_data_struct_code == dsc_concatenated) |
168 | 0 | (*ppachData)[0] = '3'; |
169 | |
|
170 | 0 | if (_data_type_code == dtc_char_string) |
171 | 0 | (*ppachData)[1] = '0'; |
172 | 0 | else if (_data_type_code == dtc_implicit_point) |
173 | 0 | (*ppachData)[1] = '1'; |
174 | 0 | else if (_data_type_code == dtc_explicit_point) |
175 | 0 | (*ppachData)[1] = '2'; |
176 | 0 | else if (_data_type_code == dtc_explicit_point_scaled) |
177 | 0 | (*ppachData)[1] = '3'; |
178 | 0 | else if (_data_type_code == dtc_char_bit_string) |
179 | 0 | (*ppachData)[1] = '4'; |
180 | 0 | else if (_data_type_code == dtc_bit_string) |
181 | 0 | (*ppachData)[1] = '5'; |
182 | 0 | else if (_data_type_code == dtc_mixed_data_type) |
183 | 0 | (*ppachData)[1] = '6'; |
184 | |
|
185 | 0 | (*ppachData)[2] = '0'; |
186 | 0 | (*ppachData)[3] = '0'; |
187 | 0 | (*ppachData)[4] = ';'; |
188 | 0 | (*ppachData)[5] = '&'; |
189 | 0 | if (iFDOffset > 6 && _escapeSequence.size() >= 1) |
190 | 0 | (*ppachData)[6] = _escapeSequence[0]; |
191 | 0 | if (iFDOffset > 7 && _escapeSequence.size() >= 2) |
192 | 0 | (*ppachData)[7] = _escapeSequence[1]; |
193 | 0 | if (iFDOffset > 8 && _escapeSequence.size() >= 3) |
194 | 0 | (*ppachData)[8] = _escapeSequence[2]; |
195 | 0 | snprintf(*ppachData + iFDOffset, *pnLength + 1 - iFDOffset, "%s", |
196 | 0 | _fieldName.c_str()); |
197 | 0 | snprintf(*ppachData + strlen(*ppachData), |
198 | 0 | *pnLength + 1 - strlen(*ppachData), "%c%s", DDF_UNIT_TERMINATOR, |
199 | 0 | _arrayDescr.c_str()); |
200 | 0 | if (!_formatControls.empty()) |
201 | 0 | { |
202 | | // empty for '0000' of S-57 & S-111 |
203 | 0 | snprintf(*ppachData + strlen(*ppachData), |
204 | 0 | *pnLength + 1 - strlen(*ppachData), "%c%s", |
205 | 0 | DDF_UNIT_TERMINATOR, _formatControls.c_str()); |
206 | 0 | } |
207 | 0 | snprintf(*ppachData + strlen(*ppachData), |
208 | 0 | *pnLength + 1 - strlen(*ppachData), "%c", DDF_FIELD_TERMINATOR); |
209 | |
|
210 | 0 | return TRUE; |
211 | 0 | } |
212 | | |
213 | | /************************************************************************/ |
214 | | /* Initialize() */ |
215 | | /* */ |
216 | | /* Initialize the field definition from the information in the */ |
217 | | /* DDR record. This is called by DDFModule::Open(). */ |
218 | | /************************************************************************/ |
219 | | |
220 | | int DDFFieldDefn::Initialize(DDFModule *poModuleIn, const char *pszTagIn, |
221 | | int nFieldEntrySize, const char *pachFieldArea) |
222 | | |
223 | 377k | { |
224 | 377k | int iFDOffset = poModuleIn->GetFieldControlLength(); |
225 | | |
226 | 377k | poModule = poModuleIn; |
227 | | |
228 | 377k | osTag = pszTagIn; |
229 | | |
230 | | /* -------------------------------------------------------------------- */ |
231 | | /* Set the data struct and type codes. */ |
232 | | /* -------------------------------------------------------------------- */ |
233 | 377k | switch (pachFieldArea[0]) |
234 | 377k | { |
235 | 22.7k | case ' ': /* for ADRG, DIGEST USRP, DIGEST ASRP files */ |
236 | 39.2k | case '0': |
237 | 39.2k | _data_struct_code = dsc_elementary; |
238 | 39.2k | break; |
239 | | |
240 | 151k | case '1': |
241 | 151k | _data_struct_code = dsc_vector; |
242 | 151k | break; |
243 | | |
244 | 94.9k | case '2': |
245 | 94.9k | _data_struct_code = dsc_array; |
246 | 94.9k | break; |
247 | | |
248 | 39.8k | case '3': |
249 | 39.8k | _data_struct_code = dsc_concatenated; |
250 | 39.8k | break; |
251 | | |
252 | 52.1k | default: |
253 | 52.1k | CPLError(CE_Failure, CPLE_AppDefined, |
254 | 52.1k | "Unrecognized data_struct_code value %c.\n" |
255 | 52.1k | "Field %s initialization incorrect.", |
256 | 52.1k | pachFieldArea[0], osTag.c_str()); |
257 | 52.1k | _data_struct_code = dsc_elementary; |
258 | 377k | } |
259 | | |
260 | 377k | switch (pachFieldArea[1]) |
261 | 377k | { |
262 | 23.1k | case ' ': /* for ADRG, DIGEST USRP, DIGEST ASRP files */ |
263 | 71.8k | case '0': |
264 | 71.8k | _data_type_code = dtc_char_string; |
265 | 71.8k | break; |
266 | | |
267 | 90.0k | case '1': |
268 | 90.0k | _data_type_code = dtc_implicit_point; |
269 | 90.0k | break; |
270 | | |
271 | 16.7k | case '2': |
272 | 16.7k | _data_type_code = dtc_explicit_point; |
273 | 16.7k | break; |
274 | | |
275 | 14.6k | case '3': |
276 | 14.6k | _data_type_code = dtc_explicit_point_scaled; |
277 | 14.6k | break; |
278 | | |
279 | 2.68k | case '4': |
280 | 2.68k | _data_type_code = dtc_char_bit_string; |
281 | 2.68k | break; |
282 | | |
283 | 3.36k | case '5': |
284 | 3.36k | _data_type_code = dtc_bit_string; |
285 | 3.36k | break; |
286 | | |
287 | 121k | case '6': |
288 | 121k | _data_type_code = dtc_mixed_data_type; |
289 | 121k | break; |
290 | | |
291 | 56.8k | default: |
292 | 56.8k | CPLError(CE_Failure, CPLE_AppDefined, |
293 | 56.8k | "Unrecognized data_type_code value %c.\n" |
294 | 56.8k | "Field %s initialization incorrect.", |
295 | 56.8k | pachFieldArea[1], osTag.c_str()); |
296 | 56.8k | _data_type_code = dtc_char_string; |
297 | 377k | } |
298 | | |
299 | 377k | if (nFieldEntrySize >= iFDOffset && iFDOffset > 6) |
300 | 173k | _escapeSequence.assign(pachFieldArea + 6, iFDOffset - 6); |
301 | | |
302 | | /* -------------------------------------------------------------------- */ |
303 | | /* Capture the field name, description (sub field names), and */ |
304 | | /* format statements. */ |
305 | | /* -------------------------------------------------------------------- */ |
306 | | |
307 | 377k | int nCharsConsumed = 0; |
308 | 377k | _fieldName = DDFFetchVariable( |
309 | 377k | pachFieldArea + iFDOffset, nFieldEntrySize - iFDOffset, |
310 | 377k | DDF_UNIT_TERMINATOR, DDF_FIELD_TERMINATOR, &nCharsConsumed); |
311 | 377k | iFDOffset += nCharsConsumed; |
312 | | |
313 | 377k | _arrayDescr = DDFFetchVariable( |
314 | 377k | pachFieldArea + iFDOffset, nFieldEntrySize - iFDOffset, |
315 | 377k | DDF_UNIT_TERMINATOR, DDF_FIELD_TERMINATOR, &nCharsConsumed); |
316 | 377k | iFDOffset += nCharsConsumed; |
317 | | |
318 | 377k | _formatControls = DDFFetchVariable( |
319 | 377k | pachFieldArea + iFDOffset, nFieldEntrySize - iFDOffset, |
320 | 377k | DDF_UNIT_TERMINATOR, DDF_FIELD_TERMINATOR, &nCharsConsumed); |
321 | | |
322 | | /* -------------------------------------------------------------------- */ |
323 | | /* Parse the subfield info. */ |
324 | | /* -------------------------------------------------------------------- */ |
325 | 377k | if (_data_struct_code != dsc_elementary) |
326 | 285k | { |
327 | 285k | if (!BuildSubfields()) |
328 | 13.4k | return false; |
329 | | |
330 | 272k | if (apoFieldParts.empty() && !ApplyFormats()) |
331 | 34.6k | return false; |
332 | 272k | } |
333 | | |
334 | 329k | return TRUE; |
335 | 377k | } |
336 | | |
337 | | /************************************************************************/ |
338 | | /* SetEscapeSequence() */ |
339 | | /************************************************************************/ |
340 | | |
341 | | void DDFFieldDefn::SetEscapeSequence(const std::string &val) |
342 | 0 | { |
343 | 0 | _escapeSequence = val; |
344 | 0 | } |
345 | | |
346 | | /************************************************************************/ |
347 | | /* Dump() */ |
348 | | /************************************************************************/ |
349 | | |
350 | | /** |
351 | | * Write out field definition info to debugging file. |
352 | | * |
353 | | * A variety of information about this field definition, and all its |
354 | | * subfields is written to the give debugging file handle. |
355 | | * |
356 | | * @param fp The standard IO file handle to write to. i.e. stderr |
357 | | */ |
358 | | |
359 | | void DDFFieldDefn::Dump(FILE *fp, int nNestingLevel) const |
360 | | |
361 | 0 | { |
362 | 0 | std::string osIndent; |
363 | 0 | for (int i = 0; i < nNestingLevel; ++i) |
364 | 0 | osIndent += " "; |
365 | |
|
366 | 0 | #define Print(...) \ |
367 | 0 | do \ |
368 | 0 | { \ |
369 | 0 | fprintf(fp, "%s", osIndent.c_str()); \ |
370 | 0 | fprintf(fp, __VA_ARGS__); \ |
371 | 0 | } while (0) |
372 | |
|
373 | 0 | const char *pszValue = ""; |
374 | 0 | CPL_IGNORE_RET_VAL(pszValue); // Make CSA happy |
375 | |
|
376 | 0 | Print("DDFFieldDefn:\n"); |
377 | 0 | Print(" Tag = `%s'\n", osTag.c_str()); |
378 | 0 | Print(" _fieldName = `%s'\n", _fieldName.c_str()); |
379 | 0 | Print(" _arrayDescr = `%s'\n", _arrayDescr.c_str()); |
380 | 0 | Print(" _formatControls = `%s'\n", _formatControls.c_str()); |
381 | |
|
382 | 0 | switch (_data_struct_code) |
383 | 0 | { |
384 | 0 | case dsc_elementary: |
385 | 0 | pszValue = "elementary"; |
386 | 0 | break; |
387 | | |
388 | 0 | case dsc_vector: |
389 | 0 | pszValue = "vector"; |
390 | 0 | break; |
391 | | |
392 | 0 | case dsc_array: |
393 | 0 | pszValue = "array"; |
394 | 0 | break; |
395 | | |
396 | 0 | case dsc_concatenated: |
397 | 0 | pszValue = "concatenated"; |
398 | 0 | break; |
399 | 0 | } |
400 | | |
401 | 0 | Print(" _data_struct_code = %s\n", pszValue); |
402 | |
|
403 | 0 | switch (_data_type_code) |
404 | 0 | { |
405 | 0 | case dtc_char_string: |
406 | 0 | pszValue = "char_string"; |
407 | 0 | break; |
408 | | |
409 | 0 | case dtc_implicit_point: |
410 | 0 | pszValue = "implicit_point"; |
411 | 0 | break; |
412 | | |
413 | 0 | case dtc_explicit_point: |
414 | 0 | pszValue = "explicit_point"; |
415 | 0 | break; |
416 | | |
417 | 0 | case dtc_explicit_point_scaled: |
418 | 0 | pszValue = "explicit_point_scaled"; |
419 | 0 | break; |
420 | | |
421 | 0 | case dtc_char_bit_string: |
422 | 0 | pszValue = "char_bit_string"; |
423 | 0 | break; |
424 | | |
425 | 0 | case dtc_bit_string: |
426 | 0 | pszValue = "bit_string"; |
427 | 0 | break; |
428 | | |
429 | 0 | case dtc_mixed_data_type: |
430 | 0 | pszValue = "mixed_data_type"; |
431 | 0 | break; |
432 | 0 | } |
433 | | |
434 | 0 | Print(" _data_type_code = %s\n", pszValue); |
435 | |
|
436 | 0 | for (const auto &poField : apoFieldParts) |
437 | 0 | poField->Dump(fp, nNestingLevel + 1); |
438 | |
|
439 | 0 | for (const auto &poSubfield : apoSubfields) |
440 | 0 | poSubfield->Dump(fp, nNestingLevel + 1); |
441 | 0 | } |
442 | | |
443 | | /************************************************************************/ |
444 | | /* BuildSubfields() */ |
445 | | /* */ |
446 | | /* Based on the _arrayDescr build a set of subfields. */ |
447 | | /************************************************************************/ |
448 | | |
449 | | bool DDFFieldDefn::BuildSubfields() |
450 | | |
451 | 327k | { |
452 | 327k | if (_data_struct_code == dsc_concatenated) |
453 | 39.8k | { |
454 | | // Split on two consecutive backslashes. |
455 | 39.8k | std::vector<std::string> aosPartDescr; |
456 | 39.8k | { |
457 | 39.8k | std::string osCur; |
458 | 2.23M | for (size_t i = 0; i < _arrayDescr.size(); ++i) |
459 | 2.19M | { |
460 | 2.19M | const char c = _arrayDescr[i]; |
461 | 2.19M | if (c == '\\' && _arrayDescr[i + 1] == '\\') |
462 | 41.4k | { |
463 | 41.4k | aosPartDescr.push_back(osCur); |
464 | 41.4k | osCur.clear(); |
465 | 41.4k | ++i; |
466 | 41.4k | } |
467 | 2.15M | else |
468 | 2.15M | { |
469 | 2.15M | osCur += c; |
470 | 2.15M | } |
471 | 2.19M | } |
472 | 39.8k | aosPartDescr.push_back(std::move(osCur)); |
473 | 39.8k | } |
474 | 39.8k | if (aosPartDescr.size() > 1 && !_formatControls.empty() && |
475 | 30.5k | _formatControls.front() == '(' && _formatControls.back() == ')') |
476 | 29.3k | { |
477 | 29.3k | const char *pszFormatCur = _formatControls.c_str() + 1; |
478 | 67.8k | for (size_t i = 0; i < aosPartDescr.size(); ++i) |
479 | 51.9k | { |
480 | 51.9k | const std::string &osPartDescr = aosPartDescr[i]; |
481 | | // Check there are no repeated subfields but in the last part |
482 | 51.9k | if (i < aosPartDescr.size() - 1 && |
483 | 34.3k | osPartDescr.find('*') != std::string::npos) |
484 | 549 | { |
485 | 549 | CPLError(CE_Failure, CPLE_NotSupported, |
486 | 549 | "Tag %s: repeated fields found in a part that is " |
487 | 549 | "not the last one: %s", |
488 | 549 | osTag.c_str(), _arrayDescr.c_str()); |
489 | 549 | return false; |
490 | 549 | } |
491 | | |
492 | 51.3k | const int nSubfieldsInPart = static_cast<int>( |
493 | 51.3k | std::count(osPartDescr.begin(), osPartDescr.end(), '!') + |
494 | 51.3k | 1); |
495 | 51.3k | int nSubFieldCounter = 0; |
496 | 51.3k | const char *pszFormatStart = pszFormatCur; |
497 | 51.3k | bool justAfterFieldFormat = true; |
498 | 51.3k | int nParenthesisLevel = 0; |
499 | 442k | while (i < aosPartDescr.size() - 1 && *pszFormatCur != '\0') |
500 | 424k | { |
501 | 424k | if (justAfterFieldFormat && *pszFormatCur >= '1' && |
502 | 99.5k | *pszFormatCur <= '9') |
503 | 38.1k | { |
504 | 38.1k | char *pszNext = nullptr; |
505 | 38.1k | const int nRepeat = static_cast<int>( |
506 | 38.1k | strtol(pszFormatCur, &pszNext, 10)); |
507 | 38.1k | if (!(*pszNext) || nRepeat <= 0 || nRepeat > 1000) |
508 | 993 | { |
509 | 993 | CPLError(CE_Failure, CPLE_AppDefined, |
510 | 993 | "Tag %s: invalid formatControls: %s", |
511 | 993 | osTag.c_str(), _formatControls.c_str()); |
512 | 993 | return false; |
513 | 993 | } |
514 | 37.1k | if (*pszNext == '(') |
515 | 5.01k | { |
516 | 5.01k | pszFormatCur = pszNext + 1; |
517 | 5.01k | int nGroupSubFieldCount = 0; |
518 | 77.0k | while (*pszFormatCur) |
519 | 76.4k | { |
520 | 76.4k | if (*pszFormatCur == '(') |
521 | 590 | { |
522 | | // Implementation limitation. Perhaps OK per the standard |
523 | 590 | CPLError(CE_Failure, CPLE_AppDefined, |
524 | 590 | "Tag %s: unsupported " |
525 | 590 | "formatControls: %s", |
526 | 590 | osTag.c_str(), |
527 | 590 | _formatControls.c_str()); |
528 | 590 | return false; |
529 | 590 | } |
530 | 75.8k | else if (*pszFormatCur >= '1' && |
531 | 41.8k | *pszFormatCur <= '9') |
532 | 10.7k | { |
533 | 10.7k | const long nIncrement = |
534 | 10.7k | strtol(pszFormatCur, &pszNext, 10); |
535 | 10.7k | if (!(*pszNext) || |
536 | 10.5k | nIncrement > |
537 | 10.5k | INT_MAX - nGroupSubFieldCount) |
538 | 747 | { |
539 | 747 | CPLError(CE_Failure, CPLE_AppDefined, |
540 | 747 | "Tag %s: invalid " |
541 | 747 | "formatControls: %s", |
542 | 747 | osTag.c_str(), |
543 | 747 | _formatControls.c_str()); |
544 | 747 | return false; |
545 | 747 | } |
546 | 9.99k | nGroupSubFieldCount += |
547 | 9.99k | static_cast<int>(nIncrement); |
548 | 9.99k | pszFormatCur = pszNext; |
549 | 9.99k | } |
550 | 65.0k | else if (*pszFormatCur == ',') |
551 | 2.64k | { |
552 | 2.64k | if (nGroupSubFieldCount == INT_MAX) |
553 | 403 | { |
554 | 403 | CPLError(CE_Failure, CPLE_AppDefined, |
555 | 403 | "Tag %s: invalid " |
556 | 403 | "formatControls: %s", |
557 | 403 | osTag.c_str(), |
558 | 403 | _formatControls.c_str()); |
559 | 403 | return false; |
560 | 403 | } |
561 | 2.24k | nGroupSubFieldCount++; |
562 | 2.24k | ++pszFormatCur; |
563 | 2.24k | } |
564 | 62.4k | else if (*pszFormatCur == ')') |
565 | 2.63k | { |
566 | 2.63k | if (nGroupSubFieldCount == INT_MAX) |
567 | 407 | { |
568 | 407 | CPLError(CE_Failure, CPLE_AppDefined, |
569 | 407 | "Tag %s: invalid " |
570 | 407 | "formatControls: %s", |
571 | 407 | osTag.c_str(), |
572 | 407 | _formatControls.c_str()); |
573 | 407 | return false; |
574 | 407 | } |
575 | 2.23k | nGroupSubFieldCount++; |
576 | 2.23k | break; |
577 | 2.63k | } |
578 | 59.7k | else |
579 | 59.7k | { |
580 | 59.7k | ++pszFormatCur; |
581 | 59.7k | } |
582 | 76.4k | } |
583 | 2.86k | if (!*pszFormatCur) |
584 | 636 | { |
585 | 636 | CPLError(CE_Failure, CPLE_AppDefined, |
586 | 636 | "Tag %s: invalid formatControls: %s", |
587 | 636 | osTag.c_str(), |
588 | 636 | _formatControls.c_str()); |
589 | 636 | return false; |
590 | 636 | } |
591 | 2.23k | ++pszFormatCur; |
592 | 2.23k | if (nGroupSubFieldCount < 0 || |
593 | 2.23k | nGroupSubFieldCount > INT_MAX / nRepeat || |
594 | 1.68k | nSubFieldCounter > |
595 | 1.68k | INT_MAX - nGroupSubFieldCount * nRepeat) |
596 | 693 | { |
597 | 693 | CPLError(CE_Failure, CPLE_AppDefined, |
598 | 693 | "Tag %s: invalid " |
599 | 693 | "formatControls: %s", |
600 | 693 | osTag.c_str(), |
601 | 693 | _formatControls.c_str()); |
602 | 693 | return false; |
603 | 693 | } |
604 | 1.53k | nSubFieldCounter += nGroupSubFieldCount * nRepeat; |
605 | 1.53k | if (*pszFormatCur == ')') |
606 | 227 | break; |
607 | 1.31k | if (*pszFormatCur != ',') |
608 | 677 | { |
609 | 677 | CPLError(CE_Failure, CPLE_AppDefined, |
610 | 677 | "Tag %s: invalid formatControls: %s", |
611 | 677 | osTag.c_str(), |
612 | 677 | _formatControls.c_str()); |
613 | 677 | return false; |
614 | 677 | } |
615 | 634 | ++pszFormatCur; |
616 | 634 | } |
617 | 32.1k | else |
618 | 32.1k | { |
619 | 32.1k | nSubFieldCounter += nRepeat; |
620 | 32.1k | pszFormatCur = pszNext; |
621 | 105k | while (*pszFormatCur && *pszFormatCur != ',' && |
622 | 74.0k | *pszFormatCur != ')') |
623 | 73.6k | { |
624 | 73.6k | ++pszFormatCur; |
625 | 73.6k | } |
626 | 32.1k | if (*pszFormatCur == ')') |
627 | 380 | break; |
628 | 31.7k | if (*pszFormatCur != ',') |
629 | 593 | { |
630 | 593 | CPLError(CE_Failure, CPLE_AppDefined, |
631 | 593 | "Tag %s: invalid formatControls: %s", |
632 | 593 | osTag.c_str(), |
633 | 593 | _formatControls.c_str()); |
634 | 593 | return false; |
635 | 593 | } |
636 | 31.1k | ++pszFormatCur; |
637 | 31.1k | } |
638 | 31.8k | justAfterFieldFormat = true; |
639 | 31.8k | } |
640 | 385k | else if (*pszFormatCur == ',') |
641 | 65.6k | { |
642 | 65.6k | ++nSubFieldCounter; |
643 | 65.6k | ++pszFormatCur; |
644 | 65.6k | justAfterFieldFormat = true; |
645 | 65.6k | } |
646 | 320k | else if (*pszFormatCur == '(') |
647 | 15.1k | { |
648 | 15.1k | ++nParenthesisLevel; |
649 | 15.1k | ++pszFormatCur; |
650 | 15.1k | justAfterFieldFormat = false; |
651 | 15.1k | } |
652 | 305k | else if (*pszFormatCur == ')') |
653 | 15.3k | { |
654 | 15.3k | if (--nParenthesisLevel < 0) |
655 | 1.54k | { |
656 | 1.54k | ++nSubFieldCounter; |
657 | 1.54k | break; |
658 | 1.54k | } |
659 | 13.7k | ++pszFormatCur; |
660 | 13.7k | justAfterFieldFormat = false; |
661 | 13.7k | } |
662 | 289k | else |
663 | 289k | { |
664 | 289k | ++pszFormatCur; |
665 | 289k | justAfterFieldFormat = false; |
666 | 289k | } |
667 | | |
668 | 416k | if (nSubFieldCounter > nSubfieldsInPart) |
669 | 850 | { |
670 | 850 | CPLError(CE_Failure, CPLE_AppDefined, |
671 | 850 | "Tag %s: mismatch between arrayDescr:%s and " |
672 | 850 | "formatControls: %s", |
673 | 850 | osTag.c_str(), _arrayDescr.c_str(), |
674 | 850 | _formatControls.c_str()); |
675 | 850 | return false; |
676 | 850 | } |
677 | 415k | else if (nSubFieldCounter == nSubfieldsInPart) |
678 | 24.6k | { |
679 | 24.6k | break; |
680 | 24.6k | } |
681 | 416k | } |
682 | 44.7k | if (i < aosPartDescr.size() - 1) |
683 | 27.1k | { |
684 | 27.1k | if (*pszFormatCur == 0 || pszFormatCur[-1] != ',' || |
685 | 25.8k | nParenthesisLevel > 0) |
686 | 1.66k | { |
687 | 1.66k | CPLError(CE_Failure, CPLE_AppDefined, |
688 | 1.66k | "Tag %s: invalid formatControls: %s", |
689 | 1.66k | osTag.c_str(), _formatControls.c_str()); |
690 | 1.66k | return false; |
691 | 1.66k | } |
692 | 25.5k | if (nSubFieldCounter != nSubfieldsInPart) |
693 | 577 | { |
694 | 577 | CPLError(CE_Failure, CPLE_AppDefined, |
695 | 577 | "Tag %s: mismatch between arrayDescr:%s and " |
696 | 577 | "formatControls: %s", |
697 | 577 | osTag.c_str(), _arrayDescr.c_str(), |
698 | 577 | _formatControls.c_str()); |
699 | 577 | return false; |
700 | 577 | } |
701 | 25.5k | } |
702 | | |
703 | 42.5k | std::string osPartFormatControls; |
704 | 42.5k | if (i < aosPartDescr.size() - 1) |
705 | 24.9k | { |
706 | 24.9k | if (pszFormatCur == pszFormatStart) |
707 | 588 | { |
708 | 588 | CPLError(CE_Failure, CPLE_AppDefined, |
709 | 588 | "Tag %s: mismatch between arrayDescr:%s and " |
710 | 588 | "formatControls or invalid formatControls: %s", |
711 | 588 | osTag.c_str(), _arrayDescr.c_str(), |
712 | 588 | _formatControls.c_str()); |
713 | 588 | return false; |
714 | 588 | } |
715 | 24.3k | osPartFormatControls = '('; |
716 | 24.3k | osPartFormatControls.append( |
717 | 24.3k | pszFormatStart, pszFormatCur - pszFormatStart - 1); |
718 | 24.3k | osPartFormatControls += ')'; |
719 | 24.3k | } |
720 | 17.6k | else |
721 | 17.6k | { |
722 | 17.6k | if (*pszFormatStart == '(' && _formatControls.size() > 2 && |
723 | 16.1k | _formatControls[_formatControls.size() - 2] == ')') |
724 | 15.7k | { |
725 | | // S101 2.0 |
726 | 15.7k | osPartFormatControls.append(pszFormatStart, |
727 | 15.7k | strlen(pszFormatStart) - 1); |
728 | 15.7k | } |
729 | 1.82k | else |
730 | 1.82k | { |
731 | | // Earlier versions |
732 | 1.82k | osPartFormatControls = '('; |
733 | 1.82k | osPartFormatControls += pszFormatStart; |
734 | 1.82k | } |
735 | 17.6k | } |
736 | | |
737 | 41.9k | auto poPartFieldDefn = std::make_unique<DDFFieldDefn>(); |
738 | 41.9k | poPartFieldDefn->poModule = poModule; |
739 | | // poPartFieldDefn->osTag: not set on purpose |
740 | | // poPartFieldDefn->_fieldName: not set on purpose |
741 | 41.9k | poPartFieldDefn->_arrayDescr = osPartDescr; |
742 | 41.9k | poPartFieldDefn->_formatControls = |
743 | 41.9k | std::move(osPartFormatControls); |
744 | 41.9k | poPartFieldDefn->_data_struct_code = |
745 | 41.9k | dsc_vector; // not necessarily exact, but good enough |
746 | 41.9k | poPartFieldDefn->_data_type_code = _data_type_code; |
747 | 41.9k | poPartFieldDefn->bRepeatingSubfields = |
748 | 41.9k | !osPartDescr.empty() && osPartDescr.front() == '*'; |
749 | | |
750 | 41.9k | if (!poPartFieldDefn->BuildSubfields() || |
751 | 41.9k | !poPartFieldDefn->ApplyFormats()) |
752 | 3.49k | return false; |
753 | | |
754 | 38.4k | apoFieldParts.push_back(std::move(poPartFieldDefn)); |
755 | 38.4k | } |
756 | | |
757 | 15.9k | return true; |
758 | 29.3k | } |
759 | 39.8k | } |
760 | | |
761 | | /* -------------------------------------------------------------------- */ |
762 | | /* It is valid to define a field with _arrayDesc */ |
763 | | /* '*STPT!CTPT!ENPT*YCOO!XCOO' and formatControls '(2b24)'. */ |
764 | | /* This basically indicates that there are 3 (YCOO,XCOO) */ |
765 | | /* structures named STPT, CTPT and ENPT. But we can't handle */ |
766 | | /* such a case gracefully here, so we just ignore the */ |
767 | | /* "structure names" and treat such a thing as a repeating */ |
768 | | /* YCOO/XCOO array. This occurs with the AR2D field of some */ |
769 | | /* AML S-57 files for instance. */ |
770 | | /* */ |
771 | | /* We accomplish this by ignoring everything before the last */ |
772 | | /* '*' in the subfield list. */ |
773 | | /* -------------------------------------------------------------------- */ |
774 | 298k | const char *pszSublist = _arrayDescr.c_str(); |
775 | 298k | if (strrchr(pszSublist, '*') != nullptr) |
776 | 100k | pszSublist = strrchr(pszSublist, '*'); |
777 | | |
778 | | /* -------------------------------------------------------------------- */ |
779 | | /* Strip off the repeating marker, when it occurs, but mark our */ |
780 | | /* field as repeating. */ |
781 | | /* -------------------------------------------------------------------- */ |
782 | 298k | if (pszSublist[0] == '*') |
783 | 100k | { |
784 | 100k | bRepeatingSubfields = TRUE; |
785 | 100k | pszSublist++; |
786 | 100k | } |
787 | | |
788 | | /* -------------------------------------------------------------------- */ |
789 | | /* split list of fields . */ |
790 | | /* -------------------------------------------------------------------- */ |
791 | 298k | const CPLStringList aosSubfieldNames( |
792 | 298k | CSLTokenizeStringComplex(pszSublist, "!", FALSE, FALSE)); |
793 | | |
794 | | /* -------------------------------------------------------------------- */ |
795 | | /* minimally initialize the subfields. More will be done later. */ |
796 | | /* -------------------------------------------------------------------- */ |
797 | 298k | for (const char *pszSubfieldName : cpl::Iterate(aosSubfieldNames)) |
798 | 1.54M | { |
799 | 1.54M | auto poSFDefn = std::make_unique<DDFSubfieldDefn>(); |
800 | | |
801 | 1.54M | poSFDefn->SetName(pszSubfieldName); |
802 | 1.54M | AddSubfield(std::move(poSFDefn), true); |
803 | 1.54M | } |
804 | | |
805 | 298k | return true; |
806 | 327k | } |
807 | | |
808 | | /************************************************************************/ |
809 | | /* ExtractSubstring() */ |
810 | | /* */ |
811 | | /* Extract a substring terminated by a comma (or end of */ |
812 | | /* string). Commas in brackets are ignored as terminated with */ |
813 | | /* bracket nesting understood gracefully. If the returned */ |
814 | | /* string would begin and end with a bracket then strip off the */ |
815 | | /* brackets. */ |
816 | | /* */ |
817 | | /* Given a string like "(A,3(B,C),D),X,Y)" return "A,3(B,C),D". */ |
818 | | /* Giveh a string like "3A,2C" return "3A". */ |
819 | | /* Giveh a string like "(3A,2C" return an empty string */ |
820 | | /* Giveh a string like "3A),2C" return an empty string */ |
821 | | /************************************************************************/ |
822 | | |
823 | | std::string DDFFieldDefn::ExtractSubstring(const char *pszSrc) |
824 | | |
825 | 535k | { |
826 | 535k | int nBracket = 0; |
827 | 535k | int i = 0; // Used after for. |
828 | 7.28M | for (; pszSrc[i] != '\0' && (nBracket > 0 || pszSrc[i] != ','); i++) |
829 | 6.75M | { |
830 | 6.75M | if (pszSrc[i] == '(') |
831 | 984k | nBracket++; |
832 | 5.76M | else if (pszSrc[i] == ')') |
833 | 983k | { |
834 | 983k | nBracket--; |
835 | 983k | if (nBracket < 0) |
836 | 1.08k | return std::string(); |
837 | 983k | } |
838 | 6.75M | } |
839 | 534k | if (nBracket > 0) |
840 | 1.78k | return std::string(); |
841 | | |
842 | 532k | if (pszSrc[0] == '(') |
843 | 283k | { |
844 | 283k | CPLAssert(i >= 2); |
845 | 283k | return std::string(pszSrc + 1, i - 2); |
846 | 283k | } |
847 | 248k | else |
848 | 248k | { |
849 | 248k | return std::string(pszSrc, i); |
850 | 248k | } |
851 | 532k | } |
852 | | |
853 | | /************************************************************************/ |
854 | | /* ExpandFormat() */ |
855 | | /************************************************************************/ |
856 | | |
857 | | std::string DDFFieldDefn::ExpandFormat(const char *pszSrc) |
858 | | |
859 | 807k | { |
860 | 807k | std::string osDest; |
861 | 807k | size_t iSrc = 0; |
862 | | |
863 | 5.81M | while (pszSrc[iSrc] != '\0') |
864 | 5.02M | { |
865 | | // This is presumably an extra level of brackets around some |
866 | | // binary stuff related to rescanning which we don't care to do |
867 | | // (see 6.4.3.3 of the standard. We just strip off the extra |
868 | | // layer of brackets. |
869 | 5.02M | if ((iSrc == 0 || pszSrc[iSrc - 1] == ',') && pszSrc[iSrc] == '(') |
870 | 279k | { |
871 | 279k | const std::string osContents = ExtractSubstring(pszSrc + iSrc); |
872 | 279k | if (osContents.empty()) |
873 | 2.71k | { |
874 | 2.71k | return std::string(); |
875 | 2.71k | } |
876 | 276k | const std::string osExpandedContents = |
877 | 276k | ExpandFormat(osContents.c_str()); |
878 | 276k | if (osExpandedContents.empty()) |
879 | 2.79k | { |
880 | 2.79k | return std::string(); |
881 | 2.79k | } |
882 | | |
883 | 273k | if (osDest.size() + osExpandedContents.size() > 1024 * 1024) |
884 | 4 | { |
885 | 4 | return std::string(); |
886 | 4 | } |
887 | | |
888 | 273k | osDest += osExpandedContents; |
889 | | |
890 | 273k | iSrc += osContents.size() + 2; |
891 | 273k | } |
892 | | |
893 | | // This is a repeated subclause. |
894 | 4.74M | else if ((iSrc == 0 || pszSrc[iSrc - 1] == ',') && |
895 | 1.22M | isdigit(static_cast<unsigned char>(pszSrc[iSrc]))) |
896 | 257k | { |
897 | 257k | const int nRepeat = atoi(pszSrc + iSrc); |
898 | | // 100: arbitrary number. Higher values might cause performance |
899 | | // problems in the below loop |
900 | 257k | if (nRepeat < 0 || nRepeat > 100) |
901 | 1.22k | { |
902 | 1.22k | CPLError(CE_Failure, CPLE_AppDefined, |
903 | 1.22k | "Too large repeat count: %d", nRepeat); |
904 | 1.22k | return std::string(); |
905 | 1.22k | } |
906 | | |
907 | | // Skip over repeat count. |
908 | 256k | const char *pszNext = pszSrc + iSrc; // Used after for. |
909 | 527k | for (; isdigit(static_cast<unsigned char>(*pszNext)); pszNext++) |
910 | 270k | iSrc++; |
911 | | |
912 | 256k | const std::string osContents = ExtractSubstring(pszNext); |
913 | 256k | if (osContents.empty()) |
914 | 984 | { |
915 | 984 | return std::string(); |
916 | 984 | } |
917 | 255k | const std::string osExpandedContents = |
918 | 255k | ExpandFormat(osContents.c_str()); |
919 | 255k | if (osExpandedContents.empty()) |
920 | 1.98k | { |
921 | 1.98k | return std::string(); |
922 | 1.98k | } |
923 | | |
924 | 253k | const size_t nExpandedContentsLen = osExpandedContents.size(); |
925 | 253k | if (osDest.size() + nExpandedContentsLen * nRepeat > 1024 * 1024) |
926 | 324 | { |
927 | 324 | return std::string(); |
928 | 324 | } |
929 | | |
930 | 1.28M | for (int i = 0; i < nRepeat; i++) |
931 | 1.02M | { |
932 | 1.02M | if (i > 0) |
933 | 777k | osDest += ','; |
934 | 1.02M | osDest += osExpandedContents; |
935 | 1.02M | } |
936 | | |
937 | 253k | if (pszNext[0] == '(') |
938 | 4.91k | iSrc += osContents.size() + 2; |
939 | 248k | else |
940 | 248k | iSrc += osContents.size(); |
941 | 253k | } |
942 | 4.48M | else |
943 | 4.48M | { |
944 | 4.48M | osDest += pszSrc[iSrc++]; |
945 | 4.48M | } |
946 | 5.02M | } |
947 | | |
948 | 797k | return osDest; |
949 | 807k | } |
950 | | |
951 | | /************************************************************************/ |
952 | | /* ApplyFormats() */ |
953 | | /* */ |
954 | | /* This method parses the format string partially, and then */ |
955 | | /* applies a subfield format string to each subfield object. */ |
956 | | /* It in turn does final parsing of the subfield formats. */ |
957 | | /************************************************************************/ |
958 | | |
959 | | int DDFFieldDefn::ApplyFormats() |
960 | | |
961 | 298k | { |
962 | | /* -------------------------------------------------------------------- */ |
963 | | /* Verify that the format string is contained within brackets. */ |
964 | | /* -------------------------------------------------------------------- */ |
965 | 298k | if (_formatControls.size() < 2 || _formatControls[0] != '(' || |
966 | 279k | _formatControls.back() != ')') |
967 | 22.7k | { |
968 | 22.7k | CPLError(CE_Warning, static_cast<CPLErrorNum>(CPLE_DiscardedFormat), |
969 | 22.7k | "Format controls for `%s' field missing brackets:%s", |
970 | 22.7k | osTag.c_str(), _formatControls.c_str()); |
971 | | |
972 | 22.7k | return FALSE; |
973 | 22.7k | } |
974 | | |
975 | | /* -------------------------------------------------------------------- */ |
976 | | /* Duplicate the string, and strip off the brackets. */ |
977 | | /* -------------------------------------------------------------------- */ |
978 | | |
979 | 275k | const std::string osFormatList = ExpandFormat(_formatControls.c_str()); |
980 | 275k | if (osFormatList.empty()) |
981 | 5.30k | { |
982 | 5.30k | CPLError(CE_Warning, static_cast<CPLErrorNum>(CPLE_DiscardedFormat), |
983 | 5.30k | "Invalid format controls for `%s': %s", osTag.c_str(), |
984 | 5.30k | _formatControls.c_str()); |
985 | 5.30k | return FALSE; |
986 | 5.30k | } |
987 | | |
988 | | /* -------------------------------------------------------------------- */ |
989 | | /* Tokenize based on commas. */ |
990 | | /* -------------------------------------------------------------------- */ |
991 | 270k | const CPLStringList aosFormatItems( |
992 | 270k | CSLTokenizeStringComplex(osFormatList.c_str(), ",", FALSE, FALSE)); |
993 | | |
994 | | /* -------------------------------------------------------------------- */ |
995 | | /* Apply the format items to subfields. */ |
996 | | /* -------------------------------------------------------------------- */ |
997 | 270k | int iFormatItem = 0; // Used after for. |
998 | | |
999 | 1.74M | for (; iFormatItem < aosFormatItems.size(); iFormatItem++) |
1000 | 1.48M | { |
1001 | 1.48M | const char *pszPastPrefix = aosFormatItems[iFormatItem]; |
1002 | 1.48M | while (*pszPastPrefix >= '0' && *pszPastPrefix <= '9') |
1003 | 0 | pszPastPrefix++; |
1004 | | |
1005 | | /////////////////////////////////////////////////////////////// |
1006 | | // Did we get too many formats for the subfields created |
1007 | | // by names? This may be legal by the 8211 specification, but |
1008 | | // isn't encountered in any formats we care about so we just |
1009 | | // blow. |
1010 | | |
1011 | 1.48M | if (iFormatItem >= GetSubfieldCount()) |
1012 | 11.1k | { |
1013 | 11.1k | CPLError(CE_Warning, static_cast<CPLErrorNum>(CPLE_DiscardedFormat), |
1014 | 11.1k | "Got more formats than subfields for field `%s'.", |
1015 | 11.1k | osTag.c_str()); |
1016 | 11.1k | break; |
1017 | 11.1k | } |
1018 | | |
1019 | 1.47M | if (!apoSubfields[iFormatItem]->SetFormat(pszPastPrefix)) |
1020 | 8.20k | { |
1021 | 8.20k | return FALSE; |
1022 | 8.20k | } |
1023 | 1.47M | } |
1024 | | |
1025 | | /* -------------------------------------------------------------------- */ |
1026 | | /* Verify that we got enough formats, cleanup and return. */ |
1027 | | /* -------------------------------------------------------------------- */ |
1028 | | |
1029 | 262k | if (iFormatItem < GetSubfieldCount()) |
1030 | 1.34k | { |
1031 | 1.34k | CPLError(CE_Warning, static_cast<CPLErrorNum>(CPLE_DiscardedFormat), |
1032 | 1.34k | "Got less formats than subfields for field `%s'.", |
1033 | 1.34k | osTag.c_str()); |
1034 | 1.34k | return FALSE; |
1035 | 1.34k | } |
1036 | | |
1037 | | /* -------------------------------------------------------------------- */ |
1038 | | /* If all the fields are fixed width, then we are fixed width */ |
1039 | | /* too. This is important for repeating fields. */ |
1040 | | /* -------------------------------------------------------------------- */ |
1041 | 260k | nFixedWidth = 0; |
1042 | 260k | for (auto &poSubfield : apoSubfields) |
1043 | 1.17M | { |
1044 | 1.17M | if (poSubfield->GetWidth() == 0) |
1045 | 97.0k | { |
1046 | 97.0k | nFixedWidth = 0; |
1047 | 97.0k | break; |
1048 | 97.0k | } |
1049 | 1.07M | else |
1050 | 1.07M | { |
1051 | 1.07M | if (nFixedWidth > INT_MAX - poSubfield->GetWidth()) |
1052 | 500 | { |
1053 | 500 | CPLError(CE_Warning, |
1054 | 500 | static_cast<CPLErrorNum>(CPLE_DiscardedFormat), |
1055 | 500 | "Invalid format controls for `%s': %s", osTag.c_str(), |
1056 | 500 | _formatControls.c_str()); |
1057 | 500 | return FALSE; |
1058 | 500 | } |
1059 | 1.07M | nFixedWidth += poSubfield->GetWidth(); |
1060 | 1.07M | } |
1061 | 1.17M | } |
1062 | | |
1063 | 260k | return TRUE; |
1064 | 260k | } |
1065 | | |
1066 | | /************************************************************************/ |
1067 | | /* FindSubfieldDefn() */ |
1068 | | /************************************************************************/ |
1069 | | |
1070 | | /** |
1071 | | * Find a subfield definition by its mnemonic tag. |
1072 | | * |
1073 | | * @param pszMnemonic The name of the field. |
1074 | | * |
1075 | | * @return The subfield pointer, or NULL if there isn't any such subfield. |
1076 | | */ |
1077 | | |
1078 | | const DDFSubfieldDefn * |
1079 | | DDFFieldDefn::FindSubfieldDefn(const char *pszMnemonic) const |
1080 | | |
1081 | 2.30M | { |
1082 | 2.30M | for (const auto &poSubfield : apoSubfields) |
1083 | 5.36M | { |
1084 | 5.36M | if (EQUAL(poSubfield->GetName(), pszMnemonic)) |
1085 | 1.17M | return poSubfield.get(); |
1086 | 5.36M | } |
1087 | | |
1088 | 1.12M | return nullptr; |
1089 | 2.30M | } |
1090 | | |
1091 | | /************************************************************************/ |
1092 | | /* GetDefaultValue() */ |
1093 | | /************************************************************************/ |
1094 | | |
1095 | | /** |
1096 | | * Return default data for field instance. |
1097 | | */ |
1098 | | |
1099 | | char *DDFFieldDefn::GetDefaultValue(int *pnSize) const |
1100 | | |
1101 | 588 | { |
1102 | 588 | if (!apoFieldParts.empty()) |
1103 | 174 | { |
1104 | 174 | std::string osData; |
1105 | 174 | for (auto &poPartFieldDefn : apoFieldParts) |
1106 | 348 | { |
1107 | 348 | if (poPartFieldDefn->IsRepeating()) // only last part |
1108 | 174 | break; |
1109 | 174 | int nPartSize = 0; |
1110 | 174 | char *pszVal = poPartFieldDefn->GetDefaultValue(&nPartSize); |
1111 | 174 | if (!pszVal) |
1112 | 0 | return nullptr; |
1113 | 174 | osData.append(pszVal, nPartSize); |
1114 | 174 | CPLFree(pszVal); |
1115 | 174 | } |
1116 | 174 | if (pnSize) |
1117 | 174 | *pnSize = static_cast<int>(osData.size()); |
1118 | 174 | char *pabyRet = static_cast<char *>(CPLMalloc(osData.size())); |
1119 | 174 | memcpy(pabyRet, osData.data(), osData.size()); |
1120 | 174 | return pabyRet; |
1121 | 174 | } |
1122 | | |
1123 | | /* -------------------------------------------------------------------- */ |
1124 | | /* Loop once collecting the sum of the subfield lengths. */ |
1125 | | /* -------------------------------------------------------------------- */ |
1126 | 414 | int nTotalSize = 0; |
1127 | 414 | for (auto &poSubfield : apoSubfields) |
1128 | 2.08k | { |
1129 | 2.08k | int nSubfieldSize = 0; |
1130 | | |
1131 | 2.08k | if (!poSubfield->GetDefaultValue(nullptr, 0, &nSubfieldSize)) |
1132 | 0 | return nullptr; |
1133 | 2.08k | nTotalSize += nSubfieldSize; |
1134 | 2.08k | } |
1135 | | |
1136 | | /* -------------------------------------------------------------------- */ |
1137 | | /* Allocate buffer. */ |
1138 | | /* -------------------------------------------------------------------- */ |
1139 | 414 | char *pachData = static_cast<char *>(CPLMalloc(nTotalSize)); |
1140 | | |
1141 | 414 | if (pnSize != nullptr) |
1142 | 414 | *pnSize = nTotalSize; |
1143 | | |
1144 | | /* -------------------------------------------------------------------- */ |
1145 | | /* Loop again, collecting actual default values. */ |
1146 | | /* -------------------------------------------------------------------- */ |
1147 | 414 | int nOffset = 0; |
1148 | 414 | for (auto &poSubfield : apoSubfields) |
1149 | 2.08k | { |
1150 | 2.08k | int nSubfieldSize; |
1151 | | |
1152 | 2.08k | if (!poSubfield->GetDefaultValue(pachData + nOffset, |
1153 | 2.08k | nTotalSize - nOffset, &nSubfieldSize)) |
1154 | 0 | { |
1155 | 0 | CPLAssert(false); |
1156 | 0 | return nullptr; |
1157 | 0 | } |
1158 | | |
1159 | 2.08k | nOffset += nSubfieldSize; |
1160 | 2.08k | } |
1161 | | |
1162 | 414 | CPLAssert(nOffset == nTotalSize); |
1163 | | |
1164 | 414 | return pachData; |
1165 | 414 | } |