26#include "cpl_string.h"
31#define STRCASECMP(a, b) (_stricmp(a, b))
33#define STRCASECMP(a, b) (stricmp(a, b))
36#define STRCASECMP(a, b) (strcasecmp(a, b))
42#define snprintf _snprintf
46#define snprintf _snprintf
50#define CPLsprintf sprintf
51#define CPLsnprintf snprintf
60#define XBASE_FILEHDR_SZ 32
62#define HEADER_RECORD_TERMINATOR 0x0D
65#define END_OF_FILE_CHARACTER 0x1A
72#define CPL_IGNORE_RET_VAL_INT(x) x
84static void DBFWriteHeader(
DBFHandle psDBF)
147static bool DBFFlushRecord(
DBFHandle psDBF)
168 szMessage,
sizeof(szMessage),
169 "Failure seeking to position before writing DBF record %d.",
177 1, psDBF->
fp) != 1) {
179 snprintf(szMessage,
sizeof(szMessage),
207static bool DBFLoadRecord(
DBFHandle psDBF,
int iRecord)
210 if (!DBFFlushRecord(psDBF))
217 if (psDBF->
sHooks.
FSeek(psDBF->
fp, nRecordOffset, SEEK_SET) != 0) {
219 snprintf(szMessage,
sizeof(szMessage),
220 "fseek(%ld) failed on DBF file.",
227 1, psDBF->
fp) != 1) {
229 snprintf(szMessage,
sizeof(szMessage),
255 DBFWriteHeader(psDBF);
257 if (!DBFFlushRecord(psDBF))
263 psDBF->
sHooks.
FRead(abyFileHeader, 1,
sizeof(abyFileHeader), psDBF->
fp);
277 psDBF->
sHooks.
FWrite(abyFileHeader,
sizeof(abyFileHeader), 1, psDBF->
fp);
306 return DBFOpenLL(pszFilename, pszAccess, &sHooks);
313static int DBFGetLenWithoutExtension(
const char *pszBasename)
315 const int nLen =
STATIC_CAST(
int, strlen(pszBasename));
316 for (
int i = nLen - 1;
317 i > 0 && pszBasename[i] !=
'/' && pszBasename[i] !=
'\\'; i--) {
318 if (pszBasename[i] ==
'.') {
337 if (strcmp(pszAccess,
"r") != 0 && strcmp(pszAccess,
"r+") != 0 &&
338 strcmp(pszAccess,
"rb") != 0 && strcmp(pszAccess,
"rb+") != 0 &&
339 strcmp(pszAccess,
"r+b") != 0)
342 if (strcmp(pszAccess,
"r") == 0)
345 if (strcmp(pszAccess,
"r+") == 0)
352 const int nLenWithoutExtension = DBFGetLenWithoutExtension(pszFilename);
353 char *pszFullname =
STATIC_CAST(
char *, malloc(nLenWithoutExtension + 5));
357 memcpy(pszFullname, pszFilename, nLenWithoutExtension);
358 memcpy(pszFullname + nLenWithoutExtension,
".dbf", 5);
369 memcpy(pszFullname + nLenWithoutExtension,
".DBF", 5);
374 memcpy(pszFullname + nLenWithoutExtension,
".cpg", 5);
377 memcpy(pszFullname + nLenWithoutExtension,
".CPG", 5);
397 const int nBufSize = 500;
398 unsigned char *pabyBuf =
STATIC_CAST(
unsigned char *, malloc(nBufSize));
417 psDBF->
nRecords = pabyBuf[4] | (pabyBuf[5] << 8) | (pabyBuf[6] << 16) |
418 ((pabyBuf[7] & 0x7f) << 24);
420 const int nHeadLen = pabyBuf[8] | (pabyBuf[9] << 8);
453 memset(pabyBuf, 0, nBufSize);
454 psDBF->
sHooks.
FRead(pabyBuf, 1, nBufSize - 1, pfCPG);
479 unsigned char *pabyBufNew =
480 STATIC_CAST(
unsigned char *, realloc(pabyBuf, nHeadLen));
489 pabyBuf = pabyBufNew;
513 for (
int iField = 0; iField < nFields; iField++) {
520 if (pabyFInfo[11] ==
'N' || pabyFInfo[11] ==
'F') {
577 DBFWriteHeader(psDBF);
628 const char *pszCodePage)
634 return DBFCreateLL(pszFilename, pszCodePage, &sHooks);
644 const char *pszCodePage,
651 const int nLenWithoutExtension = DBFGetLenWithoutExtension(pszFilename);
652 char *pszFullname =
STATIC_CAST(
char *, malloc(nLenWithoutExtension + 5));
655 memcpy(pszFullname, pszFilename, nLenWithoutExtension);
656 memcpy(pszFullname + nLenWithoutExtension,
".dbf", 5);
663 const size_t nMessageLen = strlen(pszFullname) + 256;
664 char *pszMessage =
STATIC_CAST(
char *, malloc(nMessageLen));
666 snprintf(pszMessage, nMessageLen,
"Failed to create file %s: %s",
667 pszFullname, strerror(errno));
668 psHooks->
Error(pszMessage);
676 memcpy(pszFullname + nLenWithoutExtension,
".cpg", 5);
679 if (strncmp(pszCodePage,
"LDID/", 5) == 0) {
680 ldid = atoi(pszCodePage + 5);
690 strlen(pszCodePage), 1, fpCPG);
732 STATIC_CAST(
char *, malloc(strlen(pszCodePage) + 1));
773static char DBFGetNullCharacter(
char chType)
796 char chType,
int nWidth,
int nDecimals)
799 if (!DBFFlushRecord(psDBF))
804 snprintf(szMessage,
sizeof(szMessage),
805 "Cannot add field %s. Header length limit reached "
806 "(max 65535 bytes, 2046 fields).",
823 snprintf(szMessage,
sizeof(szMessage),
824 "Cannot add field %s. Record length limit reached "
825 "(max 65535 bytes).",
839 int *panFieldOffsetNew =
841 sizeof(
int) * (psDBF->
nFields + 1)));
843 int *panFieldSizeNew =
845 sizeof(
int) * (psDBF->
nFields + 1)));
847 int *panFieldDecimalsNew =
849 sizeof(
int) * (psDBF->
nFields + 1)));
851 char *pachFieldTypeNew =
853 sizeof(
char) * (psDBF->
nFields + 1)));
862 char *pszCurrentRecordNew =
866 if (panFieldOffsetNew)
870 if (panFieldDecimalsNew)
872 if (pachFieldTypeNew)
876 if (pszCurrentRecordNew)
879 if (!panFieldOffsetNew || !panFieldSizeNew || !panFieldDecimalsNew ||
880 !pachFieldTypeNew || !pszHeaderNew || !pszCurrentRecordNew) {
922 pszFInfo[16] =
STATIC_CAST(
unsigned char, nWidth % 256);
923 pszFInfo[17] =
STATIC_CAST(
unsigned char, nWidth / 256);
927 pszFInfo[17] =
STATIC_CAST(
unsigned char, nDecimals);
938 const char chFieldFill = DBFGetNullCharacter(chType);
941 for (
int i = psDBF->
nRecords - 1; i >= 0; --i) {
947 if (psDBF->
sHooks.
FRead(pszRecord, nOldRecordLength, 1, psDBF->
fp) !=
954 memset(pszRecord + nOldRecordLength, chFieldFill, nWidth);
995static void *DBFReadAttribute(
DBFHandle psDBF,
int hEntity,
int iField,
1001 if (hEntity < 0 || hEntity >= psDBF->
nRecords)
1004 if (iField < 0 || iField >= psDBF->
nFields)
1010 if (!DBFLoadRecord(psDBF, hEntity))
1013 const unsigned char *pabyRec =
1043 if (chReqType ==
'I') {
1048 else if (chReqType ==
'N') {
1058#ifdef TRIM_DBF_WHITESPACE
1061 char *pchDst = pchSrc;
1063 while (*pchSrc ==
' ')
1066 while (*pchSrc !=
'\0')
1067 *(pchDst++) = *(pchSrc++);
1070 while (pchDst != psDBF->
pszWorkField && *(--pchDst) ==
' ')
1075 return pReturnField;
1088 STATIC_CAST(
int *, DBFReadAttribute(psDBF, iRecord, iField,
'I'));
1106 STATIC_CAST(
double *, DBFReadAttribute(psDBF, iRecord, iField,
'N'));
1124 DBFReadAttribute(psDBF, iRecord, iField,
'C'));
1137 DBFReadAttribute(psDBF, iRecord, iField,
'L'));
1150 const char *, DBFReadAttribute(psDBF, iRecord, iField,
'D'));
1159 else if (3 != sscanf(pdateValue,
"%4d%2d%2d", &date.
year, &date.
month,
1175static bool DBFIsValueNULL(
char chType,
const char *pszValue,
int size)
1188 if (pszValue[0] ==
'*')
1191 for (
int i = 0; pszValue[i] !=
'\0'; i++) {
1192 if (pszValue[i] !=
' ')
1198 const char DIGIT_ZERO =
'0';
1208 if (pszValue[0] == 0 || strncmp(pszValue,
"00000000", 8) == 0 ||
1209 strcmp(pszValue,
" ") == 0 || strcmp(pszValue,
"0") == 0)
1211 for (
int i = 0; i < size; i++)
1212 if (pszValue[i] != DIGIT_ZERO)
1219 return pszValue[0] ==
'?';
1223 return strlen(pszValue) == 0;
1243 return DBFIsValueNULL(psDBF->
pachFieldType[iField], pszValue,
1278 char *pszFieldName,
int *pnWidth,
1281 if (iField < 0 || iField >= psDBF->
nFields)
1291 strncpy(pszFieldName,
1297 i > 0 && pszFieldName[i] ==
' '; i--)
1298 pszFieldName[i] =
'\0';
1326static bool DBFWriteAttribute(
DBFHandle psDBF,
int hEntity,
int iField,
1332 if (hEntity < 0 || hEntity > psDBF->
nRecords)
1336 DBFWriteHeader(psDBF);
1342 if (!DBFFlushRecord(psDBF))
1356 if (!DBFLoadRecord(psDBF, hEntity))
1359 unsigned char *pabyRec =
1380 bool nRetResult =
true;
1389 if (
STATIC_CAST(
int,
sizeof(szSField)) - 2 < nWidth)
1390 nWidth =
sizeof(szSField) - 2;
1393 snprintf(szFormat,
sizeof(szFormat),
"%%%d.%df", nWidth,
1395 const double value = *
STATIC_CAST(
double *, pValue);
1396 CPLsnprintf(szSField,
sizeof(szSField), szFormat, value);
1397 szSField[
sizeof(szSField) - 1] =
'\0';
1400 nRetResult = psDBF->
sHooks.
Atof(szSField) == value;
1404 szSField, strlen(szSField));
1452 int iField,
const void *pValue)
1457 if (hEntity < 0 || hEntity > psDBF->
nRecords)
1461 DBFWriteHeader(psDBF);
1467 if (!DBFFlushRecord(psDBF))
1481 if (!DBFLoadRecord(psDBF, hEntity))
1485 unsigned char *pabyRec =
1521 int iField,
double dValue)
1523 return (DBFWriteAttribute(psDBF, iRecord, iField,
1534 int iField,
int nValue)
1536 double dValue = nValue;
1538 return (DBFWriteAttribute(psDBF, iRecord, iField,
1549 int iField,
const char *pszValue)
1552 DBFWriteAttribute(psDBF, iRecord, iField,
1564 return (DBFWriteAttribute(psDBF, iRecord, iField,
SHPLIB_NULLPTR));
1574 int iField,
const char lValue)
1577 DBFWriteAttribute(psDBF, iRecord, iField,
1593 if (lValue->
year < 0 || lValue->
year > 9999)
1597 if (lValue->
day < 0 || lValue->
day > 99)
1600 snprintf(dateValue,
sizeof(dateValue),
"%04d%02d%02d", lValue->
year,
1612 const void *pRawTuple)
1617 if (hEntity < 0 || hEntity > psDBF->
nRecords)
1621 DBFWriteHeader(psDBF);
1627 if (!DBFFlushRecord(psDBF))
1641 if (!DBFLoadRecord(psDBF, hEntity))
1644 unsigned char *pabyRec =
1664 if (hEntity < 0 || hEntity >= psDBF->nRecords)
1667 if (!DBFLoadRecord(psDBF, hEntity))
1670 return STATIC_CAST(
const char *, psDBF->pszCurrentRecord);
1681 const char *pszFilename)
1702 sizeof(
int) * psDBF->
nFields);
1706 sizeof(
int) * psDBF->
nFields);
1710 sizeof(
int) * psDBF->
nFields);
1714 sizeof(
char) * psDBF->
nFields);
1720 DBFWriteHeader(newDBF);
1723 newDBF =
DBFOpen(pszFilename,
"rb+");
1742 if (iField >= 0 && iField < psDBF->nFields)
1757 const char *pszFieldName)
1781 if (iShape < 0 || iShape >= psDBF->
nRecords)
1787 if (!DBFLoadRecord(psDBF, iShape))
1806 if (iShape < 0 || iShape >= psDBF->
nRecords)
1813 if (!DBFLoadRecord(psDBF, iShape))
1819 const char chNewFlag = bIsDeleted ?
'*' :
' ';
1838 return psDBF->pszCodePage;
1849 if (iField < 0 || iField >= psDBF->
nFields)
1853 if (!DBFFlushRecord(psDBF))
1863 for (
int i = iField + 1; i < psDBF->
nFields; i++) {
1912 STATIC_CAST(
char *, malloc(
sizeof(
char) * nOldRecordLength));
1915 for (
int iRecord = 0; iRecord < psDBF->
nRecords; iRecord++) {
1922 if (psDBF->
sHooks.
FRead(pszRecord, nOldRecordLength, 1, psDBF->
fp) !=
1933 psDBF->
sHooks.
FWrite(pszRecord, nDeletedFieldOffset, 1, psDBF->
fp);
1935 pszRecord + nDeletedFieldOffset + nDeletedFieldSize,
1936 nOldRecordLength - nDeletedFieldOffset - nDeletedFieldSize, 1,
1978 if (!DBFFlushRecord(psDBF))
1983 int *panFieldOffsetNew =
1985 int *panFieldSizeNew =
1987 int *panFieldDecimalsNew =
1989 char *pachFieldTypeNew =
2002 if (!panFieldOffsetNew || !panFieldSizeNew || !panFieldDecimalsNew ||
2003 !pachFieldTypeNew || !pszHeaderNew ||
2005 (!pszRecord || !pszRecordNew))) {
2006 free(panFieldOffsetNew);
2007 free(panFieldSizeNew);
2008 free(panFieldDecimalsNew);
2009 free(pachFieldTypeNew);
2018 for (
int i = 0; i < psDBF->
nFields; i++) {
2025 panFieldOffsetNew[0] = 1;
2026 for (
int i = 1; i < psDBF->
nFields; i++) {
2027 panFieldOffsetNew[i] =
2028 panFieldOffsetNew[i - 1] + panFieldSizeNew[i - 1];
2034 bool errorAbort =
false;
2043 for (
int iRecord = 0; iRecord < psDBF->
nRecords; iRecord++) {
2056 pszRecordNew[0] = pszRecord[0];
2058 for (
int i = 0; i < psDBF->
nFields; i++) {
2059 memcpy(pszRecordNew + panFieldOffsetNew[i],
2076 free(panFieldOffsetNew);
2077 free(panFieldSizeNew);
2078 free(panFieldDecimalsNew);
2079 free(pachFieldTypeNew);
2110 const char *pszFieldName,
char chType,
2111 int nWidth,
int nDecimals)
2113 if (iField < 0 || iField >= psDBF->
nFields)
2117 if (!DBFFlushRecord(psDBF))
2120 const char chFieldFill = DBFGetNullCharacter(chType);
2137 char *, malloc(nOldRecordLength +
2138 ((nWidth > nOldWidth) ? nWidth - nOldWidth : 0)));
2139 char *pszOldField =
STATIC_CAST(
char *, malloc(nOldWidth + 1));
2140 if (!pszRecord || !pszOldField) {
2146 if (nWidth != nOldWidth) {
2150 if (!pszCurrentRecordNew) {
2177 if (chType ==
'C') {
2178 pszFInfo[16] =
STATIC_CAST(
unsigned char, nWidth % 256);
2179 pszFInfo[17] =
STATIC_CAST(
unsigned char, nWidth / 256);
2182 pszFInfo[16] =
STATIC_CAST(
unsigned char, nWidth);
2183 pszFInfo[17] =
STATIC_CAST(
unsigned char, nDecimals);
2189 if (nWidth != nOldWidth) {
2190 for (
int i = iField + 1; i < psDBF->
nFields; i++)
2206 bool errorAbort =
false;
2208 if (nWidth < nOldWidth || (nWidth == nOldWidth && chType != chOldType)) {
2209 pszOldField[nOldWidth] = 0;
2212 for (
int iRecord = 0; iRecord < psDBF->
nRecords; iRecord++) {
2219 if (psDBF->
sHooks.
FRead(pszRecord, nOldRecordLength, 1,
2225 memcpy(pszOldField, pszRecord + nOffset, nOldWidth);
2226 const bool bIsNULL =
2227 DBFIsValueNULL(chOldType, pszOldField, nOldWidth);
2229 if (nWidth != nOldWidth) {
2230 if ((chOldType ==
'N' || chOldType ==
'F' ||
2231 chOldType ==
'D') &&
2232 pszOldField[0] ==
' ') {
2234 memmove(pszRecord + nOffset,
2235 pszRecord + nOffset + nOldWidth - nWidth, nWidth);
2237 if (nOffset + nOldWidth < nOldRecordLength) {
2238 memmove(pszRecord + nOffset + nWidth,
2239 pszRecord + nOffset + nOldWidth,
2240 nOldRecordLength - (nOffset + nOldWidth));
2246 memset(pszRecord + nOffset, chFieldFill, nWidth);
2270 else if (nWidth > nOldWidth) {
2271 pszOldField[nOldWidth] = 0;
2274 for (
int iRecord = psDBF->
nRecords - 1; iRecord >= 0; iRecord--) {
2281 if (psDBF->
sHooks.
FRead(pszRecord, nOldRecordLength, 1,
2287 memcpy(pszOldField, pszRecord + nOffset, nOldWidth);
2288 const bool bIsNULL =
2289 DBFIsValueNULL(chOldType, pszOldField, nOldWidth);
2291 if (nOffset + nOldWidth < nOldRecordLength) {
2292 memmove(pszRecord + nOffset + nWidth,
2293 pszRecord + nOffset + nOldWidth,
2294 nOldRecordLength - (nOffset + nOldWidth));
2299 memset(pszRecord + nOffset, chFieldFill, nWidth);
2302 if ((chOldType ==
'N' || chOldType ==
'F')) {
2304 memmove(pszRecord + nOffset + nWidth - nOldWidth,
2305 pszRecord + nOffset, nOldWidth);
2306 memset(pszRecord + nOffset,
' ', nWidth - nOldWidth);
2310 memset(pszRecord + nOffset + nOldWidth,
' ',
2311 nWidth - nOldWidth);
DBFHandle DBFCloneEmpty(const DBFHandle psDBF, const char *pszFilename)
const char * DBFGetCodePage(const DBFHandle psDBF)
int DBFWriteIntegerAttribute(DBFHandle psDBF, int iRecord, int iField, int nValue)
const char * DBFReadTuple(DBFHandle psDBF, int hEntity)
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 * DBFReadLogicalAttribute(DBFHandle psDBF, int iRecord, int iField)
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)
const char * DBFReadStringAttribute(DBFHandle psDBF, int iRecord, int iField)
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)
#define CPL_IGNORE_RET_VAL_INT(x)
#define assert(condition)
void SASetupDefaultHooks(SAHooks *psHooks)
#define XBASE_FLDNAME_LEN_WRITE
#define XBASE_FLDNAME_LEN_READ
struct DBFInfo * DBFHandle
#define XBASE_FLD_MAX_WIDTH
#define STATIC_CAST(type, x)
#define REINTERPRET_CAST(type, x)
#define CONST_CAST(type, x)
union DBFInfo::@221027375174226143142314074277223114117142327265 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)