LCOV - code coverage report
Current view: top level - frmts/nitf - nitffile.c (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1226 1552 79.0 %
Date: 2024-11-21 22:18:42 Functions: 31 34 91.2 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  * $Id$
       3             :  *
       4             :  * Project:  NITF Read/Write Library
       5             :  * Purpose:  Module responsible for opening NITF file, populating NITFFile
       6             :  *           structure, and instantiating segment specific access objects.
       7             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       8             :  *
       9             :  **********************************************************************
      10             :  * Copyright (c) 2002, Frank Warmerdam
      11             :  * Copyright (c) 2007-2013, Even Rouault <even dot rouault at spatialys.com>
      12             :  *
      13             :  * SPDX-License-Identifier: MIT
      14             :  ****************************************************************************/
      15             : 
      16             : #include "nitflib.h"
      17             : #include "cpl_vsi.h"
      18             : #include "cpl_conv.h"
      19             : #include "cpl_string.h"
      20             : #include <stdbool.h>
      21             : 
      22             : #ifdef EMBED_RESOURCE_FILES
      23             : #include "embedded_resources.h"
      24             : #endif
      25             : 
      26             : #ifndef CPL_IGNORE_RET_VAL_INT_defined
      27             : #define CPL_IGNORE_RET_VAL_INT_defined
      28             : 
      29         762 : CPL_INLINE static void CPL_IGNORE_RET_VAL_INT(CPL_UNUSED int unused)
      30             : {
      31         762 : }
      32             : #endif
      33             : 
      34             : static int NITFWriteBLOCKA(VSILFILE *fp, vsi_l_offset nOffsetUDIDL,
      35             :                            int *pnOffset, char **papszOptions);
      36             : static int NITFWriteTREsFromOptions(VSILFILE *fp, vsi_l_offset nOffsetUDIDL,
      37             :                                     int *pnOffset, char **papszOptions,
      38             :                                     const char *pszTREPrefix);
      39             : 
      40             : static int NITFCollectSegmentInfo(NITFFile *psFile, int nFileHeaderLenSize,
      41             :                                   int nOffset, const char szType[3],
      42             :                                   int nHeaderLenSize, int nDataLenSize,
      43             :                                   GUIntBig *pnNextData);
      44             : 
      45             : static void NITFExtractAndRecodeMetadata(char ***ppapszMetadata,
      46             :                                          const char *pachHeader, int nStart,
      47             :                                          int nLength, const char *pszName,
      48             :                                          const char *pszSrcEncoding);
      49             : 
      50             : static int NITFWriteOption(VSILFILE *fp, char **papszOptions, size_t nWidth,
      51             :                            GUIntBig nLocation, const char *pszName,
      52             :                            const char *pszText);
      53             : 
      54             : /************************************************************************/
      55             : /*                              NITFOpen()                              */
      56             : /************************************************************************/
      57             : 
      58          30 : NITFFile *NITFOpen(const char *pszFilename, int bUpdatable)
      59             : 
      60             : {
      61             :     VSILFILE *fp;
      62             : 
      63             :     /* -------------------------------------------------------------------- */
      64             :     /*      Open the file.                                                  */
      65             :     /* -------------------------------------------------------------------- */
      66          30 :     if (bUpdatable)
      67          14 :         fp = VSIFOpenL(pszFilename, "r+b");
      68             :     else
      69          16 :         fp = VSIFOpenL(pszFilename, "rb");
      70             : 
      71          30 :     if (fp == NULL)
      72             :     {
      73           0 :         CPLError(CE_Failure, CPLE_OpenFailed, "Failed to open file %s.",
      74             :                  pszFilename);
      75           0 :         return NULL;
      76             :     }
      77             : 
      78          30 :     return NITFOpenEx(fp, pszFilename);
      79             : }
      80             : 
      81             : /************************************************************************/
      82             : /*                             NITFOpenEx()                             */
      83             : /************************************************************************/
      84             : 
      85         770 : NITFFile *NITFOpenEx(VSILFILE *fp, const char *pszFilename)
      86             : 
      87             : {
      88             :     char *pachHeader;
      89             :     NITFFile *psFile;
      90             :     int nHeaderLen, nOffset, nHeaderLenOffset;
      91             :     GUIntBig nNextData;
      92             :     char szTemp[128], achFSDWNG[6];
      93             :     GIntBig currentPos;
      94         770 :     int bTriedStreamingFileHeader = FALSE;
      95             : 
      96             :     /* -------------------------------------------------------------------- */
      97             :     /*      Check file type.                                                */
      98             :     /* -------------------------------------------------------------------- */
      99         770 :     if (VSIFSeekL(fp, 0, SEEK_SET) != 0 || VSIFReadL(szTemp, 1, 9, fp) != 9 ||
     100         770 :         (!STARTS_WITH_CI(szTemp, "NITF") && !STARTS_WITH_CI(szTemp, "NSIF")))
     101             :     {
     102           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     103             :                  "The file %s is not an NITF file.", pszFilename);
     104           0 :         CPL_IGNORE_RET_VAL_INT(VSIFCloseL(fp));
     105           0 :         return NULL;
     106             :     }
     107             : 
     108             :     /* -------------------------------------------------------------------- */
     109             :     /*      Read the FSDWNG field.                                          */
     110             :     /* -------------------------------------------------------------------- */
     111        1540 :     if (VSIFSeekL(fp, 280, SEEK_SET) != 0 ||
     112         770 :         VSIFReadL(achFSDWNG, 1, 6, fp) != 6)
     113             :     {
     114           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     115             :                  "Unable to read FSDWNG field from NITF file.  File is either "
     116             :                  "corrupt\n"
     117             :                  "or empty.");
     118           0 :         CPL_IGNORE_RET_VAL_INT(VSIFCloseL(fp));
     119           0 :         return NULL;
     120             :     }
     121             : 
     122             :     /* -------------------------------------------------------------------- */
     123             :     /*      Get header length.                                              */
     124             :     /* -------------------------------------------------------------------- */
     125         770 :     if (STARTS_WITH_CI(szTemp, "NITF01.") ||
     126         769 :         STARTS_WITH_CI(achFSDWNG, "999998"))
     127           2 :         nHeaderLenOffset = 394;
     128             :     else
     129         768 :         nHeaderLenOffset = 354;
     130             : 
     131        1540 :     if (VSIFSeekL(fp, nHeaderLenOffset, SEEK_SET) != 0 ||
     132         770 :         VSIFReadL(szTemp, 1, 6, fp) != 6)
     133             :     {
     134           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     135             :                  "Unable to read header length from NITF file.  File is either "
     136             :                  "corrupt\n"
     137             :                  "or empty.");
     138           1 :         CPL_IGNORE_RET_VAL_INT(VSIFCloseL(fp));
     139           1 :         return NULL;
     140             :     }
     141             : 
     142         769 :     szTemp[6] = '\0';
     143         769 :     nHeaderLen = atoi(szTemp);
     144             : 
     145         769 :     if (VSIFSeekL(fp, nHeaderLen, SEEK_SET) != 0)
     146           0 :         currentPos = 0;
     147             :     else
     148         769 :         currentPos = VSIFTellL(fp);
     149         769 :     if (nHeaderLen < nHeaderLenOffset || nHeaderLen > currentPos)
     150             :     {
     151           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     152             :                  "NITF Header Length (%d) seems to be corrupt.", nHeaderLen);
     153           0 :         CPL_IGNORE_RET_VAL_INT(VSIFCloseL(fp));
     154           0 :         return NULL;
     155             :     }
     156             : 
     157             :     /* -------------------------------------------------------------------- */
     158             :     /*      Read the whole file header.                                     */
     159             :     /* -------------------------------------------------------------------- */
     160         769 :     pachHeader = (char *)VSI_MALLOC_VERBOSE(nHeaderLen);
     161         769 :     if (pachHeader == NULL)
     162             :     {
     163           0 :         CPL_IGNORE_RET_VAL_INT(VSIFCloseL(fp));
     164           0 :         return NULL;
     165             :     }
     166         769 :     if (VSIFSeekL(fp, 0, SEEK_SET) != 0 ||
     167         769 :         (int)VSIFReadL(pachHeader, 1, nHeaderLen, fp) != nHeaderLen)
     168             :     {
     169           0 :         CPLError(CE_Failure, CPLE_FileIO,
     170             :                  "Cannot read %d bytes for NITF header", (nHeaderLen));
     171           0 :         CPL_IGNORE_RET_VAL_INT(VSIFCloseL(fp));
     172           0 :         CPLFree(pachHeader);
     173           0 :         return NULL;
     174             :     }
     175             : 
     176             :     /* -------------------------------------------------------------------- */
     177             :     /*      Create and initialize info structure about file.                */
     178             :     /* -------------------------------------------------------------------- */
     179         769 :     psFile = (NITFFile *)CPLCalloc(sizeof(NITFFile), 1);
     180         769 :     psFile->fp = fp;
     181         769 :     psFile->pachHeader = pachHeader;
     182             : 
     183         769 : retry_read_header:
     184             :     /* -------------------------------------------------------------------- */
     185             :     /*      Get version.                                                    */
     186             :     /* -------------------------------------------------------------------- */
     187         769 :     NITFGetField(psFile->szVersion, pachHeader, 0, 9);
     188             : 
     189             : /* -------------------------------------------------------------------- */
     190             : /*      Collect a variety of information as metadata.                   */
     191             : /* -------------------------------------------------------------------- */
     192             : #define GetMD(target, hdr, start, length, name)                                \
     193             :     NITFExtractMetadata(&(target->papszMetadata), hdr, start, length,          \
     194             :                         "NITF_" #name);
     195             : 
     196         769 :     if (EQUAL(psFile->szVersion, "NITF02.10") ||
     197          56 :         EQUAL(psFile->szVersion, "NSIF01.00"))
     198         739 :     {
     199             :         char szWork[100];
     200             : 
     201         739 :         GetMD(psFile, pachHeader, 0, 9, FHDR);
     202         739 :         GetMD(psFile, pachHeader, 9, 2, CLEVEL);
     203         739 :         GetMD(psFile, pachHeader, 11, 4, STYPE);
     204         739 :         GetMD(psFile, pachHeader, 15, 10, OSTAID);
     205         739 :         GetMD(psFile, pachHeader, 25, 14, FDT);
     206         739 :         GetMD(psFile, pachHeader, 39, 80, FTITLE);
     207         739 :         GetMD(psFile, pachHeader, 119, 1, FSCLAS);
     208         739 :         GetMD(psFile, pachHeader, 120, 2, FSCLSY);
     209         739 :         GetMD(psFile, pachHeader, 122, 11, FSCODE);
     210         739 :         GetMD(psFile, pachHeader, 133, 2, FSCTLH);
     211         739 :         GetMD(psFile, pachHeader, 135, 20, FSREL);
     212         739 :         GetMD(psFile, pachHeader, 155, 2, FSDCTP);
     213         739 :         GetMD(psFile, pachHeader, 157, 8, FSDCDT);
     214         739 :         GetMD(psFile, pachHeader, 165, 4, FSDCXM);
     215         739 :         GetMD(psFile, pachHeader, 169, 1, FSDG);
     216         739 :         GetMD(psFile, pachHeader, 170, 8, FSDGDT);
     217         739 :         GetMD(psFile, pachHeader, 178, 43, FSCLTX);
     218         739 :         GetMD(psFile, pachHeader, 221, 1, FSCATP);
     219         739 :         GetMD(psFile, pachHeader, 222, 40, FSCAUT);
     220         739 :         GetMD(psFile, pachHeader, 262, 1, FSCRSN);
     221         739 :         GetMD(psFile, pachHeader, 263, 8, FSSRDT);
     222         739 :         GetMD(psFile, pachHeader, 271, 15, FSCTLN);
     223         739 :         GetMD(psFile, pachHeader, 286, 5, FSCOP);
     224         739 :         GetMD(psFile, pachHeader, 291, 5, FSCPYS);
     225         739 :         GetMD(psFile, pachHeader, 296, 1, ENCRYP);
     226         739 :         snprintf(szWork, sizeof(szWork), "%3d,%3d,%3d",
     227         739 :                  ((GByte *)pachHeader)[297], ((GByte *)pachHeader)[298],
     228         739 :                  ((GByte *)pachHeader)[299]);
     229         739 :         GetMD(psFile, szWork, 0, 11, FBKGC);
     230         739 :         GetMD(psFile, pachHeader, 300, 24, ONAME);
     231         739 :         GetMD(psFile, pachHeader, 324, 18, OPHONE);
     232         739 :         NITFGetField(szTemp, pachHeader, 342, 12);
     233             :     }
     234          30 :     else if (EQUAL(psFile->szVersion, "NITF02.00"))
     235             :     {
     236          29 :         int nCOff = 0;
     237             : 
     238          29 :         GetMD(psFile, pachHeader, 0, 9, FHDR);
     239          29 :         GetMD(psFile, pachHeader, 9, 2, CLEVEL);
     240          29 :         GetMD(psFile, pachHeader, 11, 4, STYPE);
     241          29 :         GetMD(psFile, pachHeader, 15, 10, OSTAID);
     242          29 :         GetMD(psFile, pachHeader, 25, 14, FDT);
     243          29 :         GetMD(psFile, pachHeader, 39, 80, FTITLE);
     244          29 :         GetMD(psFile, pachHeader, 119, 1, FSCLAS);
     245          29 :         GetMD(psFile, pachHeader, 120, 40, FSCODE);
     246          29 :         GetMD(psFile, pachHeader, 160, 40, FSCTLH);
     247          29 :         GetMD(psFile, pachHeader, 200, 40, FSREL);
     248          29 :         GetMD(psFile, pachHeader, 240, 20, FSCAUT);
     249          29 :         GetMD(psFile, pachHeader, 260, 20, FSCTLN);
     250          29 :         GetMD(psFile, pachHeader, 280, 6, FSDWNG);
     251          29 :         if (STARTS_WITH_CI(pachHeader + 280, "999998"))
     252             :         {
     253           1 :             GetMD(psFile, pachHeader, 286, 40, FSDEVT);
     254           1 :             nCOff += 40;
     255             :         }
     256          29 :         GetMD(psFile, pachHeader, 286 + nCOff, 5, FSCOP);
     257          29 :         GetMD(psFile, pachHeader, 291 + nCOff, 5, FSCPYS);
     258          29 :         GetMD(psFile, pachHeader, 296 + nCOff, 1, ENCRYP);
     259          29 :         GetMD(psFile, pachHeader, 297 + nCOff, 27, ONAME);
     260          29 :         GetMD(psFile, pachHeader, 324 + nCOff, 18, OPHONE);
     261          29 :         NITFGetField(szTemp, pachHeader, 342 + nCOff, 12);
     262             :     }
     263             : #undef GetMD
     264             : 
     265         769 :     if (!bTriedStreamingFileHeader && EQUAL(szTemp, "999999999999"))
     266             :     {
     267             :         GUIntBig nFileSize;
     268           0 :         GByte abyDELIM2_L2[12] = {0};
     269           0 :         GByte abyL1_DELIM1[11] = {0};
     270             :         int bOK;
     271             : 
     272           0 :         bTriedStreamingFileHeader = TRUE;
     273           0 :         CPLDebug("NITF",
     274             :                  "Total file unknown. Trying to get a STREAMING_FILE_HEADER");
     275             : 
     276           0 :         bOK = VSIFSeekL(fp, 0, SEEK_END) == 0;
     277           0 :         nFileSize = VSIFTellL(fp);
     278             : 
     279           0 :         bOK &= VSIFSeekL(fp, nFileSize - 11, SEEK_SET) == 0;
     280           0 :         abyDELIM2_L2[11] = '\0';
     281             : 
     282           0 :         if (bOK && VSIFReadL(abyDELIM2_L2, 1, 11, fp) == 11 &&
     283           0 :             abyDELIM2_L2[0] == 0x0E && abyDELIM2_L2[1] == 0xCA &&
     284           0 :             abyDELIM2_L2[2] == 0x14 && abyDELIM2_L2[3] == 0xBF)
     285             :         {
     286           0 :             int SFHL2 = atoi((const char *)(abyDELIM2_L2 + 4));
     287           0 :             if (SFHL2 > 0 && (nFileSize > (size_t)(11 + SFHL2 + 11)))
     288             :             {
     289           0 :                 bOK &=
     290           0 :                     VSIFSeekL(fp, nFileSize - 11 - SFHL2 - 11, SEEK_SET) == 0;
     291             : 
     292           0 :                 if (bOK && VSIFReadL(abyL1_DELIM1, 1, 11, fp) == 11 &&
     293           0 :                     abyL1_DELIM1[7] == 0x0A && abyL1_DELIM1[8] == 0x6E &&
     294           0 :                     abyL1_DELIM1[9] == 0x1D && abyL1_DELIM1[10] == 0x97 &&
     295           0 :                     memcmp(abyL1_DELIM1, abyDELIM2_L2 + 4, 7) == 0)
     296             :                 {
     297           0 :                     if (SFHL2 == nHeaderLen)
     298             :                     {
     299           0 :                         CSLDestroy(psFile->papszMetadata);
     300           0 :                         psFile->papszMetadata = NULL;
     301             : 
     302           0 :                         if ((int)VSIFReadL(pachHeader, 1, SFHL2, fp) != SFHL2)
     303             :                         {
     304           0 :                             CPL_IGNORE_RET_VAL_INT(VSIFCloseL(fp));
     305           0 :                             CPLFree(pachHeader);
     306           0 :                             CPLFree(psFile);
     307           0 :                             return NULL;
     308             :                         }
     309             : 
     310           0 :                         goto retry_read_header;
     311             :                     }
     312             :                 }
     313             :             }
     314             :         }
     315           0 :         if (!bOK)
     316             :         {
     317           0 :             NITFClose(psFile);
     318           0 :             return NULL;
     319             :         }
     320             :     }
     321             : 
     322             :     /* -------------------------------------------------------------------- */
     323             :     /*      Collect segment info for the types we care about.               */
     324             :     /* -------------------------------------------------------------------- */
     325         769 :     nNextData = nHeaderLen;
     326             : 
     327         769 :     nOffset = nHeaderLenOffset + 6;
     328             : 
     329         769 :     nOffset = NITFCollectSegmentInfo(psFile, nHeaderLen, nOffset, "IM", 6, 10,
     330             :                                      &nNextData);
     331             : 
     332         769 :     if (nOffset != -1)
     333         769 :         nOffset = NITFCollectSegmentInfo(psFile, nHeaderLen, nOffset, "GR", 4,
     334             :                                          6, &nNextData);
     335             : 
     336             :     /* LA Called NUMX in NITF 2.1 */
     337         769 :     if (nOffset != -1)
     338         769 :         nOffset = NITFCollectSegmentInfo(psFile, nHeaderLen, nOffset, "LA", 4,
     339             :                                          3, &nNextData);
     340             : 
     341         769 :     if (nOffset != -1)
     342         769 :         nOffset = NITFCollectSegmentInfo(psFile, nHeaderLen, nOffset, "TX", 4,
     343             :                                          5, &nNextData);
     344             : 
     345         769 :     if (nOffset != -1)
     346         769 :         nOffset = NITFCollectSegmentInfo(psFile, nHeaderLen, nOffset, "DE", 4,
     347             :                                          9, &nNextData);
     348             : 
     349         769 :     if (nOffset != -1)
     350         769 :         nOffset = NITFCollectSegmentInfo(psFile, nHeaderLen, nOffset, "RE", 4,
     351             :                                          7, &nNextData);
     352             : 
     353         769 :     if (nOffset < 0)
     354             :     {
     355           1 :         NITFClose(psFile);
     356           1 :         return NULL;
     357             :     }
     358             : 
     359             :     /* -------------------------------------------------------------------- */
     360             :     /*      Is there User Define Header Data? (TREs)                        */
     361             :     /* -------------------------------------------------------------------- */
     362         768 :     if (nHeaderLen < nOffset + 5)
     363             :     {
     364           0 :         CPLError(CE_Failure, CPLE_AppDefined, "NITF header too small");
     365           0 :         NITFClose(psFile);
     366           0 :         return NULL;
     367             :     }
     368             : 
     369         768 :     psFile->nTREBytes = atoi(NITFGetField(szTemp, pachHeader, nOffset, 5));
     370         768 :     if (psFile->nTREBytes < 0)
     371             :     {
     372           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid TRE size : %d",
     373             :                  psFile->nTREBytes);
     374           0 :         NITFClose(psFile);
     375           0 :         return NULL;
     376             :     }
     377         768 :     nOffset += 5;
     378             : 
     379         768 :     if (psFile->nTREBytes == 3)
     380             :     {
     381           0 :         nOffset += 3; /* UDHOFL */
     382           0 :         psFile->nTREBytes = 0;
     383             :     }
     384         768 :     else if (psFile->nTREBytes > 3)
     385             :     {
     386          24 :         nOffset += 3; /* UDHOFL */
     387          24 :         psFile->nTREBytes -= 3;
     388             : 
     389          24 :         if (nHeaderLen < nOffset + psFile->nTREBytes)
     390             :         {
     391           0 :             CPLError(CE_Failure, CPLE_AppDefined, "NITF header too small");
     392           0 :             NITFClose(psFile);
     393           0 :             return NULL;
     394             :         }
     395             : 
     396          24 :         psFile->pachTRE = (char *)VSI_MALLOC_VERBOSE(psFile->nTREBytes);
     397          24 :         if (psFile->pachTRE == NULL)
     398             :         {
     399           0 :             NITFClose(psFile);
     400           0 :             return NULL;
     401             :         }
     402          24 :         memcpy(psFile->pachTRE, pachHeader + nOffset, psFile->nTREBytes);
     403             :     }
     404             : 
     405             :     /* -------------------------------------------------------------------- */
     406             :     /*      Is there Extended Header Data?  (More TREs)                     */
     407             :     /* -------------------------------------------------------------------- */
     408         768 :     if (nHeaderLen > nOffset + 8)
     409             :     {
     410          38 :         int nXHDL = atoi(NITFGetField(szTemp, pachHeader, nOffset, 5));
     411          38 :         if (nXHDL < 0)
     412             :         {
     413           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid XHDL value : %d",
     414             :                      nXHDL);
     415           0 :             NITFClose(psFile);
     416           0 :             return NULL;
     417             :         }
     418             : 
     419          38 :         nOffset += 5; /* XHDL */
     420             : 
     421          38 :         if (nXHDL > 3)
     422             :         {
     423             :             char *pachNewTRE;
     424             : 
     425          14 :             nOffset += 3; /* XHDLOFL */
     426          14 :             nXHDL -= 3;
     427             : 
     428          14 :             if (nHeaderLen < nOffset + nXHDL)
     429             :             {
     430           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "NITF header too small");
     431           0 :                 NITFClose(psFile);
     432           0 :                 return NULL;
     433             :             }
     434             : 
     435          14 :             pachNewTRE = (char *)VSI_REALLOC_VERBOSE(psFile->pachTRE,
     436             :                                                      psFile->nTREBytes + nXHDL);
     437          14 :             if (pachNewTRE == NULL)
     438             :             {
     439           0 :                 NITFClose(psFile);
     440           0 :                 return NULL;
     441             :             }
     442          14 :             psFile->pachTRE = pachNewTRE;
     443          14 :             memcpy(psFile->pachTRE, pachHeader + nOffset, nXHDL);
     444          14 :             psFile->nTREBytes += nXHDL;
     445             :         }
     446             :     }
     447             : 
     448         768 :     return psFile;
     449             : }
     450             : 
     451             : /************************************************************************/
     452             : /*                             NITFClose()                              */
     453             : /************************************************************************/
     454             : 
     455         769 : void NITFClose(NITFFile *psFile)
     456             : 
     457             : {
     458             :     int iSegment;
     459             : 
     460       11629 :     for (iSegment = 0; iSegment < psFile->nSegmentCount; iSegment++)
     461             :     {
     462       10860 :         NITFSegmentInfo *psSegInfo = psFile->pasSegmentInfo + iSegment;
     463             : 
     464       10860 :         if (psSegInfo->hAccess == NULL)
     465        1110 :             continue;
     466             : 
     467        9750 :         if (EQUAL(psSegInfo->szSegmentType, "IM"))
     468        9750 :             NITFImageDeaccess((NITFImage *)psSegInfo->hAccess);
     469           0 :         else if (EQUAL(psSegInfo->szSegmentType, "DE"))
     470           0 :             NITFDESDeaccess((NITFDES *)psSegInfo->hAccess);
     471             :         else
     472             :         {
     473           0 :             CPLAssert(FALSE);
     474             :         }
     475             :     }
     476             : 
     477         769 :     CPLFree(psFile->pasSegmentInfo);
     478         769 :     if (psFile->fp != NULL)
     479         761 :         CPL_IGNORE_RET_VAL_INT(VSIFCloseL(psFile->fp));
     480         769 :     CPLFree(psFile->pachHeader);
     481         769 :     CSLDestroy(psFile->papszMetadata);
     482         769 :     CPLFree(psFile->pachTRE);
     483             : 
     484         769 :     if (psFile->psNITFSpecNode)
     485         745 :         CPLDestroyXMLNode(psFile->psNITFSpecNode);
     486             : 
     487         769 :     CPLFree(psFile);
     488         769 : }
     489             : 
     490      291863 : static int NITFGotoOffset(VSILFILE *fp, GUIntBig nLocation)
     491             : {
     492      291863 :     int bOK = TRUE;
     493      291863 :     GUIntBig nCurrentLocation = VSIFTellL(fp);
     494      291863 :     if (nLocation > nCurrentLocation)
     495             :     {
     496             :         GUIntBig nFileSize;
     497             :         size_t iFill;
     498      175228 :         char cSpace = ' ';
     499             : 
     500      175228 :         bOK &= VSIFSeekL(fp, 0, SEEK_END) == 0;
     501      175228 :         nFileSize = VSIFTellL(fp);
     502      175228 :         if (bOK && nLocation > nFileSize)
     503             :         {
     504     1356840 :             for (iFill = 0; bOK && iFill < nLocation - nFileSize; iFill++)
     505     1182380 :                 bOK &= VSIFWriteL(&cSpace, 1, 1, fp) == 1;
     506             :         }
     507             :         else
     508         767 :             bOK &= VSIFSeekL(fp, nLocation, SEEK_SET) == 0;
     509             :     }
     510      116635 :     else if (nLocation < nCurrentLocation)
     511             :     {
     512        2102 :         bOK &= VSIFSeekL(fp, nLocation, SEEK_SET) == 0;
     513             :     }
     514      291863 :     if (!bOK)
     515             :     {
     516         522 :         CPLError(CE_Failure, CPLE_FileIO, "I/O error");
     517             :     }
     518      291863 :     return bOK;
     519             : }
     520             : 
     521             : /************************************************************************/
     522             : /*                             NITFCreate()                             */
     523             : /*                                                                      */
     524             : /*      Create a new uncompressed NITF file.                            */
     525             : /************************************************************************/
     526             : 
     527           0 : int NITFCreate(const char *pszFilename, int nPixels, int nLines, int nBands,
     528             :                int nBitsPerSample, const char *pszPVType, char **papszOptions)
     529             : 
     530             : {
     531           0 :     return NITFCreateEx(pszFilename, nPixels, nLines, nBands, nBitsPerSample,
     532             :                         pszPVType, papszOptions, NULL, NULL, NULL, NULL);
     533             : }
     534             : 
     535         267 : int NITFCreateEx(const char *pszFilename, int nPixels, int nLines, int nBands,
     536             :                  int nBitsPerSample, const char *pszPVType, char **papszOptions,
     537             :                  int *pnIndex, int *pnImageCount, vsi_l_offset *pnImageOffset,
     538             :                  vsi_l_offset *pnICOffset)
     539             : 
     540             : {
     541             :     VSILFILE *fp;
     542         267 :     GUIntBig nCur = 0;
     543         267 :     int nOffset = 0, iBand, nIHSize, nNPPBH, nNPPBV;
     544         267 :     GUIntBig nImageSize = 0;
     545             :     int nNBPR, nNBPC;
     546             :     const char *pszIREP;
     547         267 :     const char *pszIC = CSLFetchNameValue(papszOptions, "IC");
     548             :     int nCLevel;
     549             :     const char *pszNUMT;
     550         267 :     int nNUMT = 0;
     551             :     vsi_l_offset nOffsetUDIDL;
     552             :     const char *pszVersion;
     553         267 :     int iIM, nIM = 1;
     554             :     const char *pszNUMI;
     555         267 :     int iGS, nGS = 0;     // number of graphic segment
     556             :     const char *pszNUMS;  // graphic segment option string
     557         267 :     int iDES, nDES = 0;
     558             :     int bOK;
     559             : 
     560         267 :     if (pnIndex)
     561         267 :         *pnIndex = 0;
     562             : 
     563         267 :     if (nBands <= 0 || nBands > 99999)
     564             :     {
     565           1 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid band number : %d",
     566             :                  nBands);
     567           1 :         return FALSE;
     568             :     }
     569             : 
     570         266 :     if (pszIC == NULL)
     571         243 :         pszIC = "NC";
     572             : 
     573             :     /* -------------------------------------------------------------------- */
     574             :     /*      Fetch some parameter overrides.                                 */
     575             :     /* -------------------------------------------------------------------- */
     576         266 :     pszIREP = CSLFetchNameValue(papszOptions, "IREP");
     577         266 :     if (pszIREP == NULL)
     578         160 :         pszIREP = "MONO";
     579             : 
     580         266 :     pszNUMT = CSLFetchNameValue(papszOptions, "NUMT");
     581         266 :     if (pszNUMT != NULL)
     582             :     {
     583           4 :         nNUMT = atoi(pszNUMT);
     584           4 :         if (nNUMT < 0 || nNUMT > 999)
     585             :         {
     586           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid NUMT value : %s",
     587             :                      pszNUMT);
     588           0 :             return FALSE;
     589             :         }
     590             :     }
     591             : 
     592         266 :     const bool bAppendSubdataset =
     593         266 :         CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "APPEND_SUBDATASET",
     594             :                                             "NO")) == TRUE;
     595         266 :     const bool bWriteAllImages =
     596         266 :         CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "WRITE_ALL_IMAGES",
     597             :                                             "NO")) == TRUE;
     598         266 :     pszNUMI = CSLFetchNameValue(papszOptions, "NUMI");
     599         266 :     if (pszNUMI != NULL)
     600             :     {
     601           7 :         if (bAppendSubdataset)
     602             :         {
     603           0 :             CPLError(CE_Failure, CPLE_NotSupported,
     604             :                      "NUMI not supported with APPEND_SUBDATASET");
     605           0 :             return FALSE;
     606             :         }
     607           7 :         nIM = atoi(pszNUMI);
     608           7 :         if (nIM == 0)
     609             :         {
     610           1 :             if (pnIndex)
     611           1 :                 *pnIndex = -1;
     612             :         }
     613           6 :         else if (nIM < 0 || nIM > 999)
     614             :         {
     615           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid NUMI value : %s",
     616             :                      pszNUMI);
     617           0 :             return FALSE;
     618             :         }
     619           7 :         if (nIM != 1 && !EQUAL(pszIC, "NC") && bWriteAllImages)
     620             :         {
     621           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     622             :                      "Unable to create file with multiple images and "
     623             :                      "compression at the same time");
     624           0 :             return FALSE;
     625             :         }
     626             :     }
     627         259 :     else if (bAppendSubdataset && bWriteAllImages)
     628             :     {
     629           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     630             :                  "WRITE_ALL_IMAGES=YES only supported for first image");
     631             :     }
     632             : 
     633         266 :     if (pnImageCount)
     634         266 :         *pnImageCount = nIM;
     635             : 
     636             :     // Reads and validates graphics segment number option
     637         266 :     pszNUMS = CSLFetchNameValue(papszOptions, "NUMS");
     638         266 :     if (pszNUMS != NULL)
     639             :     {
     640          12 :         nGS = atoi(pszNUMS);
     641          12 :         if (nGS < 0 || nGS > 999)
     642             :         {
     643           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid NUMS value : %s",
     644             :                      pszNUMS);
     645           0 :             return FALSE;
     646             :         }
     647             :     }
     648             : 
     649         266 :     const char *pszNUMDES = CSLFetchNameValue(papszOptions, "NUMDES");
     650         266 :     if (pszNUMDES)
     651           2 :         nDES = atoi(pszNUMDES);
     652             :     else
     653             :     {
     654         264 :         char **papszSubList = CSLFetchNameValueMultiple(papszOptions, "DES");
     655         264 :         nDES = CSLCount(papszSubList);
     656         264 :         CSLDestroy(papszSubList);
     657             :     }
     658             : 
     659             :     /* -------------------------------------------------------------------- */
     660             :     /*      Compute raw image size, blocking factors and so forth.          */
     661             :     /* -------------------------------------------------------------------- */
     662         266 :     nNPPBH = nPixels;
     663         266 :     nNPPBV = nLines;
     664             : 
     665         266 :     if (CSLFetchNameValue(papszOptions, "BLOCKXSIZE") != NULL)
     666          19 :         nNPPBH = atoi(CSLFetchNameValue(papszOptions, "BLOCKXSIZE"));
     667             : 
     668         266 :     if (CSLFetchNameValue(papszOptions, "BLOCKYSIZE") != NULL)
     669          17 :         nNPPBV = atoi(CSLFetchNameValue(papszOptions, "BLOCKYSIZE"));
     670             : 
     671         266 :     if (CSLFetchNameValue(papszOptions, "NPPBH") != NULL)
     672           0 :         nNPPBH = atoi(CSLFetchNameValue(papszOptions, "NPPBH"));
     673             : 
     674         266 :     if (CSLFetchNameValue(papszOptions, "NPPBV") != NULL)
     675           0 :         nNPPBV = atoi(CSLFetchNameValue(papszOptions, "NPPBV"));
     676             : 
     677         266 :     if ((EQUAL(pszIC, "NC") || EQUAL(pszIC, "C8")) &&
     678         256 :         (nPixels > 8192 || nLines > 8192) && nNPPBH == nPixels &&
     679             :         nNPPBV == nLines)
     680             :     {
     681             :         /* See MIL-STD-2500-C, paragraph 5.4.2.2-d (#3263) */
     682           4 :         nNBPR = 1;
     683           4 :         nNBPC = 1;
     684           4 :         nNPPBH = 0;
     685           4 :         nNPPBV = 0;
     686             : 
     687           4 :         if (EQUAL(pszIC, "NC"))
     688             :         {
     689           4 :             nImageSize =
     690           4 :                 ((nBitsPerSample) / 8) * ((GUIntBig)nPixels * nLines) * nBands;
     691             :         }
     692             :     }
     693         262 :     else if ((EQUAL(pszIC, "NC") || EQUAL(pszIC, "C8")) && nPixels > 8192 &&
     694             :              nNPPBH == nPixels)
     695             :     {
     696             :         /* See MIL-STD-2500-C, paragraph 5.4.2.2-d */
     697           0 :         nNBPR = 1;
     698           0 :         nNPPBH = 0;
     699           0 :         nNBPC = (nLines + nNPPBV - 1) / nNPPBV;
     700             : 
     701           0 :         if (nNBPC > 9999)
     702             :         {
     703           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     704             :                      "Unable to create file %s,\n"
     705             :                      "Too many blocks : %d x %d",
     706             :                      pszFilename, nNBPR, nNBPC);
     707           0 :             return FALSE;
     708             :         }
     709             : 
     710           0 :         if (EQUAL(pszIC, "NC"))
     711             :         {
     712           0 :             nImageSize = ((nBitsPerSample) / 8) *
     713           0 :                          ((GUIntBig)nPixels * (nNBPC * nNPPBV)) * nBands;
     714             :         }
     715             :     }
     716         262 :     else if ((EQUAL(pszIC, "NC") || EQUAL(pszIC, "C8")) && nLines > 8192 &&
     717             :              nNPPBV == nLines)
     718             :     {
     719             :         /* See MIL-STD-2500-C, paragraph 5.4.2.2-d */
     720           1 :         nNBPC = 1;
     721           1 :         nNPPBV = 0;
     722           1 :         nNBPR = (nPixels + nNPPBH - 1) / nNPPBH;
     723             : 
     724           1 :         if (nNBPR > 9999)
     725             :         {
     726           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     727             :                      "Unable to create file %s,\n"
     728             :                      "Too many blocks : %d x %d",
     729             :                      pszFilename, nNBPR, nNBPC);
     730           0 :             return FALSE;
     731             :         }
     732             : 
     733           1 :         if (EQUAL(pszIC, "NC"))
     734             :         {
     735           1 :             nImageSize = ((nBitsPerSample) / 8) *
     736           1 :                          ((GUIntBig)nLines * (nNBPR * nNPPBH)) * nBands;
     737             :         }
     738             :     }
     739             :     else
     740             :     {
     741         261 :         if (nNPPBH <= 0 || nNPPBV <= 0 || nNPPBH > 9999 || nNPPBV > 9999)
     742           1 :             nNPPBH = nNPPBV = 256;
     743             : 
     744         261 :         nNBPR = (nPixels + nNPPBH - 1) / nNPPBH;
     745         261 :         nNBPC = (nLines + nNPPBV - 1) / nNPPBV;
     746         261 :         if (nNBPR > 9999 || nNBPC > 9999)
     747             :         {
     748           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     749             :                      "Unable to create file %s,\n"
     750             :                      "Too many blocks : %d x %d",
     751             :                      pszFilename, nNBPR, nNBPC);
     752           0 :             return FALSE;
     753             :         }
     754             : 
     755         261 :         if (EQUAL(pszIC, "NC"))
     756             :         {
     757         239 :             nImageSize = ((nBitsPerSample) / 8) * ((GUIntBig)nNBPR * nNBPC) *
     758         239 :                          nNPPBH * nNPPBV * nBands;
     759             :         }
     760             :     }
     761             : 
     762         266 :     if (EQUAL(pszIC, "NC"))
     763             :     {
     764         244 :         if (nImageSize >= NITF_MAX_IMAGE_SIZE)
     765             :         {
     766           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     767             :                      "Unable to create file %s,\n"
     768             :                      "Too big image size : " CPL_FRMT_GUIB,
     769             :                      pszFilename, nImageSize);
     770           1 :             return FALSE;
     771             :         }
     772         243 :         if (nImageSize * nIM >= NITF_MAX_FILE_SIZE)
     773             :         {
     774           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     775             :                      "Unable to create file %s,\n"
     776             :                      "Too big file size : " CPL_FRMT_GUIB,
     777             :                      pszFilename, nImageSize * nIM);
     778           1 :             return FALSE;
     779             :         }
     780             :     }
     781             : 
     782             :     /* -------------------------------------------------------------------- */
     783             :     /*      Open new file.                                                  */
     784             :     /* -------------------------------------------------------------------- */
     785         264 :     fp = VSIFOpenL(pszFilename, bAppendSubdataset ? "rb+" : "wb+");
     786         264 :     if (fp == NULL)
     787             :     {
     788           3 :         CPLError(CE_Failure, CPLE_OpenFailed,
     789             :                  "Unable to create file %s,\n"
     790             :                  "check path and permissions.",
     791             :                  pszFilename);
     792           3 :         return FALSE;
     793             :     }
     794             : 
     795             :     /* -------------------------------------------------------------------- */
     796             :     /*      Work out the version we are producing.  For now we really       */
     797             :     /*      only support creating NITF02.10 or the nato analog              */
     798             :     /*      NSIF01.00.                                                      */
     799             :     /* -------------------------------------------------------------------- */
     800         261 :     pszVersion = CSLFetchNameValue(papszOptions, "FHDR");
     801         261 :     if (pszVersion == NULL)
     802         251 :         pszVersion = "NITF02.10";
     803          10 :     else if (!EQUAL(pszVersion, "NITF02.10") && !EQUAL(pszVersion, "NSIF01.00"))
     804             :     {
     805           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     806             :                  "FHDR=%s not supported, switching to NITF02.10.", pszVersion);
     807           0 :         pszVersion = "NITF02.10";
     808             :     }
     809             : 
     810             :     /* -------------------------------------------------------------------- */
     811             :     /*      Prepare the file header.                                        */
     812             :     /* -------------------------------------------------------------------- */
     813             : 
     814             : #define PLACE(location, name, text)                                            \
     815             :     do                                                                         \
     816             :     {                                                                          \
     817             :         const char *_text = text;                                              \
     818             :         bOK &= NITFGotoOffset(fp, location);                                   \
     819             :         bOK &= VSIFWriteL(_text, 1, strlen(_text), fp) == strlen(_text);       \
     820             :     } while (0)
     821             : 
     822             : #define OVR(width, location, name, text)                                       \
     823             :     bOK &= NITFWriteOption(fp, papszOptions, width, location, #name, text);
     824             : 
     825             : #define WRITE_BYTE(location, val)                                              \
     826             :     do                                                                         \
     827             :     {                                                                          \
     828             :         char cVal = val;                                                       \
     829             :         bOK &= NITFGotoOffset(fp, location);                                   \
     830             :         bOK &= VSIFWriteL(&cVal, 1, 1, fp) == 1;                               \
     831             :     } while (0)
     832             : 
     833         261 :     bOK = VSIFSeekL(fp, 0, SEEK_SET) == 0;
     834             : 
     835         261 :     if (!bAppendSubdataset)
     836             :     {
     837         256 :         PLACE(0, FDHR_FVER, pszVersion);
     838         256 :         OVR(2, 9, CLEVEL, "03"); /* Patched at the end */
     839         256 :         PLACE(11, STYPE, "BF01");
     840         256 :         OVR(10, 15, OSTAID, "GDAL");
     841         256 :         OVR(14, 25, FDT, "20021216151629");
     842         256 :         OVR(80, 39, FTITLE, "");
     843         256 :         OVR(1, 119, FSCLAS, "U");
     844         256 :         OVR(2, 120, FSCLSY, "");
     845         256 :         OVR(11, 122, FSCODE, "");
     846         256 :         OVR(2, 133, FSCTLH, "");
     847         256 :         OVR(20, 135, FSREL, "");
     848         256 :         OVR(2, 155, FSDCTP, "");
     849         256 :         OVR(8, 157, FSDCDT, "");
     850         256 :         OVR(4, 165, FSDCXM, "");
     851         256 :         OVR(1, 169, FSDG, "");
     852         256 :         OVR(8, 170, FSDGDT, "");
     853         256 :         OVR(43, 178, FSCLTX, "");
     854         256 :         OVR(1, 221, FSCATP, "");
     855         256 :         OVR(40, 222, FSCAUT, "");
     856         256 :         OVR(1, 262, FSCRSN, "");
     857         256 :         OVR(8, 263, FSSRDT, "");
     858         256 :         OVR(15, 271, FSCTLN, "");
     859         256 :         OVR(5, 286, FSCOP, "00000");
     860         256 :         OVR(5, 291, FSCPYS, "00000");
     861         256 :         PLACE(296, ENCRYP, "0");
     862         256 :         WRITE_BYTE(297, 0x00); /* FBKGC */
     863         256 :         WRITE_BYTE(298, 0x00);
     864         256 :         WRITE_BYTE(299, 0x00);
     865         256 :         OVR(24, 300, ONAME, "");
     866         256 :         OVR(18, 324, OPHONE, "");
     867         256 :         PLACE(342, FL, "????????????");
     868         256 :         PLACE(354, HL, "??????");
     869         256 :         PLACE(360, NUMI, CPLSPrintf("%03d", nIM));
     870             : 
     871         256 :         int nHL = 363;
     872        1514 :         for (iIM = 0; iIM < nIM; iIM++)
     873             :         {
     874             :             /* Patched when image segments are written. */
     875        1258 :             PLACE(nHL, LISHi, "??????");
     876        1258 :             PLACE(nHL + 6, LIi, "??????????");
     877        1258 :             nHL += 6 + 10;
     878             :         }
     879             : 
     880             :         // Creates Header entries for graphic segment
     881             :         //    NUMS: number of segment
     882             :         // For each segment:
     883             :         //    LSSH[i]: subheader length (4 byte), set to be 258, the size for
     884             :         //        minimal amount of information.
     885             :         //    LS[i] data length (6 byte)
     886         256 :         PLACE(nHL, NUMS, CPLSPrintf("%03d", nGS));
     887         256 :         nHL += 3;  // Move three characters
     888         259 :         for (iGS = 0; iGS < nGS; iGS++)
     889             :         {
     890             :             /* Patched when graphic segments are written. */
     891           3 :             PLACE(nHL, LSSHi, "????");
     892           3 :             nHL += 4;
     893           3 :             PLACE(nHL, LSi, "??????");
     894           3 :             nHL += 6;
     895             :         }
     896             : 
     897         256 :         PLACE(nHL, NUMX, "000");
     898         256 :         PLACE(nHL + 3, NUMT, CPLSPrintf("%03d", nNUMT));
     899             : 
     900             :         /* Patched when text segments are written. */
     901         256 :         PLACE(nHL + 6, LTSHnLTn, "");
     902             : 
     903         256 :         nHL += 6 + (4 + 5) * nNUMT;
     904             : 
     905         256 :         PLACE(nHL, NUMDES, CPLSPrintf("%03d", nDES));
     906         256 :         nHL += 3;
     907             : 
     908         268 :         for (iDES = 0; iDES < nDES; iDES++)
     909             :         {
     910             :             /* Patched when DESs are written. */
     911          12 :             PLACE(nHL, LDSH, "????");
     912          12 :             nHL += 4;
     913          12 :             PLACE(nHL, LD, "?????????");
     914          12 :             nHL += 9;
     915             :         }
     916             : 
     917         256 :         PLACE(nHL, NUMRES, "000");
     918         256 :         nHL += 3;
     919         256 :         PLACE(nHL, UDHDL, "00000");
     920         256 :         nHL += 5;
     921         256 :         PLACE(nHL, XHDL, "00000");
     922         256 :         nHL += 5;
     923             : 
     924         256 :         if (CSLFetchNameValue(papszOptions, "FILE_TRE") != NULL)
     925             :         {
     926           5 :             bOK &= NITFWriteTREsFromOptions(fp, nHL - 10, &nHL, papszOptions,
     927             :                                             "FILE_TRE=");
     928             :         }
     929             : 
     930         256 :         if (nHL > 999999)
     931             :         {
     932           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     933             :                      "Too big file header length : %d", nHL);
     934           0 :             CPL_IGNORE_RET_VAL_INT(VSIFCloseL(fp));
     935           0 :             return FALSE;
     936             :         }
     937             : 
     938             :         // update header length
     939         256 :         PLACE(354, HL, CPLSPrintf("%06d", nHL));
     940             : 
     941         256 :         nCur = nHL;
     942         256 :         iIM = 0;
     943             :     }
     944             :     else
     945             :     {
     946             :         // Append subdataset
     947           5 :         NITFFile *psFile = NITFOpenEx(fp, pszFilename);
     948           5 :         if (psFile == NULL)
     949           0 :             return FALSE;
     950             : 
     951           5 :         iIM = -1;
     952           5 :         nIM = 0;
     953          20 :         for (int i = 0; i < psFile->nSegmentCount; i++)
     954             :         {
     955          15 :             if (strcmp(psFile->pasSegmentInfo[i].szSegmentType, "IM") == 0)
     956             :             {
     957          12 :                 nIM++;
     958          12 :                 if (psFile->pasSegmentInfo[i].nSegmentHeaderSize == 0 &&
     959             :                     iIM < 0)
     960             :                 {
     961           5 :                     iIM = i;
     962           5 :                     if (pnIndex)
     963           5 :                         *pnIndex = i;
     964             :                 }
     965             :             }
     966             :         }
     967           5 :         if (pnImageCount)
     968           5 :             *pnImageCount = nIM;
     969             : 
     970           5 :         psFile->fp = NULL;
     971           5 :         NITFClose(psFile);
     972             : 
     973           5 :         if (iIM < 0)
     974             :         {
     975           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     976             :                      "Did not find free image segment");
     977           0 :             CPL_IGNORE_RET_VAL_INT(VSIFCloseL(fp));
     978           0 :             return FALSE;
     979             :         }
     980           5 :         nIM = iIM + 1;
     981             : 
     982           5 :         bOK &= VSIFSeekL(fp, 0, SEEK_END) == 0;
     983           5 :         nCur = VSIFTellL(fp);
     984             :     }
     985             : 
     986             :     /* -------------------------------------------------------------------- */
     987             :     /*      Prepare the image header.                                       */
     988             :     /* -------------------------------------------------------------------- */
     989        1260 :     for (; iIM < nIM; iIM++)
     990             :     {
     991        1258 :         char **papszIREPBANDTokens = NULL;
     992        1258 :         char **papszISUBCATTokens = NULL;
     993             : 
     994        1258 :         if (CSLFetchNameValue(papszOptions, "IREPBAND") != NULL)
     995             :         {
     996           2 :             papszIREPBANDTokens = CSLTokenizeStringComplex(
     997             :                 CSLFetchNameValue(papszOptions, "IREPBAND"), ",", 0, 0);
     998           4 :             if (papszIREPBANDTokens != NULL &&
     999           2 :                 CSLCount(papszIREPBANDTokens) != nBands)
    1000             :             {
    1001           0 :                 CSLDestroy(papszIREPBANDTokens);
    1002           0 :                 papszIREPBANDTokens = NULL;
    1003             :             }
    1004             :         }
    1005        1258 :         if (CSLFetchNameValue(papszOptions, "ISUBCAT") != NULL)
    1006             :         {
    1007           1 :             papszISUBCATTokens = CSLTokenizeStringComplex(
    1008             :                 CSLFetchNameValue(papszOptions, "ISUBCAT"), ",", 0, 0);
    1009           2 :             if (papszISUBCATTokens != NULL &&
    1010           1 :                 CSLCount(papszISUBCATTokens) != nBands)
    1011             :             {
    1012           0 :                 CSLDestroy(papszISUBCATTokens);
    1013           0 :                 papszISUBCATTokens = NULL;
    1014             :             }
    1015             :         }
    1016             : 
    1017        1258 :         bOK &= VSIFSeekL(fp, nCur, SEEK_SET) == 0;
    1018             : 
    1019        1258 :         PLACE(nCur + 0, IM, "IM");
    1020        1258 :         OVR(10, nCur + 2, IID1, "Missing");
    1021        1258 :         OVR(14, nCur + 12, IDATIM, "20021216151629");
    1022        1258 :         OVR(17, nCur + 26, TGTID, "");
    1023        1258 :         OVR(80, nCur + 43, IID2, "");
    1024        1258 :         OVR(1, nCur + 123, ISCLAS, "U");
    1025        1258 :         OVR(2, nCur + 124, ISCLSY, "");
    1026        1258 :         OVR(11, nCur + 126, ISCODE, "");
    1027        1258 :         OVR(2, nCur + 137, ISCTLH, "");
    1028        1258 :         OVR(20, nCur + 139, ISREL, "");
    1029        1258 :         OVR(2, nCur + 159, ISDCTP, "");
    1030        1258 :         OVR(8, nCur + 161, ISDCDT, "");
    1031        1258 :         OVR(4, nCur + 169, ISDCXM, "");
    1032        1258 :         OVR(1, nCur + 173, ISDG, "");
    1033        1258 :         OVR(8, nCur + 174, ISDGDT, "");
    1034        1258 :         OVR(43, nCur + 182, ISCLTX, "");
    1035        1258 :         OVR(1, nCur + 225, ISCATP, "");
    1036        1258 :         OVR(40, nCur + 226, ISCAUT, "");
    1037        1258 :         OVR(1, nCur + 266, ISCRSN, "");
    1038        1258 :         OVR(8, nCur + 267, ISSRDT, "");
    1039        1258 :         OVR(15, nCur + 275, ISCTLN, "");
    1040        1258 :         PLACE(nCur + 290, ENCRYP, "0");
    1041        1258 :         OVR(42, nCur + 291, ISORCE, "Unknown");
    1042        1258 :         PLACE(nCur + 333, NROWS, CPLSPrintf("%08d", nLines));
    1043        1258 :         PLACE(nCur + 341, NCOLS, CPLSPrintf("%08d", nPixels));
    1044        1258 :         PLACE(nCur + 349, PVTYPE, pszPVType);
    1045        1258 :         PLACE(nCur + 352, IREP, pszIREP);
    1046        1258 :         OVR(8, nCur + 360, ICAT, "VIS");
    1047             :         {
    1048        1258 :             const char *pszParamValue = CSLFetchNameValue(papszOptions, "ABPP");
    1049        1258 :             PLACE(nCur + 368, ABPP,
    1050             :                   CPLSPrintf("%02d", pszParamValue ? atoi(pszParamValue)
    1051             :                                                    : nBitsPerSample));
    1052             :         }
    1053        1258 :         OVR(1, nCur + 370, PJUST, "R");
    1054        1258 :         OVR(1, nCur + 371, ICORDS, " ");
    1055             : 
    1056        1258 :         nOffset = 372;
    1057             : 
    1058             :         {
    1059             :             const char *pszParamValue;
    1060        1258 :             pszParamValue = CSLFetchNameValue(papszOptions, "ICORDS");
    1061        1258 :             if (pszParamValue == NULL)
    1062        1165 :                 pszParamValue = " ";
    1063        1258 :             if (*pszParamValue != ' ')
    1064             :             {
    1065          93 :                 OVR(60, nCur + nOffset, IGEOLO, "");
    1066          93 :                 nOffset += 60;
    1067             :             }
    1068             :         }
    1069             : 
    1070             :         {
    1071        1258 :             const char *pszICOM = CSLFetchNameValue(papszOptions, "ICOM");
    1072        1258 :             if (pszICOM != NULL)
    1073             :             {
    1074             :                 char *pszRecodedICOM =
    1075           2 :                     CPLRecode(pszICOM, CPL_ENC_UTF8, CPL_ENC_ISO8859_1);
    1076           2 :                 int nLenICOM = (int)strlen(pszRecodedICOM);
    1077           2 :                 int nICOM = (79 + nLenICOM) / 80;
    1078             :                 size_t nToWrite;
    1079           2 :                 if (nICOM > 9)
    1080             :                 {
    1081           0 :                     CPLError(CE_Warning, CPLE_NotSupported,
    1082             :                              "ICOM will be truncated");
    1083           0 :                     nICOM = 9;
    1084             :                 }
    1085           2 :                 PLACE(nCur + nOffset, NICOM, CPLSPrintf("%01d", nICOM));
    1086           2 :                 nToWrite = MIN(nICOM * 80, nLenICOM);
    1087           2 :                 bOK &= VSIFWriteL(pszRecodedICOM, 1, nToWrite, fp) == nToWrite;
    1088           2 :                 nOffset += nICOM * 80;
    1089           2 :                 CPLFree(pszRecodedICOM);
    1090             :             }
    1091             :             else
    1092             :             {
    1093        1256 :                 PLACE(nCur + nOffset, NICOM, "0");
    1094             :             }
    1095             :         }
    1096             : 
    1097        1258 :         if (pnICOffset)
    1098             :         {
    1099        1258 :             if (iIM == 0 || bAppendSubdataset)
    1100         260 :                 *pnICOffset = nCur + nOffset + 1;
    1101             :         }
    1102        1258 :         OVR(2, nCur + nOffset + 1, IC, "NC");
    1103             : 
    1104        1258 :         if (pszIC[0] != 'N')
    1105             :         {
    1106          22 :             OVR(4, nCur + nOffset + 3, COMRAT, "    ");
    1107          22 :             nOffset += 4;
    1108             :         }
    1109             : 
    1110        1258 :         if (nBands <= 9)
    1111             :         {
    1112        1257 :             PLACE(nCur + nOffset + 3, NBANDS, CPLSPrintf("%d", nBands));
    1113             :         }
    1114             :         else
    1115             :         {
    1116           1 :             PLACE(nCur + nOffset + 3, NBANDS, "0");
    1117           1 :             PLACE(nCur + nOffset + 4, XBANDS, CPLSPrintf("%05d", nBands));
    1118           1 :             nOffset += 5;
    1119             :         }
    1120             : 
    1121        1258 :         nOffset += 4;
    1122             : 
    1123             :         /* --------------------------------------------------------------------
    1124             :          */
    1125             :         /*      Per band info */
    1126             :         /* --------------------------------------------------------------------
    1127             :          */
    1128       72632 :         for (iBand = 0; iBand < nBands; iBand++)
    1129             :         {
    1130       71374 :             const char *pszIREPBAND = "M";
    1131             : 
    1132       71374 :             if (papszIREPBANDTokens != NULL)
    1133             :             {
    1134           7 :                 if (strlen(papszIREPBANDTokens[iBand]) > 2)
    1135             :                 {
    1136           0 :                     papszIREPBANDTokens[iBand][2] = '\0';
    1137           0 :                     CPLError(CE_Warning, CPLE_NotSupported,
    1138             :                              "Truncating IREPBAND[%d] to '%s'", iBand + 1,
    1139           0 :                              papszIREPBANDTokens[iBand]);
    1140             :                 }
    1141           7 :                 pszIREPBAND = papszIREPBANDTokens[iBand];
    1142             :             }
    1143       71367 :             else if (EQUAL(pszIREP, "RGB/LUT"))
    1144           2 :                 pszIREPBAND = "LU";
    1145       71365 :             else if (EQUAL(pszIREP, "RGB"))
    1146             :             {
    1147          31 :                 if (iBand == 0)
    1148          10 :                     pszIREPBAND = "R";
    1149          21 :                 else if (iBand == 1)
    1150          10 :                     pszIREPBAND = "G";
    1151          11 :                 else if (iBand == 2)
    1152          10 :                     pszIREPBAND = "B";
    1153             :             }
    1154       71334 :             else if (STARTS_WITH_CI(pszIREP, "YCbCr"))
    1155             :             {
    1156          15 :                 if (iBand == 0)
    1157           5 :                     pszIREPBAND = "Y";
    1158          10 :                 else if (iBand == 1)
    1159           5 :                     pszIREPBAND = "Cb";
    1160           5 :                 else if (iBand == 2)
    1161           5 :                     pszIREPBAND = "Cr";
    1162             :             }
    1163             : 
    1164       71374 :             PLACE(nCur + nOffset + 0, IREPBANDn, pszIREPBAND);
    1165             : 
    1166       71374 :             if (papszISUBCATTokens != NULL)
    1167             :             {
    1168           2 :                 if (strlen(papszISUBCATTokens[iBand]) > 6)
    1169             :                 {
    1170           0 :                     papszISUBCATTokens[iBand][6] = '\0';
    1171           0 :                     CPLError(CE_Warning, CPLE_NotSupported,
    1172             :                              "Truncating ISUBCAT[%d] to '%s'", iBand + 1,
    1173           0 :                              papszISUBCATTokens[iBand]);
    1174             :                 }
    1175           2 :                 PLACE(nCur + nOffset + 2, ISUBCATn, papszISUBCATTokens[iBand]);
    1176             :             }
    1177             :             //      else
    1178             :             //          PLACE(nCur+nOffset+ 2, ISUBCATn, "" );
    1179             : 
    1180       71374 :             PLACE(nCur + nOffset + 8, IFCn, "N");
    1181             :             //      PLACE(nCur+nOffset+ 9, IMFLTn, "" );
    1182             : 
    1183       71374 :             if (!EQUAL(pszIREP, "RGB/LUT"))
    1184             :             {
    1185       71372 :                 PLACE(nCur + nOffset + 12, NLUTSn, "0");
    1186       71372 :                 nOffset += 13;
    1187             :             }
    1188             :             else
    1189             :             {
    1190           2 :                 int iC, nCount = 256;
    1191             : 
    1192           2 :                 if (CSLFetchNameValue(papszOptions, "LUT_SIZE") != NULL)
    1193           2 :                     nCount = atoi(CSLFetchNameValue(papszOptions, "LUT_SIZE"));
    1194             : 
    1195           2 :                 if (!(nCount >= 0 && nCount <= 99999))
    1196             :                 {
    1197           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    1198             :                              "Invalid LUT value : %d. Defaulting to 256",
    1199             :                              nCount);
    1200           0 :                     nCount = 256;
    1201             :                 }
    1202           2 :                 PLACE(nCur + nOffset + 12, NLUTSn, "3");
    1203           2 :                 PLACE(nCur + nOffset + 13, NELUTn, CPLSPrintf("%05d", nCount));
    1204             : 
    1205         259 :                 for (iC = 0; iC < nCount; iC++)
    1206             :                 {
    1207         257 :                     WRITE_BYTE(nCur + nOffset + 18 + iC + 0, (char)iC);
    1208         257 :                     WRITE_BYTE(nCur + nOffset + 18 + iC + nCount * 1, (char)iC);
    1209         257 :                     WRITE_BYTE(nCur + nOffset + 18 + iC + nCount * 2, (char)iC);
    1210             :                 }
    1211           2 :                 nOffset += 18 + nCount * 3;
    1212             :             }
    1213             :         }
    1214             : 
    1215        1258 :         CSLDestroy(papszIREPBANDTokens);
    1216        1258 :         CSLDestroy(papszISUBCATTokens);
    1217             : 
    1218             :         /* --------------------------------------------------------------------
    1219             :          */
    1220             :         /*      Remainder of image header info. */
    1221             :         /* --------------------------------------------------------------------
    1222             :          */
    1223        1258 :         PLACE(nCur + nOffset + 0, ISYNC, "0");
    1224             : 
    1225             :         /* RGB JPEG compressed NITF requires IMODE=P (see #3345) */
    1226        1258 :         if (nBands >= 3 && (EQUAL(pszIC, "C3") || EQUAL(pszIC, "M3")))
    1227             :         {
    1228           6 :             PLACE(nCur + nOffset + 1, IMODE, "P");
    1229             :         }
    1230             :         else
    1231             :         {
    1232        1252 :             PLACE(nCur + nOffset + 1, IMODE, "B");
    1233             :         }
    1234        1258 :         PLACE(nCur + nOffset + 2, NBPR, CPLSPrintf("%04d", nNBPR));
    1235        1258 :         PLACE(nCur + nOffset + 6, NBPC, CPLSPrintf("%04d", nNBPC));
    1236        1258 :         PLACE(nCur + nOffset + 10, NPPBH, CPLSPrintf("%04d", nNPPBH));
    1237        1258 :         PLACE(nCur + nOffset + 14, NPPBV, CPLSPrintf("%04d", nNPPBV));
    1238        1258 :         PLACE(nCur + nOffset + 18, NBPP, CPLSPrintf("%02d", nBitsPerSample));
    1239        1258 :         PLACE(nCur + nOffset + 20, IDLVL,
    1240             :               CPLSPrintf("%03d", atoi(CSLFetchNameValueDef(papszOptions,
    1241             :                                                            "IDLVL", "1"))));
    1242        1258 :         PLACE(nCur + nOffset + 23, IALVL,
    1243             :               CPLSPrintf("%03d", atoi(CSLFetchNameValueDef(papszOptions,
    1244             :                                                            "IALVL", "0"))));
    1245        1258 :         PLACE(nCur + nOffset + 26, ILOCROW,
    1246             :               CPLSPrintf("%05d", atoi(CSLFetchNameValueDef(papszOptions,
    1247             :                                                            "ILOCROW", "0"))));
    1248        1258 :         PLACE(nCur + nOffset + 31, ILOCCOL,
    1249             :               CPLSPrintf("%05d", atoi(CSLFetchNameValueDef(papszOptions,
    1250             :                                                            "ILOCCOL", "0"))));
    1251        1258 :         PLACE(nCur + nOffset + 36, IMAG, "1.0 ");
    1252        1258 :         PLACE(nCur + nOffset + 40, UDIDL, "00000");
    1253        1258 :         PLACE(nCur + nOffset + 45, IXSHDL, "00000");
    1254             : 
    1255        1258 :         nOffsetUDIDL = nCur + nOffset + 40;
    1256        1258 :         nOffset += 50;
    1257             : 
    1258             :         /* --------------------------------------------------------------------
    1259             :          */
    1260             :         /*      Add BLOCKA TRE if requested. */
    1261             :         /* --------------------------------------------------------------------
    1262             :          */
    1263        1258 :         if (CSLFetchNameValue(papszOptions, "BLOCKA_BLOCK_COUNT") != NULL)
    1264             :         {
    1265           3 :             NITFWriteBLOCKA(fp, nOffsetUDIDL, &nOffset, papszOptions);
    1266             :         }
    1267             : 
    1268        2453 :         if (CSLFetchNameValue(papszOptions, "TRE") != NULL ||
    1269        1195 :             CSLFetchNameValue(papszOptions, "RESERVE_SPACE_FOR_TRE_OVERFLOW") !=
    1270             :                 NULL)
    1271             :         {
    1272          65 :             bOK &= NITFWriteTREsFromOptions(fp, nOffsetUDIDL, &nOffset,
    1273             :                                             papszOptions, "TRE=");
    1274             :         }
    1275             : 
    1276             :         /* --------------------------------------------------------------------
    1277             :          */
    1278             :         /*      Update the image header length in the file header. */
    1279             :         /* --------------------------------------------------------------------
    1280             :          */
    1281        1258 :         nIHSize = nOffset;
    1282             : 
    1283        1258 :         if (nIHSize > 999999)
    1284             :         {
    1285           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1286             :                      "Too big image header length : %d", nIHSize);
    1287           0 :             CPL_IGNORE_RET_VAL_INT(VSIFCloseL(fp));
    1288           0 :             return FALSE;
    1289             :         }
    1290             : 
    1291        1258 :         PLACE(363 + iIM * 16, LISH1, CPLSPrintf("%06d", nIHSize));
    1292        1258 :         if (EQUAL(pszIC, "NC"))
    1293             :         {
    1294        1236 :             PLACE(
    1295             :                 369 + iIM * 16, LIi,
    1296             :                 CPLSPrintf("%010" CPL_FRMT_GB_WITHOUT_PREFIX "u", nImageSize));
    1297             :         }
    1298             : 
    1299        1258 :         nCur += nIHSize;
    1300        1258 :         if (pnImageOffset)
    1301             :         {
    1302        1258 :             if (iIM == 0 || bAppendSubdataset)
    1303         260 :                 *pnImageOffset = nCur;
    1304             :         }
    1305        1258 :         nCur += nImageSize;
    1306             : 
    1307        1258 :         if (!bWriteAllImages)
    1308         259 :             break;
    1309             :     }
    1310             : 
    1311             :     /* -------------------------------------------------------------------- */
    1312             :     /*      Fill in image data by writing one byte at the end               */
    1313             :     /* -------------------------------------------------------------------- */
    1314         261 :     if (EQUAL(pszIC, "NC"))
    1315             :     {
    1316         239 :         char cNul = 0;
    1317         239 :         bOK &= VSIFSeekL(fp, nCur - 1, SEEK_SET) == 0;
    1318         239 :         bOK &= VSIFWriteL(&cNul, 1, 1, fp) == 1;
    1319             :     }
    1320             : 
    1321             :     /* -------------------------------------------------------------------- */
    1322             :     /*      Compute and update CLEVEL ("complexity" level).                 */
    1323             :     /*      See: http://164.214.2.51/ntb/baseline/docs/2500b/2500b_not2.pdf */
    1324             :     /*            page 96u                                                  */
    1325             :     /* -------------------------------------------------------------------- */
    1326         261 :     nCLevel = 3;
    1327         261 :     if (bAppendSubdataset)
    1328             :     {
    1329             :         // Get existing CLEVEL
    1330           5 :         bOK &= VSIFSeekL(fp, 9, SEEK_SET) == 0;
    1331           5 :         char szCLEVEL[3] = {0};
    1332           5 :         bOK &= VSIFReadL(szCLEVEL, 1, 2, fp) != 0;
    1333           5 :         nCLevel = atoi(szCLEVEL);
    1334             :     }
    1335         261 :     if (nBands > 9 || nIM > 20 || nPixels > 2048 || nLines > 2048 ||
    1336         244 :         nNPPBH > 2048 || nNPPBV > 2048 || nCur > 52428799)
    1337             :     {
    1338          18 :         nCLevel = MAX(nCLevel, 5);
    1339             :     }
    1340         261 :     if (nPixels > 8192 || nLines > 8192 || nNPPBH > 8192 || nNPPBV > 8192 ||
    1341         256 :         nCur > 1073741833 || nDES > 10)
    1342             :     {
    1343           5 :         nCLevel = MAX(nCLevel, 6);
    1344             :     }
    1345         261 :     if (nBands > 256 || nPixels > 65536 || nLines > 65536 ||
    1346         260 :         nCur > 2147483647 || nDES > 50)
    1347             :     {
    1348           1 :         nCLevel = MAX(nCLevel, 7);
    1349             :     }
    1350         261 :     OVR(2, 9, CLEVEL, CPLSPrintf("%02d", nCLevel));
    1351             : 
    1352             :     /* -------------------------------------------------------------------- */
    1353             :     /*      Update total file length                                        */
    1354             :     /* -------------------------------------------------------------------- */
    1355             : 
    1356             :     /* According to the spec, CLEVEL 7 supports up to 10,737,418,330 bytes */
    1357             :     /* but we can support technically much more */
    1358         261 :     if (EQUAL(pszIC, "NC") && nCur >= 999999999999ULL)
    1359             :     {
    1360           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Too big file : " CPL_FRMT_GUIB,
    1361             :                  nCur);
    1362           0 :         CPL_IGNORE_RET_VAL_INT(VSIFCloseL(fp));
    1363           0 :         return FALSE;
    1364             :     }
    1365             : 
    1366         261 :     PLACE(342, FL, CPLSPrintf("%012" CPL_FRMT_GB_WITHOUT_PREFIX "d", nCur));
    1367             : 
    1368         261 :     if (VSIFCloseL(fp) != 0)
    1369           0 :         bOK = FALSE;
    1370             : 
    1371         261 :     return bOK;
    1372             : }
    1373             : 
    1374       37970 : static int NITFWriteOption(VSILFILE *psFile, char **papszOptions, size_t nWidth,
    1375             :                            GUIntBig nLocation, const char *pszName,
    1376             :                            const char *pszText)
    1377             : {
    1378             :     const char *pszParamValue;
    1379             :     char *pszRecodedValue;
    1380             :     size_t nToWrite;
    1381       37970 :     int bOK = TRUE;
    1382             : 
    1383       37970 :     pszParamValue = CSLFetchNameValue(papszOptions, pszName);
    1384       37970 :     if (pszParamValue == NULL)
    1385             :     {
    1386       37850 :         pszRecodedValue = CPLRecode(pszText, CPL_ENC_UTF8, CPL_ENC_ISO8859_1);
    1387             :     }
    1388             :     else
    1389             :     {
    1390             :         pszRecodedValue =
    1391         120 :             CPLRecode(pszParamValue, CPL_ENC_UTF8, CPL_ENC_ISO8859_1);
    1392             :     }
    1393             : 
    1394       37970 :     bOK &= NITFGotoOffset(psFile, nLocation);
    1395       37970 :     nToWrite = MIN(nWidth, strlen(pszRecodedValue));
    1396       37970 :     bOK &= VSIFWriteL(pszRecodedValue, 1, nToWrite, psFile) == nToWrite;
    1397       37970 :     CPLFree(pszRecodedValue);
    1398       37970 :     return bOK;
    1399             : }
    1400             : 
    1401             : /************************************************************************/
    1402             : /*                            NITFWriteTRE()                            */
    1403             : /************************************************************************/
    1404             : 
    1405          78 : static int NITFWriteTRE(VSILFILE *fp, vsi_l_offset nOffsetUDIDL, int *pnOffset,
    1406             :                         const char *pszTREName, char *pabyTREData,
    1407             :                         int nTREDataSize)
    1408             : 
    1409             : {
    1410             :     char szTemp[12];
    1411             :     int nOldOffset;
    1412          78 :     int bOK = TRUE;
    1413             : 
    1414             :     /* -------------------------------------------------------------------- */
    1415             :     /*      Update IXSHDL.                                                  */
    1416             :     /* -------------------------------------------------------------------- */
    1417          78 :     bOK &= VSIFSeekL(fp, nOffsetUDIDL + 5, SEEK_SET) == 0;
    1418          78 :     bOK &= VSIFReadL(szTemp, 1, 5, fp) == 5;
    1419          78 :     szTemp[5] = 0;
    1420          78 :     nOldOffset = atoi(szTemp);
    1421             : 
    1422          78 :     if (nOldOffset == 0)
    1423             :     {
    1424          70 :         nOldOffset = 3;
    1425          70 :         PLACE(nOffsetUDIDL + 10, IXSOFL, "000");
    1426          70 :         *pnOffset += 3;
    1427             :     }
    1428             : 
    1429          78 :     if (nOldOffset + 11 + nTREDataSize > 99999 || nTREDataSize < 0 ||
    1430             :         nTREDataSize > 99999)
    1431             :     {
    1432           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Too big TRE to be written");
    1433           0 :         return FALSE;
    1434             :     }
    1435             : 
    1436          78 :     snprintf(szTemp, sizeof(szTemp), "%05d", nOldOffset + 11 + nTREDataSize);
    1437          78 :     PLACE(nOffsetUDIDL + 5, IXSHDL, szTemp);
    1438             : 
    1439             :     /* -------------------------------------------------------------------- */
    1440             :     /*      Create TRE prefix.                                              */
    1441             :     /* -------------------------------------------------------------------- */
    1442          78 :     snprintf(szTemp, sizeof(szTemp), "%-6s%05d", pszTREName, nTREDataSize);
    1443          78 :     bOK &= VSIFSeekL(fp, nOffsetUDIDL + 10 + nOldOffset, SEEK_SET) == 0;
    1444          78 :     bOK &= VSIFWriteL(szTemp, 11, 1, fp) == 1;
    1445          78 :     bOK &= (int)VSIFWriteL(pabyTREData, 1, nTREDataSize, fp) == nTREDataSize;
    1446             : 
    1447             :     /* -------------------------------------------------------------------- */
    1448             :     /*      Increment values.                                               */
    1449             :     /* -------------------------------------------------------------------- */
    1450          78 :     *pnOffset += nTREDataSize + 11;
    1451             : 
    1452          78 :     return bOK;
    1453             : }
    1454             : 
    1455             : /************************************************************************/
    1456             : /*                   NITFWriteTREsFromOptions()                         */
    1457             : /************************************************************************/
    1458             : 
    1459          70 : static int NITFWriteTREsFromOptions(VSILFILE *fp, vsi_l_offset nOffsetUDIDL,
    1460             :                                     int *pnOffset, char **papszOptions,
    1461             :                                     const char *pszTREPrefix)
    1462             : 
    1463             : {
    1464          70 :     int bIgnoreBLOCKA =
    1465          70 :         CSLFetchNameValue(papszOptions, "BLOCKA_BLOCK_COUNT") != NULL;
    1466             :     int iOption;
    1467          70 :     const bool bReserveSpaceForTREOverflow =
    1468          70 :         CSLFetchNameValue(papszOptions, "RESERVE_SPACE_FOR_TRE_OVERFLOW") !=
    1469             :         NULL;
    1470             : 
    1471          70 :     if (papszOptions == NULL)
    1472           0 :         return TRUE;
    1473             : 
    1474         249 :     for (iOption = 0; papszOptions[iOption] != NULL; iOption++)
    1475             :     {
    1476             :         const char *pszEscapedContents;
    1477             :         char *pszUnescapedContents;
    1478             :         char *pszTREName;
    1479             :         int nContentLength;
    1480             :         const char *pszSpace;
    1481         179 :         int bIsHex = FALSE;
    1482         179 :         int nTREPrefixLen = (int)strlen(pszTREPrefix);
    1483             : 
    1484         179 :         if (!EQUALN(papszOptions[iOption], pszTREPrefix, nTREPrefixLen))
    1485         104 :             continue;
    1486             : 
    1487          76 :         if (STARTS_WITH_CI(papszOptions[iOption] + nTREPrefixLen, "BLOCKA=") &&
    1488             :             bIgnoreBLOCKA)
    1489           1 :             continue;
    1490             : 
    1491          75 :         if (STARTS_WITH_CI(papszOptions[iOption] + nTREPrefixLen, "HEX/"))
    1492             :         {
    1493           4 :             bIsHex = TRUE;
    1494           4 :             nTREPrefixLen += 4;
    1495             :         }
    1496             : 
    1497             :         /* We do no longer use CPLParseNameValue() as it removes leading spaces
    1498             :          */
    1499             :         /* from the value (see #3088) */
    1500          75 :         pszSpace = strchr(papszOptions[iOption] + nTREPrefixLen, '=');
    1501          75 :         if (pszSpace == NULL)
    1502             :         {
    1503           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1504             :                      "Could not parse creation options %s",
    1505           0 :                      papszOptions[iOption] + nTREPrefixLen);
    1506           0 :             return FALSE;
    1507             :         }
    1508             : 
    1509          75 :         pszTREName = CPLStrdup(papszOptions[iOption] + nTREPrefixLen);
    1510          75 :         pszTREName[MIN(6, pszSpace - (papszOptions[iOption] + nTREPrefixLen))] =
    1511             :             '\0';
    1512          75 :         pszEscapedContents = pszSpace + 1;
    1513             : 
    1514          75 :         pszUnescapedContents = CPLUnescapeString(
    1515             :             pszEscapedContents, &nContentLength, CPLES_BackslashQuotable);
    1516             : 
    1517          75 :         if (bIsHex)
    1518             :         {
    1519             :             int i;
    1520             :             char pszSubStr[3];
    1521             : 
    1522           4 :             if (nContentLength % 2)
    1523             :             {
    1524           0 :                 CPLError(
    1525             :                     CE_Failure, CPLE_AppDefined,
    1526             :                     "Could not parse creation options %s: invalid hex data",
    1527           0 :                     papszOptions[iOption] + nTREPrefixLen);
    1528           0 :                 CPLFree(pszTREName);
    1529           0 :                 CPLFree(pszUnescapedContents);
    1530           0 :                 return FALSE;
    1531             :             }
    1532             : 
    1533           4 :             nContentLength = nContentLength / 2;
    1534        1138 :             for (i = 0; i < nContentLength; i++)
    1535             :             {
    1536        1134 :                 CPLStrlcpy(pszSubStr, pszUnescapedContents + 2 * i, 3);
    1537        1134 :                 pszUnescapedContents[i] = (char)strtoul(pszSubStr, NULL, 16);
    1538             :             }
    1539           4 :             pszUnescapedContents[nContentLength] = '\0';
    1540             :         }
    1541             : 
    1542          75 :         if (!NITFWriteTRE(fp, nOffsetUDIDL, pnOffset, pszTREName,
    1543             :                           pszUnescapedContents, nContentLength))
    1544             :         {
    1545           0 :             CPLFree(pszTREName);
    1546           0 :             CPLFree(pszUnescapedContents);
    1547           0 :             return FALSE;
    1548             :         }
    1549             : 
    1550          75 :         CPLFree(pszTREName);
    1551          75 :         CPLFree(pszUnescapedContents);
    1552             :     }
    1553             : 
    1554          70 :     if (bReserveSpaceForTREOverflow)
    1555             :     {
    1556             :         /* --------------------------------------------------------------------
    1557             :          */
    1558             :         /*      Update IXSHDL. */
    1559             :         /* --------------------------------------------------------------------
    1560             :          */
    1561             :         int nOldOffset;
    1562             :         char szTemp[6];
    1563           2 :         bool bOK = VSIFSeekL(fp, nOffsetUDIDL + 5, SEEK_SET) == 0;
    1564           2 :         bOK &= VSIFReadL(szTemp, 1, 5, fp) == 5;
    1565           2 :         szTemp[5] = 0;
    1566           2 :         nOldOffset = atoi(szTemp);
    1567             : 
    1568           2 :         if (nOldOffset == 0)
    1569             :         {
    1570           2 :             PLACE(nOffsetUDIDL + 5, IXSHDL, "00003");
    1571             : 
    1572           2 :             PLACE(nOffsetUDIDL + 10, IXSOFL, "000");
    1573           2 :             *pnOffset += 3;
    1574             :         }
    1575             : 
    1576           2 :         return bOK;
    1577             :     }
    1578             : 
    1579          68 :     return TRUE;
    1580             : }
    1581             : 
    1582             : /************************************************************************/
    1583             : /*                          NITFWriteBLOCKA()                           */
    1584             : /************************************************************************/
    1585             : 
    1586           3 : static int NITFWriteBLOCKA(VSILFILE *fp, vsi_l_offset nOffsetUDIDL,
    1587             :                            int *pnOffset, char **papszOptions)
    1588             : 
    1589             : {
    1590             :     static const char *const apszFields[] = {"BLOCK_INSTANCE",
    1591             :                                              "0",
    1592             :                                              "2",
    1593             :                                              "N_GRAY",
    1594             :                                              "2",
    1595             :                                              "5",
    1596             :                                              "L_LINES",
    1597             :                                              "7",
    1598             :                                              "5",
    1599             :                                              "LAYOVER_ANGLE",
    1600             :                                              "12",
    1601             :                                              "3",
    1602             :                                              "SHADOW_ANGLE",
    1603             :                                              "15",
    1604             :                                              "3",
    1605             :                                              "BLANKS",
    1606             :                                              "18",
    1607             :                                              "16",
    1608             :                                              "FRLC_LOC",
    1609             :                                              "34",
    1610             :                                              "21",
    1611             :                                              "LRLC_LOC",
    1612             :                                              "55",
    1613             :                                              "21",
    1614             :                                              "LRFC_LOC",
    1615             :                                              "76",
    1616             :                                              "21",
    1617             :                                              "FRFC_LOC",
    1618             :                                              "97",
    1619             :                                              "21",
    1620             :                                              NULL,
    1621             :                                              NULL,
    1622             :                                              NULL};
    1623             :     int nBlockCount =
    1624           3 :         atoi(CSLFetchNameValue(papszOptions, "BLOCKA_BLOCK_COUNT"));
    1625             :     int iBlock;
    1626             : 
    1627             :     /* ==================================================================== */
    1628             :     /*      Loop over all the blocks we have metadata for.                  */
    1629             :     /* ==================================================================== */
    1630           6 :     for (iBlock = 1; iBlock <= nBlockCount; iBlock++)
    1631             :     {
    1632             :         char szBLOCKA[123];
    1633             :         int iField;
    1634             : 
    1635             :         /* --------------------------------------------------------------------
    1636             :          */
    1637             :         /*      Write all fields. */
    1638             :         /* --------------------------------------------------------------------
    1639             :          */
    1640          33 :         for (iField = 0; apszFields[iField * 3] != NULL; iField++)
    1641             :         {
    1642             :             char szFullFieldName[64];
    1643          30 :             int iStart = atoi(apszFields[iField * 3 + 1]);
    1644          30 :             int iSize = atoi(apszFields[iField * 3 + 2]);
    1645             :             const char *pszValue;
    1646             : 
    1647          30 :             snprintf(szFullFieldName, sizeof(szFullFieldName), "BLOCKA_%s_%02d",
    1648          30 :                      apszFields[iField * 3 + 0], iBlock);
    1649             : 
    1650          30 :             pszValue = CSLFetchNameValue(papszOptions, szFullFieldName);
    1651          30 :             if (pszValue == NULL)
    1652           6 :                 pszValue = "";
    1653             : 
    1654          30 :             if (iSize - (int)strlen(pszValue) < 0)
    1655             :             {
    1656           0 :                 CPLError(
    1657             :                     CE_Failure, CPLE_AppDefined,
    1658             :                     "Too much data for %s. Got %d bytes, max allowed is %d",
    1659           0 :                     szFullFieldName, (int)strlen(pszValue), iSize);
    1660           0 :                 return FALSE;
    1661             :             }
    1662             : 
    1663             :             /* Right align value and left pad with spaces */
    1664          30 :             memset(szBLOCKA + iStart, ' ', iSize);
    1665             :             /* unsigned is always >= 0 */
    1666             :             /* memcpy( szBLOCKA + iStart +
    1667             :              * MAX((size_t)0,iSize-strlen(pszValue)), */
    1668          30 :             memcpy(szBLOCKA + iStart + (iSize - (int)strlen(pszValue)),
    1669             :                    pszValue, strlen(pszValue));
    1670             :         }
    1671             : 
    1672             :         // required field - semantics unknown.
    1673           3 :         memcpy(szBLOCKA + 118, "010.0", 5);
    1674             : 
    1675           3 :         if (!NITFWriteTRE(fp, nOffsetUDIDL, pnOffset, "BLOCKA", szBLOCKA, 123))
    1676           0 :             return FALSE;
    1677             :     }
    1678             : 
    1679           3 :     return TRUE;
    1680             : }
    1681             : 
    1682             : /************************************************************************/
    1683             : /*                       NITFCollectSegmentInfo()                       */
    1684             : /*                                                                      */
    1685             : /*      Collect the information about a set of segments of a            */
    1686             : /*      particular type from the NITF file header, and add them to      */
    1687             : /*      the segment list in the NITFFile object.                        */
    1688             : /************************************************************************/
    1689             : 
    1690        4614 : static int NITFCollectSegmentInfo(NITFFile *psFile, int nFileHeaderLen,
    1691             :                                   int nOffset, const char szType[3],
    1692             :                                   int nHeaderLenSize, int nDataLenSize,
    1693             :                                   GUIntBig *pnNextData)
    1694             : 
    1695             : {
    1696             :     char szTemp[12];
    1697             :     int nCount, nSegDefSize, iSegment;
    1698             : 
    1699             :     /* -------------------------------------------------------------------- */
    1700             :     /*      Get the segment count, and grow the segmentinfo array           */
    1701             :     /*      accordingly.                                                    */
    1702             :     /* -------------------------------------------------------------------- */
    1703        4614 :     if (nFileHeaderLen < nOffset + 3)
    1704             :     {
    1705           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1706             :                  "Not enough bytes to read segment count");
    1707           0 :         return -1;
    1708             :     }
    1709             : 
    1710        4614 :     NITFGetField(szTemp, psFile->pachHeader, nOffset, 3);
    1711        4614 :     nCount = atoi(szTemp);
    1712             : 
    1713        4614 :     if (nCount <= 0)
    1714        3786 :         return nOffset + 3;
    1715             : 
    1716         828 :     nSegDefSize = nCount * (nHeaderLenSize + nDataLenSize);
    1717         828 :     if (nFileHeaderLen < nOffset + 3 + nSegDefSize)
    1718             :     {
    1719           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    1720             :                  "Not enough bytes to read segment info");
    1721           1 :         return -1;
    1722             :     }
    1723             : 
    1724         827 :     if (psFile->pasSegmentInfo == NULL)
    1725         765 :         psFile->pasSegmentInfo =
    1726         765 :             (NITFSegmentInfo *)CPLMalloc(sizeof(NITFSegmentInfo) * nCount);
    1727             :     else
    1728          62 :         psFile->pasSegmentInfo = (NITFSegmentInfo *)CPLRealloc(
    1729          62 :             psFile->pasSegmentInfo,
    1730          62 :             sizeof(NITFSegmentInfo) * (psFile->nSegmentCount + nCount));
    1731             : 
    1732             :     /* -------------------------------------------------------------------- */
    1733             :     /*      Collect detailed about segment.                                 */
    1734             :     /* -------------------------------------------------------------------- */
    1735       11687 :     for (iSegment = 0; iSegment < nCount; iSegment++)
    1736             :     {
    1737       10860 :         NITFSegmentInfo *psInfo =
    1738       10860 :             psFile->pasSegmentInfo + psFile->nSegmentCount;
    1739             : 
    1740       10860 :         psInfo->nDLVL = -1;
    1741       10860 :         psInfo->nALVL = -1;
    1742       10860 :         psInfo->nLOC_R = -1;
    1743       10860 :         psInfo->nLOC_C = -1;
    1744       10860 :         psInfo->nCCS_R = -1;
    1745       10860 :         psInfo->nCCS_C = -1;
    1746             : 
    1747       10860 :         psInfo->hAccess = NULL;
    1748       10860 :         strncpy(psInfo->szSegmentType, szType, sizeof(psInfo->szSegmentType));
    1749       10860 :         psInfo->szSegmentType[sizeof(psInfo->szSegmentType) - 1] = '\0';
    1750             : 
    1751       21720 :         psInfo->nSegmentHeaderSize = atoi(NITFGetField(
    1752       10860 :             szTemp, psFile->pachHeader,
    1753       10860 :             nOffset + 3 + iSegment * (nHeaderLenSize + nDataLenSize),
    1754             :             nHeaderLenSize));
    1755       10860 :         if (strchr(szTemp, '-') != NULL) /* Avoid negative values being mapped
    1756             :                                             to huge unsigned values */
    1757             :         {
    1758           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1759             :                      "Invalid segment header size : %s", szTemp);
    1760           0 :             return -1;
    1761             :         }
    1762             : 
    1763       10860 :         if (strcmp(szType, "DE") == 0 && psInfo->nSegmentHeaderSize == 207)
    1764             :         {
    1765             :             /* DMAAC A.TOC files have a wrong header size. It says 207 but it is
    1766             :              * 209 really */
    1767           0 :             psInfo->nSegmentHeaderSize = 209;
    1768             :         }
    1769             : 
    1770       10860 :         psInfo->nSegmentSize = CPLScanUIntBig(
    1771       10860 :             NITFGetField(szTemp, psFile->pachHeader,
    1772       10860 :                          nOffset + 3 +
    1773       10860 :                              iSegment * (nHeaderLenSize + nDataLenSize) +
    1774             :                              nHeaderLenSize,
    1775             :                          nDataLenSize),
    1776             :             nDataLenSize);
    1777       10860 :         if (strchr(szTemp, '-') != NULL) /* Avoid negative values being mapped
    1778             :                                             to huge unsigned values */
    1779             :         {
    1780           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid segment size : %s",
    1781             :                      szTemp);
    1782           0 :             return -1;
    1783             :         }
    1784             : 
    1785       10860 :         psInfo->nSegmentHeaderStart = *pnNextData;
    1786       10860 :         psInfo->nSegmentStart = *pnNextData + psInfo->nSegmentHeaderSize;
    1787             : 
    1788       10860 :         *pnNextData += (psInfo->nSegmentHeaderSize + psInfo->nSegmentSize);
    1789       10860 :         psFile->nSegmentCount++;
    1790             :     }
    1791             : 
    1792         827 :     return nOffset + nSegDefSize + 3;
    1793             : }
    1794             : 
    1795             : /************************************************************************/
    1796             : /*                            NITFGetField()                            */
    1797             : /*                                                                      */
    1798             : /*      Copy a field from a passed in header buffer into a temporary    */
    1799             : /*      buffer and zero terminate it.                                   */
    1800             : /************************************************************************/
    1801             : 
    1802      913347 : char *NITFGetField(char *pszTarget, const char *pszSource, int nStart,
    1803             :                    int nLength)
    1804             : 
    1805             : {
    1806      913347 :     memcpy(pszTarget, pszSource + nStart, nLength);
    1807      913347 :     pszTarget[nLength] = '\0';
    1808             : 
    1809      913347 :     return pszTarget;
    1810             : }
    1811             : 
    1812             : /************************************************************************/
    1813             : /*                            NITFFindTRE()                             */
    1814             : /************************************************************************/
    1815             : 
    1816       68579 : const char *NITFFindTRE(const char *pszTREData, int nTREBytes,
    1817             :                         const char *pszTag, int *pnFoundTRESize)
    1818             : 
    1819             : {
    1820             :     char szTemp[100];
    1821             : 
    1822       76735 :     while (nTREBytes >= 11)
    1823             :     {
    1824        8364 :         int nThisTRESize = atoi(NITFGetField(szTemp, pszTREData, 6, 5));
    1825        8364 :         if (nThisTRESize < 0)
    1826             :         {
    1827           0 :             NITFGetField(szTemp, pszTREData, 0, 6);
    1828           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1829             :                      "Invalid size (%d) for TRE %s", nThisTRESize, szTemp);
    1830           0 :             return NULL;
    1831             :         }
    1832        8364 :         if (nTREBytes - 11 < nThisTRESize)
    1833             :         {
    1834           0 :             NITFGetField(szTemp, pszTREData, 0, 6);
    1835           0 :             if (STARTS_WITH_CI(szTemp, "RPFIMG"))
    1836             :             {
    1837             :                 /* See #3848 */
    1838           0 :                 CPLDebug("NITF",
    1839             :                          "Adjusting RPFIMG TRE size from %d to %d, which is "
    1840             :                          "the remaining size",
    1841             :                          nThisTRESize, nTREBytes - 11);
    1842           0 :                 nThisTRESize = nTREBytes - 11;
    1843             :             }
    1844             :             else
    1845             :             {
    1846           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1847             :                          "Cannot read %s TRE. Not enough bytes : remaining %d, "
    1848             :                          "expected %d",
    1849             :                          szTemp, nTREBytes - 11, nThisTRESize);
    1850           0 :                 return NULL;
    1851             :             }
    1852             :         }
    1853             : 
    1854        8364 :         if (EQUALN(pszTREData, pszTag, 6))
    1855             :         {
    1856         208 :             if (pnFoundTRESize != NULL)
    1857         208 :                 *pnFoundTRESize = nThisTRESize;
    1858             : 
    1859         208 :             return pszTREData + 11;
    1860             :         }
    1861             : 
    1862        8156 :         nTREBytes -= (nThisTRESize + 11);
    1863        8156 :         pszTREData += (nThisTRESize + 11);
    1864             :     }
    1865             : 
    1866       68371 :     return NULL;
    1867             : }
    1868             : 
    1869             : /************************************************************************/
    1870             : /*                     NITFFindTREByIndex()                             */
    1871             : /************************************************************************/
    1872             : 
    1873         767 : const char *NITFFindTREByIndex(const char *pszTREData, int nTREBytes,
    1874             :                                const char *pszTag, int nTreIndex,
    1875             :                                int *pnFoundTRESize)
    1876             : 
    1877             : {
    1878             :     char szTemp[100];
    1879             : 
    1880        1001 :     while (nTREBytes >= 11)
    1881             :     {
    1882         259 :         int nThisTRESize = atoi(NITFGetField(szTemp, pszTREData, 6, 5));
    1883         259 :         if (nThisTRESize < 0)
    1884             :         {
    1885           0 :             NITFGetField(szTemp, pszTREData, 0, 6);
    1886           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1887             :                      "Invalid size (%d) for TRE %s", nThisTRESize, szTemp);
    1888           0 :             return NULL;
    1889             :         }
    1890         259 :         if (nTREBytes - 11 < nThisTRESize)
    1891             :         {
    1892           0 :             NITFGetField(szTemp, pszTREData, 0, 6);
    1893           0 :             if (STARTS_WITH_CI(szTemp, "RPFIMG"))
    1894             :             {
    1895             :                 /* See #3848 */
    1896           0 :                 CPLDebug("NITF",
    1897             :                          "Adjusting RPFIMG TRE size from %d to %d, which is "
    1898             :                          "the remaining size",
    1899             :                          nThisTRESize, nTREBytes - 11);
    1900           0 :                 nThisTRESize = nTREBytes - 11;
    1901             :             }
    1902             :             else
    1903             :             {
    1904           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1905             :                          "Cannot read %s TRE. Not enough bytes : remaining %d, "
    1906             :                          "expected %d",
    1907             :                          szTemp, nTREBytes - 11, nThisTRESize);
    1908           0 :                 return NULL;
    1909             :             }
    1910             :         }
    1911             : 
    1912         259 :         if (EQUALN(pszTREData, pszTag, 6))
    1913             :         {
    1914          47 :             if (nTreIndex <= 0)
    1915             :             {
    1916          25 :                 if (pnFoundTRESize != NULL)
    1917          25 :                     *pnFoundTRESize = nThisTRESize;
    1918             : 
    1919          25 :                 return pszTREData + 11;
    1920             :             }
    1921             : 
    1922             :             /* Found a previous one - skip it ... */
    1923          22 :             nTreIndex--;
    1924             :         }
    1925             : 
    1926         234 :         nTREBytes -= (nThisTRESize + 11);
    1927         234 :         pszTREData += (nThisTRESize + 11);
    1928             :     }
    1929             : 
    1930         742 :     return NULL;
    1931             : }
    1932             : 
    1933             : /************************************************************************/
    1934             : /*                        NITFExtractMetadata()                         */
    1935             : /************************************************************************/
    1936             : 
    1937      288937 : static void NITFExtractAndRecodeMetadata(char ***ppapszMetadata,
    1938             :                                          const char *pachHeader, int nStart,
    1939             :                                          int nLength, const char *pszName,
    1940             :                                          const char *pszSrcEncoding)
    1941             : 
    1942             : {
    1943             :     char szWork[400];
    1944             :     char *pszWork;
    1945             :     char *pszRecode;
    1946             : 
    1947      288937 :     if (nLength <= 0)
    1948           0 :         return;
    1949             : 
    1950      288937 :     if (nLength >= (int)(sizeof(szWork) - 1))
    1951           1 :         pszWork = (char *)CPLMalloc(nLength + 1);
    1952             :     else
    1953      288936 :         pszWork = szWork;
    1954             : 
    1955             :     /* trim white space */
    1956     3561160 :     while (nLength > 0 && pachHeader[nStart + nLength - 1] == ' ')
    1957     3272220 :         nLength--;
    1958             : 
    1959      288937 :     memcpy(pszWork, pachHeader + nStart, nLength);
    1960      288937 :     pszWork[nLength] = '\0';
    1961             : 
    1962      288937 :     if (strcmp(pszSrcEncoding, CPL_ENC_UTF8) != 0)
    1963             :     {
    1964      285077 :         pszRecode = CPLRecode(pszWork, pszSrcEncoding, CPL_ENC_UTF8);
    1965      285077 :         *ppapszMetadata = CSLSetNameValue(*ppapszMetadata, pszName, pszRecode);
    1966      285077 :         CPLFree(pszRecode);
    1967             :     }
    1968             :     else
    1969             :     {
    1970        3860 :         *ppapszMetadata = CSLSetNameValue(*ppapszMetadata, pszName, pszWork);
    1971             :     }
    1972             : 
    1973      288937 :     if (szWork != pszWork)
    1974           1 :         CPLFree(pszWork);
    1975             : }
    1976             : 
    1977      285077 : void NITFExtractMetadata(char ***ppapszMetadata, const char *pachHeader,
    1978             :                          int nStart, int nLength, const char *pszName)
    1979             : 
    1980             : {
    1981      285077 :     NITFExtractAndRecodeMetadata(ppapszMetadata, pachHeader, nStart, nLength,
    1982             :                                  pszName, CPL_ENC_ISO8859_1);
    1983      285077 : }
    1984             : 
    1985             : /************************************************************************/
    1986             : /*        NITF_WGS84_Geocentric_Latitude_To_Geodetic_Latitude()         */
    1987             : /*                                                                      */
    1988             : /*      The input is a geocentric latitude in degrees.  The output      */
    1989             : /*      is a geodetic latitude in degrees.                              */
    1990             : /************************************************************************/
    1991             : 
    1992             : /*
    1993             :  * "The angle L' is called "geocentric latitude" and is defined as the
    1994             :  * angle between the equatorial plane and the radius from the geocenter.
    1995             :  *
    1996             :  * The angle L is called "geodetic latitude" and is defined as the angle
    1997             :  * between the equatorial plane and the normal to the surface of the
    1998             :  * ellipsoid.  The word "latitude" usually means geodetic latitude.  This
    1999             :  * is the basis for most of the maps and charts we use.  The normal to the
    2000             :  * surface is the direction that a plumb bob would hang were it not for
    2001             :  * local anomalies in the earth's gravitational field."
    2002             :  */
    2003             : 
    2004           0 : double NITF_WGS84_Geocentric_Latitude_To_Geodetic_Latitude(double dfLat)
    2005             : 
    2006             : {
    2007             :     /* WGS84 Ellipsoid */
    2008           0 :     const double a = 6378137.0;
    2009           0 :     const double b = 6356752.3142;
    2010             : 
    2011             :     /* convert to radians */
    2012           0 :     dfLat = dfLat * M_PI / 180.0;
    2013             : 
    2014             :     /* convert to geodetic */
    2015           0 :     dfLat = atan(((a * a) / (b * b)) * tan(dfLat));
    2016             : 
    2017             :     /* convert back to degrees */
    2018           0 :     dfLat = dfLat * 180.0 / M_PI;
    2019             : 
    2020           0 :     return dfLat;
    2021             : }
    2022             : 
    2023             : /************************************************************************/
    2024             : /*                        NITFGetSeriesInfo()                           */
    2025             : /************************************************************************/
    2026             : 
    2027             : /* From
    2028             :  * http://trac.osgeo.org/gdal/attachment/ticket/5353/MIL-STD-2411_1_CHG-3.pdf */
    2029             : static const NITFSeries nitfSeries[] = {
    2030             :     {"A1", "CM", "1:10K", "Combat Charts (1:10K)", "CADRG"},
    2031             :     {"A2", "CM", "1:25K", "Combat Charts (1:25K)", "CADRG"},
    2032             :     {"A3", "CM", "1:50K", "Combat Charts (1:50K)", "CADRG"},
    2033             :     {"A4", "CM", "1:100K", "Combat Charts (1:100K)", "CADRG"},
    2034             :     {"AT", "ATC", "1:200K", "Series 200 Air Target Chart", "CADRG"},
    2035             :     {"C1", "CG", "1:10000", "City Graphics", "CADRG"},
    2036             :     {"C2", "CG", "1:10560", "City Graphics", "CADRG"},
    2037             :     {"C3", "CG", "1:11000", "City Graphics", "CADRG"},
    2038             :     {"C4", "CG", "1:11800", "City Graphics", "CADRG"},
    2039             :     {"C5", "CG", "1:12000", "City Graphics", "CADRG"},
    2040             :     {"C6", "CG", "1:12500", "City Graphics", "CADRG"},
    2041             :     {"C7", "CG", "1:12800", "City Graphics", "CADRG"},
    2042             :     {"C8", "CG", "1:14000", "City Graphics", "CADRG"},
    2043             :     {"C9", "CG", "1:14700", "City Graphics", "CADRG"},
    2044             :     {"CA", "CG", "1:15000", "City Graphics", "CADRG"},
    2045             :     {"CB", "CG", "1:15500", "City Graphics", "CADRG"},
    2046             :     {"CC", "CG", "1:16000", "City Graphics", "CADRG"},
    2047             :     {"CD", "CG", "1:16666", "City Graphics", "CADRG"},
    2048             :     {"CE", "CG", "1:17000", "City Graphics", "CADRG"},
    2049             :     {"CF", "CG", "1:17500", "City Graphics", "CADRG"},
    2050             :     {"CG", "CG", "Various", "City Graphics", "CADRG"},
    2051             :     {"CH", "CG", "1:18000", "City Graphics", "CADRG"},
    2052             :     {"CJ", "CG", "1:20000", "City Graphics", "CADRG"},
    2053             :     {"CK", "CG", "1:21000", "City Graphics", "CADRG"},
    2054             :     {"CL", "CG", "1:21120", "City Graphics", "CADRG"},
    2055             :     {"CM", "CM", "Various", "Combat Charts", "CADRG"},
    2056             :     {"CN", "CG", "1:22000", "City Graphics", "CADRG"},
    2057             :     {"CO", "CO", "Various", "Coastal Charts", "CADRG"},
    2058             :     {"CP", "CG", "1:23000", "City Graphics", "CADRG"},
    2059             :     {"CQ", "CG", "1:25000", "City Graphics", "CADRG"},
    2060             :     {"CR", "CG", "1:26000", "City Graphics", "CADRG"},
    2061             :     {"CS", "CG", "1:35000", "City Graphics", "CADRG"},
    2062             :     {"CT", "CG", "1:36000", "City Graphics", "CADRG"},
    2063             :     {"D1", "", "100m", "Elevation Data from DTED level 1", "CDTED"},
    2064             :     {"D2", "", "30m", "Elevation Data from DTED level 2", "CDTED"},
    2065             :     {"EG", "NARC", "1:11,000,000", "North Atlantic Route Chart", "CADRG"},
    2066             :     {"ES", "SEC", "1:500K", "VFR Sectional", "CADRG"},
    2067             :     {"ET", "SEC", "1:250K", "VFR Sectional Inserts", "CADRG"},
    2068             :     {"F1", "TFC-1", "1:250K", "Transit Flying Chart (TBD #1)", "CADRG"},
    2069             :     {"F2", "TFC-2", "1:250K", "Transit Flying Chart (TBD #2)", "CADRG"},
    2070             :     {"F3", "TFC-3", "1:250K", "Transit Flying Chart (TBD #3)", "CADRG"},
    2071             :     {"F4", "TFC-4", "1:250K", "Transit Flying Chart (TBD #4)", "CADRG"},
    2072             :     {"F5", "TFC-5", "1:250K", "Transit Flying Chart (TBD #5)", "CADRG"},
    2073             :     {"GN", "GNC", "1:5M", "Global Navigation Chart", "CADRG"},
    2074             :     {"HA", "HA", "Various", "Harbor and Approach Charts", "CADRG"},
    2075             :     {"I1", "", "10m", "Imagery, 10 meter resolution", "CIB"},
    2076             :     {"I2", "", "5m", "Imagery, 5 meter resolution", "CIB"},
    2077             :     {"I3", "", "2m", "Imagery, 2 meter resolution", "CIB"},
    2078             :     {"I4", "", "1m", "Imagery, 1 meter resolution", "CIB"},
    2079             :     {"I5", "", ".5m", "Imagery, .5 (half) meter resolution", "CIB"},
    2080             :     {"IV", "", "Various > 10m", "Imagery, greater than 10 meter resolution",
    2081             :      "CIB"},
    2082             :     {"JA", "JOG-A", "1:250K", "Joint Operation Graphic - Air", "CADRG"},
    2083             :     {"JG", "JOG", "1:250K", "Joint Operation Graphic", "CADRG"},
    2084             :     {"JN", "JNC", "1:2M", "Jet Navigation Chart", "CADRG"},
    2085             :     {"JO", "OPG", "1:250K", "Operational Planning Graphic", "CADRG"},
    2086             :     {"JR", "JOG-R", "1:250K", "Joint Operation Graphic - Radar", "CADRG"},
    2087             :     {"K1", "ICM", "1:8K", "Image City Maps", "CADRG"},
    2088             :     {"K2", "ICM", "1:10K", "Image City Maps", "CADRG"},
    2089             :     {"K3", "ICM", "1:10560", "Image City Maps", "CADRG"},
    2090             :     {"K7", "ICM", "1:12500", "Image City Maps", "CADRG"},
    2091             :     {"K8", "ICM", "1:12800", "Image City Maps", "CADRG"},
    2092             :     {"KB", "ICM", "1:15K", "Image City Maps", "CADRG"},
    2093             :     {"KE", "ICM", "1:16666", "Image City Maps", "CADRG"},
    2094             :     {"KM", "ICM", "1:21120", "Image City Maps", "CADRG"},
    2095             :     {"KR", "ICM", "1:25K", "Image City Maps", "CADRG"},
    2096             :     {"KS", "ICM", "1:26K", "Image City Maps", "CADRG"},
    2097             :     {"KU", "ICM", "1:36K", "Image City Maps", "CADRG"},
    2098             :     {"L1", "LFC-1", "1:500K", "Low Flying Chart (TBD #1)", "CADRG"},
    2099             :     {"L2", "LFC-2", "1:500K", "Low Flying Chart (TBD #2)", "CADRG"},
    2100             :     {"L3", "LFC-3", "1:500K", "Low Flying Chart (TBD #3)", "CADRG"},
    2101             :     {"L4", "LFC-4", "1:500K", "Low Flying Chart (TBD #4)", "CADRG"},
    2102             :     {"L5", "LFC-5", "1:500K", "Low Flying Chart (TBD #5)", "CADRG"},
    2103             :     {"LF", "LFC-FR (Day)", "1:500K", "Low Flying Chart (Day) - Host Nation",
    2104             :      "CADRG"},
    2105             :     {"LN", "LN (Night)", "1:500K", "Low Flying Chart (Night) - Host Nation",
    2106             :      "CADRG"},
    2107             :     {"M1", "MIM", "Various", "Military Installation Maps (TBD #1)", "CADRG"},
    2108             :     {"M2", "MIM", "Various", "Military Installation Maps (TBD #2)", "CADRG"},
    2109             :     {"MH", "MIM", "1:25K", "Military Installation Maps", "CADRG"},
    2110             :     {"MI", "MIM", "1:50K", "Military Installation Maps", "CADRG"},
    2111             :     {"MJ", "MIM", "1:100K", "Military Installation Maps", "CADRG"},
    2112             :     {"MM", "", "Various", "(Miscellaneous Maps & Charts)", "CADRG"},
    2113             :     {"OA", "OPAREA", "Various", "Naval Range Operation Area Chart", "CADRG"},
    2114             :     {"OH", "VHRC", "1:1M", "VFR Helicopter Route Chart", "CADRG"},
    2115             :     {"ON", "ONC", "1:1M", "Operational Navigation Chart", "CADRG"},
    2116             :     {"OW", "WAC", "1:1M", "High Flying Chart - Host Nation", "CADRG"},
    2117             :     {"P1", "", "1:25K", "Special Military Map - Overlay", "CADRG"},
    2118             :     {"P2", "", "1:25K", "Special Military Purpose", "CADRG"},
    2119             :     {"P3", "", "1:25K", "Special Military Purpose", "CADRG"},
    2120             :     {"P4", "", "1:25K", "Special Military Purpose", "CADRG"},
    2121             :     {"P5", "", "1:50K", "Special Military Map - Overlay", "CADRG"},
    2122             :     {"P6", "", "1:50K", "Special Military Purpose", "CADRG"},
    2123             :     {"P7", "", "1:50K", "Special Military Purpose", "CADRG"},
    2124             :     {"P8", "", "1:50K", "Special Military Purpose", "CADRG"},
    2125             :     {"P9", "", "1:100K", "Special Military Map - Overlay", "CADRG"},
    2126             :     {"PA", "", "1:100K", "Special Military Purpose", "CADRG"},
    2127             :     {"PB", "", "1:100K", "Special Military Purpose", "CADRG"},
    2128             :     {"PC", "", "1:100K", "Special Military Purpose", "CADRG"},
    2129             :     {"PD", "", "1:250K", "Special Military Map - Overlay", "CADRG"},
    2130             :     {"PE", "", "1:250K", "Special Military Purpose", "CADRG"},
    2131             :     {"PF", "", "1:250K", "Special Military Purpose", "CADRG"},
    2132             :     {"PG", "", "1:250K", "Special Military Purpose", "CADRG"},
    2133             :     {"PH", "", "1:500K", "Special Military Map - Overlay", "CADRG"},
    2134             :     {"PI", "", "1:500K", "Special Military Purpose", "CADRG"},
    2135             :     {"PJ", "", "1:500K", "Special Military Purpose", "CADRG"},
    2136             :     {"PK", "", "1:500K", "Special Military Purpose", "CADRG"},
    2137             :     {"PL", "", "1:1M", "Special Military Map - Overlay", "CADRG"},
    2138             :     {"PM", "", "1:1M", "Special Military Purpose", "CADRG"},
    2139             :     {"PN", "", "1:1M", "Special Military Purpose", "CADRG"},
    2140             :     {"PO", "", "1:1M", "Special Military Purpose", "CADRG"},
    2141             :     {"PP", "", "1:2M", "Special Military Map - Overlay", "CADRG"},
    2142             :     {"PQ", "", "1:2M", "Special Military Purpose", "CADRG"},
    2143             :     {"PR", "", "1:2M", "Special Military Purpose", "CADRG"},
    2144             :     {"PS", "", "1:5M", "Special Military Map - Overlay", "CADRG"},
    2145             :     {"PT", "", "1:5M", "Special Military Purpose", "CADRG"},
    2146             :     {"PU", "", "1:5M", "Special Military Purpose", "CADRG"},
    2147             :     {"PV", "", "1:5M", "Special Military Purpose", "CADRG"},
    2148             :     {"R1", "", "1:50K", "Range Charts", "CADRG"},
    2149             :     {"R2", "", "1:100K", "Range Charts", "CADRG"},
    2150             :     {"R3", "", "1:250K", "Range Charts", "CADRG"},
    2151             :     {"R4", "", "1:500K", "Range Charts", "CADRG"},
    2152             :     {"R5", "", "1:1M", "Range Charts", "CADRG"},
    2153             :     {"RC", "RGS-100", "1:100K", "Russian General Staff Maps", "CADRG"},
    2154             :     {"RL", "RGS-50", "1:50K", "Russian General Staff Maps", "CADRG"},
    2155             :     {"RR", "RGS-200", "1:200K", "Russian General Staff Maps", "CADRG"},
    2156             :     {"RV", "Riverine", "1:50K", "Riverine Map 1:50,000 scale", "CADRG"},
    2157             :     {"TC", "TLM 100", "1:100K", "Topographic Line Map 1:100,000 scale",
    2158             :      "CADRG"},
    2159             :     {"TF", "TFC (Day)", "1:250K", "Transit Flying Chart (Day)", "CADRG"},
    2160             :     {"TL", "TLM50", "1:50K", "Topographic Line Map", "CADRG"},
    2161             :     {"TN", "TFC (Night)", "1:250K",
    2162             :      "Transit Flying Chart (Night) - Host Nation", "CADRG"},
    2163             :     {"TP", "TPC", "1:500K", "Tactical Pilotage Chart", "CADRG"},
    2164             :     {"TQ", "TLM24", "1:24K", "Topographic Line Map 1:24,000 scale", "CADRG"},
    2165             :     {"TR", "TLM200", "1:200K", "Topographic Line Map 1:200,000 scale", "CADRG"},
    2166             :     {"TT", "TLM25", "1:25K", "Topographic Line Map 1:25,000 scale", "CADRG"},
    2167             :     {"UL", "TLM50 - Other", "1:50K",
    2168             :      "Topographic Line Map (other 1:50,000 scale)", "CADRG"},
    2169             :     {"V1", "Inset HRC", "1:50", "Helicopter Route Chart Inset", "CADRG"},
    2170             :     {"V2", "Inset HRC", "1:62500", "Helicopter Route Chart Inset", "CADRG"},
    2171             :     {"V3", "Inset HRC", "1:90K", "Helicopter Route Chart Inset", "CADRG"},
    2172             :     {"V4", "Inset HRC", "1:250K", "Helicopter Route Chart Inset", "CADRG"},
    2173             :     {"VH", "HRC", "1:125K", "Helicopter Route Chart", "CADRG"},
    2174             :     {"VN", "VNC", "1:500K", "Visual Navigation Charts", "CADRG"},
    2175             :     {"VT", "VTAC", "1:250K", "VFR Terminal Area Chart", "CADRG"},
    2176             :     {"WA", "", "1:250K", "IFR Enroute Low", "CADRG"},
    2177             :     {"WB", "", "1:500K", "IFR Enroute Low", "CADRG"},
    2178             :     {"WC", "", "1:750K", "IFR Enroute Low", "CADRG"},
    2179             :     {"WD", "", "1:1M", "IFR Enroute Low", "CADRG"},
    2180             :     {"WE", "", "1:1.5M", "IFR Enroute Low", "CADRG"},
    2181             :     {"WF", "", "1:2M", "IFR Enroute Low", "CADRG"},
    2182             :     {"WG", "", "1:2.5M", "IFR Enroute Low", "CADRG"},
    2183             :     {"WH", "", "1:3M", "IFR Enroute Low", "CADRG"},
    2184             :     {"WI", "", "1:3.5M", "IFR Enroute Low", "CADRG"},
    2185             :     {"WK", "", "1:4M", "IFR Enroute Low", "CADRG"},
    2186             :     {"XD", "", "1:1M", "IFR Enroute High", "CADRG"},
    2187             :     {"XE", "", "1:1.5M", "IFR Enroute High", "CADRG"},
    2188             :     {"XF", "", "1:2M", "IFR Enroute High", "CADRG"},
    2189             :     {"XG", "", "1:2.5M", "IFR Enroute High", "CADRG"},
    2190             :     {"XH", "", "1:3M", "IFR Enroute High", "CADRG"},
    2191             :     {"XI", "", "1:3.5M", "IFR Enroute High", "CADRG"},
    2192             :     {"XJ", "", "1:4M", "IFR Enroute High", "CADRG"},
    2193             :     {"XK", "", "1:4.5M", "IFR Enroute High", "CADRG"},
    2194             :     {"Y9", "", "1:16.5M", "IFR Enroute Area", "CADRG"},
    2195             :     {"YA", "", "1:250K", "IFR Enroute Area", "CADRG"},
    2196             :     {"YB", "", "1:500K", "IFR Enroute Area", "CADRG"},
    2197             :     {"YC", "", "1:750K", "IFR Enroute Area", "CADRG"},
    2198             :     {"YD", "", "1:1M", "IFR Enroute Area", "CADRG"},
    2199             :     {"YE", "", "1:1.5M", "IFR Enroute Area", "CADRG"},
    2200             :     {"YF", "", "1:2M", "IFR Enroute Area", "CADRG"},
    2201             :     {"YI", "", "1:3.5M", "IFR Enroute Area", "CADRG"},
    2202             :     {"YJ", "", "1:4M", "IFR Enroute Area", "CADRG"},
    2203             :     {"YZ", "", "1:12M", "IFR Enroute Area", "CADRG"},
    2204             :     {"ZA", "", "1:250K", "IFR Enroute High/Low", "CADRG"},
    2205             :     {"ZB", "", "1:500K", "IFR Enroute High/Low", "CADRG"},
    2206             :     {"ZC", "", "1:750K", "IFR Enroute High/Low", "CADRG"},
    2207             :     {"ZD", "", "1:1M", "IFR Enroute High/Low", "CADRG"},
    2208             :     {"ZE", "", "1:1.5M", "IFR Enroute High/Low", "CADRG"},
    2209             :     {"ZF", "", "1:2M", "IFR Enroute High/Low", "CADRG"},
    2210             :     {"ZG", "", "1:2.5M", "IFR Enroute High/Low", "CADRG"},
    2211             :     {"ZH", "", "1:3M", "IFR Enroute High/Low", "CADRG"},
    2212             :     {"ZI", "", "1:3.5M", "IFR Enroute High/Low", "CADRG"},
    2213             :     {"ZJ", "", "1:4M", "IFR Enroute High/Low", "CADRG"},
    2214             :     {"ZK", "", "1:4.5M", "IFR Enroute High/Low", "CADRG"},
    2215             :     {"ZT", "", "1:9M", "IFR Enroute High/Low", "CADRG"},
    2216             :     {"ZV", "", "1:10M", "IFR Enroute High/Low", "CADRG"},
    2217             :     {"ZZ", "", "1:12M", "IFR Enroute High/Low", "CADRG"}};
    2218             : 
    2219             : /* See 24111CN1.pdf paragraph 5.1.4 */
    2220         760 : const NITFSeries *NITFGetSeriesInfo(const char *pszFilename)
    2221             : {
    2222             :     int i;
    2223         760 :     char seriesCode[3] = {0, 0, 0};
    2224         760 :     if (pszFilename == NULL)
    2225           0 :         return NULL;
    2226        4335 :     for (i = (int)strlen(pszFilename) - 1; i >= 0; i--)
    2227             :     {
    2228        4285 :         if (pszFilename[i] == '.')
    2229             :         {
    2230         715 :             if (i < (int)strlen(pszFilename) - 3)
    2231             :             {
    2232         710 :                 seriesCode[0] = pszFilename[i + 1];
    2233         710 :                 seriesCode[1] = pszFilename[i + 2];
    2234      124770 :                 for (i = 0;
    2235      124060 :                      i < (int)(sizeof(nitfSeries) / sizeof(nitfSeries[0])); i++)
    2236             :                 {
    2237      124110 :                     if (EQUAL(seriesCode, nitfSeries[i].code))
    2238             :                     {
    2239          50 :                         return &nitfSeries[i];
    2240             :                     }
    2241             :                 }
    2242         660 :                 return NULL;
    2243             :             }
    2244             :         }
    2245             :     }
    2246          50 :     return NULL;
    2247             : }
    2248             : 
    2249             : /************************************************************************/
    2250             : /*                       NITFCollectAttachments()                       */
    2251             : /*                                                                      */
    2252             : /*      Collect attachment, display level and location info into the    */
    2253             : /*      segmentinfo structures.                                         */
    2254             : /************************************************************************/
    2255             : 
    2256         499 : int NITFCollectAttachments(NITFFile *psFile)
    2257             : 
    2258             : {
    2259             :     int iSegment;
    2260             : 
    2261             :     /* ==================================================================== */
    2262             :     /*      Loop over all segments.                                         */
    2263             :     /* ==================================================================== */
    2264       10038 :     for (iSegment = 0; iSegment < psFile->nSegmentCount; iSegment++)
    2265             :     {
    2266        9539 :         NITFSegmentInfo *psSegInfo = psFile->pasSegmentInfo + iSegment;
    2267             : 
    2268             :         /* --------------------------------------------------------------------
    2269             :          */
    2270             :         /*      For image segments, we use the normal image access stuff. */
    2271             :         /* --------------------------------------------------------------------
    2272             :          */
    2273        9539 :         if (EQUAL(psSegInfo->szSegmentType, "IM"))
    2274             :         {
    2275        9500 :             NITFImage *psImage = NITFImageAccess(psFile, iSegment);
    2276        9500 :             if (psImage == NULL)
    2277           0 :                 return FALSE;
    2278             : 
    2279        9500 :             psSegInfo->nDLVL = psImage->nIDLVL;
    2280        9500 :             psSegInfo->nALVL = psImage->nIALVL;
    2281        9500 :             psSegInfo->nLOC_R = psImage->nILOCRow;
    2282        9500 :             psSegInfo->nLOC_C = psImage->nILOCColumn;
    2283             :         }
    2284             :         /* --------------------------------------------------------------------
    2285             :          */
    2286             :         /*      For graphic file we need to process the header. */
    2287             :         /* --------------------------------------------------------------------
    2288             :          */
    2289          39 :         else if (EQUAL(psSegInfo->szSegmentType, "SY") ||
    2290          39 :                  EQUAL(psSegInfo->szSegmentType, "GR"))
    2291             :         {
    2292             :             char achSubheader[298];
    2293             :             int nSTYPEOffset;
    2294             :             char szTemp[100];
    2295             : 
    2296             :             /* --------------------------------------------------------------------
    2297             :              */
    2298             :             /*      Load the graphic subheader. */
    2299             :             /* --------------------------------------------------------------------
    2300             :              */
    2301           4 :             if (VSIFSeekL(psFile->fp, psSegInfo->nSegmentHeaderStart,
    2302           4 :                           SEEK_SET) != 0 ||
    2303           4 :                 VSIFReadL(achSubheader, 1, sizeof(achSubheader), psFile->fp) <
    2304             :                     258)
    2305             :             {
    2306           0 :                 CPLError(CE_Warning, CPLE_FileIO,
    2307             :                          "Failed to read graphic subheader at " CPL_FRMT_GUIB
    2308             :                          ".",
    2309             :                          psSegInfo->nSegmentHeaderStart);
    2310           0 :                 continue;
    2311             :             }
    2312             : 
    2313             :             // NITF 2.0. (also works for NITF 2.1)
    2314           4 :             nSTYPEOffset = 200;
    2315           4 :             if (STARTS_WITH_CI(achSubheader + 193, "999998"))
    2316           0 :                 nSTYPEOffset += 40;
    2317             : 
    2318             :             /* --------------------------------------------------------------------
    2319             :              */
    2320             :             /*      Report some standard info. */
    2321             :             /* --------------------------------------------------------------------
    2322             :              */
    2323           4 :             psSegInfo->nDLVL =
    2324           4 :                 atoi(NITFGetField(szTemp, achSubheader, nSTYPEOffset + 14, 3));
    2325           4 :             psSegInfo->nALVL =
    2326           4 :                 atoi(NITFGetField(szTemp, achSubheader, nSTYPEOffset + 17, 3));
    2327           4 :             psSegInfo->nLOC_R =
    2328           4 :                 atoi(NITFGetField(szTemp, achSubheader, nSTYPEOffset + 20, 5));
    2329           4 :             psSegInfo->nLOC_C =
    2330           4 :                 atoi(NITFGetField(szTemp, achSubheader, nSTYPEOffset + 25, 5));
    2331             :         }
    2332             :     }
    2333             : 
    2334         499 :     return TRUE;
    2335             : }
    2336             : 
    2337             : /************************************************************************/
    2338             : /*                      NITFReconcileAttachments()                      */
    2339             : /*                                                                      */
    2340             : /*      Generate the CCS location information for all the segments      */
    2341             : /*      if possible.                                                    */
    2342             : /************************************************************************/
    2343             : 
    2344         499 : int NITFReconcileAttachments(NITFFile *psFile)
    2345             : 
    2346             : {
    2347             :     int iSegment;
    2348         499 :     int bSuccess = TRUE;
    2349         499 :     int bMadeProgress = FALSE;
    2350             : 
    2351       10038 :     for (iSegment = 0; iSegment < psFile->nSegmentCount; iSegment++)
    2352             :     {
    2353        9539 :         NITFSegmentInfo *psSegInfo = psFile->pasSegmentInfo + iSegment;
    2354             :         int iOther;
    2355             : 
    2356             :         // already processed?
    2357        9539 :         if (psSegInfo->nCCS_R != -1)
    2358           0 :             continue;
    2359             : 
    2360             :         // unattached segments are straight forward.
    2361        9539 :         if (psSegInfo->nALVL < 1)
    2362             :         {
    2363        9536 :             psSegInfo->nCCS_R = psSegInfo->nLOC_R;
    2364        9536 :             psSegInfo->nCCS_C = psSegInfo->nLOC_C;
    2365        9536 :             if (psSegInfo->nCCS_R != -1)
    2366        9500 :                 bMadeProgress = TRUE;
    2367        9536 :             continue;
    2368             :         }
    2369             : 
    2370             :         // Loc for segment to which we are attached.
    2371           3 :         for (iOther = 0; iOther < psFile->nSegmentCount; iOther++)
    2372             :         {
    2373           3 :             NITFSegmentInfo *psOtherSegInfo = psFile->pasSegmentInfo + iOther;
    2374             : 
    2375           3 :             if (psSegInfo->nALVL == psOtherSegInfo->nDLVL)
    2376             :             {
    2377           3 :                 if (psOtherSegInfo->nCCS_R != -1)
    2378             :                 {
    2379           3 :                     psSegInfo->nCCS_R =
    2380           3 :                         psOtherSegInfo->nLOC_R + psSegInfo->nLOC_R;
    2381           3 :                     psSegInfo->nCCS_C =
    2382           3 :                         psOtherSegInfo->nLOC_C + psSegInfo->nLOC_C;
    2383           3 :                     if (psSegInfo->nCCS_R != -1)
    2384           3 :                         bMadeProgress = TRUE;
    2385             :                 }
    2386             :                 else
    2387             :                 {
    2388           0 :                     bSuccess = FALSE;
    2389             :                 }
    2390           3 :                 break;
    2391             :             }
    2392             :         }
    2393             : 
    2394           3 :         if (iOther == psFile->nSegmentCount)
    2395           0 :             bSuccess = FALSE;
    2396             :     }
    2397             : 
    2398             :     /* -------------------------------------------------------------------- */
    2399             :     /*      If succeeded or made no progress then return our success        */
    2400             :     /*      flag.  Otherwise make another pass, hopefully filling in        */
    2401             :     /*      more values.                                                    */
    2402             :     /* -------------------------------------------------------------------- */
    2403         499 :     if (bSuccess || !bMadeProgress)
    2404         499 :         return bSuccess;
    2405             :     else
    2406           0 :         return NITFReconcileAttachments(psFile);
    2407             : }
    2408             : 
    2409             : /************************************************************************/
    2410             : /*                        NITFFindValFromEnd()                          */
    2411             : /************************************************************************/
    2412             : 
    2413        2078 : static const char *NITFFindValFromEnd(char **papszMD, int nMDSize,
    2414             :                                       const char *pszVar,
    2415             :                                       CPL_UNUSED const char *pszDefault)
    2416             : {
    2417        2078 :     int nVarLen = (int)strlen(pszVar);
    2418        2078 :     int nIter = nMDSize - 1;
    2419       84353 :     for (; nIter >= 0; nIter--)
    2420             :     {
    2421       83142 :         if (strncmp(papszMD[nIter], pszVar, nVarLen) == 0 &&
    2422         869 :             papszMD[nIter][nVarLen] == '=')
    2423         867 :             return papszMD[nIter] + nVarLen + 1;
    2424             :     }
    2425        1211 :     return NULL;
    2426             : }
    2427             : 
    2428             : /************************************************************************/
    2429             : /*                  NITFFindValRecursive()                              */
    2430             : /************************************************************************/
    2431             : 
    2432         854 : static const char *NITFFindValRecursive(char **papszMD, int nMDSize,
    2433             :                                         const char *pszMDPrefix,
    2434             :                                         const char *pszVar)
    2435             : {
    2436         854 :     char *pszMDItemName = CPLStrdup(CPLSPrintf("%s%s", pszMDPrefix, pszVar));
    2437             :     const char *pszCondVal =
    2438         854 :         NITFFindValFromEnd(papszMD, nMDSize, pszMDItemName, NULL);
    2439             : 
    2440         854 :     if (pszCondVal == NULL)
    2441             :     {
    2442             :         /* Needed for SENSRB */
    2443             :         /* See https://github.com/OSGeo/gdal/issues/1520 */
    2444             :         /* If the condition variable is not found at this level, */
    2445             :         /* try to research it at upper levels by shortening on _ */
    2446             :         /* separators */
    2447         384 :         char *pszMDPrefixShortened = CPLStrdup(pszMDPrefix);
    2448         384 :         char *pszLastUnderscore = strrchr(pszMDPrefixShortened, '_');
    2449         384 :         if (pszLastUnderscore)
    2450             :         {
    2451         384 :             *pszLastUnderscore = 0;
    2452         384 :             pszLastUnderscore = strrchr(pszMDPrefixShortened, '_');
    2453             :         }
    2454        1211 :         while (pszLastUnderscore)
    2455             :         {
    2456        1208 :             pszLastUnderscore[1] = 0;
    2457        1208 :             CPLFree(pszMDItemName);
    2458             :             pszMDItemName =
    2459        1208 :                 CPLStrdup(CPLSPrintf("%s%s", pszMDPrefixShortened, pszVar));
    2460             :             pszCondVal =
    2461        1208 :                 NITFFindValFromEnd(papszMD, nMDSize, pszMDItemName, NULL);
    2462        1208 :             if (pszCondVal)
    2463         381 :                 break;
    2464         827 :             *pszLastUnderscore = 0;
    2465         827 :             pszLastUnderscore = strrchr(pszMDPrefixShortened, '_');
    2466             :         }
    2467         384 :         CPLFree(pszMDPrefixShortened);
    2468             : 
    2469         384 :         if (!pszCondVal)
    2470           3 :             pszCondVal = NITFFindValFromEnd(papszMD, nMDSize, pszVar, NULL);
    2471             :     }
    2472         854 :     CPLFree(pszMDItemName);
    2473             : 
    2474         854 :     return pszCondVal;
    2475             : }
    2476             : 
    2477             : /************************************************************************/
    2478             : /*                              CSLSplit()                              */
    2479             : /************************************************************************/
    2480             : 
    2481           0 : static char **CSLSplit(const char *pszStr, const char *pszSplitter)
    2482             : {
    2483           0 :     char **papszRet = NULL;
    2484           0 :     const char *pszIter = pszStr;
    2485             :     while (TRUE)
    2486           0 :     {
    2487           0 :         const char *pszNextSplitter = strstr(pszIter, pszSplitter);
    2488           0 :         if (pszNextSplitter == NULL)
    2489             :         {
    2490           0 :             papszRet = CSLAddString(papszRet, pszIter);
    2491           0 :             break;
    2492             :         }
    2493           0 :         size_t nLen = (size_t)(pszNextSplitter - pszIter);
    2494           0 :         char *pszToken = (char *)CPLMalloc(nLen + 1);
    2495           0 :         memcpy(pszToken, pszIter, nLen);
    2496           0 :         pszToken[nLen] = 0;
    2497           0 :         papszRet = CSLAddString(papszRet, pszToken);
    2498           0 :         CPLFree(pszToken);
    2499           0 :         pszIter = pszNextSplitter + strlen(pszSplitter);
    2500             :     }
    2501           0 :     return papszRet;
    2502             : }
    2503             : 
    2504             : /************************************************************************/
    2505             : /*                          NITFEvaluateCond()                          */
    2506             : /************************************************************************/
    2507             : 
    2508         624 : static int NITFEvaluateCond(const char *pszCond, char **papszMD, int *pnMDSize,
    2509             :                             const char *pszMDPrefix,
    2510             :                             const char *pszDESOrTREKind,
    2511             :                             const char *pszDESOrTREName)
    2512             : {
    2513         624 :     const char *pszAnd = strstr(pszCond, " AND ");
    2514         624 :     const char *pszOr = strstr(pszCond, " OR ");
    2515         624 :     if (pszAnd && pszOr)
    2516             :     {
    2517           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    2518             :                  "Unsupported if condition in %s %s in XML resource: %s. "
    2519             :                  "AND and OR conditions cannot be used at the same time",
    2520             :                  pszDESOrTREName, pszDESOrTREKind, pszCond);
    2521           0 :         return -1;
    2522             :     }
    2523             : 
    2524         624 :     int nRet = 0;
    2525             :     const char *pszOperator;
    2526         624 :     if (pszAnd)
    2527             :     {
    2528           0 :         char **papszTokens = CSLSplit(pszCond, " AND ");
    2529           0 :         for (char **papszIter = papszTokens; *papszIter; ++papszIter)
    2530             :         {
    2531           0 :             nRet = NITFEvaluateCond(*papszIter, papszMD, pnMDSize, pszMDPrefix,
    2532             :                                     pszDESOrTREKind, pszDESOrTREName);
    2533             :             // exit early as soon as we have a negative evaluation (or error)
    2534           0 :             if (nRet != 1)
    2535           0 :                 break;
    2536             :         }
    2537           0 :         CSLDestroy(papszTokens);
    2538             :     }
    2539         624 :     else if (pszOr)
    2540             :     {
    2541           0 :         char **papszTokens = CSLSplit(pszCond, " OR ");
    2542           0 :         for (char **papszIter = papszTokens; *papszIter; ++papszIter)
    2543             :         {
    2544           0 :             nRet = NITFEvaluateCond(*papszIter, papszMD, pnMDSize, pszMDPrefix,
    2545             :                                     pszDESOrTREKind, pszDESOrTREName);
    2546             :             // exit early as soon as we have a positive evaluation (or error)
    2547           0 :             if (nRet != 0)
    2548           0 :                 break;
    2549             :         }
    2550           0 :         CSLDestroy(papszTokens);
    2551             :     }
    2552         624 :     else if ((pszOperator = strchr(pszCond, '=')) != NULL)
    2553             :     {
    2554         424 :         char *pszCondVar = (char *)CPLMalloc(pszOperator - pszCond + 1);
    2555         424 :         const char *pszCondExpectedVal = pszOperator + 1;
    2556             :         const char *pszCondVal;
    2557         424 :         int bTestEqual = FALSE;
    2558         424 :         int bTestNotEqual = FALSE;
    2559         424 :         int bTestGreaterOrEqual = FALSE;
    2560         424 :         memcpy(pszCondVar, pszCond, pszOperator - pszCond);
    2561         424 :         if (pszOperator - pszCond > 1 &&
    2562         424 :             pszCondVar[pszOperator - pszCond - 1] == '!')
    2563             :         {
    2564          81 :             bTestNotEqual = TRUE;
    2565          81 :             pszCondVar[pszOperator - pszCond - 1] = '\0';
    2566             :         }
    2567         343 :         else if (pszOperator - pszCond > 1 &&
    2568         343 :                  pszCondVar[pszOperator - pszCond - 1] == '>')
    2569             :         {
    2570           0 :             bTestGreaterOrEqual = TRUE;
    2571           0 :             pszCondVar[pszOperator - pszCond - 1] = '\0';
    2572             :         }
    2573             :         else
    2574             :         {
    2575         343 :             bTestEqual = TRUE;
    2576             :         }
    2577         424 :         pszCondVar[pszOperator - pszCond] = '\0';
    2578             :         pszCondVal =
    2579         424 :             NITFFindValRecursive(papszMD, *pnMDSize, pszMDPrefix, pszCondVar);
    2580         424 :         if (pszCondVal == NULL)
    2581             :         {
    2582           0 :             CPLDebug("NITF", "Cannot find if cond variable %s", pszCondVar);
    2583             :         }
    2584         424 :         else if ((bTestEqual && strcmp(pszCondVal, pszCondExpectedVal) == 0) ||
    2585          81 :                  (bTestNotEqual &&
    2586         335 :                   strcmp(pszCondVal, pszCondExpectedVal) != 0) ||
    2587           0 :                  (bTestGreaterOrEqual &&
    2588           0 :                   strcmp(pszCondVal, pszCondExpectedVal) >= 0))
    2589             :         {
    2590         131 :             nRet = 1;
    2591             :         }
    2592         424 :         CPLFree(pszCondVar);
    2593             :     }
    2594         200 :     else if ((pszOperator = strchr(pszCond, ':')) != NULL)
    2595             :     {
    2596         200 :         char *pszCondVar = (char *)CPLMalloc(pszOperator - pszCond + 1);
    2597         200 :         const char *pszCondTestBit = pszOperator + 1;
    2598             :         const char *pszCondVal;
    2599         200 :         memcpy(pszCondVar, pszCond, pszOperator - pszCond);
    2600         200 :         pszCondVar[pszOperator - pszCond] = '\0';
    2601             :         pszCondVal =
    2602         200 :             NITFFindValRecursive(papszMD, *pnMDSize, pszMDPrefix, pszCondVar);
    2603         200 :         if (pszCondVal == NULL)
    2604             :         {
    2605           0 :             CPLDebug("NITF", "Cannot find if cond variable %s", pszCondVar);
    2606             :         }
    2607         200 :         else if (strtoul(pszCondVal, CPL_NULLPTR, 10) &
    2608         200 :                  (1U << (unsigned)atoi(pszCondTestBit)))
    2609             :         {
    2610          40 :             nRet = 1;
    2611             :         }
    2612         200 :         CPLFree(pszCondVar);
    2613             :     }
    2614             :     else
    2615             :     {
    2616           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    2617             :                  "Invalid if construct in %s %s in XML resource: %s. "
    2618             :                  "invalid 'cond' attribute",
    2619             :                  pszDESOrTREName, pszDESOrTREKind, pszCond);
    2620           0 :         return -1;
    2621             :     }
    2622         624 :     return nRet;
    2623             : }
    2624             : 
    2625             : /************************************************************************/
    2626             : /*                  NITFGenericMetadataReadTREInternal()                */
    2627             : /************************************************************************/
    2628             : 
    2629        2170 : static char **NITFGenericMetadataReadTREInternal(
    2630             :     char **papszMD, int *pnMDSize, int *pnMDAlloc, CPLXMLNode *psOutXMLNode,
    2631             :     const char *pszDESOrTREKind, const char *pszDESOrTREName,
    2632             :     const char *pachTRE, int nTRESize, CPLXMLNode *psTreNode, int *pnTreOffset,
    2633             :     const char *pszMDPrefix, bool bValidate, int *pbError)
    2634             : {
    2635             :     CPLXMLNode *psIter;
    2636       13186 :     for (psIter = psTreNode->psChild; psIter != NULL && *pbError == FALSE;
    2637       11016 :          psIter = psIter->psNext)
    2638             :     {
    2639       11018 :         if (psIter->eType == CXT_Element && psIter->pszValue != NULL &&
    2640        4949 :             strcmp(psIter->pszValue, "field") == 0)
    2641        4009 :         {
    2642        4011 :             const char *pszName = CPLGetXMLValue(psIter, "name", NULL);
    2643        4011 :             const char *pszLongName = CPLGetXMLValue(psIter, "longname", NULL);
    2644        4011 :             const char *pszLength = CPLGetXMLValue(psIter, "length", NULL);
    2645        4011 :             const char *pszType = CPLGetXMLValue(psIter, "type", "string");
    2646        4011 :             const char *pszMinVal = CPLGetXMLValue(psIter, "minval", NULL);
    2647        4011 :             const char *pszMaxVal = CPLGetXMLValue(psIter, "maxval", NULL);
    2648        4011 :             int nLength = -1;
    2649        4011 :             if (pszLength != NULL)
    2650        3976 :                 nLength = atoi(pszLength);
    2651             :             else
    2652             :             {
    2653             :                 const char *pszLengthVar =
    2654          35 :                     CPLGetXMLValue(psIter, "length_var", NULL);
    2655          35 :                 if (pszLengthVar != NULL)
    2656             :                 {
    2657             :                     // Preferably look for item at the same level as ours.
    2658          35 :                     const char *pszLengthValue = CSLFetchNameValue(
    2659             :                         papszMD, CPLSPrintf("%s%s", pszMDPrefix, pszLengthVar));
    2660          35 :                     if (pszLengthValue != NULL)
    2661             :                     {
    2662          31 :                         nLength = atoi(pszLengthValue);
    2663             :                     }
    2664             :                     else
    2665             :                     {
    2666           4 :                         char **papszMDIter = papszMD;
    2667         276 :                         while (papszMDIter != NULL && *papszMDIter != NULL)
    2668             :                         {
    2669         272 :                             if (strstr(*papszMDIter, pszLengthVar) != NULL)
    2670             :                             {
    2671             :                                 const char *pszEqual =
    2672           4 :                                     strchr(*papszMDIter, '=');
    2673           4 :                                 if (pszEqual != NULL)
    2674             :                                 {
    2675           4 :                                     nLength = atoi(pszEqual + 1);
    2676             :                                     // Voluntary missing break so as to find the
    2677             :                                     // "closest" item to ours in case it is not
    2678             :                                     // defined in the same level
    2679             :                                 }
    2680             :                             }
    2681         272 :                             papszMDIter++;
    2682             :                         }
    2683             :                     }
    2684             :                 }
    2685             :             }
    2686        4011 :             if (pszName != NULL && nLength > 0)
    2687        3945 :             {
    2688             :                 char *pszMDItemName;
    2689        3947 :                 char **papszTmp = NULL;
    2690        3947 :                 char *pszValue = NULL;
    2691             : 
    2692        3947 :                 if (*pnTreOffset + nLength > nTRESize)
    2693             :                 {
    2694           2 :                     *pbError = TRUE;
    2695           2 :                     CPLError(bValidate ? CE_Failure : CE_Warning,
    2696             :                              CPLE_AppDefined,
    2697             :                              "Not enough bytes when reading %s %s "
    2698             :                              "(at least %d needed, only %d available)",
    2699             :                              pszDESOrTREName, pszDESOrTREKind,
    2700           2 :                              *pnTreOffset + nLength, nTRESize);
    2701           2 :                     break;
    2702             :                 }
    2703             : 
    2704             :                 pszMDItemName =
    2705        3945 :                     CPLStrdup(CPLSPrintf("%s%s", pszMDPrefix, pszName));
    2706             : 
    2707        3945 :                 if (strcmp(pszType, "IEEE754_Float32_BigEndian") == 0)
    2708             :                 {
    2709          12 :                     if (nLength == 4)
    2710             :                     {
    2711          12 :                         const size_t nBufferSize = 128;
    2712             :                         float f;
    2713          12 :                         memcpy(&f, pachTRE + *pnTreOffset, 4);
    2714          12 :                         CPL_MSBPTR32(&f);
    2715          12 :                         pszValue = (char *)CPLMalloc(nBufferSize);
    2716          12 :                         CPLsnprintf(pszValue, nBufferSize, "%f", f);
    2717          12 :                         papszTmp =
    2718          12 :                             CSLSetNameValue(papszTmp, pszMDItemName, pszValue);
    2719             :                     }
    2720             :                     else
    2721             :                     {
    2722           0 :                         *pbError = TRUE;
    2723           0 :                         CPLError(bValidate ? CE_Failure : CE_Warning,
    2724             :                                  CPLE_AppDefined,
    2725             :                                  "IEEE754_Float32_BigEndian field must be 4 "
    2726             :                                  "bytes in %s %s",
    2727             :                                  pszDESOrTREName, pszDESOrTREKind);
    2728           0 :                         break;
    2729             :                     }
    2730             :                 }
    2731        3933 :                 else if (strcmp(pszType, "UnsignedInt_BigEndian") == 0 ||
    2732        3913 :                          strcmp(pszType, "bitmask") == 0)
    2733             :                 {
    2734          28 :                     if (nLength <= 8)
    2735             :                     {
    2736          28 :                         const size_t nBufferSize = 21;
    2737          28 :                         unsigned long long nVal = 0;
    2738             :                         GByte byData;
    2739             : 
    2740             :                         int i;
    2741         144 :                         for (i = 0; i < nLength; ++i)
    2742             :                         {
    2743         116 :                             memcpy(&byData, pachTRE + *pnTreOffset + i, 1);
    2744         116 :                             nVal += (unsigned long long)byData
    2745         116 :                                     << 8 * (nLength - i - 1);
    2746             :                         }
    2747             : 
    2748          28 :                         pszValue = (char *)CPLMalloc(nBufferSize);
    2749          28 :                         CPLsnprintf(pszValue, nBufferSize, CPL_FRMT_GUIB,
    2750             :                                     (GUIntBig)nVal);
    2751          28 :                         papszTmp =
    2752          28 :                             CSLSetNameValue(papszTmp, pszMDItemName, pszValue);
    2753             :                     }
    2754             :                     else
    2755             :                     {
    2756           0 :                         *pbError = TRUE;
    2757           0 :                         CPLError(bValidate ? CE_Failure : CE_Warning,
    2758             :                                  CPLE_AppDefined,
    2759             :                                  "UnsignedInt/bitmask field must be <= 8 bytes "
    2760             :                                  "in %s %s",
    2761             :                                  pszDESOrTREName, pszDESOrTREKind);
    2762           0 :                         break;
    2763             :                     }
    2764             :                 }
    2765        3905 :                 else if (strcmp(pszType, "ISO8859-1") == 0)
    2766             :                 {
    2767          45 :                     NITFExtractMetadata(&papszTmp, pachTRE, *pnTreOffset,
    2768             :                                         nLength, pszMDItemName);
    2769             : 
    2770             :                     pszValue =
    2771          45 :                         CPLStrdup(CSLFetchNameValue(papszTmp, pszMDItemName));
    2772             :                 }
    2773             :                 else
    2774             :                 {
    2775        3860 :                     NITFExtractAndRecodeMetadata(&papszTmp, pachTRE,
    2776             :                                                  *pnTreOffset, nLength,
    2777             :                                                  pszMDItemName, CPL_ENC_UTF8);
    2778             : 
    2779        3860 :                     pszValue = CPLStrdup(strchr(papszTmp[0], '=') + 1);
    2780             :                 }
    2781             : 
    2782        3945 :                 if (papszTmp)
    2783             :                 {
    2784        3945 :                     if (*pnMDSize + 1 >= *pnMDAlloc)
    2785             :                     {
    2786         169 :                         *pnMDAlloc = (*pnMDAlloc * 4 / 3) + 32;
    2787         169 :                         papszMD = (char **)CPLRealloc(
    2788         169 :                             papszMD, *pnMDAlloc * sizeof(char *));
    2789             :                     }
    2790        3945 :                     papszMD[*pnMDSize] = papszTmp[0];
    2791        3945 :                     papszMD[(*pnMDSize) + 1] = NULL;
    2792        3945 :                     (*pnMDSize)++;
    2793        3945 :                     papszTmp[0] = NULL;
    2794        3945 :                     CSLDestroy(papszTmp);
    2795             :                 }
    2796             : 
    2797        3945 :                 CPLXMLNode *psFieldNode = NULL;
    2798        3945 :                 if (pszValue != NULL && psOutXMLNode != NULL)
    2799             :                 {
    2800             :                     CPLXMLNode *psNameNode;
    2801             :                     CPLXMLNode *psValueNode;
    2802             : 
    2803             :                     psFieldNode =
    2804        2878 :                         CPLCreateXMLNode(psOutXMLNode, CXT_Element, "field");
    2805             :                     psNameNode =
    2806        2878 :                         CPLCreateXMLNode(psFieldNode, CXT_Attribute, "name");
    2807             :                     psValueNode =
    2808        2878 :                         CPLCreateXMLNode(psFieldNode, CXT_Attribute, "value");
    2809        2878 :                     CPLCreateXMLNode(psNameNode, CXT_Text,
    2810        2878 :                                      (pszName[0] || pszLongName == NULL)
    2811             :                                          ? pszName
    2812             :                                          : pszLongName);
    2813        2878 :                     CPLCreateXMLNode(psValueNode, CXT_Text, pszValue);
    2814             :                 }
    2815             : 
    2816        3945 :                 if (pszValue != NULL)
    2817             :                 {
    2818        3945 :                     if (pszMinVal != NULL)
    2819             :                     {
    2820         738 :                         bool bMinValConstraintOK = true;
    2821         738 :                         if (strcmp(pszType, "real") == 0)
    2822             :                         {
    2823         198 :                             bMinValConstraintOK =
    2824         198 :                                 CPLAtof(pszValue) >= CPLAtof(pszMinVal);
    2825             :                         }
    2826         540 :                         else if (strcmp(pszType, "integer") == 0)
    2827             :                         {
    2828        1064 :                             bMinValConstraintOK = CPLAtoGIntBig(pszValue) >=
    2829         532 :                                                   CPLAtoGIntBig(pszMinVal);
    2830             :                         }
    2831         738 :                         if (!bMinValConstraintOK)
    2832             :                         {
    2833          68 :                             if (bValidate)
    2834             :                             {
    2835           2 :                                 CPLError(CE_Failure, CPLE_AppDefined,
    2836             :                                          "%s %s: minimum value constraint of "
    2837             :                                          "%s for %s=%s not met",
    2838             :                                          pszDESOrTREKind, pszDESOrTREName,
    2839             :                                          pszMinVal, pszName, pszValue);
    2840             :                             }
    2841          68 :                             if (psFieldNode)
    2842             :                             {
    2843          68 :                                 CPLCreateXMLElementAndValue(
    2844             :                                     psFieldNode,
    2845             :                                     bValidate ? "error" : "warning",
    2846             :                                     CPLSPrintf("Minimum value constraint of %s "
    2847             :                                                "not met",
    2848             :                                                pszMinVal));
    2849             :                             }
    2850             :                         }
    2851             :                     }
    2852        3945 :                     if (pszMaxVal != NULL)
    2853             :                     {
    2854         699 :                         bool bMinValConstraintOK = true;
    2855         699 :                         if (strcmp(pszType, "real") == 0)
    2856             :                         {
    2857         174 :                             bMinValConstraintOK =
    2858         174 :                                 CPLAtof(pszValue) <= CPLAtof(pszMaxVal);
    2859             :                         }
    2860         525 :                         else if (strcmp(pszType, "integer") == 0)
    2861             :                         {
    2862        1034 :                             bMinValConstraintOK = CPLAtoGIntBig(pszValue) <=
    2863         517 :                                                   CPLAtoGIntBig(pszMaxVal);
    2864             :                         }
    2865         699 :                         if (!bMinValConstraintOK)
    2866             :                         {
    2867           2 :                             if (bValidate)
    2868             :                             {
    2869           2 :                                 CPLError(CE_Failure, CPLE_AppDefined,
    2870             :                                          "%s %s: maximum value constraint of "
    2871             :                                          "%s for %s=%s not met",
    2872             :                                          pszDESOrTREKind, pszDESOrTREName,
    2873             :                                          pszMaxVal, pszName, pszValue);
    2874             :                             }
    2875           2 :                             if (psFieldNode)
    2876             :                             {
    2877           2 :                                 CPLCreateXMLElementAndValue(
    2878             :                                     psFieldNode,
    2879             :                                     bValidate ? "error" : "warning",
    2880             :                                     CPLSPrintf("Maximum value constraint of %s "
    2881             :                                                "not met",
    2882             :                                                pszMaxVal));
    2883             :                             }
    2884             :                         }
    2885             :                     }
    2886             :                 }
    2887             : 
    2888        3945 :                 CPLFree(pszMDItemName);
    2889        3945 :                 CPLFree(pszValue);
    2890             : 
    2891        3945 :                 *pnTreOffset += nLength;
    2892             :             }
    2893          64 :             else if (nLength > 0)
    2894             :             {
    2895          64 :                 *pnTreOffset += nLength;
    2896             :             }
    2897             :             else
    2898             :             {
    2899           0 :                 *pbError = TRUE;
    2900           0 :                 CPLError(bValidate ? CE_Failure : CE_Warning, CPLE_AppDefined,
    2901             :                          "Invalid item construct in %s %s in XML resource",
    2902             :                          pszDESOrTREName, pszDESOrTREKind);
    2903           0 :                 break;
    2904             :             }
    2905             :         }
    2906        7007 :         else if (psIter->eType == CXT_Element && psIter->pszValue != NULL &&
    2907         938 :                  strcmp(psIter->pszValue, "loop") == 0)
    2908         310 :         {
    2909         310 :             const char *pszCounter = CPLGetXMLValue(psIter, "counter", NULL);
    2910             :             const char *pszIterations =
    2911         310 :                 CPLGetXMLValue(psIter, "iterations", NULL);
    2912         310 :             const char *pszFormula = CPLGetXMLValue(psIter, "formula", NULL);
    2913             :             const char *pszMDSubPrefix =
    2914         310 :                 CPLGetXMLValue(psIter, "md_prefix", NULL);
    2915         310 :             int nIterations = -1;
    2916             : 
    2917         310 :             if (pszCounter != NULL)
    2918             :             {
    2919         230 :                 const char *pszIterationsVal = NITFFindValRecursive(
    2920             :                     papszMD, *pnMDSize, pszMDPrefix, pszCounter);
    2921         230 :                 if (pszIterationsVal == NULL ||
    2922         230 :                     (nIterations = atoi(pszIterationsVal)) < 0)
    2923             :                 {
    2924           0 :                     CPLError(
    2925             :                         bValidate ? CE_Failure : CE_Warning, CPLE_AppDefined,
    2926             :                         "Invalid loop construct in %s %s in XML resource : "
    2927             :                         "invalid 'counter' %s",
    2928             :                         pszDESOrTREName, pszDESOrTREKind, pszCounter);
    2929           0 :                     *pbError = TRUE;
    2930           0 :                     break;
    2931             :                 }
    2932             :             }
    2933          80 :             else if (pszIterations != NULL)
    2934             :             {
    2935          68 :                 nIterations = atoi(pszIterations);
    2936             :             }
    2937          12 :             else if (pszFormula != NULL &&
    2938          12 :                      strcmp(pszFormula, "NPAR*NPARO") == 0)
    2939           1 :             {
    2940             :                 char *pszMDNPARName =
    2941           1 :                     CPLStrdup(CPLSPrintf("%s%s", pszMDPrefix, "NPAR"));
    2942           1 :                 int NPAR = atoi(NITFFindValFromEnd(papszMD, *pnMDSize,
    2943             :                                                    pszMDNPARName, "-1"));
    2944             :                 char *pszMDNPAROName =
    2945           1 :                     CPLStrdup(CPLSPrintf("%s%s", pszMDPrefix, "NPARO"));
    2946           1 :                 int NPARO = atoi(NITFFindValFromEnd(papszMD, *pnMDSize,
    2947             :                                                     pszMDNPAROName, "-1"));
    2948           1 :                 CPLFree(pszMDNPARName);
    2949           1 :                 CPLFree(pszMDNPAROName);
    2950           1 :                 if (NPAR < 0)
    2951             :                 {
    2952           0 :                     CPLError(
    2953             :                         bValidate ? CE_Failure : CE_Warning, CPLE_AppDefined,
    2954             :                         "Invalid loop construct in %s %s in XML resource : "
    2955             :                         "invalid 'counter' %s",
    2956             :                         pszDESOrTREName, pszDESOrTREKind, "NPAR");
    2957           0 :                     *pbError = TRUE;
    2958           0 :                     break;
    2959             :                 }
    2960           1 :                 if (NPARO < 0)
    2961             :                 {
    2962           0 :                     CPLError(
    2963             :                         bValidate ? CE_Failure : CE_Warning, CPLE_AppDefined,
    2964             :                         "Invalid loop construct in %s %s in XML resource : "
    2965             :                         "invalid 'counter' %s",
    2966             :                         pszDESOrTREName, pszDESOrTREKind, "NPAR0");
    2967           0 :                     *pbError = TRUE;
    2968           0 :                     break;
    2969             :                 }
    2970           1 :                 nIterations = NPAR * NPARO;
    2971             :             }
    2972          11 :             else if (pszFormula != NULL && strcmp(pszFormula, "NPLN-1") == 0)
    2973           0 :             {
    2974             :                 char *pszMDItemName =
    2975           0 :                     CPLStrdup(CPLSPrintf("%s%s", pszMDPrefix, "NPLN"));
    2976           0 :                 int NPLN = atoi(NITFFindValFromEnd(papszMD, *pnMDSize,
    2977             :                                                    pszMDItemName, "-1"));
    2978           0 :                 CPLFree(pszMDItemName);
    2979           0 :                 if (NPLN < 0)
    2980             :                 {
    2981           0 :                     CPLError(
    2982             :                         bValidate ? CE_Failure : CE_Warning, CPLE_AppDefined,
    2983             :                         "Invalid loop construct in %s %s in XML resource : "
    2984             :                         "invalid 'counter' %s",
    2985             :                         pszDESOrTREName, pszDESOrTREKind, "NPLN");
    2986           0 :                     *pbError = TRUE;
    2987           0 :                     break;
    2988             :                 }
    2989           0 :                 nIterations = NPLN - 1;
    2990             :             }
    2991          11 :             else if (pszFormula != NULL &&
    2992          11 :                      strcmp(pszFormula, "NXPTS*NYPTS") == 0)
    2993           0 :             {
    2994             :                 char *pszMDNPARName =
    2995           0 :                     CPLStrdup(CPLSPrintf("%s%s", pszMDPrefix, "NXPTS"));
    2996           0 :                 int NXPTS = atoi(NITFFindValFromEnd(papszMD, *pnMDSize,
    2997             :                                                     pszMDNPARName, "-1"));
    2998             :                 char *pszMDNPAROName =
    2999           0 :                     CPLStrdup(CPLSPrintf("%s%s", pszMDPrefix, "NYPTS"));
    3000           0 :                 int NYPTS = atoi(NITFFindValFromEnd(papszMD, *pnMDSize,
    3001             :                                                     pszMDNPAROName, "-1"));
    3002           0 :                 CPLFree(pszMDNPARName);
    3003           0 :                 CPLFree(pszMDNPAROName);
    3004           0 :                 if (NXPTS < 0)
    3005             :                 {
    3006           0 :                     CPLError(
    3007             :                         bValidate ? CE_Failure : CE_Warning, CPLE_AppDefined,
    3008             :                         "Invalid loop construct in %s %s in XML resource : "
    3009             :                         "invalid 'counter' %s",
    3010             :                         pszDESOrTREName, pszDESOrTREKind, "NXPTS");
    3011           0 :                     *pbError = TRUE;
    3012           0 :                     break;
    3013             :                 }
    3014           0 :                 if (NYPTS < 0)
    3015             :                 {
    3016           0 :                     CPLError(
    3017             :                         bValidate ? CE_Failure : CE_Warning, CPLE_AppDefined,
    3018             :                         "Invalid loop construct in %s %s in XML resource : "
    3019             :                         "invalid 'counter' %s",
    3020             :                         pszDESOrTREName, pszDESOrTREKind, "NYPTS");
    3021           0 :                     *pbError = TRUE;
    3022           0 :                     break;
    3023             :                 }
    3024           0 :                 nIterations = NXPTS * NYPTS;
    3025             :             }
    3026          11 :             else if (pszFormula)
    3027             :             {
    3028          11 :                 const char *const apszVarAndFormulaNp1NDiv2[] = {
    3029             :                     "NPAR",         "(NPART+1)*(NPART)/2",
    3030             :                     "NUMOPG",       "(NUMOPG+1)*(NUMOPG)/2",
    3031             :                     "NUM_ADJ_PARM", "(NUM_ADJ_PARM+1)*(NUM_ADJ_PARM)/2",
    3032             :                     "N1_CAL",       "(N1_CAL+1)*(N1_CAL)/2",
    3033             :                     "NUM_PARA",     "(NUM_PARA+1)*(NUM_PARA)/2",
    3034             :                     NULL,           NULL};
    3035             : 
    3036          26 :                 for (int i = 0; apszVarAndFormulaNp1NDiv2[i]; i += 2)
    3037             :                 {
    3038          26 :                     if (strcmp(pszFormula, apszVarAndFormulaNp1NDiv2[i + 1]) ==
    3039             :                         0)
    3040             :                     {
    3041          11 :                         const char *pszVar = apszVarAndFormulaNp1NDiv2[i];
    3042             :                         char *pszMDItemName =
    3043          11 :                             CPLStrdup(CPLSPrintf("%s%s", pszMDPrefix, pszVar));
    3044          11 :                         int var = atoi(NITFFindValFromEnd(papszMD, *pnMDSize,
    3045             :                                                           pszMDItemName, "-1"));
    3046          11 :                         CPLFree(pszMDItemName);
    3047          11 :                         if (var < 0)
    3048             :                         {
    3049           0 :                             CPLError(bValidate ? CE_Failure : CE_Warning,
    3050             :                                      CPLE_AppDefined,
    3051             :                                      "Invalid loop construct in %s %s in XML "
    3052             :                                      "resource : "
    3053             :                                      "invalid 'counter' %s",
    3054             :                                      pszDESOrTREName, pszDESOrTREKind, pszVar);
    3055           0 :                             *pbError = TRUE;
    3056           0 :                             return papszMD;
    3057             :                         }
    3058          11 :                         nIterations = var * (var + 1) / 2;
    3059          11 :                         break;
    3060             :                     }
    3061             :                 }
    3062             : 
    3063          11 :                 if (nIterations < 0)
    3064             :                 {
    3065           0 :                     CPLError(
    3066             :                         bValidate ? CE_Failure : CE_Warning, CPLE_AppDefined,
    3067             :                         "Invalid loop construct in %s %s in XML resource : "
    3068             :                         "missing or invalid 'counter' or 'iterations' or "
    3069             :                         "'formula'",
    3070             :                         pszDESOrTREName, pszDESOrTREKind);
    3071           0 :                     *pbError = TRUE;
    3072           0 :                     break;
    3073             :                 }
    3074             :             }
    3075             : 
    3076         310 :             if (nIterations > 0)
    3077             :             {
    3078             :                 int iIter;
    3079             :                 const char *pszPercent;
    3080         290 :                 int bHasValidPercentD = FALSE;
    3081         290 :                 CPLXMLNode *psRepeatedNode = NULL;
    3082         290 :                 CPLXMLNode *psLastChild = NULL;
    3083             : 
    3084             :                 /* Check that md_prefix has one and only %XXXXd pattern */
    3085         290 :                 if (pszMDSubPrefix != NULL &&
    3086         265 :                     (pszPercent = strchr(pszMDSubPrefix, '%')) != NULL &&
    3087         265 :                     strchr(pszPercent + 1, '%') == NULL)
    3088             :                 {
    3089         265 :                     const char *pszIter = pszPercent + 1;
    3090         777 :                     while (*pszIter != '\0')
    3091             :                     {
    3092         777 :                         if (*pszIter >= '0' && *pszIter <= '9')
    3093         512 :                             pszIter++;
    3094         265 :                         else if (*pszIter == 'd')
    3095             :                         {
    3096         265 :                             bHasValidPercentD = atoi(pszPercent + 1) <= 10;
    3097         265 :                             break;
    3098             :                         }
    3099             :                         else
    3100           0 :                             break;
    3101             :                     }
    3102             :                 }
    3103             : 
    3104         290 :                 if (psOutXMLNode != NULL)
    3105             :                 {
    3106             :                     CPLXMLNode *psNumberNode;
    3107             :                     CPLXMLNode *psNameNode;
    3108         164 :                     const char *pszName = CPLGetXMLValue(psIter, "name", NULL);
    3109             :                     psRepeatedNode =
    3110         164 :                         CPLCreateXMLNode(psOutXMLNode, CXT_Element, "repeated");
    3111         164 :                     if (pszName)
    3112             :                     {
    3113         117 :                         psNameNode = CPLCreateXMLNode(psRepeatedNode,
    3114             :                                                       CXT_Attribute, "name");
    3115         117 :                         CPLCreateXMLNode(psNameNode, CXT_Text, pszName);
    3116             :                     }
    3117         164 :                     psNumberNode = CPLCreateXMLNode(psRepeatedNode,
    3118             :                                                     CXT_Attribute, "number");
    3119         164 :                     CPLCreateXMLNode(psNumberNode, CXT_Text,
    3120             :                                      CPLSPrintf("%d", nIterations));
    3121             : 
    3122         164 :                     psLastChild = psRepeatedNode->psChild;
    3123         281 :                     while (psLastChild->psNext != NULL)
    3124         117 :                         psLastChild = psLastChild->psNext;
    3125             :                 }
    3126             : 
    3127        2173 :                 for (iIter = 0; iIter < nIterations && *pbError == FALSE;
    3128        1883 :                      iIter++)
    3129             :                 {
    3130        1883 :                     char *pszMDNewPrefix = NULL;
    3131        1883 :                     CPLXMLNode *psGroupNode = NULL;
    3132        1883 :                     if (pszMDSubPrefix != NULL)
    3133             :                     {
    3134        1829 :                         if (bHasValidPercentD)
    3135             :                         {
    3136        1829 :                             const size_t nTmpLen =
    3137        1829 :                                 strlen(pszMDSubPrefix) + 10 + 1;
    3138        1829 :                             char *szTmp = (char *)CPLMalloc(nTmpLen);
    3139        1829 :                             snprintf(szTmp, nTmpLen, pszMDSubPrefix, iIter + 1);
    3140        1829 :                             pszMDNewPrefix = CPLStrdup(
    3141             :                                 CPLSPrintf("%s%s", pszMDPrefix, szTmp));
    3142        1829 :                             CPLFree(szTmp);
    3143             :                         }
    3144             :                         else
    3145           0 :                             pszMDNewPrefix = CPLStrdup(
    3146             :                                 CPLSPrintf("%s%s%04d_", pszMDPrefix,
    3147             :                                            pszMDSubPrefix, iIter + 1));
    3148             :                     }
    3149             :                     else
    3150          54 :                         pszMDNewPrefix = CPLStrdup(
    3151             :                             CPLSPrintf("%s%04d_", pszMDPrefix, iIter + 1));
    3152             : 
    3153        1883 :                     if (psRepeatedNode != NULL)
    3154             :                     {
    3155             :                         CPLXMLNode *psIndexNode;
    3156             :                         psGroupNode =
    3157        1709 :                             CPLCreateXMLNode(NULL, CXT_Element, "group");
    3158        1709 :                         CPLAssert(psLastChild->psNext == NULL);
    3159        1709 :                         psLastChild->psNext = psGroupNode;
    3160        1709 :                         psLastChild = psGroupNode;
    3161        1709 :                         psIndexNode = CPLCreateXMLNode(psGroupNode,
    3162             :                                                        CXT_Attribute, "index");
    3163        1709 :                         CPLCreateXMLNode(psIndexNode, CXT_Text,
    3164             :                                          CPLSPrintf("%d", iIter));
    3165             :                     }
    3166             : 
    3167        1883 :                     papszMD = NITFGenericMetadataReadTREInternal(
    3168             :                         papszMD, pnMDSize, pnMDAlloc, psGroupNode,
    3169             :                         pszDESOrTREKind, pszDESOrTREName, pachTRE, nTRESize,
    3170             :                         psIter, pnTreOffset, pszMDNewPrefix, bValidate,
    3171             :                         pbError);
    3172        1883 :                     CPLFree(pszMDNewPrefix);
    3173             :                 }
    3174             :             }
    3175             :         }
    3176        6697 :         else if (psIter->eType == CXT_Element && psIter->pszValue != NULL &&
    3177         628 :                  strcmp(psIter->pszValue, "if") == 0)
    3178         624 :         {
    3179         624 :             const char *pszCond = CPLGetXMLValue(psIter, "cond", NULL);
    3180         624 :             if (pszCond == NULL)
    3181             :             {
    3182           0 :                 CPLError(bValidate ? CE_Failure : CE_Warning, CPLE_AppDefined,
    3183             :                          "Invalid if construct in %s %s in XML resource : "
    3184             :                          "missing 'cond' attribute",
    3185             :                          pszDESOrTREName, pszDESOrTREKind);
    3186           0 :                 *pbError = TRUE;
    3187           0 :                 break;
    3188             :             }
    3189             : 
    3190         624 :             int nRet = NITFEvaluateCond(pszCond, papszMD, pnMDSize, pszMDPrefix,
    3191             :                                         pszDESOrTREKind, pszDESOrTREName);
    3192         624 :             if (nRet < 0)
    3193             :             {
    3194           0 :                 *pbError = TRUE;
    3195           0 :                 break;
    3196             :             }
    3197         624 :             if (nRet > 0)
    3198             :             {
    3199         171 :                 papszMD = NITFGenericMetadataReadTREInternal(
    3200             :                     papszMD, pnMDSize, pnMDAlloc, psOutXMLNode, pszDESOrTREKind,
    3201             :                     pszDESOrTREName, pachTRE, nTRESize, psIter, pnTreOffset,
    3202             :                     pszMDPrefix, bValidate, pbError);
    3203             :             }
    3204             :         }
    3205        6073 :         else if (psIter->eType == CXT_Element && psIter->pszValue != NULL &&
    3206           4 :                  strcmp(psIter->pszValue, "if_remaining_bytes") == 0)
    3207             :         {
    3208           4 :             if (*pnTreOffset < nTRESize)
    3209             :             {
    3210           0 :                 papszMD = NITFGenericMetadataReadTREInternal(
    3211             :                     papszMD, pnMDSize, pnMDAlloc, psOutXMLNode, pszDESOrTREKind,
    3212             :                     pszDESOrTREName, pachTRE, nTRESize, psIter, pnTreOffset,
    3213             :                     pszMDPrefix, bValidate, pbError);
    3214             :             }
    3215             :         }
    3216             :         else
    3217             :         {
    3218             :             // CPLDebug("NITF", "Unknown element : %s", psIter->pszValue ?
    3219             :             // psIter->pszValue : "null");
    3220             :         }
    3221             :     }
    3222        2170 :     return papszMD;
    3223             : }
    3224             : 
    3225             : /************************************************************************/
    3226             : /*                      NITFGenericMetadataReadTRE()                    */
    3227             : /************************************************************************/
    3228             : 
    3229          55 : static char **NITFGenericMetadataReadTRE(char **papszMD, const char *pszTREName,
    3230             :                                          const char *pachTRE, int nTRESize,
    3231             :                                          CPLXMLNode *psTreNode)
    3232             : {
    3233          55 :     int bError = FALSE;
    3234          55 :     int nTreOffset = 0;
    3235             :     const char *pszMDPrefix;
    3236             :     int nMDSize, nMDAlloc;
    3237             : 
    3238          55 :     int nTreLength = atoi(CPLGetXMLValue(psTreNode, "length", "-1"));
    3239          55 :     int nTreMinLength = atoi(CPLGetXMLValue(psTreNode, "minlength", "-1"));
    3240             :     /* int nTreMaxLength = atoi(CPLGetXMLValue(psTreNode, "maxlength", "-1"));
    3241             :      */
    3242             : 
    3243          55 :     if (nTreLength > 0 && nTRESize != nTreLength)
    3244             :     {
    3245           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    3246             :                  "%s TRE wrong size (%d). Expected %d.", pszTREName, nTRESize,
    3247             :                  nTreLength);
    3248             :     }
    3249             : 
    3250          55 :     if (nTreMinLength > 0 && nTRESize < nTreMinLength)
    3251             :     {
    3252           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    3253             :                  "%s TRE wrong size (%d). Expected >= %d.", pszTREName,
    3254             :                  nTRESize, nTreMinLength);
    3255             :     }
    3256             : 
    3257          55 :     pszMDPrefix = CPLGetXMLValue(psTreNode, "md_prefix", "");
    3258             : 
    3259          55 :     nMDSize = nMDAlloc = CSLCount(papszMD);
    3260             : 
    3261          55 :     papszMD = NITFGenericMetadataReadTREInternal(
    3262             :         papszMD, &nMDSize, &nMDAlloc, NULL, "TRE", pszTREName, pachTRE,
    3263             :         nTRESize, psTreNode, &nTreOffset, pszMDPrefix,
    3264             :         false,  // bValidate
    3265             :         &bError);
    3266             : 
    3267          55 :     if (bError == FALSE && nTreLength > 0 && nTreOffset != nTreLength)
    3268             :     {
    3269           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    3270             :                  "Inconsistent declaration of %s TRE", pszTREName);
    3271             :     }
    3272          55 :     if (nTreOffset < nTRESize)
    3273           0 :         CPLDebug("NITF", "%d remaining bytes at end of %s TRE",
    3274             :                  nTRESize - nTreOffset, pszTREName);
    3275             : 
    3276          55 :     return papszMD;
    3277             : }
    3278             : 
    3279             : /************************************************************************/
    3280             : /*                           NITFLoadXMLSpec()                          */
    3281             : /************************************************************************/
    3282             : 
    3283             : #define NITF_SPEC_FILE "nitf_spec.xml"
    3284             : 
    3285         825 : static CPLXMLNode *NITFLoadXMLSpec(NITFFile *psFile)
    3286             : {
    3287             : 
    3288         825 :     if (psFile->psNITFSpecNode == NULL)
    3289             :     {
    3290             : #ifndef USE_ONLY_EMBEDDED_RESOURCE_FILES
    3291             : #ifdef EMBED_RESOURCE_FILES
    3292             :         CPLPushErrorHandler(CPLQuietErrorHandler);
    3293             : #endif
    3294         745 :         const char *pszXMLDescFilename = CPLFindFile("gdal", NITF_SPEC_FILE);
    3295             : #ifdef EMBED_RESOURCE_FILES
    3296             :         CPLPopErrorHandler();
    3297             :         CPLErrorReset();
    3298             : #endif
    3299         745 :         if (pszXMLDescFilename == NULL)
    3300             : #endif
    3301             :         {
    3302             : #ifdef EMBED_RESOURCE_FILES
    3303             :             CPLDebug("NITF", "Using embedded %s", NITF_SPEC_FILE);
    3304             :             psFile->psNITFSpecNode = CPLParseXMLString(NITFGetSpecFile());
    3305             :             CPLAssert(psFile->psNITFSpecNode);
    3306             :             return psFile->psNITFSpecNode;
    3307             : #else
    3308           0 :             CPLDebug("NITF", "Cannot find XML file : %s", NITF_SPEC_FILE);
    3309           0 :             return NULL;
    3310             : #endif
    3311             :         }
    3312             : #ifndef USE_ONLY_EMBEDDED_RESOURCE_FILES
    3313         745 :         psFile->psNITFSpecNode = CPLParseXMLFile(pszXMLDescFilename);
    3314         745 :         if (psFile->psNITFSpecNode == NULL)
    3315             :         {
    3316           0 :             CPLDebug("NITF", "Invalid XML file : %s", pszXMLDescFilename);
    3317           0 :             return NULL;
    3318             :         }
    3319             : #endif
    3320             :     }
    3321             : 
    3322         825 :     return psFile->psNITFSpecNode;
    3323             : }
    3324             : 
    3325             : /************************************************************************/
    3326             : /*                      NITFFindTREXMLDescFromName()                    */
    3327             : /************************************************************************/
    3328             : 
    3329          61 : static CPLXMLNode *NITFFindTREXMLDescFromName(NITFFile *psFile,
    3330             :                                               const char *pszTREName)
    3331             : {
    3332             :     CPLXMLNode *psTreeNode;
    3333             :     CPLXMLNode *psTresNode;
    3334             :     CPLXMLNode *psIter;
    3335             : 
    3336          61 :     psTreeNode = NITFLoadXMLSpec(psFile);
    3337          61 :     if (psTreeNode == NULL)
    3338           0 :         return NULL;
    3339             : 
    3340          61 :     psTresNode = CPLGetXMLNode(psTreeNode, "=root.tres");
    3341          61 :     if (psTresNode == NULL)
    3342             :     {
    3343           0 :         CPLDebug("NITF", "Cannot find <root><tres> root element");
    3344           0 :         return NULL;
    3345             :     }
    3346             : 
    3347        3057 :     for (psIter = psTresNode->psChild; psIter != NULL; psIter = psIter->psNext)
    3348             :     {
    3349        3052 :         if (psIter->eType == CXT_Element && psIter->pszValue != NULL &&
    3350        2152 :             strcmp(psIter->pszValue, "tre") == 0)
    3351             :         {
    3352        2152 :             const char *pszName = CPLGetXMLValue(psIter, "name", NULL);
    3353        2152 :             if (pszName != NULL && strcmp(pszName, pszTREName) == 0)
    3354             :             {
    3355          56 :                 return psIter;
    3356             :             }
    3357             :         }
    3358             :     }
    3359             : 
    3360           5 :     return NULL;
    3361             : }
    3362             : 
    3363             : /************************************************************************/
    3364             : /*                         NITFCreateXMLTre()                           */
    3365             : /************************************************************************/
    3366             : 
    3367          61 : CPLXMLNode *NITFCreateXMLTre(NITFFile *psFile, const char *pszTREName,
    3368             :                              const char *pachTRE, int nTRESize, bool bValidate,
    3369             :                              bool *pbGotError)
    3370             : {
    3371          61 :     int nTreLength, nTreMinLength = -1 /* , nTreMaxLength = -1 */;
    3372          61 :     int bError = FALSE;
    3373          61 :     int nTreOffset = 0;
    3374             :     CPLXMLNode *psTreNode;
    3375          61 :     CPLXMLNode *psOutXMLNode = NULL;
    3376          61 :     int nMDSize = 0, nMDAlloc = 0;
    3377             :     const char *pszMDPrefix;
    3378             : 
    3379          61 :     psTreNode = NITFFindTREXMLDescFromName(psFile, pszTREName);
    3380          61 :     if (psTreNode == NULL)
    3381             :     {
    3382           5 :         if (!(STARTS_WITH_CI(pszTREName, "RPF") ||
    3383           5 :               strcmp(pszTREName, "XXXXXX") == 0))
    3384             :         {
    3385           5 :             CPLDebug("NITF", "Cannot find definition of TRE %s in %s",
    3386             :                      pszTREName, NITF_SPEC_FILE);
    3387             :         }
    3388           5 :         return NULL;
    3389             :     }
    3390             : 
    3391          56 :     nTreLength = atoi(CPLGetXMLValue(psTreNode, "length", "-1"));
    3392          56 :     nTreMinLength = atoi(CPLGetXMLValue(psTreNode, "minlength", "-1"));
    3393             :     /* nTreMaxLength = atoi(CPLGetXMLValue(psTreNode, "maxlength", "-1")); */
    3394             : 
    3395          56 :     psOutXMLNode = CPLCreateXMLNode(NULL, CXT_Element, "tre");
    3396          56 :     CPLCreateXMLNode(CPLCreateXMLNode(psOutXMLNode, CXT_Attribute, "name"),
    3397             :                      CXT_Text, pszTREName);
    3398             : 
    3399          56 :     if (nTreLength > 0 && nTRESize != nTreLength)
    3400             :     {
    3401           2 :         CPLError(bValidate ? CE_Failure : CE_Warning, CPLE_AppDefined,
    3402             :                  "%s TRE wrong size (%d). Expected %d.", pszTREName, nTRESize,
    3403             :                  nTreLength);
    3404           2 :         CPLCreateXMLElementAndValue(
    3405             :             psOutXMLNode, bValidate ? "error" : "warning",
    3406             :             CPLSPrintf("%s TRE wrong size (%d). Expected %d.", pszTREName,
    3407             :                        nTRESize, nTreLength));
    3408           2 :         if (pbGotError)
    3409           2 :             *pbGotError = true;
    3410             :     }
    3411             : 
    3412          56 :     if (nTreMinLength > 0 && nTRESize < nTreMinLength)
    3413             :     {
    3414           0 :         CPLError(bValidate ? CE_Failure : CE_Warning, CPLE_AppDefined,
    3415             :                  "%s TRE wrong size (%d). Expected >= %d.", pszTREName,
    3416             :                  nTRESize, nTreMinLength);
    3417           0 :         CPLCreateXMLElementAndValue(
    3418             :             psOutXMLNode, bValidate ? "error" : "warning",
    3419             :             CPLSPrintf("%s TRE wrong size (%d). Expected >= %d.", pszTREName,
    3420             :                        nTRESize, nTreMinLength));
    3421           0 :         if (pbGotError)
    3422           0 :             *pbGotError = true;
    3423             :     }
    3424             : 
    3425          56 :     pszMDPrefix = CPLGetXMLValue(psTreNode, "md_prefix", "");
    3426          56 :     CSLDestroy(NITFGenericMetadataReadTREInternal(
    3427             :         NULL, &nMDSize, &nMDAlloc, psOutXMLNode, "TRE", pszTREName, pachTRE,
    3428             :         nTRESize, psTreNode, &nTreOffset, pszMDPrefix, bValidate, &bError));
    3429             : 
    3430          56 :     if (bError == FALSE && nTreLength > 0 && nTreOffset != nTreLength)
    3431             :     {
    3432           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    3433             :                  "Inconsistent declaration of %s TRE", pszTREName);
    3434             :     }
    3435          56 :     if (nTreOffset < nTRESize)
    3436             :     {
    3437           0 :         CPLCreateXMLElementAndValue(
    3438             :             psOutXMLNode, bValidate ? "error" : "warning",
    3439             :             CPLSPrintf("%d remaining bytes at end of %s TRE",
    3440             :                        nTRESize - nTreOffset, pszTREName));
    3441             :     }
    3442          56 :     if (pbGotError && bError)
    3443           0 :         *pbGotError = true;
    3444             : 
    3445          56 :     return psOutXMLNode;
    3446             : }
    3447             : 
    3448             : /************************************************************************/
    3449             : /*                      NITFFindTREXMLDescFromName()                    */
    3450             : /************************************************************************/
    3451             : 
    3452          19 : static CPLXMLNode *NITFFindDESXMLDescFromName(NITFFile *psFile,
    3453             :                                               const char *pszDESName)
    3454             : {
    3455             :     CPLXMLNode *psTreeNode;
    3456             :     CPLXMLNode *psTresNode;
    3457             :     CPLXMLNode *psIter;
    3458             : 
    3459          19 :     psTreeNode = NITFLoadXMLSpec(psFile);
    3460          19 :     if (psTreeNode == NULL)
    3461           0 :         return NULL;
    3462             : 
    3463          19 :     psTresNode = CPLGetXMLNode(psTreeNode, "=root.des_list");
    3464          19 :     if (psTresNode == NULL)
    3465             :     {
    3466           0 :         CPLDebug("NITF", "Cannot find <root><des_list> root element");
    3467           0 :         return NULL;
    3468             :     }
    3469             : 
    3470         263 :     for (psIter = psTresNode->psChild; psIter != NULL; psIter = psIter->psNext)
    3471             :     {
    3472         254 :         if (psIter->eType == CXT_Element && psIter->pszValue != NULL &&
    3473         112 :             strcmp(psIter->pszValue, "des") == 0)
    3474             :         {
    3475         112 :             const char *pszName = CPLGetXMLValue(psIter, "name", NULL);
    3476         112 :             if (pszName != NULL && strcmp(pszName, pszDESName) == 0)
    3477             :             {
    3478          10 :                 return psIter;
    3479             :             }
    3480             :         }
    3481             :     }
    3482             : 
    3483           9 :     return NULL;
    3484             : }
    3485             : 
    3486             : /************************************************************************/
    3487             : /*                 NITFCreateXMLDesUserDefinedSubHeader()               */
    3488             : /************************************************************************/
    3489             : 
    3490           8 : CPLXMLNode *NITFCreateXMLDesUserDefinedSubHeader(NITFFile *psFile,
    3491             :                                                  const NITFDES *psDES,
    3492             :                                                  bool bValidate,
    3493             :                                                  bool *pbGotError)
    3494             : {
    3495           8 :     const char *pszDESID = CSLFetchNameValue(psDES->papszMetadata, "DESID");
    3496           8 :     CPLXMLNode *psDESDef = NITFFindDESXMLDescFromName(psFile, pszDESID);
    3497           8 :     if (psDESDef == NULL)
    3498             :     {
    3499           4 :         CPLDebug("NITF", "Cannot find definition of DES %s in %s", pszDESID,
    3500             :                  NITF_SPEC_FILE);
    3501           4 :         return NULL;
    3502             :     }
    3503             :     CPLXMLNode *psUserDefinedFields =
    3504           4 :         CPLGetXMLNode(psDESDef, "subheader_fields");
    3505           4 :     if (psUserDefinedFields == NULL)
    3506             :     {
    3507           0 :         return NULL;
    3508             :     }
    3509             : 
    3510             :     CPLXMLNode *psOutXMLNode =
    3511           4 :         CPLCreateXMLNode(NULL, CXT_Element, "user_defined_fields");
    3512             : 
    3513           4 :     int bError = FALSE;
    3514           4 :     int nOffset = 200;
    3515           4 :     char **papszMD = CSLDuplicate(psDES->papszMetadata);
    3516           4 :     int nMDSize = CSLCount(papszMD);
    3517           4 :     int nMDAlloc = nMDSize;
    3518           4 :     const int nDESSize =
    3519           4 :         psFile->pasSegmentInfo[psDES->iSegment].nSegmentHeaderSize;
    3520           4 :     CSLDestroy(NITFGenericMetadataReadTREInternal(
    3521             :         papszMD, &nMDSize, &nMDAlloc, psOutXMLNode, "DES", pszDESID,
    3522           4 :         psDES->pachHeader, nDESSize, psUserDefinedFields, &nOffset,
    3523             :         "", /* pszMDPrefix, */
    3524             :         bValidate, &bError));
    3525             :     int nDESSHL =
    3526           4 :         atoi(CSLFetchNameValueDef(psDES->papszMetadata, "DESSHL", "0"));
    3527             : 
    3528             :     const int nLength =
    3529           4 :         atoi(CPLGetXMLValue(psUserDefinedFields, "length", "-1"));
    3530             :     const int nMinLength =
    3531           4 :         atoi(CPLGetXMLValue(psUserDefinedFields, "minlength", "-1"));
    3532             : 
    3533           4 :     if (nLength > 0 && nDESSHL != nLength)
    3534             :     {
    3535           2 :         CPLError(bValidate ? CE_Failure : CE_Warning, CPLE_AppDefined,
    3536             :                  "%s DES wrong header size (%d). Expected %d.", pszDESID,
    3537             :                  nDESSHL, nLength);
    3538           2 :         CPLCreateXMLElementAndValue(
    3539             :             psOutXMLNode, bValidate ? "error" : "warning",
    3540             :             CPLSPrintf("%s DES wrong size (%d). Expected %d.", pszDESID,
    3541             :                        nDESSHL, nLength));
    3542           2 :         if (pbGotError)
    3543           2 :             *pbGotError = true;
    3544             :     }
    3545             : 
    3546           4 :     if (nMinLength > 0 && nDESSHL < nMinLength)
    3547             :     {
    3548           0 :         CPLError(bValidate ? CE_Failure : CE_Warning, CPLE_AppDefined,
    3549             :                  "%s DES wrong size (%d). Expected >= %d.", pszDESID, nDESSHL,
    3550             :                  nMinLength);
    3551           0 :         CPLCreateXMLElementAndValue(
    3552             :             psOutXMLNode, bValidate ? "error" : "warning",
    3553             :             CPLSPrintf("%s DES wrong size (%d). Expected >= %d.", pszDESID,
    3554             :                        nDESSHL, nMinLength));
    3555           0 :         if (pbGotError)
    3556           0 :             *pbGotError = true;
    3557             :     }
    3558             : 
    3559           4 :     if (nOffset < nDESSHL)
    3560             :     {
    3561           0 :         bError = TRUE;
    3562           0 :         CPLCreateXMLElementAndValue(
    3563             :             psOutXMLNode, bValidate ? "error" : "warning",
    3564             :             CPLSPrintf(
    3565             :                 "%d remaining bytes at end of user defined subheader section",
    3566             :                 nDESSHL - nOffset));
    3567             :     }
    3568           4 :     if (pbGotError && bError)
    3569           2 :         *pbGotError = true;
    3570             : 
    3571           4 :     return psOutXMLNode;
    3572             : }
    3573             : 
    3574             : /************************************************************************/
    3575             : /*                   NITFCreateXMLDesDataFields()                       */
    3576             : /************************************************************************/
    3577             : 
    3578          11 : CPLXMLNode *NITFCreateXMLDesDataFields(NITFFile *psFile, const NITFDES *psDES,
    3579             :                                        const GByte *pabyData, int nDataLen,
    3580             :                                        bool bValidate, bool *pbGotError)
    3581             : {
    3582          11 :     const char *pszDESID = CSLFetchNameValue(psDES->papszMetadata, "DESID");
    3583          11 :     CPLXMLNode *psDESDef = NITFFindDESXMLDescFromName(psFile, pszDESID);
    3584          11 :     if (psDESDef == NULL)
    3585             :     {
    3586           5 :         CPLDebug("NITF", "Cannot find definition of DES %s in %s", pszDESID,
    3587             :                  NITF_SPEC_FILE);
    3588           5 :         return NULL;
    3589             :     }
    3590           6 :     CPLXMLNode *psFields = CPLGetXMLNode(psDESDef, "data_fields");
    3591           6 :     if (psFields == NULL)
    3592             :     {
    3593           5 :         return NULL;
    3594             :     }
    3595             : 
    3596             :     CPLXMLNode *psOutXMLNode =
    3597           1 :         CPLCreateXMLNode(NULL, CXT_Element, "data_fields");
    3598             : 
    3599           1 :     int bError = FALSE;
    3600           1 :     int nOffset = 0;
    3601           1 :     char **papszMD = CSLDuplicate(psDES->papszMetadata);
    3602           1 :     int nMDSize = CSLCount(papszMD);
    3603           1 :     int nMDAlloc = nMDSize;
    3604           1 :     CSLDestroy(NITFGenericMetadataReadTREInternal(
    3605             :         papszMD, &nMDSize, &nMDAlloc, psOutXMLNode, "DES", pszDESID,
    3606             :         (const char *)pabyData, nDataLen, psFields, &nOffset,
    3607             :         "", /* pszMDPrefix, */
    3608             :         bValidate, &bError));
    3609           1 :     if (nOffset < nDataLen)
    3610             :     {
    3611           0 :         bError = TRUE;
    3612           0 :         CPLCreateXMLElementAndValue(
    3613             :             psOutXMLNode, bValidate ? "error" : "warning",
    3614             :             CPLSPrintf("%d remaining bytes at end of data section",
    3615             :                        nDataLen - nOffset));
    3616             :     }
    3617           1 :     if (pbGotError && bError)
    3618           0 :         *pbGotError = true;
    3619             : 
    3620           1 :     return psOutXMLNode;
    3621             : }
    3622             : 
    3623             : /************************************************************************/
    3624             : /*                        NITFGenericMetadataRead()                     */
    3625             : /*                                                                      */
    3626             : /* Add metadata from TREs of file and image objects in the papszMD list */
    3627             : /* pszSpecificTRE can be NULL, in which case all TREs listed in         */
    3628             : /* data/nitf_resources.xml that have md_prefix defined will be looked   */
    3629             : /* for. If not NULL, only the specified one will be looked for.         */
    3630             : /************************************************************************/
    3631             : 
    3632         745 : char **NITFGenericMetadataRead(char **papszMD, NITFFile *psFile,
    3633             :                                NITFImage *psImage,
    3634             :                                const char *pszSpecificTREName)
    3635             : {
    3636         745 :     CPLXMLNode *psTreeNode = NULL;
    3637         745 :     CPLXMLNode *psTresNode = NULL;
    3638         745 :     CPLXMLNode *psIter = NULL;
    3639             : 
    3640         745 :     if (psFile == NULL)
    3641             :     {
    3642           0 :         if (psImage == NULL)
    3643           0 :             return papszMD;
    3644           0 :         psTreeNode = NITFLoadXMLSpec(psImage->psFile);
    3645             :     }
    3646             :     else
    3647         745 :         psTreeNode = NITFLoadXMLSpec(psFile);
    3648             : 
    3649         745 :     if (psTreeNode == NULL)
    3650           0 :         return papszMD;
    3651             : 
    3652         745 :     psTresNode = CPLGetXMLNode(psTreeNode, "=root.tres");
    3653         745 :     if (psTresNode == NULL)
    3654             :     {
    3655           0 :         CPLDebug("NITF", "Cannot find <root><tres> root element");
    3656           0 :         return papszMD;
    3657             :     }
    3658             : 
    3659       68540 :     for (psIter = psTresNode->psChild; psIter != NULL; psIter = psIter->psNext)
    3660             :     {
    3661       67795 :         if (psIter->eType == CXT_Element && psIter->pszValue != NULL &&
    3662       48425 :             strcmp(psIter->pszValue, "tre") == 0)
    3663             :         {
    3664       48425 :             const char *pszName = CPLGetXMLValue(psIter, "name", NULL);
    3665       48425 :             const char *pszMDPrefix = CPLGetXMLValue(psIter, "md_prefix", NULL);
    3666       48425 :             int bHasRightPrefix = FALSE;
    3667       48425 :             if (pszName == NULL)
    3668           0 :                 continue;
    3669       48425 :             if (pszSpecificTREName == NULL)
    3670       48425 :                 bHasRightPrefix = (pszMDPrefix != NULL);
    3671             :             else
    3672           0 :                 bHasRightPrefix = (strcmp(pszName, pszSpecificTREName) == 0);
    3673       48425 :             if (bHasRightPrefix)
    3674             :             {
    3675       16390 :                 if (psFile != NULL)
    3676             :                 {
    3677       16390 :                     const char *pachTRE = NULL;
    3678       16390 :                     int nTRESize = 0;
    3679             : 
    3680       16390 :                     pachTRE = NITFFindTRE(psFile->pachTRE, psFile->nTREBytes,
    3681             :                                           pszName, &nTRESize);
    3682       16390 :                     if (pachTRE != NULL)
    3683           3 :                         papszMD = NITFGenericMetadataReadTRE(
    3684             :                             papszMD, pszName, pachTRE, nTRESize, psIter);
    3685             :                 }
    3686       16390 :                 if (psImage != NULL)
    3687             :                 {
    3688       16390 :                     const char *pachTRE = NULL;
    3689       16390 :                     int nTRESize = 0;
    3690             : 
    3691       16390 :                     pachTRE = NITFFindTRE(psImage->pachTRE, psImage->nTREBytes,
    3692             :                                           pszName, &nTRESize);
    3693       16390 :                     if (pachTRE != NULL)
    3694          52 :                         papszMD = NITFGenericMetadataReadTRE(
    3695             :                             papszMD, pszName, pachTRE, nTRESize, psIter);
    3696             :                 }
    3697       16390 :                 if (pszSpecificTREName)
    3698           0 :                     break;
    3699             :             }
    3700             :         }
    3701             :     }
    3702             : 
    3703         745 :     return papszMD;
    3704             : }

Generated by: LCOV version 1.14