LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/mitab - mitab_maptoolblock.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 72 131 55.0 %
Date: 2024-05-05 22:37:24 Functions: 11 13 84.6 %

          Line data    Source code
       1             : /**********************************************************************
       2             :  *
       3             :  * Name:     mitab_maptoollock.cpp
       4             :  * Project:  MapInfo TAB Read/Write library
       5             :  * Language: C++
       6             :  * Purpose:  Implementation of the TABMAPToolBlock class used to handle
       7             :  *           reading/writing of the .MAP files' drawing tool blocks
       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 <cstddef>
      37             : 
      38             : #include "cpl_conv.h"
      39             : #include "cpl_error.h"
      40             : #include "cpl_vsi.h"
      41             : #include "mitab_priv.h"
      42             : 
      43             : /*=====================================================================
      44             :  *                      class TABMAPToolBlock
      45             :  *====================================================================*/
      46             : 
      47             : constexpr int MAP_TOOL_HEADER_SIZE = 8;
      48             : 
      49             : /**********************************************************************
      50             :  *                   TABMAPToolBlock::TABMAPToolBlock()
      51             :  *
      52             :  * Constructor.
      53             :  **********************************************************************/
      54        2369 : TABMAPToolBlock::TABMAPToolBlock(TABAccess eAccessMode /*= TABRead*/)
      55             :     : TABRawBinBlock(eAccessMode, TRUE), m_numDataBytes(0), m_nNextToolBlock(0),
      56             :       m_numBlocksInChain(1),  // Current block counts as 1
      57        2369 :       m_poBlockManagerRef(nullptr)
      58             : {
      59        2369 : }
      60             : 
      61             : /**********************************************************************
      62             :  *                   TABMAPToolBlock::~TABMAPToolBlock()
      63             :  *
      64             :  * Destructor.
      65             :  **********************************************************************/
      66        4738 : TABMAPToolBlock::~TABMAPToolBlock()
      67             : {
      68        4738 : }
      69             : 
      70             : /**********************************************************************
      71             :  *                   TABMAPToolBlock::EndOfChain()
      72             :  *
      73             :  * Return TRUE if we reached the end of the last block in the chain
      74             :  * TABMAPToolBlocks, or FALSE if there is still data to be read from
      75             :  * this chain.
      76             :  **********************************************************************/
      77        2489 : GBool TABMAPToolBlock::EndOfChain()
      78             : {
      79        2489 :     if (m_pabyBuf && (m_nCurPos < (m_numDataBytes + MAP_TOOL_HEADER_SIZE) ||
      80        1208 :                       m_nNextToolBlock > 0))
      81             :     {
      82        1281 :         return FALSE;  // There is still data to be read.
      83             :     }
      84             : 
      85        1208 :     return TRUE;
      86             : }
      87             : 
      88             : /**********************************************************************
      89             :  *                   TABMAPToolBlock::InitBlockFromData()
      90             :  *
      91             :  * Perform some initialization on the block after its binary data has
      92             :  * been set or changed (or loaded from a file).
      93             :  *
      94             :  * Returns 0 if successful or -1 if an error happened, in which case
      95             :  * CPLError() will have been called.
      96             :  **********************************************************************/
      97        1208 : int TABMAPToolBlock::InitBlockFromData(GByte *pabyBuf, int nBlockSize,
      98             :                                        int nSizeUsed,
      99             :                                        GBool bMakeCopy /* = TRUE */,
     100             :                                        VSILFILE *fpSrc /* = NULL */,
     101             :                                        int nOffset /* = 0 */)
     102             : {
     103             :     /*-----------------------------------------------------------------
     104             :      * First of all, we must call the base class' InitBlockFromData()
     105             :      *----------------------------------------------------------------*/
     106        1208 :     const int nStatus = TABRawBinBlock::InitBlockFromData(
     107             :         pabyBuf, nBlockSize, nSizeUsed, bMakeCopy, fpSrc, nOffset);
     108        1208 :     if (nStatus != 0)
     109           0 :         return nStatus;
     110             : 
     111             :     /*-----------------------------------------------------------------
     112             :      * Validate block type
     113             :      *----------------------------------------------------------------*/
     114        1208 :     if (m_nBlockType != TABMAP_TOOL_BLOCK)
     115             :     {
     116           0 :         CPLError(CE_Failure, CPLE_FileIO,
     117             :                  "InitBlockFromData(): Invalid Block Type: got %d expected %d",
     118             :                  m_nBlockType, TABMAP_TOOL_BLOCK);
     119           0 :         CPLFree(m_pabyBuf);
     120           0 :         m_pabyBuf = nullptr;
     121           0 :         return -1;
     122             :     }
     123             : 
     124             :     /*-----------------------------------------------------------------
     125             :      * Init member variables
     126             :      *----------------------------------------------------------------*/
     127        1208 :     GotoByteInBlock(0x002);
     128        1208 :     m_numDataBytes = ReadInt16(); /* Excluding 8 bytes header */
     129        1208 :     if (m_numDataBytes < 0 ||
     130        1208 :         m_numDataBytes + MAP_TOOL_HEADER_SIZE > nBlockSize)
     131             :     {
     132           0 :         CPLError(CE_Failure, CPLE_FileIO,
     133             :                  "TABMAPToolBlock::InitBlockFromData(): m_numDataBytes=%d "
     134             :                  "incompatible with block size %d",
     135             :                  m_numDataBytes, nBlockSize);
     136           0 :         CPLFree(m_pabyBuf);
     137           0 :         m_pabyBuf = nullptr;
     138           0 :         return -1;
     139             :     }
     140             : 
     141        1208 :     m_nNextToolBlock = ReadInt32();
     142        1208 :     if (m_nNextToolBlock != 0 &&
     143           0 :         (m_nNextToolBlock / m_nBlockSize) * m_nBlockSize == nOffset)
     144             :     {
     145           0 :         CPLError(CE_Failure, CPLE_FileIO,
     146             :                  "InitBlockFromData(): self referencing block");
     147           0 :         CPLFree(m_pabyBuf);
     148           0 :         m_pabyBuf = nullptr;
     149           0 :         return -1;
     150             :     }
     151             : 
     152             :     /*-----------------------------------------------------------------
     153             :      * The read ptr is now located at the beginning of the data part.
     154             :      *----------------------------------------------------------------*/
     155        1208 :     GotoByteInBlock(MAP_TOOL_HEADER_SIZE);
     156             : 
     157        1208 :     return 0;
     158             : }
     159             : 
     160             : /**********************************************************************
     161             :  *                   TABMAPToolBlock::CommitToFile()
     162             :  *
     163             :  * Commit the current state of the binary block to the file to which
     164             :  * it has been previously attached.
     165             :  *
     166             :  * This method makes sure all values are properly set in the map object
     167             :  * block header and then calls TABRawBinBlock::CommitToFile() to do
     168             :  * the actual writing to disk.
     169             :  *
     170             :  * Returns 0 if successful or -1 if an error happened, in which case
     171             :  * CPLError() will have been called.
     172             :  **********************************************************************/
     173        1161 : int TABMAPToolBlock::CommitToFile()
     174             : {
     175        1161 :     if (m_pabyBuf == nullptr)
     176             :     {
     177           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
     178             :                  "CommitToFile(): Block has not been initialized yet!");
     179           0 :         return -1;
     180             :     }
     181             : 
     182             :     /*-----------------------------------------------------------------
     183             :      * Nothing to do here if block has not been modified
     184             :      *----------------------------------------------------------------*/
     185        1161 :     if (!m_bModified)
     186           0 :         return 0;
     187             : 
     188             :     /*-----------------------------------------------------------------
     189             :      * Make sure 8 bytes block header is up to date.
     190             :      *----------------------------------------------------------------*/
     191        1161 :     GotoByteInBlock(0x000);
     192             : 
     193        1161 :     WriteInt16(TABMAP_TOOL_BLOCK);  // Block type code
     194        1161 :     CPLAssert(m_nSizeUsed >= MAP_TOOL_HEADER_SIZE &&
     195             :               m_nSizeUsed < MAP_TOOL_HEADER_SIZE + 32768);
     196        1161 :     WriteInt16(static_cast<GInt16>(m_nSizeUsed -
     197             :                                    MAP_TOOL_HEADER_SIZE));  // num. bytes used
     198        1161 :     WriteInt32(m_nNextToolBlock);
     199             : 
     200        1161 :     int nStatus = CPLGetLastErrorType() == CE_Failure ? -1 : 0;
     201             : 
     202             :     /*-----------------------------------------------------------------
     203             :      * OK, call the base class to write the block to disk.
     204             :      *----------------------------------------------------------------*/
     205        1161 :     if (nStatus == 0)
     206             :     {
     207             : #ifdef DEBUG_VERBOSE
     208             :         CPLDebug("MITAB", "Committing TOOL block to offset %d", m_nFileOffset);
     209             : #endif
     210        1161 :         nStatus = TABRawBinBlock::CommitToFile();
     211             :     }
     212             : 
     213        1161 :     return nStatus;
     214             : }
     215             : 
     216             : /**********************************************************************
     217             :  *                   TABMAPToolBlock::InitNewBlock()
     218             :  *
     219             :  * Initialize a newly created block so that it knows to which file it
     220             :  * is attached, its block size, etc . and then perform any specific
     221             :  * initialization for this block type, including writing a default
     222             :  * block header, etc. and leave the block ready to receive data.
     223             :  *
     224             :  * This is an alternative to calling ReadFromFile() or InitBlockFromData()
     225             :  * that puts the block in a stable state without loading any initial
     226             :  * data in it.
     227             :  *
     228             :  * Returns 0 if successful or -1 if an error happened, in which case
     229             :  * CPLError() will have been called.
     230             :  **********************************************************************/
     231        2369 : int TABMAPToolBlock::InitNewBlock(VSILFILE *fpSrc, int nBlockSize,
     232             :                                   int nFileOffset /* = 0*/)
     233             : {
     234             : #ifdef DEBUG_VERBOSE
     235             :     CPLDebug("MITAB", "Instantiating new TOOL block at offset %d", nFileOffset);
     236             : #endif
     237             : 
     238             :     /*-----------------------------------------------------------------
     239             :      * Start with the default initialization
     240             :      *----------------------------------------------------------------*/
     241        2369 :     if (TABRawBinBlock::InitNewBlock(fpSrc, nBlockSize, nFileOffset) != 0)
     242           0 :         return -1;
     243             : 
     244             :     /*-----------------------------------------------------------------
     245             :      * And then set default values for the block header.
     246             :      *----------------------------------------------------------------*/
     247        2369 :     m_nNextToolBlock = 0;
     248             : 
     249        2369 :     m_numDataBytes = 0;
     250             : 
     251        2369 :     GotoByteInBlock(0x000);
     252             : 
     253        2369 :     if (m_eAccess != TABRead)
     254             :     {
     255        1161 :         WriteInt16(TABMAP_TOOL_BLOCK);  // Block type code
     256        1161 :         WriteInt16(0);                  // num. bytes used, excluding header
     257        1161 :         WriteInt32(0);                  // Pointer to next tool block
     258             :     }
     259             : 
     260        2369 :     if (CPLGetLastErrorType() == CE_Failure)
     261           0 :         return -1;
     262             : 
     263        2369 :     return 0;
     264             : }
     265             : 
     266             : /**********************************************************************
     267             :  *                   TABMAPToolBlock::SetNextToolBlock()
     268             :  *
     269             :  * Set the address (offset from beginning of file) of the drawing tool block
     270             :  * that follows the current one.
     271             :  **********************************************************************/
     272           0 : void TABMAPToolBlock::SetNextToolBlock(GInt32 nNextToolBlockAddress)
     273             : {
     274           0 :     m_nNextToolBlock = nNextToolBlockAddress;
     275           0 : }
     276             : 
     277             : /**********************************************************************
     278             :  *                   TABMAPToolBlock::SetMAPBlockManagerRef()
     279             :  *
     280             :  * Pass a reference to the block manager object for the file this
     281             :  * block belongs to.  The block manager will be used by this object
     282             :  * when it needs to automatically allocate a new block.
     283             :  **********************************************************************/
     284        1161 : void TABMAPToolBlock::SetMAPBlockManagerRef(TABBinBlockManager *poBlockMgr)
     285             : {
     286        1161 :     m_poBlockManagerRef = poBlockMgr;
     287        1161 : }
     288             : 
     289             : /**********************************************************************
     290             :  *                   TABMAPToolBlock::ReadBytes()
     291             :  *
     292             :  * Cover function for TABRawBinBlock::ReadBytes() that will automagically
     293             :  * load the next coordinate block in the chain before reading the
     294             :  * requested bytes if we are at the end of the current block and if
     295             :  * m_nNextToolBlock is a valid block.
     296             :  *
     297             :  * Then the control is passed to TABRawBinBlock::ReadBytes() to finish the
     298             :  * work:
     299             :  * Copy the number of bytes from the data block's internal buffer to
     300             :  * the user's buffer pointed by pabyDstBuf.
     301             :  *
     302             :  * Passing pabyDstBuf = NULL will only move the read pointer by the
     303             :  * specified number of bytes as if the copy had happened... but it
     304             :  * won't crash.
     305             :  *
     306             :  * Returns 0 if successful or -1 if an error happened, in which case
     307             :  * CPLError() will have been called.
     308             :  **********************************************************************/
     309       12663 : int TABMAPToolBlock::ReadBytes(int numBytes, GByte *pabyDstBuf)
     310             : {
     311       12663 :     if (m_pabyBuf && m_nCurPos >= (m_numDataBytes + MAP_TOOL_HEADER_SIZE) &&
     312           0 :         m_nNextToolBlock > 0)
     313             :     {
     314           0 :         int nStatus = GotoByteInFile(m_nNextToolBlock);
     315           0 :         if (nStatus != 0)
     316             :         {
     317             :             // Failed.... an error has already been reported.
     318           0 :             return nStatus;
     319             :         }
     320             : 
     321           0 :         GotoByteInBlock(MAP_TOOL_HEADER_SIZE);  // Move pointer past header
     322             : 
     323             :         // FIXME ? what about a corrupted tab file that would have more than
     324             :         // 255 blocks
     325           0 :         m_numBlocksInChain++;
     326             :     }
     327             : 
     328       12663 :     return TABRawBinBlock::ReadBytes(numBytes, pabyDstBuf);
     329             : }
     330             : 
     331             : /**********************************************************************
     332             :  *                   TABMAPToolBlock::WriteBytes()
     333             :  *
     334             :  * Cover function for TABRawBinBlock::WriteBytes() that will automagically
     335             :  * CommitToFile() the current block and create a new one if we are at
     336             :  * the end of the current block.
     337             :  *
     338             :  * Then the control is passed to TABRawBinBlock::WriteBytes() to finish the
     339             :  * work.
     340             :  *
     341             :  * Passing pabySrcBuf = NULL will only move the write pointer by the
     342             :  * specified number of bytes as if the copy had happened... but it
     343             :  * won't crash.
     344             :  *
     345             :  * Returns 0 if successful or -1 if an error happened, in which case
     346             :  * CPLError() will have been called.
     347             :  **********************************************************************/
     348       16732 : int TABMAPToolBlock::WriteBytes(int nBytesToWrite, const GByte *pabySrcBuf)
     349             : {
     350       16732 :     if (m_eAccess == TABWrite && m_poBlockManagerRef &&
     351        1679 :         (m_nBlockSize - m_nCurPos) < nBytesToWrite)
     352             :     {
     353           0 :         if (m_numBlocksInChain >= 255)
     354             :         {
     355           0 :             CPLError(CE_Failure, CPLE_NotSupported,
     356             :                      "Maximum number of 255 tool blocks reached");
     357           0 :             return -1;
     358             :         }
     359             : 
     360           0 :         int nNewBlockOffset = m_poBlockManagerRef->AllocNewBlock("TOOL");
     361           0 :         SetNextToolBlock(nNewBlockOffset);
     362             : 
     363           0 :         if (CommitToFile() != 0 ||
     364           0 :             InitNewBlock(m_fp, m_nBlockSize, nNewBlockOffset) != 0)
     365             :         {
     366             :             // An error message should have already been reported.
     367           0 :             return -1;
     368             :         }
     369             : 
     370           0 :         m_numBlocksInChain++;
     371             :     }
     372             : 
     373       16732 :     return TABRawBinBlock::WriteBytes(nBytesToWrite, pabySrcBuf);
     374             : }
     375             : 
     376             : /**********************************************************************
     377             :  *                   TABMAPToolBlock::CheckAvailableSpace()
     378             :  *
     379             :  * Check if an object of the specified type can fit in
     380             :  * current block.  If it can't fit then force committing current block
     381             :  * and allocating a new one.
     382             :  *
     383             :  * Returns 0 if successful or -1 if an error happened, in which case
     384             :  * CPLError() will have been called.
     385             :  **********************************************************************/
     386        1217 : int TABMAPToolBlock::CheckAvailableSpace(int nToolType)
     387             : {
     388        1217 :     int nBytesToWrite = 0;
     389             : 
     390        1217 :     switch (nToolType)
     391             :     {
     392          45 :         case TABMAP_TOOL_PEN:
     393          45 :             nBytesToWrite = 11;
     394          45 :             break;
     395          30 :         case TABMAP_TOOL_BRUSH:
     396          30 :             nBytesToWrite = 13;
     397          30 :             break;
     398           6 :         case TABMAP_TOOL_FONT:
     399           6 :             nBytesToWrite = 37;
     400           6 :             break;
     401        1136 :         case TABMAP_TOOL_SYMBOL:
     402        1136 :             nBytesToWrite = 13;
     403        1136 :             break;
     404           0 :         default:
     405           0 :             CPLAssert(false);
     406             :     }
     407             : 
     408        1217 :     if (GetNumUnusedBytes() < nBytesToWrite)
     409             :     {
     410           0 :         if (m_numBlocksInChain >= 255)
     411             :         {
     412           0 :             CPLError(CE_Failure, CPLE_NotSupported,
     413             :                      "Maximum number of 255 tool blocks reached");
     414           0 :             return -1;
     415             :         }
     416           0 :         int nNewBlockOffset = m_poBlockManagerRef->AllocNewBlock("TOOL");
     417           0 :         SetNextToolBlock(nNewBlockOffset);
     418             : 
     419           0 :         if (CommitToFile() != 0 ||
     420           0 :             InitNewBlock(m_fp, m_nBlockSize, nNewBlockOffset) != 0)
     421             :         {
     422             :             // An error message should have already been reported.
     423           0 :             return -1;
     424             :         }
     425             : 
     426           0 :         m_numBlocksInChain++;
     427             :     }
     428             : 
     429        1217 :     return 0;
     430             : }
     431             : 
     432             : /**********************************************************************
     433             :  *                   TABMAPToolBlock::Dump()
     434             :  *
     435             :  * Dump block contents... available only in DEBUG mode.
     436             :  **********************************************************************/
     437             : #ifdef DEBUG
     438             : 
     439           0 : void TABMAPToolBlock::Dump(FILE *fpOut /*=NULL*/)
     440             : {
     441           0 :     if (fpOut == nullptr)
     442           0 :         fpOut = stdout;
     443             : 
     444           0 :     fprintf(fpOut, "----- TABMAPToolBlock::Dump() -----\n");
     445           0 :     if (m_pabyBuf == nullptr)
     446             :     {
     447           0 :         fprintf(fpOut, "Block has not been initialized yet.");
     448             :     }
     449             :     else
     450             :     {
     451           0 :         fprintf(fpOut, "Tool Block (type %d) at offset %d.\n", m_nBlockType,
     452             :                 m_nFileOffset);
     453           0 :         fprintf(fpOut, "  m_numDataBytes        = %d\n", m_numDataBytes);
     454           0 :         fprintf(fpOut, "  m_nNextToolBlock     = %d\n", m_nNextToolBlock);
     455             :     }
     456             : 
     457           0 :     fflush(fpOut);
     458           0 : }
     459             : 
     460             : #endif  // DEBUG

Generated by: LCOV version 1.14