1 /**********************************************************
2 * Version $Id$
3 *********************************************************/
4
5 ///////////////////////////////////////////////////////////
6 // //
7 // SAGA //
8 // //
9 // System for Automated Geoscientific Analyses //
10 // //
11 // Application Programming Interface //
12 // //
13 // Library: SAGA_API //
14 // //
15 //-------------------------------------------------------//
16 // //
17 // table_dbase.cpp //
18 // //
19 // Copyright (C) 2005 by Olaf Conrad //
20 // //
21 //-------------------------------------------------------//
22 // //
23 // This file is part of 'SAGA - System for Automated //
24 // Geoscientific Analyses'. //
25 // //
26 // This library is free software; you can redistribute //
27 // it and/or modify it under the terms of the GNU Lesser //
28 // General Public License as published by the Free //
29 // Software Foundation, either version 2.1 of the //
30 // License, or (at your option) any later version. //
31 // //
32 // This library is distributed in the hope that it will //
33 // be useful, but WITHOUT ANY WARRANTY; without even the //
34 // implied warranty of MERCHANTABILITY or FITNESS FOR A //
35 // PARTICULAR PURPOSE. See the GNU Lesser General Public //
36 // License for more details. //
37 // //
38 // You should have received a copy of the GNU Lesser //
39 // General Public License along with this program; if //
40 // not, see <http://www.gnu.org/licenses/>. //
41 // //
42 //-------------------------------------------------------//
43 // //
44 // contact: Olaf Conrad //
45 // Institute of Geography //
46 // University of Goettingen //
47 // Goldschmidtstr. 5 //
48 // 37077 Goettingen //
49 // Germany //
50 // //
51 // e-mail: oconrad@saga-gis.org //
52 // //
53 ///////////////////////////////////////////////////////////
54
55 //---------------------------------------------------------
56
57
58 ///////////////////////////////////////////////////////////
59 // //
60 // //
61 // //
62 ///////////////////////////////////////////////////////////
63
64 //---------------------------------------------------------
65 #include <stdlib.h>
66 #include <string.h>
67 #include <time.h>
68
69 #include "api_core.h"
70 #include "table_dbase.h"
71 #include "table.h"
72 #include "datetime.h"
73
74
75 ///////////////////////////////////////////////////////////
76 // //
77 // //
78 // //
79 ///////////////////////////////////////////////////////////
80
81 //---------------------------------------------------------
CSG_Table_DBase(int Encoding)82 CSG_Table_DBase::CSG_Table_DBase(int Encoding)
83 {
84 m_hFile = NULL;
85 m_Record = NULL;
86 m_Fields = NULL;
87 m_nFields = 0;
88 m_Encoding = Encoding;
89 }
90
91 //---------------------------------------------------------
~CSG_Table_DBase(void)92 CSG_Table_DBase::~CSG_Table_DBase(void)
93 {
94 Close();
95 }
96
97 //---------------------------------------------------------
Close(void)98 void CSG_Table_DBase::Close(void)
99 {
100 if( m_hFile )
101 {
102 Flush_Record();
103 Header_Write();
104
105 fclose(m_hFile);
106 m_hFile = NULL;
107 }
108
109 SG_FREE_SAFE(m_Record);
110 SG_FREE_SAFE(m_Fields);
111
112 m_nFields = 0;
113 m_nRecords = 0;
114 m_nHeaderBytes = 0;
115 m_nRecordBytes = 0;
116 m_nFileBytes = 0;
117
118 m_bModified = false;
119 }
120
121
122 ///////////////////////////////////////////////////////////
123 // //
124 ///////////////////////////////////////////////////////////
125
126 //---------------------------------------------------------
Open_Read(const SG_Char * FileName,CSG_Table * pTable,bool bRecords_Load)127 bool CSG_Table_DBase::Open_Read(const SG_Char *FileName, CSG_Table *pTable, bool bRecords_Load)
128 {
129 Close();
130
131 if( (m_hFile = fopen(CSG_String(FileName), "rb")) == NULL )
132 {
133 SG_UI_Msg_Add_Error(_TL("dbf read: could not open file"));
134
135 return( false );
136 }
137
138 m_bReadOnly = true;
139
140 if( !Header_Read() )
141 {
142 SG_UI_Msg_Add_Error(_TL("dbf read: could not read header"));
143
144 Close();
145
146 return( false );
147 }
148
149 fseek(m_hFile, 0, SEEK_END);
150 m_nFileBytes = ftell(m_hFile);
151 fseek(m_hFile, 0, SEEK_SET);
152
153 //-----------------------------------------------------
154 if( pTable )
155 {
156 int iField;
157
158 pTable->Destroy();
159
160 for(iField=0; iField<Get_Field_Count(); iField++)
161 {
162 switch( Get_Field_Type(iField) )
163 {
164 case DBF_FT_LOGICAL:
165 pTable->Add_Field(Get_Field_Name(iField), SG_DATATYPE_Char);
166 break;
167
168 case DBF_FT_CHARACTER: default:
169 pTable->Add_Field(Get_Field_Name(iField), SG_DATATYPE_String);
170 break;
171
172 case DBF_FT_DATE:
173 pTable->Add_Field(Get_Field_Name(iField), SG_DATATYPE_Date);
174 break;
175
176 case DBF_FT_FLOAT:
177 pTable->Add_Field(Get_Field_Name(iField), SG_DATATYPE_Double);
178 break;
179
180 case DBF_FT_NUMERIC:
181 pTable->Add_Field(Get_Field_Name(iField), Get_Field_Decimals(iField) > 0
182 ? SG_DATATYPE_Double
183 : SG_DATATYPE_Long
184 );
185 }
186 }
187
188 //-------------------------------------------------
189 if( bRecords_Load && Get_Record_Count() > 0 && Move_First() )
190 {
191 for(int iRecord=0; iRecord<Get_Record_Count() && SG_UI_Process_Set_Progress(iRecord, Get_Record_Count()); iRecord++)
192 {
193 CSG_Table_Record *pRecord = pTable->Add_Record();
194
195 for(iField=0; iField<Get_Field_Count(); iField++)
196 {
197 switch( Get_Field_Type(iField) )
198 {
199 default:
200 pRecord->Set_Value(iField, asString(iField));
201 break;
202
203 case DBF_FT_FLOAT:
204 case DBF_FT_NUMERIC:
205 {
206 double Value;
207
208 if( asDouble(iField, Value) )
209 pRecord->Set_Value(iField, Value);
210 else
211 pRecord->Set_NoData(iField);
212 }
213 break;
214 }
215 }
216
217 Move_Next();
218 }
219
220 SG_UI_Process_Set_Ready();
221 }
222 }
223
224 return( true );
225 }
226
227 //---------------------------------------------------------
Header_Read(void)228 bool CSG_Table_DBase::Header_Read(void)
229 {
230 if( !m_hFile )
231 {
232 return( false );
233 }
234
235 //-----------------------------------------------------
236 char buf[16];
237 TDBF_Header h;
238
239 fseek(m_hFile, 0, SEEK_SET);
240
241 //-----------------------------------------------------
242 // Bytes 0-31: File Header...
243 fread(&h.FileType , sizeof(char), 1, m_hFile); // 00 FoxBase+, FoxPro, dBaseIII+, dBaseIV, no memo - 0x03
244 // FoxBase+, dBaseIII+ with memo - 0x83
245 // FoxPro with memo - 0xF5
246 // dBaseIV with memo - 0x8B
247 // dBaseIV with SQL Table - 0x8E
248 fread(&h.LastUpdate , sizeof(char), 3, m_hFile); // 01-03 Last update, format YYYYMMDD **correction: it is YYMMDD**
249 fread(&m_nRecords , sizeof(char), 4, m_hFile); // 04-07 Number of records in file (32-bit number)
250 fread(&m_nHeaderBytes , sizeof(char), 2, m_hFile); // 08-09 Number of bytes in header (16-bit number)
251 fread(&m_nRecordBytes , sizeof(char), 2, m_hFile); // 10-11 Number of bytes in record (16-bit number)
252 fread( buf , sizeof(char), 2, m_hFile); // 12-13 Reserved, fill with 0x00
253 fread(&h.Transaction , sizeof(char), 1, m_hFile); // 14 dBaseIV flag, incomplete transaction
254 // Begin Transaction sets it to - 0x01
255 // End Transaction or RollBack reset it to - 0x00
256 fread(&h.bEncrypted , sizeof(char), 1, m_hFile); // 15 Encryption flag, encrypted 0x01 else 0x00
257 // Changing the flag does not encrypt or decrypt the records
258 fread( buf , sizeof(char), 12, m_hFile); // 16-27 dBaseIV multi-user environment use
259 fread(&h.ProductionIdx , sizeof(char), 1, m_hFile); // 28 Production index exists - 0x01 else 0x00
260 fread(&h.LanguageDrvID , sizeof(char), 1, m_hFile); // 29 dBaseIV language driver ID
261 fread( buf , sizeof(char), 2, m_hFile); // 30-31 Reserved fill with 0x00
262
263 //-----------------------------------------------------
264 // Bytes 32-n: Field Descriptor Array...
265 while( ftell(m_hFile) < (long)m_nHeaderBytes - 1 && !feof(m_hFile) )
266 {
267 m_Fields = (TDBF_Field *)SG_Realloc(m_Fields, (m_nFields + 1) * sizeof(TDBF_Field));
268
269 fread( m_Fields[m_nFields].Name , sizeof(char), 11, m_hFile); // 0-10 Field Name ASCII padded with 0x00
270 fread(&m_Fields[m_nFields].Type , sizeof(char), 1, m_hFile); // 11 Field Type Identifier (see table)
271 fread(&m_Fields[m_nFields].Displacement , sizeof(char), 4, m_hFile); // 12-15 Displacement of field in record
272 fread(&m_Fields[m_nFields].Width , sizeof(char), 1, m_hFile); // 16 Field length in bytes
273 fread(&m_Fields[m_nFields].Decimals , sizeof(char), 1, m_hFile); // 17 Field decimal places
274 fread( buf , sizeof(char), 2, m_hFile); // 18-19 Reserved
275 fread(&m_Fields[m_nFields].WorkAreaID , sizeof(char), 1, m_hFile); // 20 dBaseIV work area ID
276 fread( buf , sizeof(char), 10, m_hFile); // 21-30 Reserved
277 fread(&m_Fields[m_nFields].ProductionIdx , sizeof(char), 1, m_hFile); // 31 Field is part of production index - 0x01 else 0x00
278
279 m_Fields[m_nFields].Name[11] = '\0';
280
281 m_nFields++;
282 }
283
284 //-----------------------------------------------------
285 // Byte n+1: Header m_Record Terminator (0x0D)...
286 fread( buf , sizeof(char), 1, m_hFile);
287
288 if( buf[0] == 0x0d )
289 {
290 Init_Record();
291 Move_First();
292
293 return( true );
294 }
295
296 Close();
297
298 return( false );
299 }
300
301
302 ///////////////////////////////////////////////////////////
303 // //
304 ///////////////////////////////////////////////////////////
305
306 //---------------------------------------------------------
Open_Write(const SG_Char * FileName,CSG_Table * pTable,bool bRecords_Save)307 bool CSG_Table_DBase::Open_Write(const SG_Char *FileName, CSG_Table *pTable, bool bRecords_Save)
308 {
309 Close();
310
311 if( pTable == NULL || pTable->Get_Field_Count() <= 0 )
312 {
313 SG_UI_Msg_Add_Error(_TL("dbf write: invalid table"));
314
315 return( false );
316 }
317
318 if( (m_hFile = fopen(CSG_String(FileName), "w+b")) == NULL )
319 {
320 SG_UI_Msg_Add_Error(_TL("dbf write: could open file"));
321
322 return( false );
323 }
324
325 m_bReadOnly = false;
326
327 //-----------------------------------------------------
328 int iField, nBytes;
329
330 m_nFields = pTable->Get_Field_Count();
331 m_Fields = (TDBF_Field *)SG_Calloc(m_nFields, sizeof(TDBF_Field)); // init all bytes with 0x00
332
333 for(iField=0; iField<Get_Field_Count(); iField++)
334 {
335 CSG_String Name(pTable->Get_Field_Name(iField));
336
337 for(int j=0; j<11 && j<(int)Name.Length(); j++)
338 {
339 m_Fields[iField].Name[j] = Name.b_str()[j];
340 }
341
342 switch( pTable->Get_Field_Type(iField) )
343 {
344 case SG_DATATYPE_String: default:
345 m_Fields[iField].Type = DBF_FT_CHARACTER;
346 m_Fields[iField].Width = (BYTE)((nBytes = pTable->Get_Field_Length(iField, m_Encoding)) > 255 ? 255 : nBytes < 1 ? 1 : nBytes);
347 break;
348
349 case SG_DATATYPE_Date:
350 m_Fields[iField].Type = DBF_FT_DATE;
351 m_Fields[iField].Width = (BYTE)8;
352 break;
353
354 case SG_DATATYPE_Char:
355 m_Fields[iField].Type = DBF_FT_CHARACTER;
356 m_Fields[iField].Width = (BYTE)1;
357 break;
358
359 case SG_DATATYPE_Bit:
360 m_Fields[iField].Type = DBF_FT_NUMERIC;
361 m_Fields[iField].Width = (BYTE)1;
362 break;
363
364 case SG_DATATYPE_Byte:
365 m_Fields[iField].Type = DBF_FT_NUMERIC;
366 m_Fields[iField].Width = (BYTE)3;
367 break;
368
369 case SG_DATATYPE_Word:
370 case SG_DATATYPE_Short:
371 m_Fields[iField].Type = DBF_FT_NUMERIC;
372 m_Fields[iField].Width = (BYTE)6;
373 break;
374
375 case SG_DATATYPE_DWord:
376 case SG_DATATYPE_Int:
377 case SG_DATATYPE_ULong:
378 case SG_DATATYPE_Long:
379 case SG_DATATYPE_Color:
380 m_Fields[iField].Type = DBF_FT_NUMERIC;
381 m_Fields[iField].Width = (BYTE)16;
382 break;
383
384 case SG_DATATYPE_Float:
385 m_Fields[iField].Type = DBF_FT_NUMERIC;
386 m_Fields[iField].Width = (BYTE)16;
387 m_Fields[iField].Decimals = (BYTE)8;
388 break;
389
390 case SG_DATATYPE_Double:
391 m_Fields[iField].Type = DBF_FT_FLOAT;
392 m_Fields[iField].Width = (BYTE)19;
393 m_Fields[iField].Decimals = (BYTE)10;
394 break;
395 }
396 }
397
398 Header_Write();
399
400 m_nFileBytes = m_nHeaderBytes;
401
402 //-----------------------------------------------------
403 if( bRecords_Save )
404 {
405 for(int iRecord=0; iRecord<pTable->Get_Record_Count() && SG_UI_Process_Set_Progress(iRecord, pTable->Get_Record_Count()); iRecord++)
406 {
407 CSG_Table_Record *pRecord = pTable->Get_Record(iRecord);
408
409 Add_Record();
410
411 for(iField=0; iField<Get_Field_Count(); iField++)
412 {
413 if( pRecord->is_NoData(iField) )
414 {
415 Set_NoData(iField);
416 }
417 else switch( Get_Field_Type(iField) )
418 {
419 default:
420 Set_Value(iField, pRecord->asString(iField));
421 break;
422
423 case DBF_FT_FLOAT:
424 case DBF_FT_NUMERIC:
425 Set_Value(iField, pRecord->asDouble(iField));
426 break;
427 }
428 }
429
430 Flush_Record();
431 }
432
433 SG_UI_Process_Set_Ready();
434 }
435
436 return( true );
437 }
438
439 //---------------------------------------------------------
Header_Write(void)440 void CSG_Table_DBase::Header_Write(void)
441 {
442 if( !m_hFile || m_bReadOnly )
443 {
444 return;
445 }
446
447 //-----------------------------------------------------
448 char buf[16];
449 int iField;
450 time_t ltime;
451 TDBF_Header h;
452
453 time(<ime);
454 struct tm *pTime = localtime(<ime);
455
456 h.FileType = 0x03;
457 h.Transaction = 0;
458 h.bEncrypted = 0;
459 h.LanguageDrvID = 0;
460 h.ProductionIdx = 0;
461 h.LastUpdate[0] = (unsigned char)pTime->tm_year;
462 h.LastUpdate[1] = (unsigned char)pTime->tm_mon + 1;
463 h.LastUpdate[2] = (unsigned char)pTime->tm_mday;
464
465 m_nHeaderBytes = (m_nFields + 1) * 32 + 1;
466 m_nRecordBytes = 1; // Delete-Flag = Byte 0...
467
468 for(iField=0; iField<m_nFields; iField++)
469 {
470 if( m_Fields[iField].Type == DBF_FT_CHARACTER )
471 {
472 if( m_Fields[iField].Width < 1 )
473 {
474 m_Fields[iField].Width = 1;
475 }
476 else if( m_Fields[iField].Width > 255 )
477 {
478 m_Fields[iField].Width = 255;
479 }
480 }
481
482 m_nRecordBytes += m_Fields[iField].Width;
483 }
484
485 Init_Record();
486
487 fseek(m_hFile, 0, SEEK_SET);
488
489 memset(buf, 0, 16 * sizeof(char));
490
491 //-----------------------------------------------------
492 // Bytes 0-31: File Header...
493 fwrite(&h.FileType , sizeof(char), 1, m_hFile); // 00 FoxBase+, FoxPro, dBaseIII+, dBaseIV, no memo - 0x03
494 // FoxBase+, dBaseIII+ with memo - 0x83
495 // FoxPro with memo - 0xF5
496 // dBaseIV with memo - 0x8B
497 // dBaseIV with SQL Table - 0x8E
498 fwrite(&h.LastUpdate , sizeof(char), 3, m_hFile); // 01-03 Last update, format YYYYMMDD **correction: it is YYMMDD**
499 fwrite(&m_nRecords , sizeof(char), 4, m_hFile); // 04-07 Number of records in file (32-bit number)
500 fwrite(&m_nHeaderBytes , sizeof(char), 2, m_hFile); // 08-09 Number of bytes in header (16-bit number)
501 fwrite(&m_nRecordBytes , sizeof(char), 2, m_hFile); // 10-11 Number of bytes in record (16-bit number)
502 fwrite( buf , sizeof(char), 2, m_hFile); // 12-13 Reserved, fill with 0x00
503 fwrite(&h.Transaction , sizeof(char), 1, m_hFile); // 14 dBaseIV flag, incomplete transaction
504 // Begin Transaction sets it to - 0x01
505 // End Transaction or RollBack reset it to - 0x00
506 fwrite(&h.bEncrypted , sizeof(char), 1, m_hFile); // 15 Encryption flag, encrypted 0x01 else 0x00
507 // Changing the flag does not encrypt or decrypt the records
508 fwrite( buf , sizeof(char), 12, m_hFile); // 16-27 dBaseIV multi-user environment use
509 fwrite(&h.ProductionIdx , sizeof(char), 1, m_hFile); // 28 Production index exists - 0x01 else 0x00
510 fwrite(&h.LanguageDrvID , sizeof(char), 1, m_hFile); // 29 dBaseIV language driver ID
511 fwrite( buf , sizeof(char), 2, m_hFile); // 30-31 Reserved fill with 0x00
512
513 //-----------------------------------------------------
514 // Bytes 32-n: Field Descriptor Array...
515 for(iField=0; iField<m_nFields; iField++)
516 {
517 fwrite( m_Fields[iField].Name , sizeof(char), 11, m_hFile); // 00-10 Field Name ASCII padded with 0x00
518 fwrite(&m_Fields[iField].Type , sizeof(char), 1, m_hFile); // 11 Field Type Identifier (see table)
519 fwrite(&m_Fields[iField].Displacement , sizeof(char), 4, m_hFile); // 12-15 Displacement of field in record
520 fwrite(&m_Fields[iField].Width , sizeof(char), 1, m_hFile); // 16 Field length in bytes
521 fwrite(&m_Fields[iField].Decimals , sizeof(char), 1, m_hFile); // 17 Field decimal places
522 fwrite( buf , sizeof(char), 2, m_hFile); // 18-19 Reserved
523 fwrite(&m_Fields[iField].WorkAreaID , sizeof(char), 1, m_hFile); // 20 dBaseIV work area ID
524 fwrite( buf , sizeof(char), 10, m_hFile); // 21-30 Reserved
525 fwrite(&m_Fields[iField].ProductionIdx , sizeof(char), 1, m_hFile); // 31 Field is part of production index - 0x01 else 0x00
526 }
527
528 //-----------------------------------------------------
529 // Byte n+1: Header m_Record Terminator (0x0D)...
530 buf[0] = 0x0D;
531 fwrite( buf , sizeof(char), 1, m_hFile);
532 }
533
534
535 ///////////////////////////////////////////////////////////
536 // //
537 ///////////////////////////////////////////////////////////
538
539 //---------------------------------------------------------
Init_Record(void)540 void CSG_Table_DBase::Init_Record(void)
541 {
542 m_Record = (char *)SG_Realloc(m_Record, m_nRecordBytes * sizeof(char));
543 m_Record[0] = ' '; // Data records are preceded by one byte, that is, a space (0x20) if the record is not deleted, an asterisk (0x2A) if the record is deleted.
544
545 for(int iField=0, iPos=1; iField<m_nFields; iPos+=m_Fields[iField++].Width)
546 {
547 m_Fields[iField].Offset = iPos;
548 }
549 }
550
551 //---------------------------------------------------------
Get_File_Position(void)552 int CSG_Table_DBase::Get_File_Position(void)
553 {
554 return( m_hFile ? ftell(m_hFile) : 0 );
555 }
556
557
558 ///////////////////////////////////////////////////////////
559 // //
560 ///////////////////////////////////////////////////////////
561
562 //---------------------------------------------------------
Move_First(void)563 bool CSG_Table_DBase::Move_First(void)
564 {
565 bool Result = false;
566
567 if( m_hFile )
568 {
569 Flush_Record();
570
571 fseek(m_hFile, m_nHeaderBytes, SEEK_SET);
572
573 if( fread(m_Record, m_nRecordBytes, sizeof(char), m_hFile) == 1 )
574 {
575 Result = true;
576 }
577
578 fseek(m_hFile, m_nHeaderBytes, SEEK_SET);
579 }
580
581 return( Result );
582 }
583
584 //---------------------------------------------------------
Move_Next(void)585 bool CSG_Table_DBase::Move_Next(void)
586 {
587 bool Result = false;
588
589 if( m_hFile )
590 {
591 Flush_Record();
592
593 fseek(m_hFile, m_nRecordBytes, SEEK_CUR);
594
595 if( fread(m_Record, m_nRecordBytes, sizeof(char), m_hFile) == 1 )
596 {
597 Result = true;
598 }
599
600 fseek(m_hFile, -m_nRecordBytes, SEEK_CUR);
601 }
602
603 return( Result );
604 }
605
606
607 ///////////////////////////////////////////////////////////
608 // //
609 ///////////////////////////////////////////////////////////
610
611 //---------------------------------------------------------
Add_Record(void)612 void CSG_Table_DBase::Add_Record(void)
613 {
614 if( m_hFile )
615 {
616 m_bModified = true;
617
618 memset(m_Record, ' ', m_nRecordBytes);
619
620 fseek(m_hFile, 0, SEEK_END);
621 fwrite(m_Record, m_nRecordBytes, sizeof(char), m_hFile);
622 fseek(m_hFile, -m_nRecordBytes, SEEK_END);
623
624 m_nRecords ++;
625 m_nFileBytes += m_nRecordBytes;
626 }
627 }
628
629 //---------------------------------------------------------
Flush_Record(void)630 void CSG_Table_DBase::Flush_Record(void)
631 {
632 if( m_hFile && !m_bReadOnly && m_bModified )
633 {
634 m_bModified = false;
635 fwrite(m_Record, m_nRecordBytes, sizeof(char), m_hFile);
636 fseek(m_hFile, -m_nRecordBytes, SEEK_CUR);
637 }
638 }
639
640
641 ///////////////////////////////////////////////////////////
642 // //
643 ///////////////////////////////////////////////////////////
644
645 //---------------------------------------------------------
isDeleted(void)646 bool CSG_Table_DBase::isDeleted(void)
647 {
648 return( m_hFile && *m_Record == '*' );
649 }
650
651 //---------------------------------------------------------
asInt(int iField,int & Value)652 bool CSG_Table_DBase::asInt(int iField, int &Value)
653 {
654 double d;
655
656 if( asDouble(iField, d) )
657 {
658 Value = (int)d;
659
660 return( true );
661 }
662
663 return( false );
664 }
665
666 //---------------------------------------------------------
asDouble(int iField,double & Value)667 bool CSG_Table_DBase::asDouble(int iField, double &Value)
668 {
669 if( !m_hFile || iField < 0 || iField >= m_nFields )
670 {
671 return( false );
672 }
673
674 //-----------------------------------------------------
675 CSG_String s;
676
677 char *c = m_Record + m_Fields[iField].Offset;
678
679 for(int i=0; i<m_Fields[iField].Width && *c; i++, c++)
680 {
681 s += *c;
682 }
683
684 //-----------------------------------------------------
685 if( m_Fields[iField].Type == DBF_FT_FLOAT
686 || m_Fields[iField].Type == DBF_FT_NUMERIC )
687 {
688 s.Replace(",", ".");
689
690 return( s.asDouble(Value) );
691 }
692
693 //-----------------------------------------------------
694 if( m_Fields[iField].Type == DBF_FT_DATE )
695 {
696 if( s.Length() < 8 )
697 {
698 return( false );
699 }
700
701 int d = s.Mid(6, 2).asInt(); if( d < 1 ) d = 1; else if( d > 31 ) d = 31;
702 int m = s.Mid(4, 2).asInt(); if( m < 1 ) m = 1; else if( m > 12 ) m = 12;
703 int y = s.Mid(0, 4).asInt();
704
705 Value = 10000 * y + 100 * m + d;
706 }
707
708 //-----------------------------------------------------
709 return( true );
710 }
711
712 //---------------------------------------------------------
asString(int iField)713 CSG_String CSG_Table_DBase::asString(int iField)
714 {
715 CSG_String Value;
716
717 if( !m_hFile || iField < 0 || iField >= m_nFields )
718 {
719 return( Value );
720 }
721
722 //-----------------------------------------------------
723 if( m_Fields[iField].Type != DBF_FT_DATE )
724 {
725 switch( m_Encoding )
726 {
727 case SG_FILE_ENCODING_ANSI: default:
728 { char *s = m_Record + m_Fields[iField].Offset;
729
730 for(int i=0; i<m_Fields[iField].Width && *s; i++, s++)
731 {
732 Value += *s;
733 }
734 } break;
735
736 case SG_FILE_ENCODING_UTF8:
737 Value = CSG_String::from_UTF8(m_Record + m_Fields[iField].Offset, m_Fields[iField].Width);
738 break;
739 }
740
741 Value.Trim(true);
742 }
743
744 //-----------------------------------------------------
745 if( m_Fields[iField].Type == DBF_FT_DATE ) // SAGA(DD.MM.YYYY) from DBASE(YYYYMMDD)
746 {
747 char *s = m_Record + m_Fields[iField].Offset;
748
749 Value += s[0]; // Y1
750 Value += s[1]; // Y2
751 Value += s[2]; // Y3
752 Value += s[3]; // Y4
753 Value += '-';
754 Value += s[4]; // M1
755 Value += s[5]; // M2
756 Value += '-';
757 Value += s[6]; // D1
758 Value += s[7]; // D2
759 }
760
761 //-----------------------------------------------------
762 return( Value );
763 }
764
765
766 ///////////////////////////////////////////////////////////
767 // //
768 ///////////////////////////////////////////////////////////
769
770 //---------------------------------------------------------
Set_Value(int iField,double Value)771 bool CSG_Table_DBase::Set_Value(int iField, double Value)
772 {
773 if( !m_hFile || iField < 0 || iField >= m_nFields || m_Fields[iField].Width < 1 )
774 {
775 return( false );
776 }
777
778 //-----------------------------------------------------
779 if( m_Fields[iField].Type == DBF_FT_DATE )
780 { // Value is expected to be Julian Day Number
781 CSG_DateTime d(Value);
782
783 return( Set_Value(iField, CSG_String::Format("%04d-%02d-%02d",
784 d.Get_Year (),
785 1 + (int)d.Get_Month(),
786 1 + d.Get_Day ()
787 )));
788 }
789
790 //-----------------------------------------------------
791 if( m_Fields[iField].Type == DBF_FT_FLOAT )
792 { // Number stored as a string, right justified, and padded with blanks to the width of the field.
793 char s[256];
794
795 sprintf(s, "%*.*e", m_Fields[iField].Width, m_Fields[iField].Decimals, Value);
796
797 size_t n = strlen(s); if( n > m_Fields[iField].Width ) { n = m_Fields[iField].Width; }
798
799 memset(m_Record + m_Fields[iField].Offset, ' ', m_Fields[iField].Width);
800 memcpy(m_Record + m_Fields[iField].Offset, s , M_GET_MIN(strlen(s), m_Fields[iField].Width));
801
802 m_bModified = true;
803
804 return( true );
805 }
806
807 //-----------------------------------------------------
808 if( m_Fields[iField].Type == DBF_FT_NUMERIC )
809 { // Number stored as a string, right justified, and padded with blanks to the width of the field.
810 char s[256];
811
812 if( m_Fields[iField].Decimals > 0 )
813 {
814 sprintf(s, "%*.*f", m_Fields[iField].Width, m_Fields[iField].Decimals, Value);
815 }
816 else
817 {
818 sprintf(s, "%*d" , m_Fields[iField].Width, (int)Value);
819 }
820
821 memset(m_Record + m_Fields[iField].Offset, ' ', m_Fields[iField].Width);
822 memcpy(m_Record + m_Fields[iField].Offset, s , M_GET_MIN(strlen(s), m_Fields[iField].Width));
823
824 m_bModified = true;
825
826 return( true );
827 }
828
829 //-----------------------------------------------------
830 return( false );
831 }
832
833 //---------------------------------------------------------
Set_Value(int iField,const CSG_String & Value)834 bool CSG_Table_DBase::Set_Value(int iField, const CSG_String &Value)
835 {
836 if( !m_hFile || iField < 0 || iField >= m_nFields || m_Fields[iField].Width < 1 )
837 {
838 return( false );
839 }
840
841 //-----------------------------------------------------
842 if( m_Fields[iField].Type == DBF_FT_CHARACTER )
843 { // All OEM code page characters - padded with blanks to the width of the field.
844 if( Value.Length() < 1 )
845 {
846 memset(m_Record + m_Fields[iField].Offset, ' ', m_Fields[iField].Width);
847
848 m_bModified = true;
849
850 return( true );
851 }
852
853 CSG_Buffer s;
854
855 switch( m_Encoding )
856 {
857 case SG_FILE_ENCODING_ANSI: default:
858 s = Value.to_ASCII();
859 break;
860
861 case SG_FILE_ENCODING_UTF8:
862 s = Value.to_UTF8 ();
863 break;
864 }
865
866 if( s.Get_Size() >= Value.Length() )
867 {
868 memset(m_Record + m_Fields[iField].Offset, ' ', m_Fields[iField].Width);
869 memcpy(m_Record + m_Fields[iField].Offset, s.Get_Data(), M_GET_MIN(s.Get_Size(), m_Fields[iField].Width));
870
871 m_bModified = true;
872
873 return( true );
874 }
875 }
876
877 //-----------------------------------------------------
878 if( m_Fields[iField].Type == DBF_FT_DATE ) // SAGA(YYYY-MM-DD) to DBASE(YYYYMMDD)
879 { // 8 bytes - date stored as a string in the format YYYYMMDD
880 if( Value.Length() >= 10 )
881 {
882 char *s = m_Record + m_Fields[iField].Offset;
883
884 s[0] = Value.b_str()[0]; // Y1
885 s[1] = Value.b_str()[1]; // Y2
886 s[2] = Value.b_str()[2]; // Y3
887 s[3] = Value.b_str()[3]; // Y4
888 s[4] = Value.b_str()[5]; // M1
889 s[5] = Value.b_str()[6]; // M2
890 s[6] = Value.b_str()[8]; // D1
891 s[7] = Value.b_str()[9]; // D2
892
893 m_bModified = true;
894
895 return( true );
896 }
897 }
898
899 //-----------------------------------------------------
900 return( false );
901 }
902
903 //---------------------------------------------------------
Set_NoData(int iField)904 bool CSG_Table_DBase::Set_NoData(int iField)
905 {
906 if( !m_hFile || iField < 0 || iField >= m_nFields || m_Fields[iField].Width < 1 )
907 {
908 return( false );
909 }
910
911 //-----------------------------------------------------
912 memset(m_Record + m_Fields[iField].Offset, ' ', m_Fields[iField].Width);
913
914 m_bModified = true;
915
916 return( true );
917 }
918
919
920 ///////////////////////////////////////////////////////////
921 // //
922 // //
923 // //
924 ///////////////////////////////////////////////////////////
925
926 //---------------------------------------------------------
927