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(&ltime);
454 	struct tm	*pTime	= localtime(&ltime);
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