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-05-05 22:37:24 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             :  * Permission is hereby granted, free of charge, to any person obtaining a
      15             :  * copy of this software and associated documentation files (the "Software"),
      16             :  * to deal in the Software without restriction, including without limitation
      17             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      18             :  * and/or sell copies of the Software, and to permit persons to whom the
      19             :  * Software is furnished to do so, subject to the following conditions:
      20             :  *
      21             :  * The above copyright notice and this permission notice shall be included
      22             :  * in all copies or substantial portions of the Software.
      23             :  *
      24             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      25             :  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      26             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
      27             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      28             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      29             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      30             :  * DEALINGS IN THE SOFTWARE.
      31             :  **********************************************************************/
      32             : 
      33             : #include "cpl_port.h"
      34             : #include "mitab.h"
      35             : 
      36             : #include <algorithm>
      37             : #include <limits.h>
      38             : #include <string.h>
      39             : 
      40             : #include "cpl_conv.h"
      41             : #include "cpl_error.h"
      42             : #include "cpl_vsi.h"
      43             : #include "mitab_priv.h"
      44             : #include "mitab_utils.h"
      45             : 
      46             : /*=====================================================================
      47             :  *                      class TABIDFile
      48             :  *====================================================================*/
      49             : 
      50             : /**********************************************************************
      51             :  *                   TABIDFile::TABIDFile()
      52             :  *
      53             :  * Constructor.
      54             :  **********************************************************************/
      55        1425 : TABIDFile::TABIDFile()
      56             :     : m_pszFname(nullptr), m_fp(nullptr), m_eAccessMode(TABRead),
      57        1425 :       m_poIDBlock(nullptr), m_nBlockSize(0), m_nMaxId(-1)
      58             : {
      59        1425 : }
      60             : 
      61             : /**********************************************************************
      62             :  *                   TABIDFile::~TABIDFile()
      63             :  *
      64             :  * Destructor.
      65             :  **********************************************************************/
      66        2850 : TABIDFile::~TABIDFile()
      67             : {
      68        1425 :     Close();
      69        1425 : }
      70             : 
      71             : /**********************************************************************
      72             :  *                   TABIDFile::Open()
      73             :  *
      74             :  * Compatibility layer with new interface.
      75             :  * Return 0 on success, -1 in case of failure.
      76             :  **********************************************************************/
      77             : 
      78           0 : int TABIDFile::Open(const char *pszFname, const char *pszAccess)
      79             : {
      80             :     // cppcheck-suppress nullPointer
      81           0 :     if (STARTS_WITH_CI(pszAccess, "r"))
      82           0 :         return Open(pszFname, TABRead);
      83           0 :     if (STARTS_WITH_CI(pszAccess, "w"))
      84           0 :         return Open(pszFname, TABWrite);
      85             : 
      86           0 :     CPLError(CE_Failure, CPLE_FileIO,
      87             :              "Open() failed: access mode \"%s\" not supported", pszAccess);
      88           0 :     return -1;
      89             : }
      90             : 
      91             : /**********************************************************************
      92             :  *                   TABIDFile::Open()
      93             :  *
      94             :  * Open a .ID file, and initialize the structures to be ready to read
      95             :  * objects from it.
      96             :  *
      97             :  * If the filename that is passed in contains a .MAP extension then
      98             :  * the extension will be changed to .ID before trying to open the file.
      99             :  *
     100             :  * Returns 0 on success, -1 on error.
     101             :  **********************************************************************/
     102        1425 : int TABIDFile::Open(const char *pszFname, TABAccess eAccess)
     103             : {
     104        1425 :     if (m_fp)
     105             :     {
     106           0 :         CPLError(CE_Failure, CPLE_FileIO,
     107             :                  "Open() failed: object already contains an open file");
     108           0 :         return -1;
     109             :     }
     110             : 
     111             :     // Validate access mode and make sure we use binary access.
     112             :     // Note that in Write mode we need TABReadWrite since we do random
     113             :     // updates in the index as data blocks are split.
     114        1425 :     const char *pszAccess = nullptr;
     115        1425 :     if (eAccess == TABRead)
     116             :     {
     117         236 :         m_eAccessMode = TABRead;
     118         236 :         pszAccess = "rb";
     119             :     }
     120        1189 :     else if (eAccess == TABWrite)
     121             :     {
     122         116 :         m_eAccessMode = TABReadWrite;
     123         116 :         pszAccess = "wb+";
     124             :     }
     125        1073 :     else if (eAccess == TABReadWrite)
     126             :     {
     127        1073 :         m_eAccessMode = TABReadWrite;
     128        1073 :         pszAccess = "rb+";
     129             :     }
     130             :     else
     131             :     {
     132           0 :         CPLError(CE_Failure, CPLE_FileIO,
     133             :                  "Open() failed: access mode \"%d\" not supported", eAccess);
     134           0 :         return -1;
     135             :     }
     136             : 
     137             :     // Change .MAP extension to .ID if necessary.
     138        1425 :     m_pszFname = CPLStrdup(pszFname);
     139             : 
     140        1425 :     const int nLen = static_cast<int>(strlen(m_pszFname));
     141        1425 :     if (nLen > 4 && strcmp(m_pszFname + nLen - 4, ".MAP") == 0)
     142           3 :         strcpy(m_pszFname + nLen - 4, ".ID");
     143        1422 :     else if (nLen > 4 && strcmp(m_pszFname + nLen - 4, ".map") == 0)
     144        1422 :         strcpy(m_pszFname + nLen - 4, ".id");
     145             : 
     146             : #ifndef _WIN32
     147             :     // Change .MAP extension to .ID if necessary.
     148        1425 :     TABAdjustFilenameExtension(m_pszFname);
     149             : #endif
     150             : 
     151             :     // Open file.
     152        1425 :     m_fp = VSIFOpenL(m_pszFname, pszAccess);
     153             : 
     154        1425 :     if (m_fp == nullptr)
     155             :     {
     156           0 :         CPLError(CE_Failure, CPLE_FileIO, "Open() failed for %s", m_pszFname);
     157           0 :         CPLFree(m_pszFname);
     158           0 :         m_pszFname = nullptr;
     159           0 :         return -1;
     160             :     }
     161             : 
     162        1425 :     if (m_eAccessMode == TABRead || m_eAccessMode == TABReadWrite)
     163             :     {
     164             :         // READ access:
     165             :         // Establish the number of object IDs from the size of the file.
     166             :         VSIStatBufL sStatBuf;
     167        1425 :         if (VSIStatL(m_pszFname, &sStatBuf) == -1)
     168             :         {
     169           0 :             CPLError(CE_Failure, CPLE_FileIO, "stat() failed for %s",
     170             :                      m_pszFname);
     171           0 :             Close();
     172           0 :             return -1;
     173             :         }
     174             : 
     175        1425 :         if (static_cast<vsi_l_offset>(sStatBuf.st_size) >
     176             :             static_cast<vsi_l_offset>(INT_MAX / 4))
     177           0 :             m_nMaxId = INT_MAX / 4;
     178             :         else
     179        1425 :             m_nMaxId = static_cast<int>(sStatBuf.st_size / 4);
     180        1425 :         m_nBlockSize = std::min(1024, m_nMaxId * 4);
     181             : 
     182             :         // Read the first block from the file.
     183        1425 :         m_poIDBlock = new TABRawBinBlock(m_eAccessMode, FALSE);
     184             : 
     185        1425 :         if (m_nMaxId == 0)
     186             :         {
     187             :             // .ID file size = 0 ... just allocate a blank block but
     188             :             // it won't get really used anyways.
     189         127 :             m_nBlockSize = 512;
     190         127 :             m_poIDBlock->InitNewBlock(m_fp, m_nBlockSize, 0);
     191             :         }
     192        1298 :         else if (m_poIDBlock->ReadFromFile(m_fp, 0, m_nBlockSize) != 0)
     193             :         {
     194             :             // CPLError() has already been called.
     195           0 :             Close();
     196           0 :             return -1;
     197        1425 :         }
     198             :     }
     199             :     else
     200             :     {
     201             :         // WRITE access:
     202             :         // Get ready to write to the file.
     203           0 :         m_poIDBlock = new TABRawBinBlock(m_eAccessMode, FALSE);
     204           0 :         m_nMaxId = 0;
     205           0 :         m_nBlockSize = 1024;
     206           0 :         m_poIDBlock->InitNewBlock(m_fp, m_nBlockSize, 0);
     207             :     }
     208             : 
     209        1425 :     return 0;
     210             : }
     211             : 
     212             : /**********************************************************************
     213             :  *                   TABIDFile::Close()
     214             :  *
     215             :  * Close current file, and release all memory used.
     216             :  *
     217             :  * Returns 0 on success, -1 on error.
     218             :  **********************************************************************/
     219        2850 : int TABIDFile::Close()
     220             : {
     221        2850 :     if (m_fp == nullptr)
     222        1425 :         return 0;
     223             : 
     224             :     // Write access: commit latest changes to the file.
     225        1425 :     if (m_eAccessMode != TABRead)
     226        1189 :         SyncToDisk();
     227             : 
     228             :     // Delete all structures
     229        1425 :     delete m_poIDBlock;
     230        1425 :     m_poIDBlock = nullptr;
     231             : 
     232             :     // Close file
     233        1425 :     VSIFCloseL(m_fp);
     234        1425 :     m_fp = nullptr;
     235             : 
     236        1425 :     CPLFree(m_pszFname);
     237        1425 :     m_pszFname = nullptr;
     238             : 
     239        1425 :     return 0;
     240             : }
     241             : 
     242             : /************************************************************************/
     243             : /*                            SyncToDisk()                             */
     244             : /************************************************************************/
     245             : 
     246        2384 : int TABIDFile::SyncToDisk()
     247             : {
     248        2384 :     if (m_eAccessMode == TABRead)
     249             :     {
     250           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     251             :                  "SyncToDisk() can be used only with Write access.");
     252           0 :         return -1;
     253             :     }
     254             : 
     255        2384 :     if (m_poIDBlock == nullptr)
     256           0 :         return 0;
     257             : 
     258        2384 :     return m_poIDBlock->CommitToFile();
     259             : }
     260             : 
     261             : /**********************************************************************
     262             :  *                   TABIDFile::GetObjPtr()
     263             :  *
     264             :  * Return the offset in the .MAP file where the map object with the
     265             :  * specified id is located.
     266             :  *
     267             :  * Note that object ids are positive and start at 1.
     268             :  *
     269             :  * An object Id of '0' means that object has no geometry.
     270             :  *
     271             :  * Returns a value >= 0 on success, -1 on error.
     272             :  **********************************************************************/
     273      153043 : GInt32 TABIDFile::GetObjPtr(GInt32 nObjId)
     274             : {
     275      153043 :     if (m_poIDBlock == nullptr)
     276           0 :         return -1;
     277             : 
     278      153043 :     if (nObjId < 1 || nObjId > m_nMaxId)
     279             :     {
     280           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
     281             :                  "GetObjPtr(): Invalid object ID %d (valid range is [1..%d])",
     282             :                  nObjId, m_nMaxId);
     283           0 :         return -1;
     284             :     }
     285             : 
     286      153043 :     if (m_poIDBlock->GotoByteInFile((nObjId - 1) * 4) != 0)
     287           0 :         return -1;
     288             : 
     289      153043 :     return m_poIDBlock->ReadInt32();
     290             : }
     291             : 
     292             : /**********************************************************************
     293             :  *                   TABIDFile::SetObjPtr()
     294             :  *
     295             :  * Set the offset in the .MAP file where the map object with the
     296             :  * specified id is located.
     297             :  *
     298             :  * Note that object ids are positive and start at 1.
     299             :  *
     300             :  * An object Id of '0' means that object has no geometry.
     301             :  *
     302             :  * Returns a value of 0 on success, -1 on error.
     303             :  **********************************************************************/
     304       27218 : int TABIDFile::SetObjPtr(GInt32 nObjId, GInt32 nObjPtr)
     305             : {
     306       27218 :     if (m_poIDBlock == nullptr)
     307           0 :         return -1;
     308             : 
     309       27218 :     if (m_eAccessMode == TABRead)
     310             :     {
     311           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     312             :                  "SetObjPtr() can be used only with Write access.");
     313           0 :         return -1;
     314             :     }
     315             : 
     316       27218 :     if (nObjId < 1)
     317             :     {
     318           0 :         CPLError(
     319             :             CE_Failure, CPLE_IllegalArg,
     320             :             "SetObjPtr(): Invalid object ID %d (must be greater than zero)",
     321             :             nObjId);
     322           0 :         return -1;
     323             :     }
     324             : 
     325             :     // GotoByteInFile() will automagically commit current block and init
     326             :     // a new one if necessary.
     327       27218 :     const GInt32 nLastIdBlock = ((m_nMaxId - 1) * 4) / m_nBlockSize;
     328       27218 :     const GInt32 nTargetIdBlock = ((nObjId - 1) * 4) / m_nBlockSize;
     329       27218 :     if (m_nMaxId > 0 && nTargetIdBlock <= nLastIdBlock)
     330             :     {
     331             :         // Pass second arg to GotoByteInFile() to force reading from file
     332             :         // when going back to blocks already committed.
     333       26798 :         if (m_poIDBlock->GotoByteInFile((nObjId - 1) * 4, TRUE) != 0)
     334           0 :             return -1;
     335             :     }
     336             :     else
     337             :     {
     338             :         // If we reach EOF then a new empty block will have to be allocated.
     339         420 :         if (m_poIDBlock->GotoByteInFile((nObjId - 1) * 4) != 0)
     340           0 :             return -1;
     341             :     }
     342             : 
     343       27218 :     m_nMaxId = std::max(m_nMaxId, nObjId);
     344             : 
     345       27218 :     return m_poIDBlock->WriteInt32(nObjPtr);
     346             : }
     347             : 
     348             : /**********************************************************************
     349             :  *                   TABIDFile::GetMaxObjId()
     350             :  *
     351             :  * Return the value of the biggest valid object id.
     352             :  *
     353             :  * Note that object ids are positive and start at 1.
     354             :  *
     355             :  * Returns a value >= 0 on success, -1 on error.
     356             :  **********************************************************************/
     357           0 : GInt32 TABIDFile::GetMaxObjId()
     358             : {
     359           0 :     return m_nMaxId;
     360             : }
     361             : 
     362             : /**********************************************************************
     363             :  *                   TABIDFile::Dump()
     364             :  *
     365             :  * Dump block contents... available only in DEBUG mode.
     366             :  **********************************************************************/
     367             : #ifdef DEBUG
     368             : 
     369           0 : void TABIDFile::Dump(FILE *fpOut /*=NULL*/)
     370             : {
     371           0 :     if (fpOut == nullptr)
     372           0 :         fpOut = stdout;
     373             : 
     374           0 :     fprintf(fpOut, "----- TABIDFile::Dump() -----\n");
     375             : 
     376           0 :     if (m_fp == nullptr)
     377             :     {
     378           0 :         fprintf(fpOut, "File is not opened.\n");
     379             :     }
     380             :     else
     381             :     {
     382           0 :         fprintf(fpOut, "File is opened: %s\n", m_pszFname);
     383           0 :         fprintf(fpOut, "Current index block follows ...\n\n");
     384           0 :         m_poIDBlock->Dump(fpOut);
     385           0 :         fprintf(fpOut, "... end of index block.\n\n");
     386             :     }
     387             : 
     388           0 :     fflush(fpOut);
     389           0 : }
     390             : 
     391             : #endif  // DEBUG

Generated by: LCOV version 1.14