1 /**********************************************************************
2  * $Id: mitab_rawbinblock.cpp,v 1.11 2007-06-11 14:40:03 dmorissette Exp $
3  *
4  * Name:     mitab_rawbinblock.cpp
5  * Project:  MapInfo TAB Read/Write library
6  * Language: C++
7  * Purpose:  Implementation of the TABRawBinBlock class used to handle
8  *           reading/writing blocks in the .MAP files
9  * Author:   Daniel Morissette, dmorissette@dmsolutions.ca
10  *
11  **********************************************************************
12  * Copyright (c) 1999, 2000, Daniel Morissette
13  * Copyright (c) 2014, Even Rouault <even.rouault at spatialys.com>
14  *
15  * Permission is hereby granted, free of charge, to any person obtaining a
16  * copy of this software and associated documentation files (the "Software"),
17  * to deal in the Software without restriction, including without limitation
18  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
19  * and/or sell copies of the Software, and to permit persons to whom the
20  * Software is furnished to do so, subject to the following conditions:
21  *
22  * The above copyright notice and this permission notice shall be included
23  * in all copies or substantial portions of the Software.
24  *
25  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
28  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
30  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
31  * DEALINGS IN THE SOFTWARE.
32  **********************************************************************
33  *
34  * $Log: mitab_rawbinblock.cpp,v $
35  * Revision 1.11  2007-06-11 14:40:03  dmorissette
36  * Fixed another issue related to attempting to read past EOF while writing
37  * collections (bug 1657)
38  *
39  * Revision 1.10  2007/02/22 18:35:53  dmorissette
40  * Fixed problem writing collections where MITAB was sometimes trying to
41  * read past EOF in write mode (bug 1657).
42  *
43  * Revision 1.9  2006/11/28 18:49:08  dmorissette
44  * Completed changes to split TABMAPObjectBlocks properly and produce an
45  * optimal spatial index (bug 1585)
46  *
47  * Revision 1.8  2005/10/06 19:15:31  dmorissette
48  * Collections: added support for reading/writing pen/brush/symbol ids and
49  * for writing collection objects to .TAB/.MAP (bug 1126)
50  *
51  * Revision 1.7  2004/12/01 18:25:03  dmorissette
52  * Fixed potential memory leaks in error conditions (bug 881)
53  *
54  * Revision 1.6  2004/06/30 20:29:04  dmorissette
55  * Fixed refs to old address danmo@videotron.ca
56  *
57  * Revision 1.5  2000/02/28 17:06:06  daniel
58  * Added m_bModified flag
59  *
60  * Revision 1.4  2000/01/15 22:30:45  daniel
61  * Switch to MIT/X-Consortium OpenSource license
62  *
63  * Revision 1.3  1999/09/26 14:59:37  daniel
64  * Implemented write support
65  *
66  * Revision 1.2  1999/09/16 02:39:17  daniel
67  * Completed read support for most feature types
68  *
69  * Revision 1.1  1999/07/12 04:18:25  daniel
70  * Initial checkin
71  *
72  **********************************************************************/
73 
74 #include "mitab.h"
75 
76 /*=====================================================================
77  *                      class TABRawBinBlock
78  *====================================================================*/
79 
80 
81 /**********************************************************************
82  *                   TABRawBinBlock::TABRawBinBlock()
83  *
84  * Constructor.
85  **********************************************************************/
TABRawBinBlock(TABAccess eAccessMode,GBool bHardBlockSize)86 TABRawBinBlock::TABRawBinBlock(TABAccess eAccessMode /*= TABRead*/,
87                                GBool bHardBlockSize /*= TRUE*/)
88 {
89     m_fp = NULL;
90     m_pabyBuf = NULL;
91     m_nFirstBlockPtr = 0;
92     m_nBlockSize = m_nSizeUsed = m_nFileOffset = m_nCurPos = 0;
93     m_bHardBlockSize = bHardBlockSize;
94     m_nFileSize = -1;
95 
96     m_bModified = FALSE;
97 
98     m_eAccess = eAccessMode;
99 
100 }
101 
102 /**********************************************************************
103  *                   TABRawBinBlock::~TABRawBinBlock()
104  *
105  * Destructor.
106  **********************************************************************/
~TABRawBinBlock()107 TABRawBinBlock::~TABRawBinBlock()
108 {
109     if (m_pabyBuf)
110         CPLFree(m_pabyBuf);
111 }
112 
113 
114 /**********************************************************************
115  *                   TABRawBinBlock::ReadFromFile()
116  *
117  * Load data from the specified file location and initialize the block.
118  *
119  * Returns 0 if succesful or -1 if an error happened, in which case
120  * CPLError() will have been called.
121  **********************************************************************/
ReadFromFile(VSILFILE * fpSrc,int nOffset,int nSize)122 int     TABRawBinBlock::ReadFromFile(VSILFILE *fpSrc, int nOffset,
123                                      int nSize /*= 512*/)
124 {
125     GByte *pabyBuf;
126 
127     if (fpSrc == NULL || nSize == 0)
128     {
129         CPLError(CE_Failure, CPLE_AssertionFailed,
130                  "TABRawBinBlock::ReadFromFile(): Assertion Failed!");
131         return -1;
132     }
133 
134     m_fp = fpSrc;
135 
136     VSIFSeekL(fpSrc, 0, SEEK_END);
137     m_nFileSize = (int)VSIFTellL(m_fp);
138 
139     m_nFileOffset = nOffset;
140     m_nCurPos = 0;
141     m_bModified = FALSE;
142 
143     /*----------------------------------------------------------------
144      * Alloc a buffer to contain the data
145      *---------------------------------------------------------------*/
146     pabyBuf = (GByte*)CPLMalloc(nSize*sizeof(GByte));
147 
148     /*----------------------------------------------------------------
149      * Read from the file
150      *---------------------------------------------------------------*/
151     if (VSIFSeekL(fpSrc, nOffset, SEEK_SET) != 0 ||
152         (m_nSizeUsed = VSIFReadL(pabyBuf, sizeof(GByte), nSize, fpSrc) ) == 0 ||
153         (m_bHardBlockSize && m_nSizeUsed != nSize ) )
154     {
155         CPLError(CE_Failure, CPLE_FileIO,
156                  "ReadFromFile() failed reading %d bytes at offset %d.",
157                  nSize, nOffset);
158         CPLFree(pabyBuf);
159         return -1;
160     }
161 
162     /*----------------------------------------------------------------
163      * Init block with the data we just read
164      *---------------------------------------------------------------*/
165     return InitBlockFromData(pabyBuf, nSize, m_nSizeUsed,
166                              FALSE, fpSrc, nOffset);
167 }
168 
169 
170 /**********************************************************************
171  *                   TABRawBinBlock::CommitToFile()
172  *
173  * Commit the current state of the binary block to the file to which
174  * it has been previously attached.
175  *
176  * Derived classes may want to (optionally) reimplement this method if
177  * they need to do special processing before committing the block to disk.
178  *
179  * For files created with bHardBlockSize=TRUE, a complete block of
180  * the specified size is always written, otherwise only the number of
181  * used bytes in the block will be written to disk.
182  *
183  * Returns 0 if succesful or -1 if an error happened, in which case
184  * CPLError() will have been called.
185  **********************************************************************/
CommitToFile()186 int     TABRawBinBlock::CommitToFile()
187 {
188     int nStatus = 0;
189 
190     if (m_fp == NULL || m_nBlockSize <= 0 || m_pabyBuf == NULL ||
191         m_nFileOffset < 0)
192     {
193         CPLError(CE_Failure, CPLE_AssertionFailed,
194         "TABRawBinBlock::CommitToFile(): Block has not been initialized yet!");
195         return -1;
196     }
197 
198     /*----------------------------------------------------------------
199      * If block has not been modified, then just return... nothing to do.
200      *---------------------------------------------------------------*/
201     if (!m_bModified)
202         return 0;
203 
204     /*----------------------------------------------------------------
205      * Move the output file pointer to the right position...
206      *---------------------------------------------------------------*/
207     if (VSIFSeekL(m_fp, m_nFileOffset, SEEK_SET) != 0)
208     {
209         /*------------------------------------------------------------
210          * Moving pointer failed... we may need to pad with zeros if
211          * block destination is beyond current end of file.
212          *-----------------------------------------------------------*/
213         int nCurPos;
214         nCurPos = (int)VSIFTellL(m_fp);
215 
216         if (nCurPos < m_nFileOffset &&
217             VSIFSeekL(m_fp, 0L, SEEK_END) == 0 &&
218             (nCurPos = (int)VSIFTellL(m_fp)) < m_nFileOffset)
219         {
220             GByte cZero = 0;
221 
222             while(nCurPos < m_nFileOffset && nStatus == 0)
223             {
224                 if (VSIFWriteL(&cZero, 1, 1, m_fp) != 1)
225                 {
226                     CPLError(CE_Failure, CPLE_FileIO,
227                              "Failed writing 1 byte at offset %d.", nCurPos);
228                     nStatus = -1;
229                     break;
230                 }
231                 nCurPos++;
232             }
233         }
234 
235         if (nCurPos != m_nFileOffset)
236             nStatus = -1; // Error message will follow below
237 
238     }
239 
240     /*----------------------------------------------------------------
241      * At this point we are ready to write to the file.
242      *
243      * If m_bHardBlockSize==FALSE, then we do not write a complete block;
244      * we write only the part of the block that was used.
245      *---------------------------------------------------------------*/
246     int numBytesToWrite = m_bHardBlockSize?m_nBlockSize:m_nSizeUsed;
247 
248     /*CPLDebug("MITAB", "Commiting to offset %d", m_nFileOffset);*/
249 
250     if (nStatus != 0 ||
251         VSIFWriteL(m_pabyBuf,sizeof(GByte),
252                     numBytesToWrite, m_fp) != (size_t)numBytesToWrite )
253     {
254         CPLError(CE_Failure, CPLE_FileIO,
255                  "Failed writing %d bytes at offset %d.",
256                  numBytesToWrite, m_nFileOffset);
257         return -1;
258     }
259     if( m_nFileOffset + numBytesToWrite > m_nFileSize )
260     {
261         m_nFileSize = m_nFileOffset + numBytesToWrite;
262     }
263 
264     VSIFFlushL(m_fp);
265 
266     m_bModified = FALSE;
267 
268     return 0;
269 }
270 
271 /**********************************************************************
272  *                   TABRawBinBlock::CommitAsDeleted()
273  *
274  * Commit current block to file using block type 4 (garbage block)
275  *
276  * Returns 0 if succesful or -1 if an error happened, in which case
277  * CPLError() will have been called.
278  **********************************************************************/
CommitAsDeleted(GInt32 nNextBlockPtr)279 int     TABRawBinBlock::CommitAsDeleted(GInt32 nNextBlockPtr)
280 {
281     int nStatus = 0;
282 
283     CPLErrorReset();
284 
285     if ( m_pabyBuf == NULL )
286     {
287         CPLError(CE_Failure, CPLE_AssertionFailed,
288                  "CommitAsDeleted(): Block has not been initialized yet!");
289         return -1;
290     }
291 
292     /*-----------------------------------------------------------------
293      * Create deleted block header
294      *----------------------------------------------------------------*/
295     GotoByteInBlock(0x000);
296     WriteInt16(TABMAP_GARB_BLOCK);    // Block type code
297     WriteInt32(nNextBlockPtr);
298 
299     if( CPLGetLastErrorType() == CE_Failure )
300         nStatus = CPLGetLastErrorNo();
301 
302     /*-----------------------------------------------------------------
303      * OK, call the base class to write the block to disk.
304      *----------------------------------------------------------------*/
305     if (nStatus == 0)
306     {
307 #ifdef DEBUG_VERBOSE
308         CPLDebug("MITAB", "Commiting GARBAGE block to offset %d", m_nFileOffset);
309 #endif
310         nStatus = TABRawBinBlock::CommitToFile();
311         m_nSizeUsed = 0;
312     }
313 
314     return nStatus;
315 }
316 
317 /**********************************************************************
318  *                   TABRawBinBlock::InitBlockFromData()
319  *
320  * Set the binary data buffer and initialize the block.
321  *
322  * Calling ReadFromFile() will automatically call InitBlockFromData() to
323  * complete the initialization of the block after the data is read from the
324  * file.  Derived classes should implement their own version of
325  * InitBlockFromData() if they need specific initialization... in this
326  * case the derived InitBlockFromData() should call
327  * TABRawBinBlock::InitBlockFromData() before doing anything else.
328  *
329  * By default, the buffer will be copied, but if bMakeCopy = FALSE then
330  * it won't be copied, and the object will keep a reference to the
331  * user's buffer... and this object will eventually free the user's buffer.
332  *
333  * Returns 0 if succesful or -1 if an error happened, in which case
334  * CPLError() will have been called.
335  **********************************************************************/
InitBlockFromData(GByte * pabyBuf,int nBlockSize,int nSizeUsed,GBool bMakeCopy,VSILFILE * fpSrc,int nOffset)336 int     TABRawBinBlock::InitBlockFromData(GByte *pabyBuf,
337                                           int nBlockSize, int nSizeUsed,
338                                           GBool bMakeCopy /* = TRUE */,
339                                           VSILFILE *fpSrc /* = NULL */,
340                                           int nOffset /* = 0 */)
341 {
342     m_fp = fpSrc;
343     m_nFileOffset = nOffset;
344     m_nCurPos = 0;
345     m_bModified = FALSE;
346 
347     /*----------------------------------------------------------------
348      * Alloc or realloc the buffer to contain the data if necessary
349      *---------------------------------------------------------------*/
350     if (!bMakeCopy)
351     {
352         if (m_pabyBuf != NULL)
353             CPLFree(m_pabyBuf);
354         m_pabyBuf = pabyBuf;
355         m_nBlockSize = nBlockSize;
356         m_nSizeUsed = nSizeUsed;
357     }
358     else if (m_pabyBuf == NULL || nBlockSize != m_nBlockSize)
359     {
360         m_pabyBuf = (GByte*)CPLRealloc(m_pabyBuf, nBlockSize*sizeof(GByte));
361         m_nBlockSize = nBlockSize;
362         m_nSizeUsed = nSizeUsed;
363         memcpy(m_pabyBuf, pabyBuf, m_nSizeUsed);
364     }
365 
366     /*----------------------------------------------------------------
367      * Extract block type... header block (first block in a file) has
368      * no block type, so we assign one by default.
369      *---------------------------------------------------------------*/
370     if (m_nFileOffset == 0)
371         m_nBlockType = TABMAP_HEADER_BLOCK;
372     else
373     {
374         // Block type will be validated only if GetBlockType() is called
375         m_nBlockType = (int)m_pabyBuf[0];
376     }
377 
378     return 0;
379 }
380 
381 /**********************************************************************
382  *                   TABRawBinBlock::InitNewBlock()
383  *
384  * Initialize the block so that it knows to which file is is attached,
385  * its block size, etc.
386  *
387  * This is an alternative to calling ReadFromFile() or InitBlockFromData()
388  * that puts the block in a stable state without loading any initial
389  * data in it.
390  *
391  * Returns 0 if succesful or -1 if an error happened, in which case
392  * CPLError() will have been called.
393  **********************************************************************/
InitNewBlock(VSILFILE * fpSrc,int nBlockSize,int nFileOffset)394 int     TABRawBinBlock::InitNewBlock(VSILFILE *fpSrc, int nBlockSize,
395                                      int nFileOffset /* = 0*/)
396 {
397     m_fp = fpSrc;
398     m_nBlockSize = nBlockSize;
399     m_nSizeUsed = 0;
400     m_nCurPos = 0;
401     m_bModified = FALSE;
402 
403     if (nFileOffset > 0)
404         m_nFileOffset = nFileOffset;
405     else
406         m_nFileOffset = 0;
407 
408     if( m_fp != NULL && m_nFileSize < 0 && m_eAccess == TABReadWrite )
409     {
410         int nCurPos = (int)VSIFTellL(m_fp);
411         VSIFSeekL(fpSrc, 0, SEEK_END);
412         m_nFileSize = (int)VSIFTellL(m_fp);
413         VSIFSeekL(fpSrc, nCurPos, SEEK_SET);
414     }
415 
416     m_nBlockType = -1;
417 
418     m_pabyBuf = (GByte*)CPLRealloc(m_pabyBuf, m_nBlockSize*sizeof(GByte));
419     memset(m_pabyBuf, 0, m_nBlockSize);
420 
421     return 0;
422 }
423 
424 
425 /**********************************************************************
426  *                   TABRawBinBlock::GetBlockType()
427  *
428  * Return the block type for the current object.
429  *
430  * Returns a block type >= 0 if succesful or -1 if an error happened, in
431  * which case  CPLError() will have been called.
432  **********************************************************************/
GetBlockType()433 int     TABRawBinBlock::GetBlockType()
434 {
435     if (m_pabyBuf == NULL)
436     {
437         CPLError(CE_Failure, CPLE_AppDefined,
438                  "GetBlockType(): Block has not been initialized.");
439         return -1;
440     }
441 
442     if (m_nBlockType > TABMAP_LAST_VALID_BLOCK_TYPE)
443     {
444         CPLError(CE_Failure, CPLE_NotSupported,
445                  "GetBlockType(): Unsupported block type %d.",
446                  m_nBlockType);
447         return -1;
448     }
449 
450     return m_nBlockType;
451 }
452 
453 /**********************************************************************
454  *                   TABRawBinBlock::GotoByteInBlock()
455  *
456  * Move the block pointer to the specified position relative to the
457  * beginning of the block.
458  *
459  * Returns 0 if succesful or -1 if an error happened, in which case
460  * CPLError() will have been called.
461  **********************************************************************/
GotoByteInBlock(int nOffset)462 int     TABRawBinBlock::GotoByteInBlock(int nOffset)
463 {
464     if ( (m_eAccess == TABRead && nOffset > m_nSizeUsed) ||
465          (m_eAccess != TABRead && nOffset > m_nBlockSize) )
466     {
467         CPLError(CE_Failure, CPLE_AppDefined,
468                  "GotoByteInBlock(): Attempt to go past end of data block.");
469         return -1;
470     }
471 
472     if (nOffset < 0)
473     {
474         CPLError(CE_Failure, CPLE_AppDefined,
475                "GotoByteInBlock(): Attempt to go before start of data block.");
476         return -1;
477     }
478 
479     m_nCurPos = nOffset;
480 
481     m_nSizeUsed = MAX(m_nSizeUsed, m_nCurPos);
482 
483     return 0;
484 }
485 
486 /**********************************************************************
487  *                   TABRawBinBlock::GotoByteRel()
488  *
489  * Move the block pointer by the specified number of bytes relative
490  * to its current position.
491  *
492  * Returns 0 if succesful or -1 if an error happened, in which case
493  * CPLError() will have been called.
494  **********************************************************************/
GotoByteRel(int nOffset)495 int     TABRawBinBlock::GotoByteRel(int nOffset)
496 {
497     return GotoByteInBlock(m_nCurPos + nOffset);
498 }
499 
500 /**********************************************************************
501  *                   TABRawBinBlock::GotoByteInFile()
502  *
503  * Move the block pointer to the specified position relative to the
504  * beginning of the file.
505  *
506  * In read access, the current block may be reloaded to contain a right
507  * block of binary data if necessary.
508  *
509  * In write mode, the current block may automagically be committed to
510  * disk and a new block initialized if necessary.
511  *
512  * bForceReadFromFile is used in write mode to read the new block data from
513  * file instead of creating an empty block. (Useful for TABCollection
514  * or other cases that need to do random access in the file in write mode.)
515  *
516  * bOffsetIsEndOfData is set to TRUE to indicate that the nOffset
517  * to which we are attempting to go is the end of the used data in this
518  * block (we are positioninig ourselves to append data), so if the nOffset
519  * corresponds to the beginning of a 512 bytes block then we should really
520  * be positioning ourselves at the end of the block that ends at this
521  * address instead of at the beginning of the blocks that starts at this
522  * address. This case can happen when going back and forth to write collection
523  * objects to a Coordblock and is documented in bug 1657.
524  *
525  * Returns 0 if succesful or -1 if an error happened, in which case
526  * CPLError() will have been called.
527  **********************************************************************/
GotoByteInFile(int nOffset,GBool bForceReadFromFile,GBool bOffsetIsEndOfData)528 int     TABRawBinBlock::GotoByteInFile(int nOffset,
529                                        GBool bForceReadFromFile /*=FALSE*/,
530                                        GBool bOffsetIsEndOfData /*=FALSE*/)
531 {
532     int nNewBlockPtr;
533 
534     if (nOffset < 0)
535     {
536         CPLError(CE_Failure, CPLE_AppDefined,
537                "GotoByteInFile(): Attempt to go before start of file.");
538         return -1;
539     }
540 
541     nNewBlockPtr = ( (nOffset-m_nFirstBlockPtr)/m_nBlockSize)*m_nBlockSize +
542                      m_nFirstBlockPtr;
543 
544     if (m_eAccess == TABRead)
545     {
546         if ( (nOffset<m_nFileOffset || nOffset>=m_nFileOffset+m_nSizeUsed) &&
547              ReadFromFile(m_fp, nNewBlockPtr, m_nBlockSize) != 0)
548         {
549             // Failed reading new block... error has already been reported.
550             return -1;
551         }
552     }
553     else if (m_eAccess == TABWrite)
554     {
555         if ( (nOffset<m_nFileOffset || nOffset>=m_nFileOffset+m_nBlockSize) &&
556              (CommitToFile() != 0 ||
557               InitNewBlock(m_fp, m_nBlockSize, nNewBlockPtr) != 0 ) )
558         {
559             // Failed reading new block... error has already been reported.
560             return -1;
561         }
562     }
563     else if (m_eAccess == TABReadWrite)
564     {
565         // TODO: THIS IS NOT REAL read/write access (it's more extended write)
566         // Currently we try to read from file only if explicitly requested.
567         // If we ever want true read/write mode we should implement
568         // more smarts to detect whether the caller wants an existing block to
569         // be read, or a new one to be created from scratch.
570         // CommitToFile() should only be called only if something changed.
571         //
572         if (bOffsetIsEndOfData &&  nOffset%m_nBlockSize == 0)
573         {
574             /* We're trying to go byte 512 of a block that's full of data.
575              * In this case it's okay to place the m_nCurPos at byte 512
576              * which is past the end of the block.
577              */
578 
579             /* Make sure we request the block that ends with requested
580              * address and not the following block that doesn't exist
581              * yet on disk */
582             nNewBlockPtr -= m_nBlockSize;
583 
584             if ( (nOffset < m_nFileOffset ||
585                   nOffset > m_nFileOffset+m_nBlockSize) &&
586                  (CommitToFile() != 0 ||
587                   (!bForceReadFromFile &&
588                    InitNewBlock(m_fp, m_nBlockSize, nNewBlockPtr) != 0) ||
589                   (bForceReadFromFile &&
590                    ReadFromFile(m_fp, nNewBlockPtr, m_nBlockSize) != 0) )  )
591             {
592                 // Failed reading new block... error has already been reported.
593                 return -1;
594             }
595         }
596         else
597         {
598             if( !bForceReadFromFile && m_nFileSize > 0 &&
599                 nOffset < m_nFileSize )
600             {
601                 bForceReadFromFile = TRUE;
602                 if ( !(nOffset < m_nFileOffset ||
603                        nOffset >= m_nFileOffset+m_nBlockSize) )
604                 {
605                     if ( (nOffset<m_nFileOffset || nOffset>=m_nFileOffset+m_nSizeUsed) &&
606                          (CommitToFile() != 0 ||
607                           ReadFromFile(m_fp, nNewBlockPtr, m_nBlockSize) != 0) )
608                     {
609                         // Failed reading new block... error has already been reported.
610                         return -1;
611                     }
612                 }
613             }
614 
615             if ( (nOffset < m_nFileOffset ||
616                   nOffset >= m_nFileOffset+m_nBlockSize) &&
617                  (CommitToFile() != 0 ||
618                   (!bForceReadFromFile &&
619                    InitNewBlock(m_fp, m_nBlockSize, nNewBlockPtr) != 0) ||
620                   (bForceReadFromFile &&
621                    ReadFromFile(m_fp, nNewBlockPtr, m_nBlockSize) != 0) )  )
622             {
623                 // Failed reading new block... error has already been reported.
624                 return -1;
625             }
626         }
627     }
628     else
629     {
630         CPLError(CE_Failure, CPLE_NotSupported,
631                  "Access mode not supported yet!");
632         return -1;
633     }
634 
635     m_nCurPos = nOffset-m_nFileOffset;
636 
637     m_nSizeUsed = MAX(m_nSizeUsed, m_nCurPos);
638 
639     return 0;
640 }
641 
642 
643 /**********************************************************************
644  *                   TABRawBinBlock::SetFirstBlockPtr()
645  *
646  * Set the position in the file at which the first block starts.
647  * This value will usually be the header size and needs to be specified
648  * only if the header size is different from the other blocks size.
649  *
650  * This value will be used by GotoByteInFile() to properly align the data
651  * blocks that it loads automatically when a requested position is outside
652  * of the block currently in memory.
653  **********************************************************************/
SetFirstBlockPtr(int nOffset)654 void  TABRawBinBlock::SetFirstBlockPtr(int nOffset)
655 {
656     m_nFirstBlockPtr = nOffset;
657 }
658 
659 
660 /**********************************************************************
661  *                   TABRawBinBlock::GetNumUnusedBytes()
662  *
663  * Return the number of unused bytes in this block.
664  **********************************************************************/
GetNumUnusedBytes()665 int     TABRawBinBlock::GetNumUnusedBytes()
666 {
667     return (m_nBlockSize - m_nSizeUsed);
668 }
669 
670 /**********************************************************************
671  *                   TABRawBinBlock::GetFirstUnusedByteOffset()
672  *
673  * Return the position of the first unused byte in this block relative
674  * to the beginning of the file, or -1 if the block is full.
675  **********************************************************************/
GetFirstUnusedByteOffset()676 int     TABRawBinBlock::GetFirstUnusedByteOffset()
677 {
678     if (m_nSizeUsed < m_nBlockSize)
679         return m_nFileOffset + m_nSizeUsed;
680     else
681         return -1;
682 }
683 
684 /**********************************************************************
685  *                   TABRawBinBlock::GetCurAddress()
686  *
687  * Return the current pointer position, relative to beginning of file.
688  **********************************************************************/
GetCurAddress()689 int     TABRawBinBlock::GetCurAddress()
690 {
691     return (m_nFileOffset + m_nCurPos);
692 }
693 
694 /**********************************************************************
695  *                   TABRawBinBlock::ReadBytes()
696  *
697  * Copy the number of bytes from the data block's internal buffer to
698  * the user's buffer pointed by pabyDstBuf.
699  *
700  * Passing pabyDstBuf = NULL will only move the read pointer by the
701  * specified number of bytes as if the copy had happened... but it
702  * won't crash.
703  *
704  * Returns 0 if succesful or -1 if an error happened, in which case
705  * CPLError() will have been called.
706  **********************************************************************/
ReadBytes(int numBytes,GByte * pabyDstBuf)707 int     TABRawBinBlock::ReadBytes(int numBytes, GByte *pabyDstBuf)
708 {
709     /*----------------------------------------------------------------
710      * Make sure block is initialized with Read access and that the
711      * operation won't go beyond the buffer's size.
712      *---------------------------------------------------------------*/
713     if (m_pabyBuf == NULL)
714     {
715         CPLError(CE_Failure, CPLE_AppDefined,
716                  "ReadBytes(): Block has not been initialized.");
717         return -1;
718     }
719 
720     if (m_nCurPos + numBytes > m_nSizeUsed)
721     {
722         CPLError(CE_Failure, CPLE_AppDefined,
723                  "ReadBytes(): Attempt to read past end of data block.");
724         return -1;
725     }
726 
727     if (pabyDstBuf)
728     {
729         memcpy(pabyDstBuf, m_pabyBuf + m_nCurPos, numBytes);
730     }
731 
732     m_nCurPos += numBytes;
733 
734     return 0;
735 }
736 
737 /**********************************************************************
738  *                   TABRawBinBlock::Read<datatype>()
739  *
740  * MapInfo files are binary files with LSB first (Intel) byte
741  * ordering.  The following functions will read from the input file
742  * and return a value with the bytes ordered properly for the current
743  * platform.
744  **********************************************************************/
ReadByte()745 GByte  TABRawBinBlock::ReadByte()
746 {
747     GByte byValue;
748 
749     ReadBytes(1, (GByte*)(&byValue));
750 
751     return byValue;
752 }
753 
ReadInt16()754 GInt16  TABRawBinBlock::ReadInt16()
755 {
756     GInt16 n16Value;
757 
758     ReadBytes(2, (GByte*)(&n16Value));
759 
760 #ifdef CPL_MSB
761     return (GInt16)CPL_SWAP16(n16Value);
762 #else
763     return n16Value;
764 #endif
765 }
766 
ReadInt32()767 GInt32  TABRawBinBlock::ReadInt32()
768 {
769     GInt32 n32Value;
770 
771     ReadBytes(4, (GByte*)(&n32Value));
772 
773 #ifdef CPL_MSB
774     return (GInt32)CPL_SWAP32(n32Value);
775 #else
776     return n32Value;
777 #endif
778 }
779 
ReadFloat()780 float   TABRawBinBlock::ReadFloat()
781 {
782     float fValue;
783 
784     ReadBytes(4, (GByte*)(&fValue));
785 
786 #ifdef CPL_MSB
787     *(GUInt32*)(&fValue) = CPL_SWAP32(*(GUInt32*)(&fValue));
788 #endif
789     return fValue;
790 }
791 
ReadDouble()792 double  TABRawBinBlock::ReadDouble()
793 {
794     double dValue;
795 
796     ReadBytes(8, (GByte*)(&dValue));
797 
798 #ifdef CPL_MSB
799     CPL_SWAPDOUBLE(&dValue);
800 #endif
801 
802     return dValue;
803 }
804 
805 
806 
807 /**********************************************************************
808  *                   TABRawBinBlock::WriteBytes()
809  *
810  * Copy the number of bytes from the user's buffer pointed by pabySrcBuf
811  * to the data block's internal buffer.
812  * Note that this call only writes to the memory buffer... nothing is
813  * written to the file until WriteToFile() is called.
814  *
815  * Passing pabySrcBuf = NULL will only move the write pointer by the
816  * specified number of bytes as if the copy had happened... but it
817  * won't crash.
818  *
819  * Returns 0 if succesful or -1 if an error happened, in which case
820  * CPLError() will have been called.
821  **********************************************************************/
WriteBytes(int nBytesToWrite,GByte * pabySrcBuf)822 int  TABRawBinBlock::WriteBytes(int nBytesToWrite, GByte *pabySrcBuf)
823 {
824     /*----------------------------------------------------------------
825      * Make sure block is initialized with Write access and that the
826      * operation won't go beyond the buffer's size.
827      *---------------------------------------------------------------*/
828     if (m_pabyBuf == NULL)
829     {
830         CPLError(CE_Failure, CPLE_AppDefined,
831                  "WriteBytes(): Block has not been initialized.");
832         return -1;
833     }
834 
835     if (m_eAccess == TABRead )
836     {
837         CPLError(CE_Failure, CPLE_AppDefined,
838                  "WriteBytes(): Block does not support write operations.");
839         return -1;
840     }
841 
842     if (m_nCurPos + nBytesToWrite > m_nBlockSize)
843     {
844         CPLError(CE_Failure, CPLE_AppDefined,
845                  "WriteBytes(): Attempt to write past end of data block.");
846         return -1;
847     }
848 
849     /*----------------------------------------------------------------
850      * Everything is OK... copy the data
851      *---------------------------------------------------------------*/
852     if (pabySrcBuf)
853     {
854         memcpy(m_pabyBuf + m_nCurPos, pabySrcBuf, nBytesToWrite);
855     }
856 
857     m_nCurPos += nBytesToWrite;
858 
859     m_nSizeUsed = MAX(m_nSizeUsed, m_nCurPos);
860 
861     m_bModified = TRUE;
862 
863     return 0;
864 }
865 
866 
867 /**********************************************************************
868  *                    TABRawBinBlock::Write<datatype>()
869  *
870  * Arc/Info files are binary files with MSB first (Motorola) byte
871  * ordering.  The following functions will reorder the byte for the
872  * value properly and write that to the output file.
873  *
874  * If a problem happens, then CPLError() will be called and
875  * CPLGetLastErrNo() can be used to test if a write operation was
876  * succesful.
877  **********************************************************************/
WriteByte(GByte byValue)878 int  TABRawBinBlock::WriteByte(GByte byValue)
879 {
880     return WriteBytes(1, (GByte*)&byValue);
881 }
882 
WriteInt16(GInt16 n16Value)883 int  TABRawBinBlock::WriteInt16(GInt16 n16Value)
884 {
885 #ifdef CPL_MSB
886     n16Value = (GInt16)CPL_SWAP16(n16Value);
887 #endif
888 
889     return WriteBytes(2, (GByte*)&n16Value);
890 }
891 
WriteInt32(GInt32 n32Value)892 int  TABRawBinBlock::WriteInt32(GInt32 n32Value)
893 {
894 #ifdef CPL_MSB
895     n32Value = (GInt32)CPL_SWAP32(n32Value);
896 #endif
897 
898     return WriteBytes(4, (GByte*)&n32Value);
899 }
900 
WriteFloat(float fValue)901 int  TABRawBinBlock::WriteFloat(float fValue)
902 {
903 #ifdef CPL_MSB
904     *(GUInt32*)(&fValue) = CPL_SWAP32(*(GUInt32*)(&fValue));
905 #endif
906 
907     return WriteBytes(4, (GByte*)&fValue);
908 }
909 
WriteDouble(double dValue)910 int  TABRawBinBlock::WriteDouble(double dValue)
911 {
912 #ifdef CPL_MSB
913     CPL_SWAPDOUBLE(&dValue);
914 #endif
915 
916     return WriteBytes(8, (GByte*)&dValue);
917 }
918 
919 
920 /**********************************************************************
921  *                    TABRawBinBlock::WriteZeros()
922  *
923  * Write a number of zeros (sepcified in bytes) at the current position
924  * in the file.
925  *
926  * If a problem happens, then CPLError() will be called and
927  * CPLGetLastErrNo() can be used to test if a write operation was
928  * succesful.
929  **********************************************************************/
WriteZeros(int nBytesToWrite)930 int  TABRawBinBlock::WriteZeros(int nBytesToWrite)
931 {
932     char acZeros[8] = {0, 0, 0, 0, 0, 0, 0, 0};
933     int i;
934     int nStatus = 0;
935 
936     /* Write by 8 bytes chunks.  The last chunk may be less than 8 bytes
937      */
938     for(i=0; nStatus == 0 && i< nBytesToWrite; i+=8)
939     {
940         nStatus = WriteBytes(MIN(8,(nBytesToWrite-i)), (GByte*)acZeros);
941     }
942 
943     return nStatus;
944 }
945 
946 /**********************************************************************
947  *                   TABRawBinBlock::WritePaddedString()
948  *
949  * Write a string and pad the end of the field (up to nFieldSize) with
950  * spaces number of spaces at the current position in the file.
951  *
952  * If a problem happens, then CPLError() will be called and
953  * CPLGetLastErrNo() can be used to test if a write operation was
954  * succesful.
955  **********************************************************************/
WritePaddedString(int nFieldSize,const char * pszString)956 int  TABRawBinBlock::WritePaddedString(int nFieldSize, const char *pszString)
957 {
958     char acSpaces[8] = {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '};
959     int i, nLen, numSpaces;
960     int nStatus = 0;
961 
962     nLen = strlen(pszString);
963     nLen = MIN(nLen, nFieldSize);
964     numSpaces = nFieldSize - nLen;
965 
966     if (nLen > 0)
967         nStatus = WriteBytes(nLen, (GByte*)pszString);
968 
969     /* Write spaces by 8 bytes chunks.  The last chunk may be less than 8 bytes
970      */
971     for(i=0; nStatus == 0 && i< numSpaces; i+=8)
972     {
973         nStatus = WriteBytes(MIN(8,(numSpaces-i)), (GByte*)acSpaces);
974     }
975 
976     return nStatus;
977 }
978 
979 /**********************************************************************
980  *                   TABRawBinBlock::Dump()
981  *
982  * Dump block contents... available only in DEBUG mode.
983  **********************************************************************/
984 #ifdef DEBUG
985 
Dump(FILE * fpOut)986 void TABRawBinBlock::Dump(FILE *fpOut /*=NULL*/)
987 {
988     if (fpOut == NULL)
989         fpOut = stdout;
990 
991     fprintf(fpOut, "----- TABRawBinBlock::Dump() -----\n");
992     if (m_pabyBuf == NULL)
993     {
994         fprintf(fpOut, "Block has not been initialized yet.");
995     }
996     else
997     {
998         if( m_nBlockType == TABMAP_GARB_BLOCK )
999         {
1000             fprintf(fpOut,"Garbage Block (type %d) at offset %d.\n",
1001                                                     m_nBlockType, m_nFileOffset);
1002             int nNextGarbageBlock;
1003             memcpy(&nNextGarbageBlock, m_pabyBuf + 2, 4);
1004             CPL_LSBPTR32(&nNextGarbageBlock);
1005             fprintf(fpOut,"  m_nNextGarbageBlock     = %d\n", nNextGarbageBlock);
1006         }
1007         else
1008         {
1009             fprintf(fpOut, "Block (type %d) size=%d bytes at offset %d in file.\n",
1010                     m_nBlockType, m_nBlockSize, m_nFileOffset);
1011             fprintf(fpOut, "Current pointer at byte %d\n", m_nCurPos);
1012         }
1013     }
1014 
1015     fflush(fpOut);
1016 }
1017 
1018 #endif // DEBUG
1019 
1020 
1021 /**********************************************************************
1022  *                          DumpBytes()
1023  *
1024  * Read and dump the contents of an Binary file.
1025  **********************************************************************/
DumpBytes(GInt32 nValue,int nOffset,FILE * fpOut)1026 void TABRawBinBlock::DumpBytes(GInt32 nValue, int nOffset /*=0*/,
1027                                FILE *fpOut /*=NULL*/)
1028 {
1029     GInt32      anVal[2];
1030     GInt16      n16Val1, n16Val2;
1031     float       fValue;
1032     char        *pcValue;
1033     double      dValue;
1034 
1035     pcValue = (char*)&nValue;
1036     memcpy(&fValue, &nValue, 4);
1037 
1038     memcpy(&n16Val1, pcValue + 2, sizeof(GInt16));
1039     memcpy(&n16Val2, pcValue, sizeof(GInt16));
1040 
1041     anVal[0] = anVal[1] = 0;
1042 
1043     /* For double precision values, we only use the first half
1044      * of the height bytes... and leave the other 4 bytes as zeros!
1045      * It's a bit of a hack, but it seems to be enough for the
1046      * precision of the values we print!
1047      */
1048 #ifdef CPL_MSB
1049     anVal[0] = nValue;
1050 #else
1051     anVal[1] = nValue;
1052 #endif
1053     memcpy(&dValue, anVal, 8);
1054 
1055     if (fpOut == NULL)
1056         fpOut = stdout;
1057 
1058     fprintf(fpOut, "%d\t0x%8.8x  %-5d\t%-6d %-6d %5.3e  d=%5.3e",
1059                     nOffset, nValue, nValue,
1060                     n16Val1, n16Val2, fValue, dValue);
1061 
1062     printf("\t[%c%c%c%c]\n", isprint(pcValue[0])?pcValue[0]:'.',
1063                              isprint(pcValue[1])?pcValue[1]:'.',
1064                              isprint(pcValue[2])?pcValue[2]:'.',
1065                              isprint(pcValue[3])?pcValue[3]:'.');
1066 }
1067 
1068 
1069 
1070 /**********************************************************************
1071  *                   TABCreateMAPBlockFromFile()
1072  *
1073  * Load data from the specified file location and create and initialize
1074  * a TABMAP*Block of the right type to handle it.
1075  *
1076  * Returns the new object if succesful or NULL if an error happened, in
1077  * which case CPLError() will have been called.
1078  **********************************************************************/
TABCreateMAPBlockFromFile(VSILFILE * fpSrc,int nOffset,int nSize,GBool bHardBlockSize,TABAccess eAccessMode)1079 TABRawBinBlock *TABCreateMAPBlockFromFile(VSILFILE *fpSrc, int nOffset,
1080                                           int nSize /*= 512*/,
1081                                           GBool bHardBlockSize /*= TRUE */,
1082                                           TABAccess eAccessMode /*= TABRead*/)
1083 {
1084     TABRawBinBlock *poBlock = NULL;
1085     GByte *pabyBuf;
1086 
1087     if (fpSrc == NULL || nSize == 0)
1088     {
1089         CPLError(CE_Failure, CPLE_AssertionFailed,
1090                  "TABCreateMAPBlockFromFile(): Assertion Failed!");
1091         return NULL;
1092     }
1093 
1094     /*----------------------------------------------------------------
1095      * Alloc a buffer to contain the data
1096      *---------------------------------------------------------------*/
1097     pabyBuf = (GByte*)CPLMalloc(nSize*sizeof(GByte));
1098 
1099     /*----------------------------------------------------------------
1100      * Read from the file
1101      *---------------------------------------------------------------*/
1102     if (VSIFSeekL(fpSrc, nOffset, SEEK_SET) != 0 ||
1103         VSIFReadL(pabyBuf, sizeof(GByte), nSize, fpSrc)!=(unsigned int)nSize )
1104     {
1105         CPLError(CE_Failure, CPLE_FileIO,
1106          "TABCreateMAPBlockFromFile() failed reading %d bytes at offset %d.",
1107                  nSize, nOffset);
1108         CPLFree(pabyBuf);
1109         return NULL;
1110     }
1111 
1112     /*----------------------------------------------------------------
1113      * Create an object of the right type
1114      * Header block is different: it does not start with the object
1115      * type byte but it is always the first block in a file
1116      *---------------------------------------------------------------*/
1117     if (nOffset == 0)
1118     {
1119         poBlock = new TABMAPHeaderBlock(eAccessMode);
1120     }
1121     else
1122     {
1123         switch(pabyBuf[0])
1124         {
1125           case TABMAP_INDEX_BLOCK:
1126             poBlock = new TABMAPIndexBlock(eAccessMode);
1127             break;
1128           case TABMAP_OBJECT_BLOCK:
1129             poBlock = new TABMAPObjectBlock(eAccessMode);
1130             break;
1131           case TABMAP_COORD_BLOCK:
1132             poBlock = new TABMAPCoordBlock(eAccessMode);
1133             break;
1134           case TABMAP_TOOL_BLOCK:
1135             poBlock = new TABMAPToolBlock(eAccessMode);
1136             break;
1137           case TABMAP_GARB_BLOCK:
1138           default:
1139             poBlock = new TABRawBinBlock(eAccessMode, bHardBlockSize);
1140             break;
1141         }
1142     }
1143 
1144     /*----------------------------------------------------------------
1145      * Init new object with the data we just read
1146      *---------------------------------------------------------------*/
1147     if (poBlock->InitBlockFromData(pabyBuf, nSize, nSize,
1148                                    FALSE, fpSrc, nOffset) != 0)
1149     {
1150         // Some error happened... and CPLError() has been called
1151         delete poBlock;
1152         poBlock = NULL;
1153     }
1154 
1155     return poBlock;
1156 }
1157 
1158 /*=====================================================================
1159  *                      class TABBinBlockManager
1160  *====================================================================*/
1161 
1162 
1163 /**********************************************************************
1164  *                   TABBinBlockManager::TABBinBlockManager()
1165  *
1166  * Constructor.
1167  **********************************************************************/
TABBinBlockManager(int nBlockSize)1168 TABBinBlockManager::TABBinBlockManager(int nBlockSize /*=512*/)
1169 {
1170 
1171     m_nBlockSize=nBlockSize;
1172     m_nLastAllocatedBlock = -1;
1173     m_psGarbageBlocksFirst = NULL;
1174     m_psGarbageBlocksLast = NULL;
1175     m_szName[0] = '\0';
1176 }
1177 
1178 /**********************************************************************
1179  *                   TABBinBlockManager::~TABBinBlockManager()
1180  *
1181  * Destructor.
1182  **********************************************************************/
~TABBinBlockManager()1183 TABBinBlockManager::~TABBinBlockManager()
1184 {
1185     Reset();
1186 }
1187 
1188 /**********************************************************************
1189  *                   TABBinBlockManager::SetName()
1190  **********************************************************************/
SetName(const char * pszName)1191 void TABBinBlockManager::SetName(const char* pszName)
1192 {
1193     strncpy(m_szName, pszName, sizeof(m_szName));
1194     m_szName[sizeof(m_szName)-1] = '\0';
1195 }
1196 
1197 /**********************************************************************
1198  *                   TABBinBlockManager::AllocNewBlock()
1199  *
1200  * Returns and reserves the address of the next available block, either a
1201  * brand new block at end of file, or recycle a garbage block if one is
1202  * available.
1203  **********************************************************************/
AllocNewBlock(CPL_UNUSED const char * pszReason)1204 GInt32  TABBinBlockManager::AllocNewBlock(CPL_UNUSED const char* pszReason)
1205 {
1206     // Try to reuse garbage blocks first
1207     if (GetFirstGarbageBlock() > 0)
1208     {
1209         int nRetValue = PopGarbageBlock();
1210 #ifdef DEBUG_VERBOSE
1211         CPLDebug("MITAB", "AllocNewBlock(%s, %s) = %d (recycling garbage block)", m_szName, pszReason, nRetValue);
1212 #endif
1213         return nRetValue;
1214     }
1215 
1216     // ... or alloc a new block at EOF
1217     if (m_nLastAllocatedBlock==-1)
1218         m_nLastAllocatedBlock = 0;
1219     else
1220         m_nLastAllocatedBlock+=m_nBlockSize;
1221 
1222 #ifdef DEBUG_VERBOSE
1223     CPLDebug("MITAB", "AllocNewBlock(%s, %s) = %d", m_szName, pszReason, m_nLastAllocatedBlock);
1224 #endif
1225     return m_nLastAllocatedBlock;
1226 }
1227 
1228 /**********************************************************************
1229  *                   TABBinBlockManager::Reset()
1230  *
1231  **********************************************************************/
Reset()1232 void TABBinBlockManager::Reset()
1233 {
1234     m_nLastAllocatedBlock = -1;
1235 
1236     // Flush list of garbage blocks
1237     while (m_psGarbageBlocksFirst != NULL)
1238     {
1239         TABBlockRef *psNext = m_psGarbageBlocksFirst->psNext;
1240         CPLFree(m_psGarbageBlocksFirst);
1241         m_psGarbageBlocksFirst = psNext;
1242     }
1243     m_psGarbageBlocksLast = NULL;
1244 }
1245 
1246 /**********************************************************************
1247  *                   TABBinBlockManager::PushGarbageBlockAsFirst()
1248  *
1249  * Insert a garbage block at the head of the list of garbage blocks.
1250  **********************************************************************/
PushGarbageBlockAsFirst(GInt32 nBlockPtr)1251 void TABBinBlockManager::PushGarbageBlockAsFirst(GInt32 nBlockPtr)
1252 {
1253     TABBlockRef *psNewBlockRef = (TABBlockRef *)CPLMalloc(sizeof(TABBlockRef));
1254 
1255     psNewBlockRef->nBlockPtr = nBlockPtr;
1256     psNewBlockRef->psPrev = NULL;
1257     psNewBlockRef->psNext = m_psGarbageBlocksFirst;
1258 
1259     if( m_psGarbageBlocksFirst != NULL )
1260         m_psGarbageBlocksFirst->psPrev = psNewBlockRef;
1261     m_psGarbageBlocksFirst = psNewBlockRef;
1262     if( m_psGarbageBlocksLast == NULL )
1263         m_psGarbageBlocksLast = m_psGarbageBlocksFirst;
1264 }
1265 
1266 /**********************************************************************
1267  *                   TABBinBlockManager::PushGarbageBlockAsLast()
1268  *
1269  * Insert a garbage block at the tail of the list of garbage blocks.
1270  **********************************************************************/
PushGarbageBlockAsLast(GInt32 nBlockPtr)1271 void TABBinBlockManager::PushGarbageBlockAsLast(GInt32 nBlockPtr)
1272 {
1273     TABBlockRef *psNewBlockRef = (TABBlockRef *)CPLMalloc(sizeof(TABBlockRef));
1274 
1275     psNewBlockRef->nBlockPtr = nBlockPtr;
1276     psNewBlockRef->psPrev = m_psGarbageBlocksLast;
1277     psNewBlockRef->psNext = NULL;
1278 
1279     if( m_psGarbageBlocksLast != NULL )
1280         m_psGarbageBlocksLast->psNext = psNewBlockRef;
1281     m_psGarbageBlocksLast = psNewBlockRef;
1282     if( m_psGarbageBlocksFirst == NULL )
1283         m_psGarbageBlocksFirst = m_psGarbageBlocksLast;
1284 }
1285 
1286 /**********************************************************************
1287  *                   TABBinBlockManager::GetFirstGarbageBlock()
1288  *
1289  * Return address of the block at the head of the list of garbage blocks
1290  * or 0 if the list is empty.
1291  **********************************************************************/
GetFirstGarbageBlock()1292 GInt32 TABBinBlockManager::GetFirstGarbageBlock()
1293 {
1294     if (m_psGarbageBlocksFirst)
1295         return m_psGarbageBlocksFirst->nBlockPtr;
1296 
1297     return 0;
1298 }
1299 
1300 /**********************************************************************
1301  *                   TABBinBlockManager::PopGarbageBlock()
1302  *
1303  * Return address of the block at the head of the list of garbage blocks
1304  * and remove that block from the list.
1305  * Retuns 0 if the list is empty.
1306  **********************************************************************/
PopGarbageBlock()1307 GInt32 TABBinBlockManager::PopGarbageBlock()
1308 {
1309     GInt32 nBlockPtr = 0;
1310 
1311     if (m_psGarbageBlocksFirst)
1312     {
1313         nBlockPtr = m_psGarbageBlocksFirst->nBlockPtr;
1314         TABBlockRef *psNext = m_psGarbageBlocksFirst->psNext;
1315         CPLFree(m_psGarbageBlocksFirst);
1316         if( psNext != NULL )
1317             psNext->psPrev = NULL;
1318         else
1319             m_psGarbageBlocksLast = NULL;
1320         m_psGarbageBlocksFirst = psNext;
1321     }
1322 
1323     return nBlockPtr;
1324 }
1325