LCOV - code coverage report
Current view: directory - ogr/ogrsf_frmts/mitab - mitab_datfile.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 614 242 39.4 %
Date: 2012-04-28 Functions: 42 22 52.4 %

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

Generated by: LCOV version 1.7