24 #include "cpl_string.h"
28 #define STRCASECMP(a, b) (_stricmp(a, b))
30 #define STRCASECMP(a, b) (stricmp(a, b))
33 #define STRCASECMP(a, b) (strcasecmp(a, b))
38 #define snprintf _snprintf
42 #define snprintf _snprintf
46 #define CPLsprintf sprintf
47 #define CPLsnprintf snprintf
56 #define XBASE_FILEHDR_SZ 32
58 #define HEADER_RECORD_TERMINATOR 0x0D
61 #define END_OF_FILE_CHARACTER 0x1A
68 #define CPL_IGNORE_RET_VAL_INT(x) x
80 static void DBFWriteHeader(
DBFHandle psDBF)
143 static bool DBFFlushRecord(
DBFHandle psDBF)
164 szMessage,
sizeof(szMessage),
165 "Failure seeking to position before writing DBF record %d.",
173 1, psDBF->
fp) != 1) {
175 snprintf(szMessage,
sizeof(szMessage),
203 static bool DBFLoadRecord(
DBFHandle psDBF,
int iRecord)
206 if (!DBFFlushRecord(psDBF))
213 if (psDBF->
sHooks.
FSeek(psDBF->
fp, nRecordOffset, SEEK_SET) != 0) {
215 snprintf(szMessage,
sizeof(szMessage),
216 "fseek(%ld) failed on DBF file.",
223 1, psDBF->
fp) != 1) {
225 snprintf(szMessage,
sizeof(szMessage),
251 DBFWriteHeader(psDBF);
253 if (!DBFFlushRecord(psDBF))
259 psDBF->
sHooks.
FRead(abyFileHeader, 1,
sizeof(abyFileHeader), psDBF->
fp);
273 psDBF->
sHooks.
FWrite(abyFileHeader,
sizeof(abyFileHeader), 1, psDBF->
fp);
302 return DBFOpenLL(pszFilename, pszAccess, &sHooks);
309 static int DBFGetLenWithoutExtension(
const char *pszBasename)
311 const int nLen =
STATIC_CAST(
int, strlen(pszBasename));
312 for (
int i = nLen - 1;
313 i > 0 && pszBasename[i] !=
'/' && pszBasename[i] !=
'\\'; i--) {
314 if (pszBasename[i] ==
'.') {
333 if (strcmp(pszAccess,
"r") != 0 && strcmp(pszAccess,
"r+") != 0 &&
334 strcmp(pszAccess,
"rb") != 0 && strcmp(pszAccess,
"rb+") != 0 &&
335 strcmp(pszAccess,
"r+b") != 0)
338 if (strcmp(pszAccess,
"r") == 0)
341 if (strcmp(pszAccess,
"r+") == 0)
348 const int nLenWithoutExtension = DBFGetLenWithoutExtension(pszFilename);
350 memcpy(pszFullname, pszFilename, nLenWithoutExtension);
351 memcpy(pszFullname + nLenWithoutExtension,
".dbf", 5);
358 memcpy(pszFullname + nLenWithoutExtension,
".DBF", 5);
363 memcpy(pszFullname + nLenWithoutExtension,
".cpg", 5);
366 memcpy(pszFullname + nLenWithoutExtension,
".CPG", 5);
386 const int nBufSize = 500;
399 psDBF->
nRecords = pabyBuf[4] | (pabyBuf[5] << 8) | (pabyBuf[6] << 16) |
400 ((pabyBuf[7] & 0x7f) << 24);
402 const int nHeadLen = pabyBuf[8] | (pabyBuf[9] << 8);
427 memset(pabyBuf, 0, nBufSize);
428 psDBF->
sHooks.
FRead(pabyBuf, 1, nBufSize - 1, pfCPG);
448 pabyBuf =
STATIC_CAST(
unsigned char *, realloc(pabyBuf, nHeadLen));
467 for (
int iField = 0; iField < nFields; iField++) {
474 if (pabyFInfo[11] ==
'N' || pabyFInfo[11] ==
'F') {
531 DBFWriteHeader(psDBF);
582 const char *pszCodePage)
588 return DBFCreateLL(pszFilename, pszCodePage, &sHooks);
598 const char *pszCodePage,
605 const int nLenWithoutExtension = DBFGetLenWithoutExtension(pszFilename);
607 memcpy(pszFullname, pszFilename, nLenWithoutExtension);
608 memcpy(pszFullname + nLenWithoutExtension,
".dbf", 5);
619 memcpy(pszFullname + nLenWithoutExtension,
".cpg", 5);
622 if (strncmp(pszCodePage,
"LDID/", 5) == 0) {
623 ldid = atoi(pszCodePage + 5);
633 strlen(pszCodePage), 1, fpCPG);
712 static char DBFGetNullCharacter(
char chType)
735 char chType,
int nWidth,
int nDecimals)
738 if (!DBFFlushRecord(psDBF))
743 snprintf(szMessage,
sizeof(szMessage),
744 "Cannot add field %s. Header length limit reached "
745 "(max 65535 bytes, 2046 fields).",
762 snprintf(szMessage,
sizeof(szMessage),
763 "Cannot add field %s. Record length limit reached "
764 "(max 65535 bytes).",
819 pszFInfo[16] =
STATIC_CAST(
unsigned char, nWidth % 256);
820 pszFInfo[17] =
STATIC_CAST(
unsigned char, nWidth / 256);
824 pszFInfo[17] =
STATIC_CAST(
unsigned char, nDecimals);
845 const char chFieldFill = DBFGetNullCharacter(chType);
848 for (
int i = psDBF->
nRecords - 1; i >= 0; --i) {
854 if (psDBF->
sHooks.
FRead(pszRecord, nOldRecordLength, 1, psDBF->
fp) !=
861 memset(pszRecord + nOldRecordLength, chFieldFill, nWidth);
902 static void *DBFReadAttribute(
DBFHandle psDBF,
int hEntity,
int iField,
908 if (hEntity < 0 || hEntity >= psDBF->
nRecords)
911 if (iField < 0 || iField >= psDBF->
nFields)
917 if (!DBFLoadRecord(psDBF, hEntity))
920 const unsigned char *pabyRec =
950 if (chReqType ==
'I') {
955 else if (chReqType ==
'N') {
965 #ifdef TRIM_DBF_WHITESPACE
968 char *pchDst = pchSrc;
970 while (*pchSrc ==
' ')
973 while (*pchSrc !=
'\0')
974 *(pchDst++) = *(pchSrc++);
977 while (pchDst != psDBF->
pszWorkField && *(--pchDst) ==
' ')
995 STATIC_CAST(
int *, DBFReadAttribute(psDBF, iRecord, iField,
'I'));
1013 STATIC_CAST(
double *, DBFReadAttribute(psDBF, iRecord, iField,
'N'));
1031 DBFReadAttribute(psDBF, iRecord, iField,
'C'));
1044 DBFReadAttribute(psDBF, iRecord, iField,
'L'));
1057 const char *, DBFReadAttribute(psDBF, iRecord, iField,
'D'));
1066 else if (3 != sscanf(pdateValue,
"%4d%2d%2d", &date.
year, &date.
month,
1082 static bool DBFIsValueNULL(
char chType,
const char *pszValue)
1095 if (pszValue[0] ==
'*')
1098 for (
int i = 0; pszValue[i] !=
'\0'; i++) {
1099 if (pszValue[i] !=
' ')
1114 return pszValue[0] == 0 || strncmp(pszValue,
"00000000", 8) == 0 ||
1115 strcmp(pszValue,
" ") == 0 || strcmp(pszValue,
"0") == 0;
1119 return pszValue[0] ==
'?';
1123 return strlen(pszValue) == 0;
1143 return DBFIsValueNULL(psDBF->
pachFieldType[iField], pszValue);
1177 char *pszFieldName,
int *pnWidth,
1180 if (iField < 0 || iField >= psDBF->
nFields)
1190 strncpy(pszFieldName,
1196 i > 0 && pszFieldName[i] ==
' '; i--)
1197 pszFieldName[i] =
'\0';
1225 static bool DBFWriteAttribute(
DBFHandle psDBF,
int hEntity,
int iField,
1231 if (hEntity < 0 || hEntity > psDBF->
nRecords)
1235 DBFWriteHeader(psDBF);
1241 if (!DBFFlushRecord(psDBF))
1255 if (!DBFLoadRecord(psDBF, hEntity))
1258 unsigned char *pabyRec =
1279 bool nRetResult =
true;
1288 if (
STATIC_CAST(
int,
sizeof(szSField)) - 2 < nWidth)
1289 nWidth =
sizeof(szSField) - 2;
1292 snprintf(szFormat,
sizeof(szFormat),
"%%%d.%df", nWidth,
1296 szSField[
sizeof(szSField) - 1] =
'\0';
1303 szSField, strlen(szSField));
1351 int iField,
const void *pValue)
1356 if (hEntity < 0 || hEntity > psDBF->
nRecords)
1360 DBFWriteHeader(psDBF);
1366 if (!DBFFlushRecord(psDBF))
1380 if (!DBFLoadRecord(psDBF, hEntity))
1384 unsigned char *pabyRec =
1420 int iField,
double dValue)
1422 return (DBFWriteAttribute(psDBF, iRecord, iField,
1433 int iField,
int nValue)
1435 double dValue = nValue;
1437 return (DBFWriteAttribute(psDBF, iRecord, iField,
1448 int iField,
const char *pszValue)
1451 DBFWriteAttribute(psDBF, iRecord, iField,
1463 return (DBFWriteAttribute(psDBF, iRecord, iField,
SHPLIB_NULLPTR));
1473 int iField,
const char lValue)
1476 DBFWriteAttribute(psDBF, iRecord, iField,
1492 if (lValue->
year < 0 || lValue->
year > 9999)
1496 if (lValue->
day < 0 || lValue->
day > 99)
1499 snprintf(dateValue,
sizeof(dateValue),
"%04d%02d%02d", lValue->
year,
1511 const void *pRawTuple)
1516 if (hEntity < 0 || hEntity > psDBF->
nRecords)
1520 DBFWriteHeader(psDBF);
1526 if (!DBFFlushRecord(psDBF))
1540 if (!DBFLoadRecord(psDBF, hEntity))
1543 unsigned char *pabyRec =
1563 if (hEntity < 0 || hEntity >= psDBF->nRecords)
1566 if (!DBFLoadRecord(psDBF, hEntity))
1569 return STATIC_CAST(
const char *, psDBF->pszCurrentRecord);
1580 const char *pszFilename)
1601 sizeof(
int) * psDBF->
nFields);
1605 sizeof(
int) * psDBF->
nFields);
1609 sizeof(
int) * psDBF->
nFields);
1613 sizeof(
char) * psDBF->
nFields);
1619 DBFWriteHeader(newDBF);
1622 newDBF =
DBFOpen(pszFilename,
"rb+");
1641 if (iField >= 0 && iField < psDBF->nFields)
1656 const char *pszFieldName)
1680 if (iShape < 0 || iShape >= psDBF->
nRecords)
1686 if (!DBFLoadRecord(psDBF, iShape))
1705 if (iShape < 0 || iShape >= psDBF->
nRecords)
1712 if (!DBFLoadRecord(psDBF, iShape))
1741 return psDBF->pszCodePage;
1752 if (iField < 0 || iField >= psDBF->
nFields)
1756 if (!DBFFlushRecord(psDBF))
1766 for (
int i = iField + 1; i < psDBF->
nFields; i++) {
1818 for (
int iRecord = 0; iRecord < psDBF->
nRecords; iRecord++) {
1825 if (psDBF->
sHooks.
FRead(pszRecord, nOldRecordLength, 1, psDBF->
fp) !=
1836 psDBF->
sHooks.
FWrite(pszRecord, nDeletedFieldOffset, 1, psDBF->
fp);
1838 pszRecord + nDeletedFieldOffset + nDeletedFieldSize,
1839 nOldRecordLength - nDeletedFieldOffset - nDeletedFieldSize, 1,
1881 if (!DBFFlushRecord(psDBF))
1886 int *panFieldOffsetNew =
1888 int *panFieldSizeNew =
1890 int *panFieldDecimalsNew =
1892 char *pachFieldTypeNew =
1898 for (
int i = 0; i < psDBF->
nFields; i++) {
1905 panFieldOffsetNew[0] = 1;
1906 for (
int i = 1; i < psDBF->
nFields; i++) {
1907 panFieldOffsetNew[i] =
1908 panFieldOffsetNew[i - 1] + panFieldSizeNew[i - 1];
1914 bool errorAbort =
false;
1925 char *pszRecordNew =
1929 for (
int iRecord = 0; iRecord < psDBF->
nRecords; iRecord++) {
1942 pszRecordNew[0] = pszRecord[0];
1944 for (
int i = 0; i < psDBF->
nFields; i++) {
1945 memcpy(pszRecordNew + panFieldOffsetNew[i],
1962 free(panFieldOffsetNew);
1963 free(panFieldSizeNew);
1964 free(panFieldDecimalsNew);
1965 free(pachFieldTypeNew);
1996 const char *pszFieldName,
char chType,
1997 int nWidth,
int nDecimals)
1999 if (iField < 0 || iField >= psDBF->
nFields)
2003 if (!DBFFlushRecord(psDBF))
2006 const char chFieldFill = DBFGetNullCharacter(chType);
2041 if (chType ==
'C') {
2042 pszFInfo[16] =
STATIC_CAST(
unsigned char, nWidth % 256);
2043 pszFInfo[17] =
STATIC_CAST(
unsigned char, nWidth / 256);
2046 pszFInfo[16] =
STATIC_CAST(
unsigned char, nWidth);
2047 pszFInfo[17] =
STATIC_CAST(
unsigned char, nDecimals);
2053 if (nWidth != nOldWidth) {
2054 for (
int i = iField + 1; i < psDBF->
nFields; i++)
2070 bool errorAbort =
false;
2072 if (nWidth < nOldWidth || (nWidth == nOldWidth && chType != chOldType)) {
2078 pszOldField[nOldWidth] = 0;
2081 for (
int iRecord = 0; iRecord < psDBF->
nRecords; iRecord++) {
2088 if (psDBF->
sHooks.
FRead(pszRecord, nOldRecordLength, 1,
2094 memcpy(pszOldField, pszRecord + nOffset, nOldWidth);
2095 const bool bIsNULL = DBFIsValueNULL(chOldType, pszOldField);
2097 if (nWidth != nOldWidth) {
2098 if ((chOldType ==
'N' || chOldType ==
'F' ||
2099 chOldType ==
'D') &&
2100 pszOldField[0] ==
' ') {
2102 memmove(pszRecord + nOffset,
2103 pszRecord + nOffset + nOldWidth - nWidth, nWidth);
2105 if (nOffset + nOldWidth < nOldRecordLength) {
2106 memmove(pszRecord + nOffset + nWidth,
2107 pszRecord + nOffset + nOldWidth,
2108 nOldRecordLength - (nOffset + nOldWidth));
2114 memset(pszRecord + nOffset, chFieldFill, nWidth);
2141 else if (nWidth > nOldWidth) {
2147 pszOldField[nOldWidth] = 0;
2150 for (
int iRecord = psDBF->
nRecords - 1; iRecord >= 0; iRecord--) {
2157 if (psDBF->
sHooks.
FRead(pszRecord, nOldRecordLength, 1,
2163 memcpy(pszOldField, pszRecord + nOffset, nOldWidth);
2164 const bool bIsNULL = DBFIsValueNULL(chOldType, pszOldField);
2166 if (nOffset + nOldWidth < nOldRecordLength) {
2167 memmove(pszRecord + nOffset + nWidth,
2168 pszRecord + nOffset + nOldWidth,
2169 nOldRecordLength - (nOffset + nOldWidth));
2174 memset(pszRecord + nOffset, chFieldFill, nWidth);
2177 if ((chOldType ==
'N' || chOldType ==
'F')) {
2179 memmove(pszRecord + nOffset + nWidth - nOldWidth,
2180 pszRecord + nOffset, nOldWidth);
2181 memset(pszRecord + nOffset,
' ', nWidth - nOldWidth);
2185 memset(pszRecord + nOffset + nOldWidth,
' ',
2186 nWidth - nOldWidth);
DBFHandle DBFCloneEmpty(const DBFHandle psDBF, const char *pszFilename)
const char * DBFReadStringAttribute(DBFHandle psDBF, int iRecord, int iField)
const char * DBFGetCodePage(const DBFHandle psDBF)
int DBFWriteIntegerAttribute(DBFHandle psDBF, int iRecord, int iField, int nValue)
DBFHandle DBFCreate(const char *pszFilename)
int DBFWriteStringAttribute(DBFHandle psDBF, int iRecord, int iField, const char *pszValue)
int DBFGetFieldCount(const DBFHandle psDBF)
int DBFMarkRecordDeleted(DBFHandle psDBF, int iShape, int bIsDeleted)
int DBFWriteLogicalAttribute(DBFHandle psDBF, int iRecord, int iField, const char lValue)
DBFHandle DBFOpenLL(const char *pszFilename, const char *pszAccess, const SAHooks *psHooks)
int DBFDeleteField(DBFHandle psDBF, int iField)
void DBFClose(DBFHandle psDBF)
int DBFAddField(DBFHandle psDBF, const char *pszFieldName, DBFFieldType eType, int nWidth, int nDecimals)
int DBFAlterFieldDefn(DBFHandle psDBF, int iField, const char *pszFieldName, char chType, int nWidth, int nDecimals)
int DBFWriteNULLAttribute(DBFHandle psDBF, int iRecord, int iField)
SHPDate DBFReadDateAttribute(DBFHandle psDBF, int iRecord, int iField)
int DBFIsRecordDeleted(const DBFHandle psDBF, int iShape)
void DBFSetWriteEndOfFileChar(DBFHandle psDBF, int bWriteFlag)
int DBFReadIntegerAttribute(DBFHandle psDBF, int iRecord, int iField)
DBFHandle DBFCreateEx(const char *pszFilename, const char *pszCodePage)
int DBFIsAttributeNULL(const DBFHandle psDBF, int iRecord, int iField)
DBFFieldType DBFGetFieldInfo(const DBFHandle psDBF, int iField, char *pszFieldName, int *pnWidth, int *pnDecimals)
int DBFReorderFields(DBFHandle psDBF, const int *panMap)
const char * DBFReadTuple(DBFHandle psDBF, int hEntity)
int DBFWriteDoubleAttribute(DBFHandle psDBF, int iRecord, int iField, double dValue)
double DBFReadDoubleAttribute(DBFHandle psDBF, int iRecord, int iField)
#define HEADER_RECORD_TERMINATOR
void DBFSetLastModifiedDate(DBFHandle psDBF, int nYYSince1900, int nMM, int nDD)
void DBFUpdateHeader(DBFHandle psDBF)
char DBFGetNativeFieldType(const DBFHandle psDBF, int iField)
DBFHandle DBFCreateLL(const char *pszFilename, const char *pszCodePage, const SAHooks *psHooks)
int DBFAddNativeFieldType(DBFHandle psDBF, const char *pszFieldName, char chType, int nWidth, int nDecimals)
#define END_OF_FILE_CHARACTER
int DBFWriteAttributeDirectly(DBFHandle psDBF, int hEntity, int iField, const void *pValue)
int DBFGetRecordCount(const DBFHandle psDBF)
int DBFWriteDateAttribute(DBFHandle psDBF, int iRecord, int iField, const SHPDate *lValue)
int DBFWriteTuple(DBFHandle psDBF, int hEntity, const void *pRawTuple)
DBFHandle DBFOpen(const char *pszFilename, const char *pszAccess)
int DBFGetFieldIndex(const DBFHandle psDBF, const char *pszFieldName)
const char * DBFReadLogicalAttribute(DBFHandle psDBF, int iRecord, int iField)
#define CPL_IGNORE_RET_VAL_INT(x)
void SASetupDefaultHooks(SAHooks *psHooks)
#define XBASE_FLDNAME_LEN_WRITE
#define XBASE_FLDNAME_LEN_READ
#define XBASE_FLD_MAX_WIDTH
#define STATIC_CAST(type, x)
#define REINTERPRET_CAST(type, x)
#define CONST_CAST(type, x)
union DBFInfo::@16 fieldValue
int bCurrentRecordModified
int bRequireNextWriteSeek
void(* Error)(const char *message)
SAOffset(* FTell)(SAFile file)
int(* FFlush)(SAFile file)
SAFile(* FOpen)(const char *filename, const char *access, void *pvUserData)
double(* Atof)(const char *str)
SAOffset(* FWrite)(const void *p, SAOffset size, SAOffset nmemb, SAFile file)
int(* FClose)(SAFile file)
int(* Remove)(const char *filename, void *pvUserData)
SAOffset(* FRead)(void *p, SAOffset size, SAOffset nmemb, SAFile file)
SAOffset(* FSeek)(SAFile file, SAOffset offset, int whence)