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 :
|