Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: ISO 8211 Access
4 : * Purpose: Main declarations for ISO 8211.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 1999, Frank Warmerdam <warmerdam@pobox.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #ifndef ISO8211_H_INCLUDED
14 : #define ISO8211_H_INCLUDED
15 :
16 : #include "cpl_port.h"
17 : #include "cpl_vsi.h"
18 :
19 : #include <array>
20 : #include <memory>
21 : #include <vector>
22 :
23 : /**
24 : General data type
25 : */
26 : typedef enum
27 : {
28 : DDFInt,
29 : DDFFloat,
30 : DDFString,
31 : DDFBinaryString
32 : } DDFDataType;
33 :
34 : /************************************************************************/
35 : /* These should really be private to the library ... they are */
36 : /* mostly conveniences. */
37 : /************************************************************************/
38 :
39 : int CPL_ODLL DDFScanInt(const char *pszString, int nMaxChars);
40 : int CPL_ODLL DDFScanVariable(const char *pszString, int nMaxChars,
41 : int nDelimChar);
42 : std::string CPL_ODLL DDFFetchVariable(const char *pszString, int nMaxChars,
43 : int nDelimChar1, int nDelimChar2,
44 : int *pnConsumedChars);
45 :
46 : #define DDF_FIELD_TERMINATOR 30
47 : #define DDF_UNIT_TERMINATOR 31
48 :
49 : /************************************************************************/
50 : /* Predeclarations */
51 : /************************************************************************/
52 :
53 : class DDFFieldDefn;
54 : class DDFSubfieldDefn;
55 : class DDFRecord;
56 : class DDFField;
57 :
58 : /************************************************************************/
59 : /* DDFModule */
60 : /************************************************************************/
61 :
62 : /**
63 : The primary class for reading ISO 8211 files. This class contains all
64 : the information read from the DDR record, and is used to read records
65 : from the file.
66 : */
67 :
68 181 : class CPL_ODLL DDFModule
69 : {
70 : public:
71 : DDFModule();
72 : ~DDFModule();
73 :
74 : int Open(const char *pszFilename, int bFailQuietly = FALSE);
75 : int Create(const char *pszFilename);
76 : void Close();
77 :
78 : int Initialize(char chInterchangeLevel = '3', char chLeaderIden = 'L',
79 : char chCodeExtensionIndicator = 'E',
80 : char chVersionNumber = '1', char chAppIndicator = ' ',
81 : const std::array<char, 3> &achExtendedCharSet = {' ', '!',
82 : ' '},
83 : int nSizeFieldLength = 3, int nSizeFieldPos = 4,
84 : int nSizeFieldTag = 4);
85 :
86 : void Dump(FILE *fp) const;
87 :
88 : DDFRecord *ReadRecord();
89 : void Rewind(long) = delete;
90 : void Rewind(vsi_l_offset nOffset = static_cast<vsi_l_offset>(-1));
91 :
92 : const DDFFieldDefn *FindFieldDefn(const char *) const;
93 :
94 111555 : DDFFieldDefn *FindFieldDefn(const char *name)
95 : {
96 : return const_cast<DDFFieldDefn *>(
97 111555 : const_cast<const DDFModule *>(this)->FindFieldDefn(name));
98 : }
99 :
100 : /** Fetch the number of defined fields. */
101 :
102 21 : int GetFieldCount() const
103 : {
104 21 : return static_cast<int>(apoFieldDefns.size());
105 : }
106 :
107 : DDFFieldDefn *GetField(int);
108 : void AddField(std::unique_ptr<DDFFieldDefn> poNewFDefn);
109 :
110 : // This is really just for internal use.
111 2497 : int GetFieldControlLength() const
112 : {
113 2497 : return _fieldControlLength;
114 : }
115 :
116 : // This is just for DDFRecord.
117 56517 : VSILFILE *GetFP()
118 : {
119 56517 : return fpDDF;
120 : }
121 :
122 27975 : int GetSizeFieldTag() const
123 : {
124 27975 : return _sizeFieldTag;
125 : }
126 :
127 : // Advanced uses for 8211dump/8211createfromxml
128 2 : int GetSizeFieldPos() const
129 : {
130 2 : return _sizeFieldPos;
131 : }
132 :
133 2 : int GetSizeFieldLength() const
134 : {
135 2 : return _sizeFieldLength;
136 : }
137 :
138 2 : char GetInterchangeLevel() const
139 : {
140 2 : return _interchangeLevel;
141 : }
142 :
143 2 : char GetLeaderIden() const
144 : {
145 2 : return _leaderIden;
146 : }
147 :
148 2 : char GetCodeExtensionIndicator() const
149 : {
150 2 : return _inlineCodeExtensionIndicator;
151 : }
152 :
153 2 : char GetVersionNumber() const
154 : {
155 2 : return _versionNumber;
156 : }
157 :
158 2 : char GetAppIndicator() const
159 : {
160 2 : return _appIndicator;
161 : }
162 :
163 6 : const std::array<char, 3> &GetExtendedCharSet() const
164 : {
165 6 : return _extendedCharSet;
166 : }
167 :
168 1 : void SetFieldControlLength(int nVal)
169 : {
170 1 : _fieldControlLength = nVal;
171 1 : }
172 :
173 : private:
174 : VSILFILE *fpDDF = nullptr;
175 : bool bReadOnly = false;
176 : vsi_l_offset nFirstRecordOffset = 0;
177 :
178 : char _interchangeLevel = '\0';
179 : char _inlineCodeExtensionIndicator = '\0';
180 : char _versionNumber = '\0';
181 : char _appIndicator = '\0';
182 : int _fieldControlLength = 9;
183 : std::array<char, 3> _extendedCharSet = {' ', '!', ' '};
184 :
185 : int _recLength = 0;
186 : char _leaderIden = 'L';
187 : int _fieldAreaStart = 0;
188 : int _sizeFieldLength = 0;
189 : int _sizeFieldPos = 0;
190 : int _sizeFieldTag = 0;
191 :
192 : // One DirEntry per field.
193 : std::vector<std::unique_ptr<DDFFieldDefn>> apoFieldDefns{};
194 :
195 : std::unique_ptr<DDFRecord> poRecord{};
196 :
197 : CPL_DISALLOW_COPY_ASSIGN(DDFModule)
198 : };
199 :
200 : /************************************************************************/
201 : /* DDFFieldDefn */
202 : /************************************************************************/
203 :
204 : typedef enum
205 : {
206 : dsc_elementary,
207 : dsc_vector,
208 : dsc_array,
209 : dsc_concatenated
210 : } DDF_data_struct_code;
211 :
212 : typedef enum
213 : {
214 : dtc_char_string,
215 : dtc_implicit_point,
216 : dtc_explicit_point,
217 : dtc_explicit_point_scaled,
218 : dtc_char_bit_string,
219 : dtc_bit_string,
220 : dtc_mixed_data_type
221 : } DDF_data_type_code;
222 :
223 : /**
224 : * Information from the DDR defining one field. Note that just because
225 : * a field is defined for a DDFModule doesn't mean that it actually occurs
226 : * on any records in the module. DDFFieldDefns are normally just significant
227 : * as containers of the DDFSubfieldDefns.
228 : */
229 :
230 3508 : class CPL_ODLL DDFFieldDefn
231 : {
232 : public:
233 : DDFFieldDefn();
234 : ~DDFFieldDefn();
235 :
236 : int Create(const char *pszTag, const char *pszFieldName,
237 : const char *pszDescription, DDF_data_struct_code eDataStructCode,
238 : DDF_data_type_code eDataTypeCode,
239 : const char *pszFormat = nullptr);
240 : void AddSubfield(std::unique_ptr<DDFSubfieldDefn> poNewSFDefn,
241 : bool bDontAddToFormat = false);
242 : void AddSubfield(const char *pszName, const char *pszFormat);
243 : int GenerateDDREntry(DDFModule *poModule, char **ppachData, int *pnLength);
244 :
245 : int Initialize(DDFModule *poModule, const char *pszTag, int nSize,
246 : const char *pachRecord);
247 :
248 : void Dump(FILE *fp) const;
249 :
250 : /** Fetch a pointer to the field name (tag).
251 : * @return this is an internal copy and should not be freed.
252 : */
253 2678796 : const char *GetName() const
254 : {
255 2678796 : return osTag.c_str();
256 : }
257 :
258 : /** Fetch a longer description of this field.
259 : * @return this is an internal copy and should not be freed.
260 : */
261 40 : const char *GetDescription() const
262 : {
263 40 : return _fieldName.c_str();
264 : }
265 :
266 : /** Get the number of subfields. */
267 36445 : int GetSubfieldCount() const
268 : {
269 36445 : return static_cast<int>(apoSubfields.size());
270 : }
271 :
272 879189 : const std::vector<std::unique_ptr<DDFSubfieldDefn>> &GetSubfields() const
273 : {
274 879189 : return apoSubfields;
275 : }
276 :
277 : const DDFSubfieldDefn *FindSubfieldDefn(const char *) const;
278 :
279 : /**
280 : * Get the width of this field. This function isn't normally used
281 : * by applications.
282 : *
283 : * @return The width of the field in bytes, or zero if the field is not
284 : * apparently of a fixed width.
285 : */
286 903595 : int GetFixedWidth() const
287 : {
288 903595 : return nFixedWidth;
289 : }
290 :
291 : /**
292 : * Fetch repeating flag.
293 : * @see DDFField::GetRepeatCount()
294 : * @return TRUE if the field is marked as repeating.
295 : */
296 92397 : bool IsRepeating() const
297 : {
298 92397 : return bRepeatingSubfields;
299 : }
300 :
301 : static std::string ExpandFormat(const char *);
302 :
303 : /** this is just for an S-57 hack for swedish data */
304 0 : void SetRepeatingFlag(bool bRepeating)
305 : {
306 0 : bRepeatingSubfields = bRepeating;
307 0 : }
308 :
309 : char *GetDefaultValue(int *pnSize) const;
310 :
311 40 : const char *GetArrayDescr() const
312 : {
313 40 : return _arrayDescr.c_str();
314 : }
315 :
316 40 : const char *GetFormatControls() const
317 : {
318 40 : return _formatControls.c_str();
319 : }
320 :
321 40 : DDF_data_struct_code GetDataStructCode() const
322 : {
323 40 : return _data_struct_code;
324 : }
325 :
326 40 : DDF_data_type_code GetDataTypeCode() const
327 : {
328 40 : return _data_type_code;
329 : }
330 :
331 : void SetFormatControls(const char *pszVal);
332 :
333 : private:
334 : static std::string ExtractSubstring(const char *);
335 :
336 : DDFModule *poModule = nullptr;
337 : std::string osTag{};
338 :
339 : std::string _fieldName{};
340 : std::string _arrayDescr{};
341 : std::string _formatControls{};
342 :
343 : bool bRepeatingSubfields = false;
344 : int nFixedWidth = 0; // zero if variable.
345 :
346 : void BuildSubfields();
347 : int ApplyFormats();
348 :
349 : DDF_data_struct_code _data_struct_code = dsc_elementary;
350 :
351 : DDF_data_type_code _data_type_code = dtc_char_string;
352 :
353 : std::vector<std::unique_ptr<DDFSubfieldDefn>> apoSubfields{};
354 :
355 : CPL_DISALLOW_COPY_ASSIGN(DDFFieldDefn)
356 : };
357 :
358 : /************************************************************************/
359 : /* DDFSubfieldDefn */
360 : /* */
361 : /* Information from the DDR record for one subfield of a */
362 : /* particular field. */
363 : /************************************************************************/
364 :
365 : /**
366 : * Information from the DDR record describing one subfield of a DDFFieldDefn.
367 : * All subfields of a field will occur in each occurrence of that field
368 : * (as a DDFField) in a DDFRecord. Subfield's actually contain formatted
369 : * data (as instances within a record).
370 : */
371 :
372 16112 : class CPL_ODLL DDFSubfieldDefn
373 : {
374 : public:
375 : DDFSubfieldDefn();
376 : ~DDFSubfieldDefn();
377 :
378 : void SetName(const char *pszName);
379 :
380 : /** Get pointer to subfield name. */
381 987765 : const char *GetName() const
382 : {
383 987765 : return osName.c_str();
384 : }
385 :
386 : /** Get pointer to subfield format string */
387 57596 : const char *GetFormat() const
388 : {
389 57596 : return osFormatString.c_str();
390 : }
391 :
392 : int SetFormat(const char *pszFormat);
393 :
394 : /**
395 : * Get the general type of the subfield. This can be used to
396 : * determine which of ExtractFloatData(), ExtractIntData() or
397 : * ExtractStringData() should be used.
398 : * @return The subfield type. One of DDFInt, DDFFloat, DDFString or
399 : * DDFBinaryString.
400 : */
401 :
402 2748 : DDFDataType GetType() const
403 : {
404 2748 : return eType;
405 : }
406 :
407 : double ExtractFloatData(const char *pachData, int nMaxBytes,
408 : int *pnConsumedBytes) const;
409 : int ExtractIntData(const char *pachData, int nMaxBytes,
410 : int *pnConsumedBytes) const;
411 : const char *ExtractStringData(const char *pachData, int nMaxBytes,
412 : int *pnConsumedBytes) const;
413 : int GetDataLength(const char *, int, int *) const;
414 : void DumpData(const char *pachData, int nMaxBytes, FILE *fp) const;
415 :
416 : int FormatStringValue(char *pachData, int nBytesAvailable, int *pnBytesUsed,
417 : const char *pszValue, int nValueLength = -1) const;
418 :
419 : int FormatIntValue(char *pachData, int nBytesAvailable, int *pnBytesUsed,
420 : int nNewValue) const;
421 :
422 : int FormatFloatValue(char *pachData, int nBytesAvailable, int *pnBytesUsed,
423 : double dfNewValue) const;
424 :
425 : /** Get the subfield width (zero for variable). */
426 64223 : int GetWidth() const
427 : {
428 64223 : return nFormatWidth;
429 : } // zero for variable.
430 :
431 : int GetDefaultValue(char *pachData, int nBytesAvailable,
432 : int *pnBytesUsed) const;
433 :
434 : void Dump(FILE *fp) const;
435 :
436 : /**
437 : Binary format: this is the digit immediately following the B or b for
438 : binary formats.
439 : */
440 : typedef enum
441 : {
442 : NotBinary = 0,
443 : UInt = 1,
444 : SInt = 2,
445 : FPReal = 3,
446 : FloatReal = 4,
447 : FloatComplex = 5
448 : } DDFBinaryFormat;
449 :
450 11565 : DDFBinaryFormat GetBinaryFormat() const
451 : {
452 11565 : return eBinaryFormat;
453 : }
454 :
455 : private:
456 : std::string osName{}; // a.k.a. subfield mnemonic
457 : std::string osFormatString{};
458 :
459 : DDFDataType eType = DDFString;
460 : DDFBinaryFormat eBinaryFormat = NotBinary;
461 :
462 : /* -------------------------------------------------------------------- */
463 : /* bIsVariable determines whether we using the */
464 : /* chFormatDelimiter (TRUE), or the fixed width (FALSE). */
465 : /* -------------------------------------------------------------------- */
466 : bool bIsVariable = true;
467 :
468 : char chFormatDelimiter = DDF_UNIT_TERMINATOR;
469 : int nFormatWidth = 0;
470 :
471 : /* -------------------------------------------------------------------- */
472 : /* Fetched string cache. This is where we hold the values */
473 : /* returned from ExtractStringData(). */
474 : /* -------------------------------------------------------------------- */
475 : mutable std::string osBuffer{};
476 : };
477 :
478 : /************************************************************************/
479 : /* DDFField */
480 : /* */
481 : /* This object represents one field in a DDFRecord. */
482 : /************************************************************************/
483 :
484 : /**
485 : * This object represents one field in a DDFRecord. This
486 : * models an instance of the fields data, rather than its data definition,
487 : * which is handled by the DDFFieldDefn class. Note that a DDFField
488 : * doesn't have DDFSubfield children as you would expect. To extract
489 : * subfield values use GetSubfieldData() to find the right data pointer and
490 : * then use ExtractIntData(), ExtractFloatData() or ExtractStringData().
491 : */
492 :
493 : class CPL_ODLL DDFField
494 : {
495 : public:
496 220536 : DDFField() = default;
497 :
498 : void Initialize(const DDFFieldDefn *, const char *pszData, int nSize);
499 :
500 : void Dump(FILE *fp) const;
501 :
502 : const char *GetSubfieldData(const DDFSubfieldDefn *, int * = nullptr,
503 : int = 0) const;
504 :
505 : const char *GetInstanceData(int nInstance, int *pnSize);
506 :
507 : /**
508 : * Return the pointer to the entire data block for this record. This
509 : * is an internal copy, and should not be freed by the application.
510 : */
511 120128 : const char *GetData() const
512 : {
513 120128 : return pachData;
514 : }
515 :
516 : /** Return the number of bytes in the data block returned by GetData(). */
517 121127 : int GetDataSize() const
518 : {
519 121127 : return nDataSize;
520 : }
521 :
522 : int GetRepeatCount() const;
523 :
524 : /** Fetch the corresponding DDFFieldDefn. */
525 2474776 : const DDFFieldDefn *GetFieldDefn() const
526 : {
527 2474776 : return poDefn;
528 : }
529 :
530 : private:
531 : const DDFFieldDefn *poDefn = nullptr;
532 :
533 : int nDataSize = 0;
534 :
535 : const char *pachData = nullptr;
536 : };
537 :
538 : /************************************************************************/
539 : /* DDFRecord */
540 : /* */
541 : /* Class that contains one DR record from a file. We read into */
542 : /* the same record object repeatedly to ensure that repeated */
543 : /* leaders can be easily preserved. */
544 : /************************************************************************/
545 :
546 : /**
547 : * Contains instance data from one data record (DR). The data is contained
548 : * as a list of DDFField instances partitioning the raw data into fields.
549 : */
550 :
551 : class CPL_ODLL DDFRecord
552 : {
553 : public:
554 : explicit DDFRecord(DDFModule *);
555 : ~DDFRecord();
556 :
557 : std::unique_ptr<DDFRecord> Clone() const;
558 : bool TransferTo(DDFModule *poTargetModule);
559 :
560 : void Dump(FILE *) const;
561 :
562 : /** Get the number of DDFFields on this record. */
563 212662 : int GetFieldCount() const
564 : {
565 212662 : return static_cast<int>(aoFields.size());
566 : }
567 :
568 : const DDFField *FindField(const char *, int = 0) const;
569 :
570 3300 : DDFField *FindField(const char *name, int i = 0)
571 : {
572 : return const_cast<DDFField *>(
573 3300 : const_cast<const DDFRecord *>(this)->FindField(name, i));
574 : }
575 :
576 : const DDFField *GetField(int) const;
577 :
578 28397 : DDFField *GetField(int i)
579 : {
580 : return const_cast<DDFField *>(
581 28397 : const_cast<const DDFRecord *>(this)->GetField(i));
582 : }
583 :
584 : int GetIntSubfield(const char *, int, const char *, int,
585 : int * = nullptr) const;
586 : double GetFloatSubfield(const char *, int, const char *, int,
587 : int * = nullptr) const;
588 : const char *GetStringSubfield(const char *, int, const char *, int,
589 : int * = nullptr) const;
590 :
591 : int SetIntSubfield(const char *pszField, int iFieldIndex,
592 : const char *pszSubfield, int iSubfieldIndex, int nValue);
593 : int SetStringSubfield(const char *pszField, int iFieldIndex,
594 : const char *pszSubfield, int iSubfieldIndex,
595 : const char *pszValue, int nValueLength = -1);
596 : int SetFloatSubfield(const char *pszField, int iFieldIndex,
597 : const char *pszSubfield, int iSubfieldIndex,
598 : double dfNewValue);
599 :
600 : /** Fetch size of records raw data (GetData()) in bytes. */
601 140 : int GetDataSize() const
602 : {
603 140 : return static_cast<int>(osData.size());
604 : }
605 :
606 : /**
607 : * Fetch the raw data for this record. The returned pointer is effectively
608 : * to the data for the first field of the record, and is of size
609 : * GetDataSize().
610 : */
611 224 : const char *GetData() const
612 : {
613 224 : return osData.c_str();
614 : }
615 :
616 : /**
617 : * Fetch the DDFModule with which this record is associated.
618 : */
619 :
620 0 : DDFModule *GetModule()
621 : {
622 0 : return poModule;
623 : }
624 :
625 : int ResizeField(DDFField *poField, int nNewDataSize);
626 : int DeleteField(DDFField *poField);
627 : DDFField *AddField(DDFFieldDefn *);
628 :
629 : int CreateDefaultFieldInstance(DDFField *poField, int iIndexWithinField);
630 :
631 : int SetFieldRaw(DDFField *poField, int iIndexWithinField,
632 : const char *pachRawData, int nRawDataSize);
633 : int UpdateFieldRaw(DDFField *poField, int iIndexWithinField,
634 : int nStartOffset, int nOldSize, const char *pachRawData,
635 : int nRawDataSize);
636 :
637 : int Write();
638 :
639 : // Advanced uses for 8211dump/8211createfromxml
640 140 : bool GetReuseHeader() const
641 : {
642 140 : return bReuseHeader;
643 : }
644 :
645 210 : int GetSizeFieldTag() const
646 : {
647 210 : return _sizeFieldTag;
648 : }
649 :
650 210 : int GetSizeFieldPos() const
651 : {
652 210 : return _sizeFieldPos;
653 : }
654 :
655 210 : int GetSizeFieldLength() const
656 : {
657 210 : return _sizeFieldLength;
658 : }
659 :
660 70 : void SetSizeFieldTag(int nVal)
661 : {
662 70 : _sizeFieldTag = nVal;
663 70 : }
664 :
665 70 : void SetSizeFieldPos(int nVal)
666 : {
667 70 : _sizeFieldPos = nVal;
668 70 : }
669 :
670 70 : void SetSizeFieldLength(int nVal)
671 : {
672 70 : _sizeFieldLength = nVal;
673 70 : }
674 :
675 : // This is really just for the DDFModule class.
676 : int Read();
677 : void Clear();
678 : void ResetDirectory();
679 :
680 : private:
681 : int ReadHeader();
682 :
683 : DDFModule *poModule = nullptr;
684 :
685 : bool bReuseHeader = false;
686 :
687 : int nFieldOffset = 0; // field data area, not dir entries.
688 :
689 : int _sizeFieldTag = 0;
690 : int _sizeFieldPos = 5;
691 : int _sizeFieldLength = 5;
692 :
693 : std::string osData{}; // Whole record except leader with header
694 :
695 : std::vector<DDFField> aoFields{};
696 :
697 : CPL_DISALLOW_COPY_ASSIGN(DDFRecord)
698 : };
699 :
700 : #endif /* ndef ISO8211_H_INCLUDED */
|