47 #include "cpl_string.h"
50 #if defined(WIN32) || defined(_WIN32)
51 #define STRCASECMP(a, b) (stricmp(a, b))
54 #define STRCASECMP(a, b) (strcasecmp(a, b))
59 #define snprintf _snprintf
61 #elif defined(WIN32) || defined(_WIN32)
63 #define snprintf _snprintf
67 #define CPLsprintf sprintf
68 #define CPLsnprintf snprintf
79 #define XBASE_FILEHDR_SZ 32
81 #define HEADER_RECORD_TERMINATOR 0x0D
84 #define END_OF_FILE_CHARACTER 0x1A
91 #define CPL_IGNORE_RET_VAL_INT(x) x
95 #define STATIC_CAST(type, x) static_cast<type>(x)
96 #define REINTERPRET_CAST(type, x) reinterpret_cast<type>(x)
97 #define CONST_CAST(type, x) const_cast<type>(x)
98 #define SHPLIB_NULLPTR nullptr
100 #define STATIC_CAST(type, x) ((type)(x))
101 #define REINTERPRET_CAST(type, x) ((type)(x))
102 #define CONST_CAST(type, x) ((type)(x))
103 #define SHPLIB_NULLPTR NULL
113 static void *SfRealloc(
void *pMem,
int nNewSize)
118 return realloc(pMem, nNewSize);
130 static void DBFWriteHeader(
DBFHandle psDBF)
193 static bool DBFFlushRecord(
DBFHandle psDBF)
214 szMessage,
sizeof(szMessage),
215 "Failure seeking to position before writing DBF record %d.",
223 1, psDBF->
fp) != 1) {
225 snprintf(szMessage,
sizeof(szMessage),
253 static bool DBFLoadRecord(
DBFHandle psDBF,
int iRecord)
256 if (!DBFFlushRecord(psDBF))
263 if (psDBF->
sHooks.
FSeek(psDBF->
fp, nRecordOffset, SEEK_SET) != 0) {
265 snprintf(szMessage,
sizeof(szMessage),
266 "fseek(%ld) failed on DBF file.",
273 1, psDBF->
fp) != 1) {
275 snprintf(szMessage,
sizeof(szMessage),
301 DBFWriteHeader(psDBF);
303 if (!DBFFlushRecord(psDBF))
309 psDBF->
sHooks.
FRead(abyFileHeader, 1,
sizeof(abyFileHeader), psDBF->
fp);
323 psDBF->
sHooks.
FWrite(abyFileHeader,
sizeof(abyFileHeader), 1, psDBF->
fp);
353 return DBFOpenLL(pszFilename, pszAccess, &sHooks);
360 static int DBFGetLenWithoutExtension(
const char *pszBasename)
362 const int nLen =
STATIC_CAST(
int, strlen(pszBasename));
363 for (
int i = nLen - 1;
364 i > 0 && pszBasename[i] !=
'/' && pszBasename[i] !=
'\\'; i--) {
365 if (pszBasename[i] ==
'.') {
384 if (strcmp(pszAccess,
"r") != 0 && strcmp(pszAccess,
"r+") != 0 &&
385 strcmp(pszAccess,
"rb") != 0 && strcmp(pszAccess,
"rb+") != 0 &&
386 strcmp(pszAccess,
"r+b") != 0)
389 if (strcmp(pszAccess,
"r") == 0)
392 if (strcmp(pszAccess,
"r+") == 0)
399 const int nLenWithoutExtension = DBFGetLenWithoutExtension(pszFilename);
401 memcpy(pszFullname, pszFilename, nLenWithoutExtension);
402 memcpy(pszFullname + nLenWithoutExtension,
".dbf", 5);
405 psDBF->
fp = psHooks->
FOpen(pszFullname, pszAccess);
409 memcpy(pszFullname + nLenWithoutExtension,
".DBF", 5);
413 memcpy(pszFullname + nLenWithoutExtension,
".cpg", 5);
416 memcpy(pszFullname + nLenWithoutExtension,
".CPG", 5);
417 pfCPG = psHooks->
FOpen(pszFullname,
"r");
436 const int nBufSize = 500;
449 psDBF->
nRecords = pabyBuf[4] | (pabyBuf[5] << 8) | (pabyBuf[6] << 16) |
450 ((pabyBuf[7] & 0x7f) << 24);
452 const int nHeadLen = pabyBuf[8] | (pabyBuf[9] << 8);
477 memset(pabyBuf, 0, nBufSize);
478 psDBF->
sHooks.
FRead(pabyBuf, 1, nBufSize - 1, pfCPG);
498 pabyBuf =
STATIC_CAST(
unsigned char *, SfRealloc(pabyBuf, nHeadLen));
517 for (
int iField = 0; iField < nFields; iField++) {
524 if (pabyFInfo[11] ==
'N' || pabyFInfo[11] ==
'F') {
581 DBFWriteHeader(psDBF);
632 const char *pszCodePage)
638 return DBFCreateLL(pszFilename, pszCodePage, &sHooks);
648 const char *pszCodePage,
SAHooks *psHooks)
654 const int nLenWithoutExtension = DBFGetLenWithoutExtension(pszFilename);
656 memcpy(pszFullname, pszFilename, nLenWithoutExtension);
657 memcpy(pszFullname + nLenWithoutExtension,
".dbf", 5);
669 psHooks->
FWrite(&chZero, 1, 1, fp);
672 fp = psHooks->
FOpen(pszFullname,
"rb+");
678 memcpy(pszFullname + nLenWithoutExtension,
".cpg", 5);
681 if (strncmp(pszCodePage,
"LDID/", 5) == 0) {
682 ldid = atoi(pszCodePage + 5);
691 strlen(pszCodePage), 1, fpCPG);
696 psHooks->
Remove(pszFullname);
770 static char DBFGetNullCharacter(
char chType)
793 char chType,
int nWidth,
int nDecimals)
796 if (!DBFFlushRecord(psDBF))
801 snprintf(szMessage,
sizeof(szMessage),
802 "Cannot add field %s. Header length limit reached "
803 "(max 65535 bytes, 2046 fields).",
820 snprintf(szMessage,
sizeof(szMessage),
821 "Cannot add field %s. Record length limit reached "
822 "(max 65535 bytes).",
845 sizeof(
int) * psDBF->
nFields));
878 pszFInfo[16] =
STATIC_CAST(
unsigned char, nWidth % 256);
879 pszFInfo[17] =
STATIC_CAST(
unsigned char, nWidth / 256);
883 pszFInfo[17] =
STATIC_CAST(
unsigned char, nDecimals);
904 const char chFieldFill = DBFGetNullCharacter(chType);
907 for (
int i = psDBF->
nRecords - 1; i >= 0; --i) {
913 if (psDBF->
sHooks.
FRead(pszRecord, nOldRecordLength, 1, psDBF->
fp) !=
920 memset(pszRecord + nOldRecordLength, chFieldFill, nWidth);
961 static void *DBFReadAttribute(
DBFHandle psDBF,
int hEntity,
int iField,
967 if (hEntity < 0 || hEntity >= psDBF->
nRecords)
970 if (iField < 0 || iField >= psDBF->
nFields)
976 if (!DBFLoadRecord(psDBF, hEntity))
979 unsigned char *pabyRec =
1009 if (chReqType ==
'I') {
1014 else if (chReqType ==
'N') {
1024 #ifdef TRIM_DBF_WHITESPACE
1027 char *pchDst = pchSrc;
1029 while (*pchSrc ==
' ')
1032 while (*pchSrc !=
'\0')
1033 *(pchDst++) = *(pchSrc++);
1036 while (pchDst != psDBF->
pszWorkField && *(--pchDst) ==
' ')
1041 return pReturnField;
1054 STATIC_CAST(
int *, DBFReadAttribute(psDBF, iRecord, iField,
'I'));
1072 STATIC_CAST(
double *, DBFReadAttribute(psDBF, iRecord, iField,
'N'));
1091 DBFReadAttribute(psDBF, iRecord, iField,
'C'));
1105 DBFReadAttribute(psDBF, iRecord, iField,
'L'));
1114 static bool DBFIsValueNULL(
char chType,
const char *pszValue)
1127 if (pszValue[0] ==
'*')
1130 for (
int i = 0; pszValue[i] !=
'\0'; i++) {
1131 if (pszValue[i] !=
' ')
1138 return strncmp(pszValue,
"00000000", 8) == 0;
1142 return pszValue[0] ==
'?';
1146 return strlen(pszValue) == 0;
1165 return DBFIsValueNULL(psDBF->
pachFieldType[iField], pszValue);
1201 char *pszFieldName,
int *pnWidth,
1205 if (iField < 0 || iField >= psDBF->
nFields)
1215 strncpy(pszFieldName,
1221 i > 0 && pszFieldName[i] ==
' '; i--)
1222 pszFieldName[i] =
'\0';
1251 static bool DBFWriteAttribute(
DBFHandle psDBF,
int hEntity,
int iField,
1257 if (hEntity < 0 || hEntity > psDBF->
nRecords)
1261 DBFWriteHeader(psDBF);
1267 if (!DBFFlushRecord(psDBF))
1281 if (!DBFLoadRecord(psDBF, hEntity))
1284 unsigned char *pabyRec =
1305 bool nRetResult =
true;
1314 if (
STATIC_CAST(
int,
sizeof(szSField)) - 2 < nWidth)
1315 nWidth =
sizeof(szSField) - 2;
1318 snprintf(szFormat,
sizeof(szFormat),
"%%%d.%df", nWidth,
1322 szSField[
sizeof(szSField) - 1] =
'\0';
1329 szSField, strlen(szSField));
1373 int iField,
void *pValue)
1378 if (hEntity < 0 || hEntity > psDBF->
nRecords)
1382 DBFWriteHeader(psDBF);
1388 if (!DBFFlushRecord(psDBF))
1402 if (!DBFLoadRecord(psDBF, hEntity))
1405 unsigned char *pabyRec =
1437 int iField,
double dValue)
1439 return (DBFWriteAttribute(psDBF, iRecord, iField,
1450 int iField,
int nValue)
1452 double dValue = nValue;
1454 return (DBFWriteAttribute(psDBF, iRecord, iField,
1465 int iField,
const char *pszValue)
1469 DBFWriteAttribute(psDBF, iRecord, iField,
1482 return (DBFWriteAttribute(psDBF, iRecord, iField,
SHPLIB_NULLPTR));
1492 int iField,
const char lValue)
1496 DBFWriteAttribute(psDBF, iRecord, iField,
1511 if (hEntity < 0 || hEntity > psDBF->
nRecords)
1515 DBFWriteHeader(psDBF);
1521 if (!DBFFlushRecord(psDBF))
1535 if (!DBFLoadRecord(psDBF, hEntity))
1538 unsigned char *pabyRec =
1559 if (hEntity < 0 || hEntity >= psDBF->nRecords)
1562 if (!DBFLoadRecord(psDBF, hEntity))
1565 return STATIC_CAST(
const char *, psDBF->pszCurrentRecord);
1594 sizeof(
int) * psDBF->
nFields);
1598 sizeof(
int) * psDBF->
nFields);
1602 sizeof(
int) * psDBF->
nFields);
1606 sizeof(
char) * psDBF->
nFields);
1612 DBFWriteHeader(newDBF);
1615 newDBF =
DBFOpen(pszFilename,
"rb+");
1635 if (iField >= 0 && iField < psDBF->nFields)
1673 if (iShape < 0 || iShape >= psDBF->
nRecords)
1679 if (!DBFLoadRecord(psDBF, iShape))
1698 if (iShape < 0 || iShape >= psDBF->
nRecords)
1705 if (!DBFLoadRecord(psDBF, iShape))
1734 return psDBF->pszCodePage;
1745 if (iField < 0 || iField >= psDBF->
nFields)
1749 if (!DBFFlushRecord(psDBF))
1759 for (
int i = iField + 1; i < psDBF->
nFields; i++) {
1778 sizeof(
int) * psDBF->
nFields));
1812 for (
int iRecord = 0; iRecord < psDBF->
nRecords; iRecord++) {
1819 if (psDBF->
sHooks.
FRead(pszRecord, nOldRecordLength, 1, psDBF->
fp) !=
1830 psDBF->
sHooks.
FWrite(pszRecord, nDeletedFieldOffset, 1, psDBF->
fp);
1832 pszRecord + nDeletedFieldOffset + nDeletedFieldSize,
1833 nOldRecordLength - nDeletedFieldOffset - nDeletedFieldSize, 1,
1875 if (!DBFFlushRecord(psDBF))
1880 int *panFieldOffsetNew =
1882 int *panFieldSizeNew =
1884 int *panFieldDecimalsNew =
1886 char *pachFieldTypeNew =
1892 for (
int i = 0; i < psDBF->
nFields; i++) {
1899 panFieldOffsetNew[0] = 1;
1900 for (
int i = 1; i < psDBF->
nFields; i++) {
1901 panFieldOffsetNew[i] =
1902 panFieldOffsetNew[i - 1] + panFieldSizeNew[i - 1];
1908 bool errorAbort =
false;
1919 char *pszRecordNew =
1923 for (
int iRecord = 0; iRecord < psDBF->
nRecords; iRecord++) {
1936 pszRecordNew[0] = pszRecord[0];
1938 for (
int i = 0; i < psDBF->
nFields; i++) {
1939 memcpy(pszRecordNew + panFieldOffsetNew[i],
1956 free(panFieldOffsetNew);
1957 free(panFieldSizeNew);
1958 free(panFieldDecimalsNew);
1959 free(pachFieldTypeNew);
1990 const char *pszFieldName,
char chType,
1991 int nWidth,
int nDecimals)
1993 if (iField < 0 || iField >= psDBF->
nFields)
1997 if (!DBFFlushRecord(psDBF))
2000 const char chFieldFill = DBFGetNullCharacter(chType);
2035 if (chType ==
'C') {
2036 pszFInfo[16] =
STATIC_CAST(
unsigned char, nWidth % 256);
2037 pszFInfo[17] =
STATIC_CAST(
unsigned char, nWidth / 256);
2040 pszFInfo[16] =
STATIC_CAST(
unsigned char, nWidth);
2041 pszFInfo[17] =
STATIC_CAST(
unsigned char, nDecimals);
2047 if (nWidth != nOldWidth) {
2048 for (
int i = iField + 1; i < psDBF->
nFields; i++)
2064 bool errorAbort =
false;
2066 if (nWidth < nOldWidth || (nWidth == nOldWidth && chType != chOldType)) {
2073 pszOldField[nOldWidth] = 0;
2076 for (
int iRecord = 0; iRecord < psDBF->
nRecords; iRecord++) {
2083 if (psDBF->
sHooks.
FRead(pszRecord, nOldRecordLength, 1,
2089 memcpy(pszOldField, pszRecord + nOffset, nOldWidth);
2090 const bool bIsNULL = DBFIsValueNULL(chOldType, pszOldField);
2092 if (nWidth != nOldWidth) {
2093 if ((chOldType ==
'N' || chOldType ==
'F' ||
2094 chOldType ==
'D') &&
2095 pszOldField[0] ==
' ') {
2097 memmove(pszRecord + nOffset,
2098 pszRecord + nOffset + nOldWidth - nWidth, nWidth);
2100 if (nOffset + nOldWidth < nOldRecordLength) {
2101 memmove(pszRecord + nOffset + nWidth,
2102 pszRecord + nOffset + nOldWidth,
2103 nOldRecordLength - (nOffset + nOldWidth));
2109 memset(pszRecord + nOffset, chFieldFill, nWidth);
2136 else if (nWidth > nOldWidth) {
2143 pszOldField[nOldWidth] = 0;
2146 for (
int iRecord = psDBF->
nRecords - 1; iRecord >= 0; iRecord--) {
2153 if (psDBF->
sHooks.
FRead(pszRecord, nOldRecordLength, 1,
2159 memcpy(pszOldField, pszRecord + nOffset, nOldWidth);
2160 const bool bIsNULL = DBFIsValueNULL(chOldType, pszOldField);
2162 if (nOffset + nOldWidth < nOldRecordLength) {
2163 memmove(pszRecord + nOffset + nWidth,
2164 pszRecord + nOffset + nOldWidth,
2165 nOldRecordLength - (nOffset + nOldWidth));
2170 memset(pszRecord + nOffset, chFieldFill, nWidth);
2173 if ((chOldType ==
'N' || chOldType ==
'F')) {
2175 memmove(pszRecord + nOffset + nWidth - nOldWidth,
2176 pszRecord + nOffset, nOldWidth);
2177 memset(pszRecord + nOffset,
' ', nWidth - nOldWidth);
2181 memset(pszRecord + nOffset + nOldWidth,
' ',
2182 nWidth - nOldWidth);
int SHPAPI_CALL DBFMarkRecordDeleted(DBFHandle psDBF, int iShape, int bIsDeleted)
int SHPAPI_CALL DBFWriteDoubleAttribute(DBFHandle psDBF, int iRecord, int iField, double dValue)
int SHPAPI_CALL DBFGetFieldCount(DBFHandle psDBF)
int SHPAPI_CALL DBFReadIntegerAttribute(DBFHandle psDBF, int iRecord, int iField)
int SHPAPI_CALL DBFAlterFieldDefn(DBFHandle psDBF, int iField, const char *pszFieldName, char chType, int nWidth, int nDecimals)
const char SHPAPI_CALL1 * DBFReadTuple(DBFHandle psDBF, int hEntity){ if(hEntity< 0||hEntity >=psDBF->nRecords) return SHPLIB_NULLPTR;if(!DBFLoadRecord(psDBF, hEntity)) return SHPLIB_NULLPTR;return STATIC_CAST(const char *, psDBF->pszCurrentRecord
DBFHandle SHPAPI_CALL DBFOpen(const char *pszFilename, const char *pszAccess)
double SHPAPI_CALL DBFReadDoubleAttribute(DBFHandle psDBF, int iRecord, int iField)
int SHPAPI_CALL DBFAddField(DBFHandle psDBF, const char *pszFieldName, DBFFieldType eType, int nWidth, int nDecimals)
const char SHPAPI_CALL1 * DBFReadLogicalAttribute(DBFHandle psDBF, int iRecord, int iField){ return STATIC_CAST(const char *, DBFReadAttribute(psDBF, iRecord, iField, 'L')
DBFHandle SHPAPI_CALL DBFCloneEmpty(DBFHandle psDBF, const char *pszFilename)
DBFHandle SHPAPI_CALL DBFCreateLL(const char *pszFilename, const char *pszCodePage, SAHooks *psHooks)
int SHPAPI_CALL DBFGetFieldIndex(DBFHandle psDBF, const char *pszFieldName)
int SHPAPI_CALL DBFGetRecordCount(DBFHandle psDBF)
int SHPAPI_CALL DBFWriteIntegerAttribute(DBFHandle psDBF, int iRecord, int iField, int nValue)
#define STATIC_CAST(type, x)
int SHPAPI_CALL DBFWriteStringAttribute(DBFHandle psDBF, int iRecord, int iField, const char *pszValue)
DBFHandle SHPAPI_CALL DBFCreateEx(const char *pszFilename, const char *pszCodePage)
#define HEADER_RECORD_TERMINATOR
#define REINTERPRET_CAST(type, x)
void SHPAPI_CALL DBFClose(DBFHandle psDBF)
int SHPAPI_CALL DBFAddNativeFieldType(DBFHandle psDBF, const char *pszFieldName, char chType, int nWidth, int nDecimals)
DBFHandle SHPAPI_CALL DBFCreate(const char *pszFilename)
#define END_OF_FILE_CHARACTER
void SHPAPI_CALL DBFSetLastModifiedDate(DBFHandle psDBF, int nYYSince1900, int nMM, int nDD)
void SHPAPI_CALL DBFSetWriteEndOfFileChar(DBFHandle psDBF, int bWriteFlag)
DBFHandle SHPAPI_CALL DBFOpenLL(const char *pszFilename, const char *pszAccess, SAHooks *psHooks)
int SHPAPI_CALL DBFWriteNULLAttribute(DBFHandle psDBF, int iRecord, int iField)
int SHPAPI_CALL DBFIsAttributeNULL(DBFHandle psDBF, int iRecord, int iField)
const char SHPAPI_CALL1 * DBFGetCodePage(DBFHandle psDBF){ if(psDBF==SHPLIB_NULLPTR) return SHPLIB_NULLPTR;return psDBF->pszCodePage;}int SHPAPI_CALL DBFDeleteField(DBFHandle psDBF, int iField
int SHPAPI_CALL DBFWriteTuple(DBFHandle psDBF, int hEntity, void *pRawTuple)
int SHPAPI_CALL DBFWriteAttributeDirectly(DBFHandle psDBF, int hEntity, int iField, void *pValue)
int SHPAPI_CALL DBFReorderFields(DBFHandle psDBF, int *panMap)
#define CONST_CAST(type, x)
int SHPAPI_CALL DBFIsRecordDeleted(DBFHandle psDBF, int iShape)
int SHPAPI_CALL DBFWriteLogicalAttribute(DBFHandle psDBF, int iRecord, int iField, const char lValue)
char SHPAPI_CALL DBFGetNativeFieldType(DBFHandle psDBF, int iField)
const char SHPAPI_CALL1 * DBFReadStringAttribute(DBFHandle psDBF, int iRecord, int iField){ return STATIC_CAST(const char *, DBFReadAttribute(psDBF, iRecord, iField, 'C')
#define CPL_IGNORE_RET_VAL_INT(x)
void SHPAPI_CALL DBFUpdateHeader(DBFHandle psDBF)
DBFFieldType SHPAPI_CALL DBFGetFieldInfo(DBFHandle psDBF, int iField, char *pszFieldName, int *pnWidth, int *pnDecimals)
void SASetupDefaultHooks(SAHooks *psHooks)
int SHPAPI_CALL DBFDeleteField(DBFHandle hDBF, int iField)
#define SHP_CVSID(string)
#define XBASE_FLDNAME_LEN_WRITE
#define XBASE_FLDNAME_LEN_READ
#define XBASE_FLD_MAX_WIDTH
union DBFInfo::@16 fieldValue
int bCurrentRecordModified
int bRequireNextWriteSeek
void(* Error)(const char *message)
SAFile(* FOpen)(const char *filename, const char *access)
SAOffset(* FTell)(SAFile file)
int(* FFlush)(SAFile file)
SAOffset(* FWrite)(void *p, SAOffset size, SAOffset nmemb, SAFile file)
double(* Atof)(const char *str)
int(* Remove)(const char *filename)
int(* FClose)(SAFile file)
SAOffset(* FRead)(void *p, SAOffset size, SAOffset nmemb, SAFile file)
SAOffset(* FSeek)(SAFile file, SAOffset offset, int whence)