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