LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/mitab - mitab_idfile.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 71 124 57.3 %
Date: 2024-11-21 22:18:42 Functions: 7 10 70.0 %

          Line data    Source code
       1             : /**********************************************************************
       2             :  *
       3             :  * Name:     mitab_idfile.cpp
       4             :  * Project:  MapInfo TAB Read/Write library
       5             :  * Language: C++
       6             :  * Purpose:  Implementation of the TABIDFile class used to handle
       7             :  *           reading/writing of the .ID file attached to a .MAP file
       8             :  * Author:   Daniel Morissette, dmorissette@dmsolutions.ca
       9             :  *
      10             :  **********************************************************************
      11             :  * Copyright (c) 1999, 2000, Daniel Morissette
      12             :  * Copyright (c) 2014, Even Rouault <even.rouault at spatialys.com>
      13             :  *
      14             :  * SPDX-License-Identifier: MIT
      15             :  **********************************************************************/
      16             : 
      17             : #include "cpl_port.h"
      18             : #include "mitab.h"
      19             : 
      20             : #include <algorithm>
      21             : #include <limits.h>
      22             : #include <string.h>
      23             : 
      24             : #include "cpl_conv.h"
      25             : #include "cpl_error.h"
      26             : #include "cpl_vsi.h"
      27             : #include "mitab_priv.h"
      28             : #include "mitab_utils.h"
      29             : 
      30             : /*=====================================================================
      31             :  *                      class TABIDFile
      32             :  *====================================================================*/
      33             : 
      34             : /**********************************************************************
      35             :  *                   TABIDFile::TABIDFile()
      36             :  *
      37             :  * Constructor.
      38             :  **********************************************************************/
      39        1439 : TABIDFile::TABIDFile()
      40             :     : m_pszFname(nullptr), m_fp(nullptr), m_eAccessMode(TABRead),
      41        1439 :       m_poIDBlock(nullptr), m_nBlockSize(0), m_nMaxId(-1)
      42             : {
      43        1439 : }
      44             : 
      45             : /**********************************************************************
      46             :  *                   TABIDFile::~TABIDFile()
      47             :  *
      48             :  * Destructor.
      49             :  **********************************************************************/
      50        2878 : TABIDFile::~TABIDFile()
      51             : {
      52        1439 :     Close();
      53        1439 : }
      54             : 
      55             : /**********************************************************************
      56             :  *                   TABIDFile::Open()
      57             :  *
      58             :  * Compatibility layer with new interface.
      59             :  * Return 0 on success, -1 in case of failure.
      60             :  **********************************************************************/
      61             : 
      62           0 : int TABIDFile::Open(const char *pszFname, const char *pszAccess)
      63             : {
      64             :     // cppcheck-suppress nullPointer
      65           0 :     if (STARTS_WITH_CI(pszAccess, "r"))
      66           0 :         return Open(pszFname, TABRead);
      67           0 :     if (STARTS_WITH_CI(pszAccess, "w"))
      68           0 :         return Open(pszFname, TABWrite);
      69             : 
      70           0 :     CPLError(CE_Failure, CPLE_FileIO,
      71             :              "Open() failed: access mode \"%s\" not supported", pszAccess);
      72           0 :     return -1;
      73             : }
      74             : 
      75             : /**********************************************************************
      76             :  *                   TABIDFile::Open()
      77             :  *
      78             :  * Open a .ID file, and initialize the structures to be ready to read
      79             :  * objects from it.
      80             :  *
      81             :  * If the filename that is passed in contains a .MAP extension then
      82             :  * the extension will be changed to .ID before trying to open the file.
      83             :  *
      84             :  * Returns 0 on success, -1 on error.
      85             :  **********************************************************************/
      86        1439 : int TABIDFile::Open(const char *pszFname, TABAccess eAccess)
      87             : {
      88        1439 :     if (m_fp)
      89             :     {
      90           0 :         CPLError(CE_Failure, CPLE_FileIO,
      91             :                  "Open() failed: object already contains an open file");
      92           0 :         return -1;
      93             :     }
      94             : 
      95             :     // Validate access mode and make sure we use binary access.
      96             :     // Note that in Write mode we need TABReadWrite since we do random
      97             :     // updates in the index as data blocks are split.
      98        1439 :     const char *pszAccess = nullptr;
      99        1439 :     if (eAccess == TABRead)
     100             :     {
     101         242 :         m_eAccessMode = TABRead;
     102         242 :         pszAccess = "rb";
     103             :     }
     104        1197 :     else if (eAccess == TABWrite)
     105             :     {
     106         122 :         m_eAccessMode = TABReadWrite;
     107         122 :         pszAccess = "wb+";
     108             :     }
     109        1075 :     else if (eAccess == TABReadWrite)
     110             :     {
     111        1075 :         m_eAccessMode = TABReadWrite;
     112        1075 :         pszAccess = "rb+";
     113             :     }
     114             :     else
     115             :     {
     116           0 :         CPLError(CE_Failure, CPLE_FileIO,
     117             :                  "Open() failed: access mode \"%d\" not supported", eAccess);
     118           0 :         return -1;
     119             :     }
     120             : 
     121             :     // Change .MAP extension to .ID if necessary.
     122        1439 :     m_pszFname = CPLStrdup(pszFname);
     123             : 
     124        1439 :     const int nLen = static_cast<int>(strlen(m_pszFname));
     125        1439 :     if (nLen > 4 && strcmp(m_pszFname + nLen - 4, ".MAP") == 0)
     126           3 :         strcpy(m_pszFname + nLen - 4, ".ID");
     127        1436 :     else if (nLen > 4 && strcmp(m_pszFname + nLen - 4, ".map") == 0)
     128        1436 :         strcpy(m_pszFname + nLen - 4, ".id");
     129             : 
     130             : #ifndef _WIN32
     131             :     // Change .MAP extension to .ID if necessary.
     132        1439 :     TABAdjustFilenameExtension(m_pszFname);
     133             : #endif
     134             : 
     135             :     // Open file.
     136        1439 :     m_fp = VSIFOpenL(m_pszFname, pszAccess);
     137             : 
     138        1439 :     if (m_fp == nullptr)
     139             :     {
     140           0 :         CPLError(CE_Failure, CPLE_FileIO, "Open() failed for %s", m_pszFname);
     141           0 :         CPLFree(m_pszFname);
     142           0 :         m_pszFname = nullptr;
     143           0 :         return -1;
     144             :     }
     145             : 
     146        1439 :     if (m_eAccessMode == TABRead || m_eAccessMode == TABReadWrite)
     147             :     {
     148             :         // READ access:
     149             :         // Establish the number of object IDs from the size of the file.
     150             :         VSIStatBufL sStatBuf;
     151        1439 :         if (VSIStatL(m_pszFname, &sStatBuf) == -1)
     152             :         {
     153           0 :             CPLError(CE_Failure, CPLE_FileIO, "stat() failed for %s",
     154             :                      m_pszFname);
     155           0 :             Close();
     156           0 :             return -1;
     157             :         }
     158             : 
     159        1439 :         if (static_cast<vsi_l_offset>(sStatBuf.st_size) >
     160             :             static_cast<vsi_l_offset>(INT_MAX / 4))
     161           0 :             m_nMaxId = INT_MAX / 4;
     162             :         else
     163        1439 :             m_nMaxId = static_cast<int>(sStatBuf.st_size / 4);
     164        1439 :         m_nBlockSize = std::min(1024, m_nMaxId * 4);
     165             : 
     166             :         // Read the first block from the file.
     167        1439 :         m_poIDBlock = new TABRawBinBlock(m_eAccessMode, FALSE);
     168             : 
     169        1439 :         if (m_nMaxId == 0)
     170             :         {
     171             :             // .ID file size = 0 ... just allocate a blank block but
     172             :             // it won't get really used anyways.
     173         133 :             m_nBlockSize = 512;
     174         133 :             m_poIDBlock->InitNewBlock(m_fp, m_nBlockSize, 0);
     175             :         }
     176        1306 :         else if (m_poIDBlock->ReadFromFile(m_fp, 0, m_nBlockSize) != 0)
     177             :         {
     178             :             // CPLError() has already been called.
     179           0 :             Close();
     180           0 :             return -1;
     181        1439 :         }
     182             :     }
     183             :     else
     184             :     {
     185             :         // WRITE access:
     186             :         // Get ready to write to the file.
     187           0 :         m_poIDBlock = new TABRawBinBlock(m_eAccessMode, FALSE);
     188           0 :         m_nMaxId = 0;
     189           0 :         m_nBlockSize = 1024;
     190           0 :         m_poIDBlock->InitNewBlock(m_fp, m_nBlockSize, 0);
     191             :     }
     192             : 
     193        1439 :     return 0;
     194             : }
     195             : 
     196             : /**********************************************************************
     197             :  *                   TABIDFile::Close()
     198             :  *
     199             :  * Close current file, and release all memory used.
     200             :  *
     201             :  * Returns 0 on success, -1 on error.
     202             :  **********************************************************************/
     203        2878 : int TABIDFile::Close()
     204             : {
     205        2878 :     if (m_fp == nullptr)
     206        1439 :         return 0;
     207             : 
     208             :     // Write access: commit latest changes to the file.
     209        1439 :     if (m_eAccessMode != TABRead)
     210        1197 :         SyncToDisk();
     211             : 
     212             :     // Delete all structures
     213        1439 :     delete m_poIDBlock;
     214        1439 :     m_poIDBlock = nullptr;
     215             : 
     216             :     // Close file
     217        1439 :     VSIFCloseL(m_fp);
     218        1439 :     m_fp = nullptr;
     219             : 
     220        1439 :     CPLFree(m_pszFname);
     221        1439 :     m_pszFname = nullptr;
     222             : 
     223        1439 :     return 0;
     224             : }
     225             : 
     226             : /************************************************************************/
     227             : /*                            SyncToDisk()                             */
     228             : /************************************************************************/
     229             : 
     230        2398 : int TABIDFile::SyncToDisk()
     231             : {
     232        2398 :     if (m_eAccessMode == TABRead)
     233             :     {
     234           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     235             :                  "SyncToDisk() can be used only with Write access.");
     236           0 :         return -1;
     237             :     }
     238             : 
     239        2398 :     if (m_poIDBlock == nullptr)
     240           0 :         return 0;
     241             : 
     242        2398 :     return m_poIDBlock->CommitToFile();
     243             : }
     244             : 
     245             : /**********************************************************************
     246             :  *                   TABIDFile::GetObjPtr()
     247             :  *
     248             :  * Return the offset in the .MAP file where the map object with the
     249             :  * specified id is located.
     250             :  *
     251             :  * Note that object ids are positive and start at 1.
     252             :  *
     253             :  * An object Id of '0' means that object has no geometry.
     254             :  *
     255             :  * Returns a value >= 0 on success, -1 on error.
     256             :  **********************************************************************/
     257      153055 : GInt32 TABIDFile::GetObjPtr(GInt32 nObjId)
     258             : {
     259      153055 :     if (m_poIDBlock == nullptr)
     260           0 :         return -1;
     261             : 
     262      153055 :     if (nObjId < 1 || nObjId > m_nMaxId)
     263             :     {
     264           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
     265             :                  "GetObjPtr(): Invalid object ID %d (valid range is [1..%d])",
     266             :                  nObjId, m_nMaxId);
     267           0 :         return -1;
     268             :     }
     269             : 
     270      153055 :     if (m_poIDBlock->GotoByteInFile((nObjId - 1) * 4) != 0)
     271           0 :         return -1;
     272             : 
     273      153055 :     return m_poIDBlock->ReadInt32();
     274             : }
     275             : 
     276             : /**********************************************************************
     277             :  *                   TABIDFile::SetObjPtr()
     278             :  *
     279             :  * Set the offset in the .MAP file where the map object with the
     280             :  * specified id is located.
     281             :  *
     282             :  * Note that object ids are positive and start at 1.
     283             :  *
     284             :  * An object Id of '0' means that object has no geometry.
     285             :  *
     286             :  * Returns a value of 0 on success, -1 on error.
     287             :  **********************************************************************/
     288       26949 : int TABIDFile::SetObjPtr(GInt32 nObjId, GInt32 nObjPtr)
     289             : {
     290       26949 :     if (m_poIDBlock == nullptr)
     291           0 :         return -1;
     292             : 
     293       26949 :     if (m_eAccessMode == TABRead)
     294             :     {
     295           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     296             :                  "SetObjPtr() can be used only with Write access.");
     297           0 :         return -1;
     298             :     }
     299             : 
     300       26949 :     if (nObjId < 1)
     301             :     {
     302           0 :         CPLError(
     303             :             CE_Failure, CPLE_IllegalArg,
     304             :             "SetObjPtr(): Invalid object ID %d (must be greater than zero)",
     305             :             nObjId);
     306           0 :         return -1;
     307             :     }
     308             : 
     309             :     // GotoByteInFile() will automagically commit current block and init
     310             :     // a new one if necessary.
     311       26949 :     const GInt32 nLastIdBlock = ((m_nMaxId - 1) * 4) / m_nBlockSize;
     312       26949 :     const GInt32 nTargetIdBlock = ((nObjId - 1) * 4) / m_nBlockSize;
     313       26949 :     if (m_nMaxId > 0 && nTargetIdBlock <= nLastIdBlock)
     314             :     {
     315             :         // Pass second arg to GotoByteInFile() to force reading from file
     316             :         // when going back to blocks already committed.
     317       26523 :         if (m_poIDBlock->GotoByteInFile((nObjId - 1) * 4, TRUE) != 0)
     318           0 :             return -1;
     319             :     }
     320             :     else
     321             :     {
     322             :         // If we reach EOF then a new empty block will have to be allocated.
     323         426 :         if (m_poIDBlock->GotoByteInFile((nObjId - 1) * 4) != 0)
     324           0 :             return -1;
     325             :     }
     326             : 
     327       26949 :     m_nMaxId = std::max(m_nMaxId, nObjId);
     328             : 
     329       26949 :     return m_poIDBlock->WriteInt32(nObjPtr);
     330             : }
     331             : 
     332             : /**********************************************************************
     333             :  *                   TABIDFile::GetMaxObjId()
     334             :  *
     335             :  * Return the value of the biggest valid object id.
     336             :  *
     337             :  * Note that object ids are positive and start at 1.
     338             :  *
     339             :  * Returns a value >= 0 on success, -1 on error.
     340             :  **********************************************************************/
     341           0 : GInt32 TABIDFile::GetMaxObjId()
     342             : {
     343           0 :     return m_nMaxId;
     344             : }
     345             : 
     346             : /**********************************************************************
     347             :  *                   TABIDFile::Dump()
     348             :  *
     349             :  * Dump block contents... available only in DEBUG mode.
     350             :  **********************************************************************/
     351             : #ifdef DEBUG
     352             : 
     353           0 : void TABIDFile::Dump(FILE *fpOut /*=NULL*/)
     354             : {
     355           0 :     if (fpOut == nullptr)
     356           0 :         fpOut = stdout;
     357             : 
     358           0 :     fprintf(fpOut, "----- TABIDFile::Dump() -----\n");
     359             : 
     360           0 :     if (m_fp == nullptr)
     361             :     {
     362           0 :         fprintf(fpOut, "File is not opened.\n");
     363             :     }
     364             :     else
     365             :     {
     366           0 :         fprintf(fpOut, "File is opened: %s\n", m_pszFname);
     367           0 :         fprintf(fpOut, "Current index block follows ...\n\n");
     368           0 :         m_poIDBlock->Dump(fpOut);
     369           0 :         fprintf(fpOut, "... end of index block.\n\n");
     370             :     }
     371             : 
     372           0 :     fflush(fpOut);
     373           0 : }
     374             : 
     375             : #endif  // DEBUG

Generated by: LCOV version 1.14