LTP GCOV extension - code coverage report
Current view: directory - ogr/ogrsf_frmts/mitab - mitab_datfile.cpp
Test: gdal_filtered.info
Date: 2010-07-12 Instrumented lines: 565
Code covered: 39.5 % Executed lines: 223

       1                 : /**********************************************************************
       2                 :  * $Id: mitab_datfile.cpp,v 1.19 2008/01/29 20:46:32 dmorissette Exp $
       3                 :  *
       4                 :  * Name:     mitab_datfile.cpp
       5                 :  * Project:  MapInfo TAB Read/Write library
       6                 :  * Language: C++
       7                 :  * Purpose:  Implementation of the TABIDFile class used to handle
       8                 :  *           reading/writing of the .DAT file
       9                 :  * Author:   Daniel Morissette, dmorissette@dmsolutions.ca
      10                 :  *
      11                 :  **********************************************************************
      12                 :  * Copyright (c) 1999-2001, Daniel Morissette
      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                 :  * $Log: mitab_datfile.cpp,v $
      34                 :  * Revision 1.19  2008/01/29 20:46:32  dmorissette
      35                 :  * Added support for v9 Time and DateTime fields (byg 1754)
      36                 :  *
      37                 :  * Revision 1.18  2007/10/09 17:43:16  fwarmerdam
      38                 :  * Remove static variables that interfere with reentrancy. (GDAL #1883)
      39                 :  *
      40                 :  * Revision 1.17  2004/06/30 20:29:03  dmorissette
      41                 :  * Fixed refs to old address danmo@videotron.ca
      42                 :  *
      43                 :  * Revision 1.16  2001/05/01 12:32:03  daniel
      44                 :  * Get rid of leading spaces in WriteDateField().
      45                 :  *
      46                 :  * Revision 1.15  2000/04/27 15:42:03  daniel
      47                 :  * Map variable field length (width=0) coming from OGR to acceptable default
      48                 :  *
      49                 :  * Revision 1.14  2000/02/28 16:52:52  daniel
      50                 :  * Added support for writing indexes, removed validation on field name in
      51                 :  * NATIVE tables, and remove trailing spaces in DBF char field values
      52                 :  *
      53                 :  * Revision 1.13  2000/01/28 07:31:49  daniel
      54                 :  * Validate char field width (must be <= 254 chars)
      55                 :  *
      56                 :  * Revision 1.12  2000/01/16 19:08:48  daniel
      57                 :  * Added support for reading 'Table Type DBF' tables
      58                 :  *
      59                 :  * Revision 1.11  2000/01/15 22:30:43  daniel
      60                 :  * Switch to MIT/X-Consortium OpenSource license
      61                 :  *
      62                 :  * Revision 1.10  1999/12/20 18:59:20  daniel
      63                 :  * Dates again... now returned as "YYYYMMDD"
      64                 :  *
      65                 :  * Revision 1.9  1999/12/16 17:11:45  daniel
      66                 :  * Date fields: return as "YYYY/MM/DD", and accept 3 diff. formats as input
      67                 :  *
      68                 :  * Revision 1.8  1999/12/14 03:58:29  daniel
      69                 :  * Fixed date read/write (bytes were reversed)
      70                 :  *
      71                 :  * Revision 1.7  1999/11/09 07:34:35  daniel
      72                 :  * Return default values when deleted attribute records are encountered
      73                 :  *
      74                 :  * Revision 1.6  1999/10/19 06:09:25  daniel
      75                 :  * Removed obsolete GetFieldDef() method
      76                 :  *
      77                 :  * Revision 1.5  1999/10/01 03:56:28  daniel
      78                 :  * Avoid multiple InitWriteHeader() calls (caused a leak) and added a fix
      79                 :  * in WriteCharField() to prevent reading bytes past end of string buffer
      80                 :  *
      81                 :  * Revision 1.4  1999/10/01 02:02:36  warmerda
      82                 :  * Added assertions to try and track TABRawBinBlock leak.
      83                 :  *
      84                 :  * Revision 1.3  1999/09/26 14:59:36  daniel
      85                 :  * Implemented write support
      86                 :  *
      87                 :  * Revision 1.2  1999/09/20 18:43:20  daniel
      88                 :  * Use binary access to open file.
      89                 :  *
      90                 :  * Revision 1.1  1999/07/12 04:18:23  daniel
      91                 :  * Initial checkin
      92                 :  *
      93                 :  **********************************************************************/
      94                 : 
      95                 : #include "mitab.h"
      96                 : 
      97                 : /*=====================================================================
      98                 :  *                      class TABDATFile
      99                 :  *
     100                 :  * Note that the .DAT files are .DBF files with some exceptions:
     101                 :  *
     102                 :  * All fields in the DBF header are defined as 'C' type (strings),
     103                 :  * even for binary integers.  So we have to look in the associated .TAB
     104                 :  * file to find the real field definition.
     105                 :  *
     106                 :  * Even though binary integers are defined as 'C' type, they are stored
     107                 :  * in binary form inside a 4 bytes string field.
     108                 :  *====================================================================*/
     109                 : 
     110                 : 
     111                 : /**********************************************************************
     112                 :  *                   TABDATFile::TABDATFile()
     113                 :  *
     114                 :  * Constructor.
     115                 :  **********************************************************************/
     116               4 : TABDATFile::TABDATFile()
     117                 : {
     118               4 :     m_fp = NULL;
     119               4 :     m_pszFname = NULL;
     120               4 :     m_eTableType = TABTableNative;
     121                 : 
     122               4 :     m_poHeaderBlock = NULL;
     123               4 :     m_poRecordBlock = NULL;
     124               4 :     m_pasFieldDef = NULL;
     125                 : 
     126               4 :     m_numFields = -1;
     127               4 :     m_numRecords = -1;
     128               4 :     m_nFirstRecordPtr = 0;
     129               4 :     m_nBlockSize = 0;
     130               4 :     m_nRecordSize = -1;
     131               4 :     m_nCurRecordId = -1;
     132               4 :     m_bCurRecordDeletedFlag = FALSE;
     133               4 :     m_bWriteHeaderInitialized = FALSE;
     134               4 : }
     135                 : 
     136                 : /**********************************************************************
     137                 :  *                   TABDATFile::~TABDATFile()
     138                 :  *
     139                 :  * Destructor.
     140                 :  **********************************************************************/
     141               4 : TABDATFile::~TABDATFile()
     142                 : {
     143               4 :     Close();
     144               4 : }
     145                 : 
     146                 : /**********************************************************************
     147                 :  *                   TABDATFile::Open()
     148                 :  *
     149                 :  * Open a .DAT file, and initialize the structures to be ready to read
     150                 :  * records from it.
     151                 :  *
     152                 :  * We currently support NATIVE and DBF tables for reading, and only
     153                 :  * NATIVE tables for writing.
     154                 :  *
     155                 :  * Returns 0 on success, -1 on error.
     156                 :  **********************************************************************/
     157                 : int TABDATFile::Open(const char *pszFname, const char *pszAccess,
     158               4 :                      TABTableType eTableType /*=TABNativeTable*/)
     159                 : {
     160                 :     int i;
     161                 : 
     162               4 :     if (m_fp)
     163                 :     {
     164                 :         CPLError(CE_Failure, CPLE_FileIO,
     165               0 :                  "Open() failed: object already contains an open file");
     166               0 :         return -1;
     167                 :     }
     168                 : 
     169                 :     /*-----------------------------------------------------------------
     170                 :      * Validate access mode and make sure we use binary access.
     171                 :      *----------------------------------------------------------------*/
     172               6 :     if (EQUALN(pszAccess, "r", 1) && (eTableType==TABTableNative ||
     173                 :                                       eTableType==TABTableDBF)  )
     174                 :     {
     175               2 :         m_eAccessMode = TABRead;
     176               2 :         pszAccess = "rb";
     177                 :     }
     178               4 :     else if (EQUALN(pszAccess, "w", 1) && eTableType==TABTableNative)
     179                 :     {
     180               2 :         m_eAccessMode = TABWrite;
     181               2 :         pszAccess = "wb";
     182                 :     }
     183                 :     else
     184                 :     {
     185                 :         CPLError(CE_Failure, CPLE_FileIO,
     186               0 :                  "Open() failed: access mode \"%s\" not supported", pszAccess);
     187               0 :         return -1;
     188                 :     }
     189                 : 
     190                 :     /*-----------------------------------------------------------------
     191                 :      * Open file for reading
     192                 :      *----------------------------------------------------------------*/
     193               4 :     m_pszFname = CPLStrdup(pszFname);
     194               4 :     m_fp = VSIFOpen(m_pszFname, pszAccess);
     195               4 :     m_eTableType = eTableType;
     196                 : 
     197               4 :     if (m_fp == NULL)
     198                 :     {
     199                 :         CPLError(CE_Failure, CPLE_FileIO,
     200               0 :                  "Open() failed for %s", m_pszFname);
     201               0 :         CPLFree(m_pszFname);
     202               0 :         m_pszFname = NULL;
     203               0 :         return -1;
     204                 :     }
     205                 : 
     206               4 :     if (m_eAccessMode == TABRead)
     207                 :     {
     208                 :         /*------------------------------------------------------------
     209                 :          * READ ACCESS:
     210                 :          * Read .DAT file header (record size, num records, etc...)
     211                 :          * m_poHeaderBlock will be reused later to read field definition
     212                 :          *-----------------------------------------------------------*/
     213               2 :         m_poHeaderBlock = new TABRawBinBlock(m_eAccessMode, TRUE);
     214               2 :         m_poHeaderBlock->ReadFromFile(m_fp, 0, 32);
     215                 : 
     216               2 :         m_poHeaderBlock->ReadByte();       // Table type ??? 0x03
     217               2 :         m_poHeaderBlock->ReadByte();       // Last update year
     218               2 :         m_poHeaderBlock->ReadByte();       // Last update month
     219               2 :         m_poHeaderBlock->ReadByte();       // Last update day
     220                 : 
     221               2 :         m_numRecords      = m_poHeaderBlock->ReadInt32();
     222               2 :         m_nFirstRecordPtr = m_poHeaderBlock->ReadInt16();
     223               2 :         m_nRecordSize     = m_poHeaderBlock->ReadInt16();
     224                 : 
     225               2 :         m_numFields = m_nFirstRecordPtr/32 - 1;
     226                 : 
     227                 :         /*-------------------------------------------------------------
     228                 :          * Read the field definitions
     229                 :          * First 32 bytes field definition starts at byte 32 in file
     230                 :          *------------------------------------------------------------*/
     231                 :         m_pasFieldDef = (TABDATFieldDef*)CPLCalloc(m_numFields, 
     232               2 :                                                    sizeof(TABDATFieldDef));
     233                 : 
     234               6 :         for(i=0; i<m_numFields; i++)
     235                 :         {
     236               4 :             m_poHeaderBlock->GotoByteInFile((i+1)*32);
     237               4 :             m_poHeaderBlock->ReadBytes(11, (GByte*)m_pasFieldDef[i].szName);
     238               4 :             m_pasFieldDef[i].szName[10] = '\0';
     239               4 :             m_pasFieldDef[i].cType = (char)m_poHeaderBlock->ReadByte();
     240                 : 
     241               4 :             m_poHeaderBlock->ReadInt32();       // Skip Bytes 12-15
     242               4 :             m_pasFieldDef[i].byLength = m_poHeaderBlock->ReadByte();
     243               4 :             m_pasFieldDef[i].byDecimals = m_poHeaderBlock->ReadByte();
     244                 : 
     245               4 :             m_pasFieldDef[i].eTABType = TABFUnknown;
     246                 :         }
     247                 : 
     248                 :         /*-------------------------------------------------------------
     249                 :          * Establish a good record block size to use based on record size, and 
     250                 :          * then create m_poRecordBlock
     251                 :          * Record block size has to be a multiple of record size.
     252                 :          *------------------------------------------------------------*/
     253               2 :         m_nBlockSize = ((1024/m_nRecordSize)+1)*m_nRecordSize;
     254               2 :         m_nBlockSize = MIN(m_nBlockSize, (m_numRecords*m_nRecordSize));
     255                 : 
     256               2 :         CPLAssert( m_poRecordBlock == NULL );
     257               2 :         m_poRecordBlock = new TABRawBinBlock(m_eAccessMode, FALSE);
     258               2 :         m_poRecordBlock->InitNewBlock(m_fp, m_nBlockSize);
     259               2 :         m_poRecordBlock->SetFirstBlockPtr(m_nFirstRecordPtr);
     260                 :     }
     261                 :     else
     262                 :     {
     263                 :         /*------------------------------------------------------------
     264                 :          * WRITE ACCESS:
     265                 :          * Set acceptable defaults for all class members.
     266                 :          * The real header initialization will be done when the first
     267                 :          * record is written
     268                 :          *-----------------------------------------------------------*/
     269               2 :         m_poHeaderBlock = NULL;
     270                 : 
     271               2 :         m_numRecords      = 0;
     272               2 :         m_nFirstRecordPtr = 0;
     273               2 :         m_nRecordSize     = 0;
     274               2 :         m_numFields = 0;
     275               2 :         m_pasFieldDef = NULL;
     276               2 :         m_bWriteHeaderInitialized = FALSE;
     277                 :     }
     278                 : 
     279               4 :     return 0;
     280                 : }
     281                 : 
     282                 : /**********************************************************************
     283                 :  *                   TABDATFile::Close()
     284                 :  *
     285                 :  * Close current file, and release all memory used.
     286                 :  *
     287                 :  * Returns 0 on success, -1 on error.
     288                 :  **********************************************************************/
     289               8 : int TABDATFile::Close()
     290                 : {
     291               8 :     if (m_fp == NULL)
     292               4 :         return 0;
     293                 : 
     294                 :     /*----------------------------------------------------------------
     295                 :      * Write access: Update the header with number of records, etc.
     296                 :      * and add a CTRL-Z char at the end of the file.
     297                 :      *---------------------------------------------------------------*/
     298               4 :     if (m_eAccessMode == TABWrite)
     299                 :     {
     300               2 :         WriteHeader();
     301                 : 
     302               2 :         char cEOF = 26;
     303               2 :         if (VSIFSeek(m_fp, 0L, SEEK_END) == 0)
     304               2 :             VSIFWrite(&cEOF, 1, 1, m_fp);
     305                 :     }
     306                 :     
     307                 :     // Delete all structures 
     308               4 :     if (m_poHeaderBlock)
     309                 :     {
     310               4 :         delete m_poHeaderBlock;
     311               4 :         m_poHeaderBlock = NULL;
     312                 :     }
     313                 : 
     314               4 :     if (m_poRecordBlock)
     315                 :     {
     316               4 :         delete m_poRecordBlock;
     317               4 :         m_poRecordBlock = NULL;
     318                 :     }
     319                 : 
     320                 :     // Close file
     321               4 :     VSIFClose(m_fp);
     322               4 :     m_fp = NULL;
     323                 : 
     324               4 :     CPLFree(m_pszFname);
     325               4 :     m_pszFname = NULL;
     326                 : 
     327               4 :     CPLFree(m_pasFieldDef);
     328               4 :     m_pasFieldDef = NULL;
     329                 : 
     330               4 :     m_numFields = -1;
     331               4 :     m_numRecords = -1;
     332               4 :     m_nFirstRecordPtr = 0;
     333               4 :     m_nBlockSize = 0;
     334               4 :     m_nRecordSize = -1;
     335               4 :     m_nCurRecordId = -1;
     336               4 :     m_bWriteHeaderInitialized = FALSE;
     337                 : 
     338               4 :     return 0;
     339                 : }
     340                 : 
     341                 : 
     342                 : /**********************************************************************
     343                 :  *                   TABDATFile::InitWriteHeader()
     344                 :  *
     345                 :  * Init the header members to be ready to write the header and data records
     346                 :  * to a newly created data file.
     347                 :  *
     348                 :  * Returns 0 on success, -1 on error.
     349                 :  **********************************************************************/
     350               2 : int  TABDATFile::InitWriteHeader()
     351                 : {
     352                 :     int i;
     353                 : 
     354               2 :     if (m_eAccessMode != TABWrite || m_bWriteHeaderInitialized)
     355               0 :         return 0;
     356                 : 
     357                 :     /*------------------------------------------------------------
     358                 :      * Compute values for Record size, header size, etc.
     359                 :      *-----------------------------------------------------------*/
     360               2 :     m_nFirstRecordPtr = (m_numFields+1)*32 + 1;
     361                 : 
     362               2 :     m_nRecordSize = 1;
     363               6 :     for(i=0; i<m_numFields; i++)
     364                 :     {
     365               4 :         m_nRecordSize += m_pasFieldDef[i].byLength;
     366                 :     }
     367                 : 
     368                 :     /*-------------------------------------------------------------
     369                 :      * Create m_poRecordBlock the size of a data record.
     370                 :      *------------------------------------------------------------*/
     371               2 :     m_nBlockSize = m_nRecordSize;
     372                 : 
     373               2 :     CPLAssert( m_poRecordBlock == NULL );
     374               2 :     m_poRecordBlock = new TABRawBinBlock(m_eAccessMode, FALSE);
     375               2 :     m_poRecordBlock->InitNewBlock(m_fp, m_nBlockSize);
     376               2 :     m_poRecordBlock->SetFirstBlockPtr(m_nFirstRecordPtr);
     377                 : 
     378                 :     /*-------------------------------------------------------------
     379                 :      * Make sure this init. will be performed only once
     380                 :      *------------------------------------------------------------*/
     381               2 :     m_bWriteHeaderInitialized = TRUE;
     382                 : 
     383               2 :     return 0;
     384                 : }
     385                 : 
     386                 : /**********************************************************************
     387                 :  *                   TABDATFile::WriteHeader()
     388                 :  *
     389                 :  * Init the header members to be ready to write the header and data records
     390                 :  * to a newly created data file.
     391                 :  *
     392                 :  * Returns 0 on success, -1 on error.
     393                 :  **********************************************************************/
     394               4 : int  TABDATFile::WriteHeader()
     395                 : {
     396                 :     int i;
     397                 : 
     398               4 :     if (m_eAccessMode != TABWrite)
     399                 :     {
     400                 :         CPLError(CE_Failure, CPLE_NotSupported,
     401               0 :                  "WriteHeader() can be used only with Write access.");
     402               0 :         return -1;
     403                 :     }
     404                 : 
     405               4 :     if (!m_bWriteHeaderInitialized)
     406               2 :         InitWriteHeader();
     407                 : 
     408                 :     /*------------------------------------------------------------
     409                 :      * Create a single block that will be used to generate the whole header.
     410                 :      *-----------------------------------------------------------*/
     411               4 :     if (m_poHeaderBlock == NULL)
     412               2 :         m_poHeaderBlock = new TABRawBinBlock(m_eAccessMode, TRUE);
     413               4 :     m_poHeaderBlock->InitNewBlock(m_fp, m_nFirstRecordPtr, 0);
     414                 : 
     415                 :     /*------------------------------------------------------------
     416                 :      * First 32 bytes: main header block
     417                 :      *-----------------------------------------------------------*/
     418               4 :     m_poHeaderBlock->WriteByte(0x03);  // Table type ??? 0x03
     419                 : 
     420                 :     // __TODO__ Write the correct update date value
     421               4 :     m_poHeaderBlock->WriteByte(99);    // Last update year
     422               4 :     m_poHeaderBlock->WriteByte(9);     // Last update month
     423               4 :     m_poHeaderBlock->WriteByte(9);     // Last update day
     424                 : 
     425               4 :     m_poHeaderBlock->WriteInt32(m_numRecords);
     426               4 :     m_poHeaderBlock->WriteInt16(m_nFirstRecordPtr);
     427               4 :     m_poHeaderBlock->WriteInt16(m_nRecordSize);
     428                 : 
     429               4 :     m_poHeaderBlock->WriteZeros(20);    // Pad rest with zeros
     430                 : 
     431                 :     /*-------------------------------------------------------------
     432                 :      * Field definitions follow.  Each field def is 32 bytes.
     433                 :      *------------------------------------------------------------*/
     434              12 :     for(i=0; i<m_numFields; i++)
     435                 :     {
     436               8 :         m_poHeaderBlock->WriteBytes(11, (GByte*)m_pasFieldDef[i].szName);
     437               8 :         m_poHeaderBlock->WriteByte(m_pasFieldDef[i].cType);
     438                 : 
     439               8 :         m_poHeaderBlock->WriteInt32(0);       // Skip Bytes 12-15
     440                 : 
     441               8 :         m_poHeaderBlock->WriteByte(m_pasFieldDef[i].byLength);
     442               8 :         m_poHeaderBlock->WriteByte(m_pasFieldDef[i].byDecimals);
     443                 : 
     444               8 :         m_poHeaderBlock->WriteZeros(14);    // Pad rest with zeros
     445                 :     }
     446                 : 
     447                 :     /*-------------------------------------------------------------
     448                 :      * Header ends with a 0x0d character.
     449                 :      *------------------------------------------------------------*/
     450               4 :     m_poHeaderBlock->WriteByte(0x0d);
     451                 : 
     452                 :     /*-------------------------------------------------------------
     453                 :      * Write the block to the file and return.
     454                 :      *------------------------------------------------------------*/
     455               4 :     return m_poHeaderBlock->CommitToFile();
     456                 : }
     457                 : 
     458                 : 
     459                 : 
     460                 : /**********************************************************************
     461                 :  *                   TABDATFile::GetNumFields()
     462                 :  *
     463                 :  * Return the number of fields in this table.
     464                 :  *
     465                 :  * Returns a value >= 0 on success, -1 on error.
     466                 :  **********************************************************************/
     467              72 : int  TABDATFile::GetNumFields()
     468                 : {
     469              72 :     return m_numFields;
     470                 : }
     471                 : 
     472                 : /**********************************************************************
     473                 :  *                   TABDATFile::GetNumRecords()
     474                 :  *
     475                 :  * Return the number of records in this table.
     476                 :  *
     477                 :  * Returns a value >= 0 on success, -1 on error.
     478                 :  **********************************************************************/
     479               4 : int  TABDATFile::GetNumRecords()
     480                 : {
     481               4 :     return m_numRecords;
     482                 : }
     483                 : 
     484                 : /**********************************************************************
     485                 :  *                   TABDATFile::GetRecordBlock()
     486                 :  *
     487                 :  * Return a TABRawBinBlock reference positioned at the beginning of the
     488                 :  * specified record and ready to read (or write) field values from/to it.
     489                 :  * In read access, the returned block is guaranteed to contain at least one
     490                 :  * full record of data, and in write access, it is at least big enough to
     491                 :  * hold one full record.
     492                 :  * 
     493                 :  * Note that record ids are positive and start at 1.
     494                 :  *
     495                 :  * In Write access, CommitRecordToFile() MUST be called after the
     496                 :  * data items have been written to the record, otherwise the record 
     497                 :  * will never make it to the file.
     498                 :  *
     499                 :  * Returns a reference to the TABRawBinBlock on success or NULL on error.
     500                 :  * The returned pointer is a reference to a block object owned by this 
     501                 :  * TABDATFile object and should not be freed by the caller.
     502                 :  **********************************************************************/
     503             109 : TABRawBinBlock *TABDATFile::GetRecordBlock(int nRecordId)
     504                 : {
     505             109 :     m_bCurRecordDeletedFlag = FALSE;
     506                 : 
     507             109 :     if (m_eAccessMode == TABRead)
     508                 :     {
     509                 :         /*-------------------------------------------------------------
     510                 :          * READ ACCESS
     511                 :          *------------------------------------------------------------*/
     512                 :         int nFileOffset;
     513                 : 
     514              96 :         nFileOffset = m_nFirstRecordPtr+(nRecordId-1)*m_nRecordSize;
     515                 : 
     516                 :         /*-------------------------------------------------------------
     517                 :          * Move record block pointer to the right location
     518                 :          *------------------------------------------------------------*/
     519              96 :         if ( m_poRecordBlock == NULL || 
     520                 :              nRecordId < 1 || nRecordId > m_numRecords ||
     521                 :              m_poRecordBlock->GotoByteInFile(nFileOffset) != 0 )
     522                 :         {
     523                 :             CPLError(CE_Failure, CPLE_FileIO,
     524                 :                      "Failed reading .DAT record block for record #%d in %s",
     525               0 :                      nRecordId, m_pszFname);
     526               0 :             return NULL;
     527                 :         }
     528                 : 
     529                 :         /*-------------------------------------------------------------
     530                 :          * The first char of the record is a ' ' for an active record, or
     531                 :          * '*' for a deleted one.
     532                 :          * In the case of a deleted record, we simply return default
     533                 :          * values for each attribute... this is what MapInfo seems to do
     534                 :          * when it takes a .TAB with deleted records and exports it to .MIF
     535                 :          *------------------------------------------------------------*/
     536              96 :         if (m_poRecordBlock->ReadByte() != ' ')
     537                 :         {
     538               0 :             m_bCurRecordDeletedFlag = TRUE;
     539                 :         }
     540                 :     }
     541              13 :     else if (m_eAccessMode == TABWrite && nRecordId > 0)
     542                 :     {
     543                 :         /*-------------------------------------------------------------
     544                 :          * WRITE ACCESS
     545                 :          *------------------------------------------------------------*/
     546                 :         int nFileOffset;
     547                 : 
     548                 :         /*-------------------------------------------------------------
     549                 :          * Before writing the first record, we must generate the file 
     550                 :          * header.  We will also initialize class members such as record
     551                 :          * size, etc. and will create m_poRecordBlock.
     552                 :          *------------------------------------------------------------*/
     553              13 :         if (!m_bWriteHeaderInitialized)
     554                 :         {
     555               2 :             WriteHeader();
     556                 :         }
     557                 : 
     558              13 :         m_numRecords = MAX(nRecordId, m_numRecords);
     559                 : 
     560              13 :         nFileOffset = m_nFirstRecordPtr+(nRecordId-1)*m_nRecordSize;
     561                 : 
     562              13 :         m_poRecordBlock->InitNewBlock(m_fp, m_nRecordSize, nFileOffset);
     563                 : 
     564                 :         /*-------------------------------------------------------------
     565                 :          * The first char of the record is the active/deleted flag.
     566                 :          * Automatically set it to ' ' (active).
     567                 :          *------------------------------------------------------------*/
     568              13 :         m_poRecordBlock->WriteByte(' ');
     569                 : 
     570                 :     }
     571                 : 
     572             109 :     m_nCurRecordId = nRecordId;
     573                 : 
     574             109 :     return m_poRecordBlock;
     575                 : }
     576                 : 
     577                 : /**********************************************************************
     578                 :  *                   TABDATFile::CommitRecordToFile()
     579                 :  *
     580                 :  * Commit the data record previously initialized with GetRecordBlock()
     581                 :  * to the file.  This function must be called after writing the data
     582                 :  * values to a record otherwise the record will never make it to the
     583                 :  * file.
     584                 :  *
     585                 :  * Returns 0 on success, -1 on error.
     586                 :  **********************************************************************/
     587              13 : int  TABDATFile::CommitRecordToFile()
     588                 : {
     589              13 :     if (m_eAccessMode != TABWrite || m_poRecordBlock == NULL)
     590               0 :         return -1;
     591                 : 
     592              13 :     return m_poRecordBlock->CommitToFile();
     593                 : }
     594                 : 
     595                 : 
     596                 : /**********************************************************************
     597                 :  *                   TABDATFile::ValidateFieldInfoFromTAB()
     598                 :  *
     599                 :  * Check that the value read from the .TAB file by the caller are 
     600                 :  * consistent with what is found in the .DAT header.
     601                 :  *
     602                 :  * Note that field ids are positive and start at 0.
     603                 :  *
     604                 :  * We have to use this function when opening a file for reading since 
     605                 :  * the .DAT file does not contain the full field types information...
     606                 :  * a .DAT file is actually a .DBF file in which the .DBF types are
     607                 :  * handled in a special way... type 'C' fields are used to store binary 
     608                 :  * values for most MapInfo types.
     609                 :  *
     610                 :  * For TABTableDBF, we actually have no validation to do since all types
     611                 :  * are stored as strings internally, so we'll just convert from string.
     612                 :  *
     613                 :  * Returns a value >= 0 if OK, -1 on error.
     614                 :  **********************************************************************/
     615                 : int  TABDATFile::ValidateFieldInfoFromTAB(int iField, const char *pszName,
     616                 :                                           TABFieldType eType,
     617               4 :                                           int nWidth, int nPrecision)
     618                 : {
     619               4 :     int i = iField;  // Just to make things shorter
     620                 : 
     621               4 :     CPLAssert(m_pasFieldDef);
     622                 : 
     623               4 :     if (m_pasFieldDef == NULL || iField < 0 || iField >= m_numFields)
     624                 :     {
     625                 :         CPLError(CE_Failure, CPLE_FileIO,
     626                 :           "Invalid field %d (%s) in .TAB header. %s contains only %d fields.",
     627               0 :                  iField+1, pszName, m_pszFname, m_pasFieldDef? m_numFields:0);
     628               0 :         return -1;
     629                 :     }
     630                 : 
     631                 :     /*-----------------------------------------------------------------
     632                 :      * We used to check that the .TAB field name matched the .DAT
     633                 :      * name stored internally, but apparently some tools that rename table
     634                 :      * field names only update the .TAB file and not the .DAT, so we won't
     635                 :      * do that name validation any more... we'll just check the type.
     636                 :      *
     637                 :      * With TABTableNative, we have to validate the field sizes as well
     638                 :      * because .DAT files use char fields to store binary values.
     639                 :      * With TABTableDBF, no need to validate field type since all
     640                 :      * fields are stored as strings internally.
     641                 :      *----------------------------------------------------------------*/
     642               4 :     if ((m_eTableType == TABTableNative && 
     643                 :          ((eType == TABFChar && (m_pasFieldDef[i].cType != 'C' ||
     644                 :                                 m_pasFieldDef[i].byLength != nWidth )) ||
     645                 :           (eType == TABFDecimal && (m_pasFieldDef[i].cType != 'N' ||
     646                 :                                   m_pasFieldDef[i].byLength != nWidth||
     647                 :                                    m_pasFieldDef[i].byDecimals!=nPrecision)) ||
     648                 :           (eType == TABFInteger && (m_pasFieldDef[i].cType != 'C' ||
     649                 :                                    m_pasFieldDef[i].byLength != 4  )) ||
     650                 :           (eType == TABFSmallInt && (m_pasFieldDef[i].cType != 'C' ||
     651                 :                                     m_pasFieldDef[i].byLength != 2 )) ||
     652                 :           (eType == TABFFloat && (m_pasFieldDef[i].cType != 'C' ||
     653                 :                                  m_pasFieldDef[i].byLength != 8    )) ||
     654                 :           (eType == TABFDate && (m_pasFieldDef[i].cType != 'C' ||
     655                 :                                 m_pasFieldDef[i].byLength != 4     )) ||
     656                 :           (eType == TABFTime && (m_pasFieldDef[i].cType != 'C' ||
     657                 :                                  m_pasFieldDef[i].byLength != 4    )) ||
     658                 :           (eType == TABFDateTime && (m_pasFieldDef[i].cType != 'C' ||
     659                 :                                     m_pasFieldDef[i].byLength != 8 )) ||
     660                 :           (eType == TABFLogical && (m_pasFieldDef[i].cType != 'L' ||
     661                 :                                    m_pasFieldDef[i].byLength != 1  ))   ) ))
     662                 :     {
     663                 :         CPLError(CE_Failure, CPLE_FileIO,
     664                 :                  "Definition of field %d (%s) from .TAB file does not match "
     665                 :                  "what is found in %s (name=%s, type=%c, width=%d, prec=%d)",
     666                 :                  iField+1, pszName, m_pszFname,
     667                 :                  m_pasFieldDef[i].szName, m_pasFieldDef[i].cType, 
     668               0 :                  m_pasFieldDef[i].byLength, m_pasFieldDef[i].byDecimals);
     669               0 :         return -1;
     670                 :     }
     671                 : 
     672               4 :     m_pasFieldDef[i].eTABType = eType;
     673                 : 
     674               4 :     return 0;
     675                 : }
     676                 : 
     677                 : /**********************************************************************
     678                 :  *                   TABDATFile::AddField()
     679                 :  *
     680                 :  * Create a new field (column) in a newly created table.  This function
     681                 :  * must be called after the file has been opened, but before writing the
     682                 :  * first record.
     683                 :  *
     684                 :  * Returns the new field index (a value >= 0) if OK, -1 on error.
     685                 :  **********************************************************************/
     686                 : int  TABDATFile::AddField(const char *pszName, TABFieldType eType,
     687               4 :                           int nWidth, int nPrecision /*=0*/)
     688                 : {
     689               4 :     if (m_eAccessMode != TABWrite || m_bWriteHeaderInitialized ||
     690                 :         m_eTableType != TABTableNative)
     691                 :     {
     692                 :         CPLError(CE_Failure, CPLE_NotSupported,
     693                 :                  "Addition of new table fields is not supported after the "
     694               0 :                  "first data item has been written.");
     695               0 :         return -1;
     696                 :     }
     697                 : 
     698                 :     /*-----------------------------------------------------------------
     699                 :      * Validate field width... must be <= 254
     700                 :      *----------------------------------------------------------------*/
     701               4 :     if (nWidth > 254)
     702                 :     {
     703                 :         CPLError(CE_Failure, CPLE_IllegalArg,
     704                 :                  "Invalid size (%d) for field '%s'.  "
     705               0 :                  "Size must be 254 or less.", nWidth, pszName);
     706               0 :         return -1;
     707                 :     }
     708                 : 
     709                 :     /*-----------------------------------------------------------------
     710                 :      * Map fields with width=0 (variable length in OGR) to a valid default
     711                 :      *----------------------------------------------------------------*/
     712               4 :     if (eType == TABFDecimal && nWidth == 0)
     713               0 :         nWidth=20;
     714               4 :     else if (nWidth == 0)
     715               0 :         nWidth=254; /* char fields */
     716                 : 
     717               4 :     if (m_numFields < 0)
     718               0 :         m_numFields = 0;
     719                 : 
     720               4 :     m_numFields++;
     721                 :     m_pasFieldDef = (TABDATFieldDef*)CPLRealloc(m_pasFieldDef, 
     722               4 :                                           m_numFields*sizeof(TABDATFieldDef));
     723                 : 
     724               4 :     strncpy(m_pasFieldDef[m_numFields-1].szName, pszName, 10);
     725               4 :     m_pasFieldDef[m_numFields-1].szName[10] = '\0';
     726               4 :     m_pasFieldDef[m_numFields-1].eTABType = eType;
     727               4 :     m_pasFieldDef[m_numFields-1].byLength = (GByte)nWidth;
     728               4 :     m_pasFieldDef[m_numFields-1].byDecimals = (GByte)nPrecision;
     729                 : 
     730               4 :     switch(eType)
     731                 :     {
     732                 :       case TABFChar:
     733               2 :         m_pasFieldDef[m_numFields-1].cType = 'C';
     734               2 :         break;
     735                 :       case TABFDecimal:
     736               0 :         m_pasFieldDef[m_numFields-1].cType = 'N';
     737               0 :         break;
     738                 :       case TABFInteger:
     739               1 :         m_pasFieldDef[m_numFields-1].cType = 'C';
     740               1 :         m_pasFieldDef[m_numFields-1].byLength = 4;
     741               1 :         break;
     742                 :       case TABFSmallInt:
     743               0 :         m_pasFieldDef[m_numFields-1].cType = 'C';
     744               0 :         m_pasFieldDef[m_numFields-1].byLength = 2;
     745               0 :         break;
     746                 :       case TABFFloat:
     747               1 :         m_pasFieldDef[m_numFields-1].cType = 'C';
     748               1 :         m_pasFieldDef[m_numFields-1].byLength = 8;
     749               1 :         break;
     750                 :       case TABFDate:
     751               0 :         m_pasFieldDef[m_numFields-1].cType = 'C';
     752               0 :         m_pasFieldDef[m_numFields-1].byLength = 4;
     753               0 :         break;
     754                 :       case TABFTime:
     755               0 :         m_pasFieldDef[m_numFields-1].cType = 'C';
     756               0 :         m_pasFieldDef[m_numFields-1].byLength = 4;
     757               0 :         break;
     758                 :       case TABFDateTime:
     759               0 :         m_pasFieldDef[m_numFields-1].cType = 'C';
     760               0 :         m_pasFieldDef[m_numFields-1].byLength = 8;
     761               0 :         break;
     762                 :       case TABFLogical:
     763               0 :         m_pasFieldDef[m_numFields-1].cType = 'L';
     764               0 :         m_pasFieldDef[m_numFields-1].byLength = 1;
     765               0 :         break;
     766                 :       default:
     767                 :         CPLError(CE_Failure, CPLE_NotSupported,
     768               0 :                  "Unsupported field type for field `%s'", pszName);
     769               0 :         return -1;
     770                 :     }
     771                 : 
     772               4 :     return 0;
     773                 : }
     774                 : 
     775                 : /**********************************************************************
     776                 :  *                   TABDATFile::GetFieldType()
     777                 :  *
     778                 :  * Returns the native field type for field # nFieldId as previously set
     779                 :  * by ValidateFieldInfoFromTAB().
     780                 :  *
     781                 :  * Note that field ids are positive and start at 0.
     782                 :  **********************************************************************/
     783             190 : TABFieldType TABDATFile::GetFieldType(int nFieldId)
     784                 : {
     785             190 :     if (m_pasFieldDef == NULL || nFieldId < 0 || nFieldId >= m_numFields)
     786               0 :         return TABFUnknown;
     787                 : 
     788             190 :     return m_pasFieldDef[nFieldId].eTABType;
     789                 : }
     790                 : 
     791                 : /**********************************************************************
     792                 :  *                   TABDATFile::GetFieldWidth()
     793                 :  *
     794                 :  * Returns the width for field # nFieldId as previously read from the
     795                 :  * .DAT header.
     796                 :  *
     797                 :  * Note that field ids are positive and start at 0.
     798                 :  **********************************************************************/
     799             166 : int   TABDATFile::GetFieldWidth(int nFieldId)
     800                 : {
     801             166 :     if (m_pasFieldDef == NULL || nFieldId < 0 || nFieldId >= m_numFields)
     802               0 :         return 0;
     803                 : 
     804             166 :     return m_pasFieldDef[nFieldId].byLength;
     805                 : }
     806                 : 
     807                 : /**********************************************************************
     808                 :  *                   TABDATFile::GetFieldPrecision()
     809                 :  *
     810                 :  * Returns the precision for field # nFieldId as previously read from the
     811                 :  * .DAT header.
     812                 :  *
     813                 :  * Note that field ids are positive and start at 0.
     814                 :  **********************************************************************/
     815               0 : int   TABDATFile::GetFieldPrecision(int nFieldId)
     816                 : {
     817               0 :     if (m_pasFieldDef == NULL || nFieldId < 0 || nFieldId >= m_numFields)
     818               0 :         return 0;
     819                 : 
     820               0 :     return m_pasFieldDef[nFieldId].byDecimals;
     821                 : }
     822                 : 
     823                 : /**********************************************************************
     824                 :  *                   TABDATFile::ReadCharField()
     825                 :  *
     826                 :  * Read the character field value at the current position in the data 
     827                 :  * block.
     828                 :  * 
     829                 :  * Use GetRecordBlock() to position the data block to the beginning of
     830                 :  * a record before attempting to read values.
     831                 :  *
     832                 :  * nWidth is the field length, as defined in the .DAT header.
     833                 :  *
     834                 :  * Returns a reference to an internal buffer that will be valid only until
     835                 :  * the next field is read, or "" if the operation failed, in which case
     836                 :  * CPLError() will have been called.
     837                 :  **********************************************************************/
     838              53 : const char *TABDATFile::ReadCharField(int nWidth)
     839                 : {
     840                 :     // If current record has been deleted, then return an acceptable 
     841                 :     // default value.
     842              53 :     if (m_bCurRecordDeletedFlag)
     843               0 :         return "";
     844                 : 
     845              53 :     if (m_poRecordBlock == NULL)
     846                 :     {
     847                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
     848               0 :                  "Can't read field value: file is not opened.");
     849               0 :         return "";
     850                 :     }
     851                 : 
     852              53 :     if (nWidth < 1 || nWidth > 255)
     853                 :     {
     854                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
     855               0 :                  "Illegal width for a char field: %d", nWidth);
     856               0 :         return "";
     857                 :     }
     858                 : 
     859              53 :     if (m_poRecordBlock->ReadBytes(nWidth, (GByte*)m_szBuffer) != 0)
     860               0 :         return "";
     861                 : 
     862              53 :     m_szBuffer[nWidth] = '\0';
     863                 : 
     864                 :     // NATIVE tables are padded with '\0' chars, but DBF tables are padded
     865                 :     // with spaces... get rid of the trailing spaces.
     866              53 :     if (m_eTableType == TABTableDBF)
     867                 :     {
     868               0 :         int nLen = strlen(m_szBuffer)-1;
     869               0 :         while(nLen>=0 && m_szBuffer[nLen] == ' ')
     870               0 :             m_szBuffer[nLen--] = '\0';
     871                 :     }
     872                 : 
     873              53 :     return m_szBuffer;
     874                 : }
     875                 : 
     876                 : /**********************************************************************
     877                 :  *                   TABDATFile::ReadIntegerField()
     878                 :  *
     879                 :  * Read the integer field value at the current position in the data 
     880                 :  * block.
     881                 :  * 
     882                 :  * Note: nWidth is used only with TABTableDBF types.
     883                 :  *
     884                 :  * CPLError() will have been called if something fails.
     885                 :  **********************************************************************/
     886              50 : GInt32 TABDATFile::ReadIntegerField(int nWidth)
     887                 : {
     888                 :     // If current record has been deleted, then return an acceptable 
     889                 :     // default value.
     890              50 :     if (m_bCurRecordDeletedFlag)
     891               0 :         return 0;
     892                 : 
     893              50 :     if (m_poRecordBlock == NULL)
     894                 :     {
     895                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
     896               0 :                  "Can't read field value: file is not opened.");
     897               0 :         return 0;
     898                 :     }
     899                 : 
     900              50 :     if (m_eTableType == TABTableDBF)
     901               0 :         return atoi(ReadCharField(nWidth));
     902                 : 
     903              50 :     return m_poRecordBlock->ReadInt32();
     904                 : }
     905                 : 
     906                 : /**********************************************************************
     907                 :  *                   TABDATFile::ReadSmallIntField()
     908                 :  *
     909                 :  * Read the smallint field value at the current position in the data 
     910                 :  * block.
     911                 :  * 
     912                 :  * Note: nWidth is used only with TABTableDBF types.
     913                 :  *
     914                 :  * CPLError() will have been called if something fails.
     915                 :  **********************************************************************/
     916               0 : GInt16 TABDATFile::ReadSmallIntField(int nWidth)
     917                 : {
     918                 :     // If current record has been deleted, then return an acceptable 
     919                 :     // default value.
     920               0 :     if (m_bCurRecordDeletedFlag)
     921               0 :         return 0;
     922                 : 
     923               0 :     if (m_poRecordBlock == NULL)
     924                 :     {
     925                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
     926               0 :                  "Can't read field value: file is not opened.");
     927               0 :         return 0;
     928                 :     }
     929                 : 
     930               0 :     if (m_eTableType == TABTableDBF)
     931               0 :         return atoi(ReadCharField(nWidth));
     932                 : 
     933               0 :     return m_poRecordBlock->ReadInt16();
     934                 : }
     935                 : 
     936                 : /**********************************************************************
     937                 :  *                   TABDATFile::ReadFloatField()
     938                 :  *
     939                 :  * Read the float field value at the current position in the data 
     940                 :  * block.
     941                 :  * 
     942                 :  * Note: nWidth is used only with TABTableDBF types.
     943                 :  *
     944                 :  * CPLError() will have been called if something fails.
     945                 :  **********************************************************************/
     946              50 : double TABDATFile::ReadFloatField(int nWidth)
     947                 : {
     948                 :     // If current record has been deleted, then return an acceptable 
     949                 :     // default value.
     950              50 :     if (m_bCurRecordDeletedFlag)
     951               0 :         return 0.0;
     952                 : 
     953              50 :     if (m_poRecordBlock == NULL)
     954                 :     {
     955                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
     956               0 :                  "Can't read field value: file is not opened.");
     957               0 :         return 0.0;
     958                 :     }
     959                 : 
     960              50 :     if (m_eTableType == TABTableDBF)
     961               0 :         return atof(ReadCharField(nWidth));
     962                 : 
     963              50 :     return m_poRecordBlock->ReadDouble();
     964                 : }
     965                 : 
     966                 : /**********************************************************************
     967                 :  *                   TABDATFile::ReadLogicalField()
     968                 :  *
     969                 :  * Read the logical field value at the current position in the data 
     970                 :  * block.
     971                 :  *
     972                 :  * The file contains either 0 or 1, and we return a string with 
     973                 :  * "F" (false) or "T" (true)
     974                 :  * 
     975                 :  * Note: nWidth is used only with TABTableDBF types.
     976                 :  *
     977                 :  * CPLError() will have been called if something fails.
     978                 :  **********************************************************************/
     979               0 : const char *TABDATFile::ReadLogicalField(int nWidth)
     980                 : {
     981                 :     GByte bValue;
     982                 : 
     983                 :     // If current record has been deleted, then return an acceptable 
     984                 :     // default value.
     985               0 :     if (m_bCurRecordDeletedFlag)
     986               0 :         return "F";
     987                 : 
     988               0 :     if (m_poRecordBlock == NULL)
     989                 :     {
     990                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
     991               0 :                  "Can't read field value: file is not opened.");
     992               0 :         return "";
     993                 :     }
     994                 : 
     995               0 :     if (m_eTableType == TABTableDBF)
     996                 :     {
     997               0 :         const char *pszVal = ReadCharField(nWidth);
     998               0 :         bValue = (pszVal && strchr("1YyTt", pszVal[0]) != NULL);
     999                 :     }
    1000                 :     else
    1001                 :     {
    1002                 :         // In Native tables, we are guaranteed it is 1 byte with 0/1 value
    1003               0 :         bValue =  m_poRecordBlock->ReadByte();
    1004                 :     }
    1005                 : 
    1006               0 :     return bValue? "T":"F";
    1007                 : }
    1008                 : 
    1009                 : /**********************************************************************
    1010                 :  *                   TABDATFile::ReadDateField()
    1011                 :  *
    1012                 :  * Read the logical field value at the current position in the data 
    1013                 :  * block.
    1014                 :  *
    1015                 :  * A date field is a 4 bytes binary value in which the first byte is
    1016                 :  * the day, followed by 1 byte for the month, and 2 bytes for the year.
    1017                 :  *
    1018                 :  * We return an 8 chars string in the format "YYYYMMDD"
    1019                 :  * 
    1020                 :  * Note: nWidth is used only with TABTableDBF types.
    1021                 :  *
    1022                 :  * Returns a reference to an internal buffer that will be valid only until
    1023                 :  * the next field is read, or "" if the operation failed, in which case
    1024                 :  * CPLError() will have been called.
    1025                 :  **********************************************************************/
    1026               0 : const char *TABDATFile::ReadDateField(int nWidth)
    1027                 : {
    1028                 :     int nDay, nMonth, nYear;
    1029                 : 
    1030                 :     // If current record has been deleted, then return an acceptable 
    1031                 :     // default value.
    1032               0 :     if (m_bCurRecordDeletedFlag)
    1033               0 :         return "";
    1034                 : 
    1035               0 :     if (m_poRecordBlock == NULL)
    1036                 :     {
    1037                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1038               0 :                  "Can't read field value: file is not opened.");
    1039               0 :         return "";
    1040                 :     }
    1041                 : 
    1042                 :     // With .DBF files, there is nothing to do... the value should already
    1043                 :     // be stored in YYYYMMDD format according to DBF specs.
    1044               0 :     if (m_eTableType == TABTableDBF)
    1045               0 :         return ReadCharField(nWidth);
    1046                 : 
    1047                 : 
    1048               0 :     nYear  = m_poRecordBlock->ReadInt16();
    1049               0 :     nMonth = m_poRecordBlock->ReadByte();
    1050               0 :     nDay   = m_poRecordBlock->ReadByte();
    1051                 : 
    1052               0 :     if (CPLGetLastErrorNo() != 0 || (nYear==0 && nMonth==0 && nDay==0))
    1053               0 :         return "";
    1054                 : 
    1055               0 :     sprintf(m_szBuffer, "%4.4d%2.2d%2.2d", nYear, nMonth, nDay);
    1056                 : 
    1057               0 :     return m_szBuffer;
    1058                 : }
    1059                 : 
    1060                 : /**********************************************************************
    1061                 :  *                   TABDATFile::ReadTimeField()
    1062                 :  *
    1063                 :  * Read the Time field value at the current position in the data 
    1064                 :  * block.
    1065                 :  *
    1066                 :  * A time field is a 4 bytes binary value which represents the number
    1067                 :  * of milliseconds since midnight.
    1068                 :  *
    1069                 :  * We return a 9 char string in the format "HHMMSSMMM"
    1070                 :  * 
    1071                 :  * Note: nWidth is used only with TABTableDBF types.
    1072                 :  *
    1073                 :  * Returns a reference to an internal buffer that will be valid only until
    1074                 :  * the next field is read, or "" if the operation failed, in which case
    1075                 :  * CPLError() will have been called.
    1076                 :  **********************************************************************/
    1077               0 : const char *TABDATFile::ReadTimeField(int nWidth)
    1078                 : {
    1079                 :     GInt32 nS;
    1080                 :     static char szBuf[20];
    1081                 : 
    1082                 :     // If current record has been deleted, then return an acceptable 
    1083                 :     // default value.
    1084               0 :     if (m_bCurRecordDeletedFlag)
    1085               0 :         return "";
    1086                 : 
    1087               0 :     if (m_poRecordBlock == NULL)
    1088                 :     {
    1089                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1090               0 :                  "Can't read field value: file is not opened.");
    1091               0 :         return "";
    1092                 :     }
    1093                 : 
    1094                 :     // With .DBF files, there is nothing to do... the value should already
    1095                 :     // be stored in HHMMSSMMM format according to DBF specs.
    1096               0 :     if (m_eTableType == TABTableDBF)
    1097               0 :         return ReadCharField(nWidth);
    1098                 : 
    1099                 : 
    1100               0 :     nS  = m_poRecordBlock->ReadInt32(); // Convert time from ms to sec
    1101                 : 
    1102                 :     // nS is set to -1 when the value is 'not set'
    1103               0 :     if (CPLGetLastErrorNo() != 0 || nS < 0 || (nS>86400000))
    1104               0 :         return "";
    1105                 : 
    1106               0 :     int nHour = int(nS/3600000);
    1107               0 :     int nMin  = int((nS/1000 - nHour*3600)/60);
    1108               0 :     int nSec  = int(nS/1000 - nHour*3600 - nMin*60);
    1109               0 :     int nMS   = int(nS-nHour*3600000-nMin*60000-nSec*1000);
    1110               0 :     sprintf(szBuf, "%2.2d%2.2d%2.2d%3.3d", nHour, nMin, nSec, nMS);
    1111                 : 
    1112               0 :     return szBuf;
    1113                 : }
    1114                 : 
    1115                 : /**********************************************************************
    1116                 :  *                   TABDATFile::ReadDateTimeField()
    1117                 :  *
    1118                 :  * Read the DateTime field value at the current position in the data 
    1119                 :  * block.
    1120                 :  *
    1121                 :  * A datetime field is an 8 bytes binary value in which the first byte is
    1122                 :  * the day, followed by 1 byte for the month, and 2 bytes for the year. After
    1123                 :  * this is 4 bytes which represents the number of milliseconds since midnight.
    1124                 :  *
    1125                 :  * We return an 17 chars string in the format "YYYYMMDDhhmmssmmm"
    1126                 :  * 
    1127                 :  * Note: nWidth is used only with TABTableDBF types.
    1128                 :  *
    1129                 :  * Returns a reference to an internal buffer that will be valid only until
    1130                 :  * the next field is read, or "" if the operation failed, in which case
    1131                 :  * CPLError() will have been called.
    1132                 :  **********************************************************************/
    1133               0 : const char *TABDATFile::ReadDateTimeField(int nWidth)
    1134                 : {
    1135                 :     int nDay, nMonth, nYear;
    1136                 :     GInt32 nS;
    1137                 :     static char szBuf[20];
    1138                 : 
    1139                 :     // If current record has been deleted, then return an acceptable 
    1140                 :     // default value.
    1141               0 :     if (m_bCurRecordDeletedFlag)
    1142               0 :         return "";
    1143                 : 
    1144               0 :     if (m_poRecordBlock == NULL)
    1145                 :     {
    1146                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1147               0 :                  "Can't read field value: file is not opened.");
    1148               0 :         return "";
    1149                 :     }
    1150                 : 
    1151                 :     // With .DBF files, there is nothing to do... the value should already
    1152                 :     // be stored in YYYYMMDD format according to DBF specs.
    1153               0 :     if (m_eTableType == TABTableDBF)
    1154               0 :         return ReadCharField(nWidth);
    1155                 : 
    1156                 : 
    1157               0 :     nYear  = m_poRecordBlock->ReadInt16();
    1158               0 :     nMonth = m_poRecordBlock->ReadByte();
    1159               0 :     nDay   = m_poRecordBlock->ReadByte();
    1160               0 :     nS     = m_poRecordBlock->ReadInt32();
    1161                 : 
    1162               0 :     if (CPLGetLastErrorNo() != 0 || 
    1163                 :         (nYear==0 && nMonth==0 && nDay==0) || (nS>86400000))
    1164               0 :         return "";
    1165                 : 
    1166               0 :     int nHour = int(nS/3600000);
    1167               0 :     int nMin  = int((nS/1000 - nHour*3600)/60);
    1168               0 :     int nSec  = int(nS/1000 - nHour*3600 - nMin*60);
    1169               0 :     int nMS   = int(nS-nHour*3600000-nMin*60000-nSec*1000);
    1170                 : 
    1171                 :     sprintf(szBuf, "%4.4d%2.2d%2.2d%2.2d%2.2d%2.2d%3.3d", 
    1172               0 :             nYear, nMonth, nDay, nHour, nMin, nSec, nMS);
    1173                 : 
    1174               0 :     return szBuf;
    1175                 : }
    1176                 : 
    1177                 : /**********************************************************************
    1178                 :  *                   TABDATFile::ReadDecimalField()
    1179                 :  *
    1180                 :  * Read the decimal field value at the current position in the data 
    1181                 :  * block.
    1182                 :  *
    1183                 :  * A decimal field is a floating point value with a fixed number of digits
    1184                 :  * stored as a character string.
    1185                 :  *
    1186                 :  * nWidth is the field length, as defined in the .DAT header.
    1187                 :  *
    1188                 :  * We return the value as a binary double.
    1189                 :  * 
    1190                 :  * CPLError() will have been called if something fails.
    1191                 :  **********************************************************************/
    1192               0 : double TABDATFile::ReadDecimalField(int nWidth)
    1193                 : {
    1194                 :     const char *pszVal;
    1195                 : 
    1196                 :     // If current record has been deleted, then return an acceptable 
    1197                 :     // default value.
    1198               0 :     if (m_bCurRecordDeletedFlag)
    1199               0 :         return 0.0;
    1200                 : 
    1201               0 :     pszVal = ReadCharField(nWidth);
    1202                 : 
    1203               0 :     return atof(pszVal);
    1204                 : }
    1205                 : 
    1206                 : 
    1207                 : /**********************************************************************
    1208                 :  *                   TABDATFile::WriteCharField()
    1209                 :  *
    1210                 :  * Write the character field value at the current position in the data 
    1211                 :  * block.
    1212                 :  * 
    1213                 :  * Use GetRecordBlock() to position the data block to the beginning of
    1214                 :  * a record before attempting to write values.
    1215                 :  *
    1216                 :  * nWidth is the field length, as defined in the .DAT header.
    1217                 :  *
    1218                 :  * Returns 0 on success, or -1 if the operation failed, in which case
    1219                 :  * CPLError() will have been called.
    1220                 :  **********************************************************************/
    1221                 : int TABDATFile::WriteCharField(const char *pszStr, int nWidth,
    1222              13 :                                TABINDFile *poINDFile, int nIndexNo)
    1223                 : {
    1224              13 :     if (m_poRecordBlock == NULL)
    1225                 :     {
    1226                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1227               0 :             "Can't write field value: GetRecordBlock() has not been called.");
    1228               0 :         return -1;
    1229                 :     }
    1230                 : 
    1231              13 :     if (nWidth < 1 || nWidth > 255)
    1232                 :     {
    1233                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1234               0 :                  "Illegal width for a char field: %d", nWidth);
    1235               0 :         return -1;
    1236                 :     }
    1237                 :     
    1238                 :     //
    1239                 :     // Write the buffer after making sure that we don't try to read
    1240                 :     // past the end of the source buffer.  The rest of the field will
    1241                 :     // be padded with zeros if source string is shorter than specified
    1242                 :     // field width.
    1243                 :     //
    1244              13 :     int nLen = strlen(pszStr);
    1245              13 :     nLen = MIN(nLen, nWidth);
    1246                 : 
    1247              13 :     if ((nLen>0 && m_poRecordBlock->WriteBytes(nLen, (GByte*)pszStr) != 0) ||
    1248                 :         (nWidth-nLen > 0 && m_poRecordBlock->WriteZeros(nWidth-nLen)!=0) )
    1249               0 :         return -1;
    1250                 : 
    1251                 :     // Update Index
    1252              13 :     if (poINDFile && nIndexNo > 0)
    1253                 :     {
    1254               0 :         GByte *pKey = poINDFile->BuildKey(nIndexNo, pszStr);
    1255               0 :         if (poINDFile->AddEntry(nIndexNo, pKey, m_nCurRecordId) != 0)
    1256               0 :             return -1;
    1257                 :     }
    1258                 : 
    1259              13 :     return 0;
    1260                 : }
    1261                 : 
    1262                 : /**********************************************************************
    1263                 :  *                   TABDATFile::WriteIntegerField()
    1264                 :  *
    1265                 :  * Write the integer field value at the current position in the data 
    1266                 :  * block.
    1267                 :  * 
    1268                 :  * CPLError() will have been called if something fails.
    1269                 :  **********************************************************************/
    1270                 : int TABDATFile::WriteIntegerField(GInt32 nValue,
    1271              10 :                                   TABINDFile *poINDFile, int nIndexNo)
    1272                 : {
    1273              10 :     if (m_poRecordBlock == NULL)
    1274                 :     {
    1275                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1276               0 :             "Can't write field value: GetRecordBlock() has not been called.");
    1277               0 :         return -1;
    1278                 :     }
    1279                 : 
    1280                 :     // Update Index
    1281              10 :     if (poINDFile && nIndexNo > 0)
    1282                 :     {
    1283               0 :         GByte *pKey = poINDFile->BuildKey(nIndexNo, nValue);
    1284               0 :         if (poINDFile->AddEntry(nIndexNo, pKey, m_nCurRecordId) != 0)
    1285               0 :             return -1;
    1286                 :     }
    1287                 : 
    1288              10 :     return m_poRecordBlock->WriteInt32(nValue);
    1289                 : }
    1290                 : 
    1291                 : /**********************************************************************
    1292                 :  *                   TABDATFile::WriteSmallIntField()
    1293                 :  *
    1294                 :  * Write the smallint field value at the current position in the data 
    1295                 :  * block.
    1296                 :  * 
    1297                 :  * CPLError() will have been called if something fails.
    1298                 :  **********************************************************************/
    1299                 : int TABDATFile::WriteSmallIntField(GInt16 nValue,
    1300               0 :                                    TABINDFile *poINDFile, int nIndexNo)
    1301                 : {
    1302               0 :     if (m_poRecordBlock == NULL)
    1303                 :     {
    1304                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1305               0 :             "Can't write field value: GetRecordBlock() has not been called.");
    1306               0 :         return -1;
    1307                 :     }
    1308                 : 
    1309                 :     // Update Index
    1310               0 :     if (poINDFile && nIndexNo > 0)
    1311                 :     {
    1312               0 :         GByte *pKey = poINDFile->BuildKey(nIndexNo, nValue);
    1313               0 :         if (poINDFile->AddEntry(nIndexNo, pKey, m_nCurRecordId) != 0)
    1314               0 :             return -1;
    1315                 :     }
    1316                 : 
    1317               0 :     return m_poRecordBlock->WriteInt16(nValue);
    1318                 : }
    1319                 : 
    1320                 : /**********************************************************************
    1321                 :  *                   TABDATFile::WriteFloatField()
    1322                 :  *
    1323                 :  * Write the float field value at the current position in the data 
    1324                 :  * block.
    1325                 :  * 
    1326                 :  * CPLError() will have been called if something fails.
    1327                 :  **********************************************************************/
    1328                 : int TABDATFile::WriteFloatField(double dValue,
    1329              10 :                                 TABINDFile *poINDFile, int nIndexNo)
    1330                 : {
    1331              10 :     if (m_poRecordBlock == NULL)
    1332                 :     {
    1333                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1334               0 :             "Can't write field value: GetRecordBlock() has not been called.");
    1335               0 :         return -1;
    1336                 :     }
    1337                 : 
    1338                 :     // Update Index
    1339              10 :     if (poINDFile && nIndexNo > 0)
    1340                 :     {
    1341               0 :         GByte *pKey = poINDFile->BuildKey(nIndexNo, dValue);
    1342               0 :         if (poINDFile->AddEntry(nIndexNo, pKey, m_nCurRecordId) != 0)
    1343               0 :             return -1;
    1344                 :     }
    1345                 : 
    1346              10 :     return m_poRecordBlock->WriteDouble(dValue);
    1347                 : }
    1348                 : 
    1349                 : /**********************************************************************
    1350                 :  *                   TABDATFile::WriteLogicalField()
    1351                 :  *
    1352                 :  * Write the logical field value at the current position in the data 
    1353                 :  * block.
    1354                 :  *
    1355                 :  * The value written to the file is either 0 or 1, but this function
    1356                 :  * takes as input a string with "F" (false) or "T" (true)
    1357                 :  * 
    1358                 :  * CPLError() will have been called if something fails.
    1359                 :  **********************************************************************/
    1360                 : int TABDATFile::WriteLogicalField(const char *pszValue,
    1361               0 :                                   TABINDFile *poINDFile, int nIndexNo)
    1362                 : {
    1363                 :     GByte bValue;
    1364                 : 
    1365               0 :     if (m_poRecordBlock == NULL)
    1366                 :     {
    1367                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1368               0 :             "Can't write field value: GetRecordBlock() has not been called.");
    1369               0 :         return -1;
    1370                 :     }
    1371                 : 
    1372               0 :     if (EQUALN(pszValue, "T", 1))
    1373               0 :         bValue = 1;
    1374                 :     else
    1375               0 :         bValue = 0;
    1376                 : 
    1377                 :     // Update Index
    1378               0 :     if (poINDFile && nIndexNo > 0)
    1379                 :     {
    1380               0 :         GByte *pKey = poINDFile->BuildKey(nIndexNo, (int)bValue);
    1381               0 :         if (poINDFile->AddEntry(nIndexNo, pKey, m_nCurRecordId) != 0)
    1382               0 :             return -1;
    1383                 :     }
    1384                 : 
    1385               0 :     return m_poRecordBlock->WriteByte(bValue);
    1386                 : }
    1387                 : 
    1388                 : /**********************************************************************
    1389                 :  *                   TABDATFile::WriteDateField()
    1390                 :  *
    1391                 :  * Write the date field value at the current position in the data 
    1392                 :  * block.
    1393                 :  *
    1394                 :  * A date field is a 4 bytes binary value in which the first byte is
    1395                 :  * the day, followed by 1 byte for the month, and 2 bytes for the year.
    1396                 :  *
    1397                 :  * The expected input is a 10 chars string in the format "YYYY/MM/DD"
    1398                 :  * or "DD/MM/YYYY" or "YYYYMMDD"
    1399                 :  * 
    1400                 :  * Returns 0 on success, or -1 if the operation failed, in which case
    1401                 :  * CPLError() will have been called.
    1402                 :  **********************************************************************/
    1403                 : int TABDATFile::WriteDateField(const char *pszValue,
    1404               0 :                                TABINDFile *poINDFile, int nIndexNo)
    1405                 : {
    1406                 :     int nDay, nMonth, nYear;
    1407               0 :     char **papszTok = NULL;
    1408                 : 
    1409               0 :     if (m_poRecordBlock == NULL)
    1410                 :     {
    1411                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1412               0 :             "Can't write field value: GetRecordBlock() has not been called.");
    1413               0 :         return -1;
    1414                 :     }
    1415                 : 
    1416                 :     /*-----------------------------------------------------------------
    1417                 :      * Get rid of leading spaces.
    1418                 :      *----------------------------------------------------------------*/
    1419               0 :     while ( *pszValue == ' ' ) { pszValue++; }
    1420                 : 
    1421                 :     /*-----------------------------------------------------------------
    1422                 :      * Try to automagically detect date format, one of:
    1423                 :      * "YYYY/MM/DD", "DD/MM/YYYY", or "YYYYMMDD"
    1424                 :      *----------------------------------------------------------------*/
    1425                 :     
    1426               0 :     if (strlen(pszValue) == 8)
    1427                 :     {
    1428                 :         /*-------------------------------------------------------------
    1429                 :          * "YYYYMMDD"
    1430                 :          *------------------------------------------------------------*/
    1431                 :         char szBuf[9];
    1432               0 :         strcpy(szBuf, pszValue);
    1433               0 :         nDay = atoi(szBuf+6);
    1434               0 :         szBuf[6] = '\0';
    1435               0 :         nMonth = atoi(szBuf+4);
    1436               0 :         szBuf[4] = '\0';
    1437               0 :         nYear = atoi(szBuf);
    1438                 :     }
    1439               0 :     else if (strlen(pszValue) == 10 &&
    1440                 :              (papszTok = CSLTokenizeStringComplex(pszValue, "/", 
    1441                 :                                                   FALSE, FALSE)) != NULL &&
    1442                 :              CSLCount(papszTok) == 3 &&
    1443                 :              (strlen(papszTok[0]) == 4 || strlen(papszTok[2]) == 4) )
    1444                 :     {
    1445                 :         /*-------------------------------------------------------------
    1446                 :          * Either "YYYY/MM/DD" or "DD/MM/YYYY"
    1447                 :          *------------------------------------------------------------*/
    1448               0 :         if (strlen(papszTok[0]) == 4)
    1449                 :         {
    1450               0 :             nYear = atoi(papszTok[0]);
    1451               0 :             nMonth = atoi(papszTok[1]);
    1452               0 :             nDay = atoi(papszTok[2]);
    1453                 :         }
    1454                 :         else
    1455                 :         {
    1456               0 :             nYear = atoi(papszTok[2]);
    1457               0 :             nMonth = atoi(papszTok[1]);
    1458               0 :             nDay = atoi(papszTok[0]);
    1459                 :         }
    1460                 :     }
    1461               0 :     else if (strlen(pszValue) == 0)
    1462                 :     {
    1463               0 :         nYear = nMonth = nDay = 0;
    1464                 :     }
    1465                 :     else
    1466                 :     {
    1467                 :         CPLError(CE_Failure, CPLE_AppDefined,
    1468                 :                  "Invalid date field value `%s'.  Date field values must "
    1469                 :                  "be in the format `YYYY/MM/DD', `MM/DD/YYYY' or `YYYYMMDD'",
    1470               0 :                  pszValue);
    1471               0 :         CSLDestroy(papszTok);
    1472               0 :         return -1;
    1473                 :     }
    1474               0 :     CSLDestroy(papszTok);
    1475                 : 
    1476               0 :     m_poRecordBlock->WriteInt16(nYear);
    1477               0 :     m_poRecordBlock->WriteByte(nMonth);
    1478               0 :     m_poRecordBlock->WriteByte(nDay);
    1479                 : 
    1480               0 :     if (CPLGetLastErrorNo() != 0)
    1481               0 :         return -1;
    1482                 : 
    1483                 :     // Update Index
    1484               0 :     if (poINDFile && nIndexNo > 0)
    1485                 :     {
    1486                 :         GByte *pKey = poINDFile->BuildKey(nIndexNo, (nYear*0x10000 +
    1487               0 :                                                      nMonth * 0x100 + nDay));
    1488               0 :         if (poINDFile->AddEntry(nIndexNo, pKey, m_nCurRecordId) != 0)
    1489               0 :             return -1;
    1490                 :     }
    1491                 : 
    1492               0 :     return 0;
    1493                 : }
    1494                 : 
    1495                 : /**********************************************************************
    1496                 :  *                   TABDATFile::WriteTimeField()
    1497                 :  *
    1498                 :  * Write the date field value at the current position in the data 
    1499                 :  * block.
    1500                 :  *
    1501                 :  * A time field is a 4 byte binary value which represents the number
    1502                 :  * of milliseconds since midnight.
    1503                 :  *
    1504                 :  * The expected input is a 10 chars string in the format "HH:MM:SS"
    1505                 :  * or "HHMMSSmmm"
    1506                 :  * 
    1507                 :  * Returns 0 on success, or -1 if the operation failed, in which case
    1508                 :  * CPLError() will have been called.
    1509                 :  **********************************************************************/
    1510                 : int TABDATFile::WriteTimeField(const char *pszValue,
    1511               0 :                                TABINDFile *poINDFile, int nIndexNo)
    1512                 : {
    1513                 :     int nHour, nMin, nSec, nMS;
    1514               0 :     GInt32 nS = -1;
    1515               0 :     char **papszTok = NULL;
    1516                 : 
    1517               0 :     if (m_poRecordBlock == NULL)
    1518                 :     {
    1519                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1520               0 :             "Can't write field value: GetRecordBlock() has not been called.");
    1521               0 :         return -1;
    1522                 :     }
    1523                 : 
    1524                 :     /*-----------------------------------------------------------------
    1525                 :      * Get rid of leading spaces.
    1526                 :      *----------------------------------------------------------------*/
    1527               0 :     while ( *pszValue == ' ' ) { pszValue++; }
    1528                 : 
    1529                 :     /*-----------------------------------------------------------------
    1530                 :      * Try to automagically detect time format, one of:
    1531                 :      * "HH:MM:SS", or "HHMMSSmmm"
    1532                 :      *----------------------------------------------------------------*/
    1533                 :     
    1534               0 :     if (strlen(pszValue) == 8)
    1535                 :     {
    1536                 :         /*-------------------------------------------------------------
    1537                 :          * "HH:MM:SS"
    1538                 :          *------------------------------------------------------------*/
    1539                 :         char szBuf[9];
    1540               0 :         strcpy(szBuf, pszValue);
    1541               0 :         szBuf[2]=0;
    1542               0 :         szBuf[5]=0;
    1543               0 :         nHour = atoi(szBuf);
    1544               0 :         nMin  = atoi(szBuf+3);
    1545               0 :         nSec  = atoi(szBuf+6);
    1546               0 :         nMS   = 0;
    1547                 : 
    1548               0 :         nS = (nHour*3600+nMin*60+nSec)*1000+nMS;
    1549                 :     }
    1550               0 :     else if (strlen(pszValue) == 9)
    1551                 :     {
    1552                 :         /*-------------------------------------------------------------
    1553                 :          * "HHMMSSmmm"
    1554                 :          *------------------------------------------------------------*/
    1555                 :         char szBuf[4];
    1556               0 :         strncpy(szBuf,pszValue,2);
    1557               0 :         szBuf[2]=0;
    1558               0 :         nHour = atoi(szBuf);
    1559                 : 
    1560               0 :         strncpy(szBuf,pszValue+2,2);
    1561               0 :         szBuf[2]=0;
    1562               0 :         nMin = atoi(szBuf);
    1563                 : 
    1564               0 :         strncpy(szBuf,pszValue+4,2);
    1565               0 :         szBuf[2]=0;
    1566               0 :         nSec = atoi(szBuf);
    1567                 : 
    1568               0 :         strncpy(szBuf,pszValue+6,3);
    1569               0 :         szBuf[3]=0;
    1570               0 :         nMS = atoi(szBuf);
    1571                 : 
    1572               0 :         nS = (nHour*3600+nMin*60+nSec)*1000+nMS;
    1573                 :     }
    1574               0 :     else if (strlen(pszValue) == 0)
    1575                 :     {
    1576               0 :         nS = -1;  // Write -1 to .DAT file if value is not set
    1577                 :     }
    1578                 :     else
    1579                 :     {
    1580                 :         CPLError(CE_Failure, CPLE_AppDefined,
    1581                 :                  "Invalid time field value `%s'.  Time field values must "
    1582                 :                  "be in the format `HH:MM:SS', or `HHMMSSmmm'",
    1583               0 :                  pszValue);
    1584               0 :         CSLDestroy(papszTok);
    1585               0 :         return -1;
    1586                 :     }
    1587               0 :     CSLDestroy(papszTok);
    1588                 : 
    1589               0 :     m_poRecordBlock->WriteInt32(nS);
    1590                 : 
    1591               0 :     if (CPLGetLastErrorNo() != 0)
    1592               0 :         return -1;
    1593                 : 
    1594                 :     // Update Index
    1595               0 :     if (poINDFile && nIndexNo > 0)
    1596                 :     {
    1597               0 :         GByte *pKey = poINDFile->BuildKey(nIndexNo, (nS));
    1598               0 :         if (poINDFile->AddEntry(nIndexNo, pKey, m_nCurRecordId) != 0)
    1599               0 :             return -1;
    1600                 :     }
    1601                 : 
    1602               0 :     return 0;
    1603                 : }
    1604                 : 
    1605                 : /**********************************************************************
    1606                 :  *                   TABDATFile::WriteDateTimeField()
    1607                 :  *
    1608                 :  * Write the DateTime field value at the current position in the data 
    1609                 :  * block.
    1610                 :  *
    1611                 :  * A datetime field is a 8 bytes binary value in which the first byte is
    1612                 :  * the day, followed by 1 byte for the month, and 2 bytes for the year.
    1613                 :  * After this the time value is stored as a 4 byte integer 
    1614                 :  * (milliseconds since midnight)
    1615                 :  *
    1616                 :  * The expected input is a 10 chars string in the format "YYYY/MM/DD HH:MM:SS"
    1617                 :  * or "DD/MM/YYYY HH:MM:SS" or "YYYYMMDDhhmmssmmm"
    1618                 :  * 
    1619                 :  * Returns 0 on success, or -1 if the operation failed, in which case
    1620                 :  * CPLError() will have been called.
    1621                 :  **********************************************************************/
    1622                 : int TABDATFile::WriteDateTimeField(const char *pszValue,
    1623               0 :                                    TABINDFile *poINDFile, int nIndexNo)
    1624                 : {
    1625                 :     int nDay, nMonth, nYear, nHour, nMin, nSec, nMS;
    1626               0 :     char **papszTok = NULL;
    1627                 : 
    1628               0 :     if (m_poRecordBlock == NULL)
    1629                 :     {
    1630                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1631               0 :             "Can't write field value: GetRecordBlock() has not been called.");
    1632               0 :         return -1;
    1633                 :     }
    1634                 : 
    1635                 :     /*-----------------------------------------------------------------
    1636                 :      * Get rid of leading spaces.
    1637                 :      *----------------------------------------------------------------*/
    1638               0 :     while ( *pszValue == ' ' ) { pszValue++; }
    1639                 : 
    1640                 :     /*-----------------------------------------------------------------
    1641                 :      * Try to automagically detect date format, one of:
    1642                 :      * "YYYY/MM/DD HH:MM:SS", "DD/MM/YYYY HH:MM:SS", or "YYYYMMDDhhmmssmmm"
    1643                 :      *----------------------------------------------------------------*/
    1644                 :     
    1645               0 :     if (strlen(pszValue) == 17)
    1646                 :     {
    1647                 :         /*-------------------------------------------------------------
    1648                 :          * "YYYYMMDDhhmmssmmm"
    1649                 :          *------------------------------------------------------------*/
    1650                 :         char szBuf[18];
    1651               0 :         strcpy(szBuf, pszValue);
    1652               0 :         nMS  = atoi(szBuf+14);
    1653               0 :         szBuf[14]=0;
    1654               0 :         nSec = atoi(szBuf+12);
    1655               0 :         szBuf[12]=0;
    1656               0 :         nMin = atoi(szBuf+10);
    1657               0 :         szBuf[10]=0;
    1658               0 :         nHour = atoi(szBuf+8);
    1659               0 :         szBuf[8]=0;
    1660               0 :         nDay = atoi(szBuf+6);
    1661               0 :         szBuf[6] = 0;
    1662               0 :         nMonth = atoi(szBuf+4);
    1663               0 :         szBuf[4] = 0;
    1664               0 :         nYear = atoi(szBuf);
    1665                 :     }
    1666               0 :     else if (strlen(pszValue) == 19 &&
    1667                 :              (papszTok = CSLTokenizeStringComplex(pszValue, "/ :", 
    1668                 :                                                   FALSE, FALSE)) != NULL &&
    1669                 :              CSLCount(papszTok) == 6 &&
    1670                 :              (strlen(papszTok[0]) == 4 || strlen(papszTok[2]) == 4) )
    1671                 :     {
    1672                 :         /*-------------------------------------------------------------
    1673                 :          * Either "YYYY/MM/DD HH:MM:SS" or "DD/MM/YYYY HH:MM:SS"
    1674                 :          *------------------------------------------------------------*/
    1675               0 :         if (strlen(papszTok[0]) == 4)
    1676                 :         {
    1677               0 :             nYear = atoi(papszTok[0]);
    1678               0 :             nMonth= atoi(papszTok[1]);
    1679               0 :             nDay  = atoi(papszTok[2]);
    1680               0 :             nHour = atoi(papszTok[3]);
    1681               0 :             nMin  = atoi(papszTok[4]);
    1682               0 :             nSec  = atoi(papszTok[5]);
    1683               0 :             nMS   = 0;
    1684                 :         }
    1685                 :         else
    1686                 :         {
    1687               0 :             nYear = atoi(papszTok[2]);
    1688               0 :             nMonth= atoi(papszTok[1]);
    1689               0 :             nDay  = atoi(papszTok[0]);
    1690               0 :             nHour = atoi(papszTok[3]);
    1691               0 :             nMin  = atoi(papszTok[4]);
    1692               0 :             nSec  = atoi(papszTok[5]);
    1693               0 :             nMS   = 0;
    1694                 :         }
    1695                 :     }
    1696               0 :     else if (strlen(pszValue) == 0)
    1697                 :     {
    1698               0 :         nYear = nMonth = nDay = 0;
    1699               0 :         nHour = nMin = nSec = nMS = 0;
    1700                 :     }
    1701                 :     else
    1702                 :     {
    1703                 :         CPLError(CE_Failure, CPLE_AppDefined,
    1704                 :                  "Invalid date field value `%s'.  Date field values must "
    1705                 :                  "be in the format `YYYY/MM/DD HH:MM:SS', "
    1706                 :                  "`MM/DD/YYYY HH:MM:SS' or `YYYYMMDDhhmmssmmm'",
    1707               0 :                  pszValue);
    1708               0 :         CSLDestroy(papszTok);
    1709               0 :         return -1;
    1710                 :     }
    1711               0 :     CSLDestroy(papszTok);
    1712                 : 
    1713               0 :     GInt32 nS = (nHour*3600+nMin*60+nSec)*1000+nMS;
    1714                 : 
    1715               0 :     m_poRecordBlock->WriteInt16(nYear);
    1716               0 :     m_poRecordBlock->WriteByte(nMonth);
    1717               0 :     m_poRecordBlock->WriteByte(nDay);
    1718               0 :     m_poRecordBlock->WriteInt32(nS);
    1719                 : 
    1720               0 :     if (CPLGetLastErrorNo() != 0)
    1721               0 :         return -1;
    1722                 : 
    1723                 :     // Update Index
    1724               0 :     if (poINDFile && nIndexNo > 0)
    1725                 :     {
    1726                 :         // __TODO__  (see bug #1844)
    1727                 :         // Indexing on DateTime Fields not currently supported, that will 
    1728                 :         // require passing the 8 bytes datetime value to BuildKey() here...
    1729               0 :         CPLAssert(FALSE);
    1730                 :         GByte *pKey = poINDFile->BuildKey(nIndexNo, (nYear*0x10000 +
    1731               0 :                                                      nMonth * 0x100 + nDay));
    1732               0 :         if (poINDFile->AddEntry(nIndexNo, pKey, m_nCurRecordId) != 0)
    1733               0 :             return -1;
    1734                 :     }
    1735                 : 
    1736               0 :     return 0;
    1737                 : }
    1738                 : 
    1739                 : /**********************************************************************
    1740                 :  *                   TABDATFile::WriteDecimalField()
    1741                 :  *
    1742                 :  * Write the decimal field value at the current position in the data 
    1743                 :  * block.
    1744                 :  *
    1745                 :  * A decimal field is a floating point value with a fixed number of digits
    1746                 :  * stored as a character string.
    1747                 :  *
    1748                 :  * nWidth is the field length, as defined in the .DAT header.
    1749                 :  *
    1750                 :  * CPLError() will have been called if something fails.
    1751                 :  **********************************************************************/
    1752                 : int TABDATFile::WriteDecimalField(double dValue, int nWidth, int nPrec,
    1753               0 :                                   TABINDFile *poINDFile, int nIndexNo)
    1754                 : {
    1755                 :     const char *pszVal;
    1756                 : 
    1757               0 :     pszVal = CPLSPrintf("%*.*f", nWidth, nPrec, dValue);
    1758               0 :     if ((int)strlen(pszVal) > nWidth)
    1759               0 :         pszVal += strlen(pszVal) - nWidth;
    1760                 : 
    1761                 :     // Update Index
    1762               0 :     if (poINDFile && nIndexNo > 0)
    1763                 :     {
    1764               0 :         GByte *pKey = poINDFile->BuildKey(nIndexNo, dValue);
    1765               0 :         if (poINDFile->AddEntry(nIndexNo, pKey, m_nCurRecordId) != 0)
    1766               0 :             return -1;
    1767                 :     }
    1768                 : 
    1769               0 :     return m_poRecordBlock->WriteBytes(nWidth, (GByte*)pszVal);
    1770                 : }
    1771                 : 
    1772                 : 
    1773                 : 
    1774                 : /**********************************************************************
    1775                 :  *                   TABDATFile::Dump()
    1776                 :  *
    1777                 :  * Dump block contents... available only in DEBUG mode.
    1778                 :  **********************************************************************/
    1779                 : #ifdef DEBUG
    1780                 : 
    1781               0 : void TABDATFile::Dump(FILE *fpOut /*=NULL*/)
    1782                 : {
    1783               0 :     if (fpOut == NULL)
    1784               0 :         fpOut = stdout;
    1785                 : 
    1786               0 :     fprintf(fpOut, "----- TABDATFile::Dump() -----\n");
    1787                 : 
    1788               0 :     if (m_fp == NULL)
    1789                 :     {
    1790               0 :         fprintf(fpOut, "File is not opened.\n");
    1791                 :     }
    1792                 :     else
    1793                 :     {
    1794               0 :         fprintf(fpOut, "File is opened: %s\n", m_pszFname);
    1795               0 :         fprintf(fpOut, "m_numFields  = %d\n", m_numFields);
    1796               0 :         fprintf(fpOut, "m_numRecords = %d\n", m_numRecords);
    1797                 :     }
    1798                 : 
    1799               0 :     fflush(fpOut);
    1800               0 : }
    1801                 : 
    1802                 : #endif // DEBUG
    1803                 : 
    1804                 : 
    1805                 : 
    1806                 : 
    1807                 : 

Generated by: LTP GCOV extension version 1.5