From a7f42c9f3764beae178c96897a4c97196714b245 Mon Sep 17 00:00:00 2001 From: Gregory Morse Date: Sat, 11 Jul 2026 02:46:02 +0200 Subject: [PATCH 1/6] Use fixed-width reads for Btrieve file fields --- BtrieveFileSaverLib.c | 185 ++++++++++++++++++++++++------------------ BtrieveFileSaverLib.h | 41 +++++----- 2 files changed, 129 insertions(+), 97 deletions(-) diff --git a/BtrieveFileSaverLib.c b/BtrieveFileSaverLib.c index f11d266..9ecd083 100644 --- a/BtrieveFileSaverLib.c +++ b/BtrieveFileSaverLib.c @@ -100,14 +100,51 @@ unsigned short int freeClient (CLIENT_STRUCT *cl, unsigned short int ErrorCode) * Just swap the bytes of a 32 bit int value * */ -static unsigned long int byte_swap(unsigned long int i ) -{ - return ((i>>16)&0xFFFF) | (i<<16); -} - -/* -* -* Function getPageType +static uint32_t byte_swap(uint32_t i ) +{ + return ((i>>16)&0xFFFF) | (i<<16); +} + +static uint16_t read_le16(const void *data) +{ + const unsigned char *bytes = (const unsigned char*)data; + return (uint16_t)(bytes[0] | ((uint16_t)bytes[1] << 8)); +} + +static uint32_t read_le32(const void *data) +{ + const unsigned char *bytes = (const unsigned char*)data; + return (uint32_t)bytes[0] | + ((uint32_t)bytes[1] << 8) | + ((uint32_t)bytes[2] << 16) | + ((uint32_t)bytes[3] << 24); +} + +static uint32_t read_word_swapped_u32(const void *data) +{ + return (uint32_t)byte_swap(read_le32(data)); +} + +static void write_le32(void *data, uint32_t value) +{ + unsigned char *bytes = (unsigned char*)data; + bytes[0] = (unsigned char)(value & 0xff); + bytes[1] = (unsigned char)((value >> 8) & 0xff); + bytes[2] = (unsigned char)((value >> 16) & 0xff); + bytes[3] = (unsigned char)((value >> 24) & 0xff); +} + +static uint32_t read_vrec_page_id_v5(const char *data) +{ + uint32_t value = (uint32_t)(unsigned char)data[0] | + ((uint32_t)(unsigned char)data[1] << 16) | + ((uint32_t)(unsigned char)data[2] << 24); + return value == PAGE_KICK_OFF ? value : byte_swap(value); +} + +/* +* +* Function getPageType * * cl CLIENT_STRUCT pointer * tmpPage char pointer // the page to be identified @@ -120,11 +157,11 @@ static unsigned long int byte_swap(unsigned long int i ) char getPageType (CLIENT_STRUCT *cl, char *tmpPage) { switch (cl->fVersion){ - case BTRIEVE_FILE_V3: - case BTRIEVE_FILE_V4: - case BTRIEVE_FILE_V5:{ - if (*(unsigned short int*)(tmpPage + 4) & 0x8000) return DAT_PAGE_ID; - }break; + case BTRIEVE_FILE_V3: + case BTRIEVE_FILE_V4: + case BTRIEVE_FILE_V5:{ + if (read_le16(tmpPage + 4) & 0x8000) return DAT_PAGE_ID; + }break; case BTRIEVE_FILE_V6: case BTRIEVE_FILE_V61: case BTRIEVE_FILE_V7:{ @@ -189,15 +226,15 @@ unsigned long int addPage (CLIENT_STRUCT *cl, char *tmpPage, char pageType) elementCnt = &cl->numVATPages; }else return NO_ERROR; - if (cl->fVersion >= BTRIEVE_FILE_V8){ - pId = *(short*) tmpPage; - pUsage = (short)*(tmpPage + 6); - }else{ - pId = *(unsigned long int*)tmpPage; - pId &= PAGE_KICK_OFF; - pId = byte_swap(pId); - pUsage = *(signed short*)(tmpPage + 4); - } + if (cl->fVersion >= BTRIEVE_FILE_V8){ + pId = read_le16(tmpPage); + pUsage = read_le16(tmpPage + 6); + }else{ + pId = read_le32(tmpPage); + pId &= PAGE_KICK_OFF; + pId = byte_swap(pId); + pUsage = read_le16(tmpPage + 4); + } /* check if you have allready a page using the same id but lower usage counter */ for (pCnt = 0; pCnt < *elementCnt; pCnt++){ @@ -278,12 +315,12 @@ unsigned short int readAllPagesFromFile (CLIENT_STRUCT *cl) char pType = 0x00; /* read page by page to check if there is a PAT page available */ - while ((pType = getNextPhysicalPage(cl, cl->CUR_DPAGE)) != 0x00) - addPage (cl, cl->CUR_DPAGE, pType); - /* reset the header of the data page container */ - *(unsigned long int*)cl->CUR_DPAGE = 0L; - return NO_ERROR; -} + while ((pType = getNextPhysicalPage(cl, cl->CUR_DPAGE)) != 0x00) + addPage (cl, cl->CUR_DPAGE, pType); + /* reset the header of the data page container */ + write_le32(cl->CUR_DPAGE, 0); + return NO_ERROR; +} /* * @@ -392,24 +429,24 @@ unsigned short int BF_OPEN (CLIENT_STRUCT *cl, char *fName) } if (fseek (cl->fHandle, cl->fPageSize, SEEK_SET) != NO_ERROR) return IO_ERROR; /* read the shadow page to validate both pairs and retrieve the number of records */ - if ((fread (&lcFCR_shadow, sizeof(FCR), 1, cl->fHandle)) != 1) return freeClient (cl, IO_ERROR); - if (lcFCR.FCRUsageCount > lcFCR_shadow.FCRUsageCount) - cl->numRecs = byte_swap (*((unsigned long*)&lcFCR.numRecs)); - else - cl->numRecs = byte_swap (*((unsigned long*)&lcFCR_shadow.numRecs)); - }else{ /* less then 6x format */ - cl->fPageSize = lcFCR.PageSize; - cl->VFragParam = VFRAG_CUT_V5; - cl->numRecs = byte_swap (*((unsigned long*)&lcFCR.numRecs)); - } + if ((fread (&lcFCR_shadow, sizeof(FCR), 1, cl->fHandle)) != 1) return freeClient (cl, IO_ERROR); + if (lcFCR.FCRUsageCount > lcFCR_shadow.FCRUsageCount) + cl->numRecs = read_word_swapped_u32(lcFCR.numRecs); + else + cl->numRecs = read_word_swapped_u32(lcFCR_shadow.numRecs); + }else{ /* less then 6x format */ + cl->fPageSize = lcFCR.PageSize; + cl->VFragParam = VFRAG_CUT_V5; + cl->numRecs = read_word_swapped_u32(lcFCR.numRecs); + } if (cl->fVersion >= BTRIEVE_FILE_V6 && cl->fVersion < BTRIEVE_FILE_V7){ cl->numPointerPerPat = (cl->fPageSize - PAT_PAGE_HEADER_SIZE_V6) / PAT_POINTER_SIZE_V6; } - /* allocate memory to hold the current data page */ - if ((cl->CUR_DPAGE = (char*) malloc (cl->fPageSize)) == NULL) return IO_ERROR; - else *(long*)cl->CUR_DPAGE = 0L; + /* allocate memory to hold the current data page */ + if ((cl->CUR_DPAGE = (char*) malloc (cl->fPageSize)) == NULL) return IO_ERROR; + else write_le32(cl->CUR_DPAGE, 0); /* get the max number of pages */ fseek (cl->fHandle, 0, SEEK_END); @@ -421,10 +458,10 @@ unsigned short int BF_OPEN (CLIENT_STRUCT *cl, char *fName) sortPages (cl, DAT_PAGE_ID); /* if var-rec's, allocate memory to hold the current var-data page */ - if (cl->VarRecsAllowed){ - if ((cl->CUR_VPAGE = (char*) malloc (cl->fPageSize)) == NULL) return IO_ERROR; - else *(long*)cl->CUR_VPAGE = 0L; - } + if (cl->VarRecsAllowed){ + if ((cl->CUR_VPAGE = (char*) malloc (cl->fPageSize)) == NULL) return IO_ERROR; + else write_le32(cl->CUR_VPAGE, 0); + } return NO_ERROR; } @@ -462,10 +499,13 @@ static long int lp_pp(CLIENT_STRUCT *cl, long int lp ) ret += (long)(( lp << 2 ) + 4L ); // position in PAT fseek( cl->fHandle, ret, 0 ); - fread( &lp, 4, 1, cl->fHandle ); // read it into LP - - lp = byte_swap( lp ); // and un-word-swap it - lp &= 0xFFFFFFL; + { + unsigned char rawLp[4] = {0, 0, 0, 0}; + fread( rawLp, 4, 1, cl->fHandle ); // read it into LP + + lp = byte_swap( read_le32(rawLp) ); // and un-word-swap it + } + lp &= 0xFFFFFFL; } if( lp == 0xFFFFFFL || lp == -1L ) // NULL pointer values @@ -499,10 +539,10 @@ unsigned short int getNextDataPage (CLIENT_STRUCT *cl) if ((retval = readPageFromFile (cl, cl->CUR_DPAGE, pOff)) != NO_ERROR) return retval; - if (cl->fVersion == BTRIEVE_FILE_V6 && *(unsigned short*)(cl->CUR_DPAGE+2) != cPId -1){ - *(unsigned long*)cl->CUR_DPAGE = 0L; - continue; - } + if (cl->fVersion == BTRIEVE_FILE_V6 && read_le16(cl->CUR_DPAGE+2) != cPId -1){ + write_le32(cl->CUR_DPAGE, 0); + continue; + } if (cl->fVersion == BTRIEVE_FILE_V3){ unsigned long usage = 0L; @@ -534,7 +574,7 @@ unsigned short int getVariableData (CLIENT_STRUCT *cl, char *dataBuffer, unsigne { unsigned short int numFrag = 0; unsigned short int retval = 0; - unsigned long int VPageId = 0; + uint32_t VPageId = 0; unsigned short int FragId = 0; unsigned short int FragOff = 0; unsigned long int pCnt = 0L; @@ -546,17 +586,13 @@ unsigned short int getVariableData (CLIENT_STRUCT *cl, char *dataBuffer, unsigne if (dataBuffer && *dbLen > 0) memcpy (dataBuffer, curRecAdr + cl->recHeaderSize, (cl->FixRecLen > *dbLen) ? *dbLen : cl->FixRecLen); - if (cl->fVersion >= BTRIEVE_FILE_V8){ - VPageId = *(short*)(curRecAdr + cl->FixRecLen + cl->recHeaderSize); - numFrag = *(short*)(curRecAdr + cl->FixRecLen + 10); - }else{ - ((unsigned char*)&VPageId)[0] = * (curRecAdr + cl->recHeaderSize + curLen); - ((unsigned char*)&VPageId)[1] = 0x00; - ((unsigned char*)&VPageId)[2] = *(curRecAdr + cl->recHeaderSize + curLen + 1); - ((unsigned char*)&VPageId)[3] = *(curRecAdr + cl->recHeaderSize + curLen + 2); - ((unsigned char*)&numFrag)[0] = *(curRecAdr + cl->recHeaderSize + curLen + 3); - if (VPageId != PAGE_KICK_OFF) VPageId = byte_swap (VPageId); - } + if (cl->fVersion >= BTRIEVE_FILE_V8){ + VPageId = read_le16(curRecAdr + cl->FixRecLen + cl->recHeaderSize); + numFrag = read_le16(curRecAdr + cl->FixRecLen + 10); + }else{ + VPageId = read_vrec_page_id_v5(curRecAdr + cl->recHeaderSize + curLen); + numFrag = (unsigned char)*(curRecAdr + cl->recHeaderSize + curLen + 3); + } while (VPageId != PAGE_KICK_OFF && FragId != MAX_SHORT_INT_VAL){ /* get the VAT page offset within the file */ @@ -590,16 +626,11 @@ unsigned short int getVariableData (CLIENT_STRUCT *cl, char *dataBuffer, unsigne if (addLen > cl->fPageSize * 2 || addLen == 0) break; if (cl->fVersion >= BTRIEVE_FILE_V8){ - VPageId = *(short*)(&((char*)cl->CUR_VPAGE)[ FragOff]); - numFrag = *(short*)(&((char*)cl->CUR_VPAGE)[ FragOff +4]); - }else{ - ((unsigned char*)&VPageId)[0] = * (cl->CUR_VPAGE + FragOff); - ((unsigned char*)&VPageId)[1] = 0x00; - ((unsigned char*)&VPageId)[2] = *(cl->CUR_VPAGE + FragOff + 1); - ((unsigned char*)&VPageId)[3] = *(cl->CUR_VPAGE + FragOff + 2); - ((unsigned char*)&numFrag)[0] = *(cl->CUR_VPAGE + FragOff + 3); - - if (VPageId != PAGE_KICK_OFF) VPageId = byte_swap (VPageId); + VPageId = read_le16(&((char*)cl->CUR_VPAGE)[ FragOff]); + numFrag = read_le16(&((char*)cl->CUR_VPAGE)[ FragOff +4]); + }else{ + VPageId = read_vrec_page_id_v5(cl->CUR_VPAGE + FragOff); + numFrag = (unsigned char)*(cl->CUR_VPAGE + FragOff + 3); if (cl->fVersion < BTRIEVE_FILE_V6){ if (((short*)cl->CUR_VPAGE)[ FragId] & 0x8000){ @@ -675,13 +706,13 @@ unsigned short int BF_GET_REC (CLIENT_STRUCT *cl, char *dataBuffer, unsigned lon if (cl->numRecs == 0L) return END_OF_FILE; while ((retval = getNextRecord (cl)) == NO_ERROR){ - if (cl->fVersion >= BTRIEVE_FILE_V6 && *(unsigned short*)(cl->curRecAdr) != 0x0000){ + if (cl->fVersion >= BTRIEVE_FILE_V6 && read_le16(cl->curRecAdr) != 0x0000){ break; }else if (cl->fVersion < BTRIEVE_FILE_V6){ unsigned long int i = 0; char isEmpty = true; - if (cl->fVersion == BTRIEVE_FILE_V3 && *(unsigned short int*)cl->curRecAdr == 0x0000) continue; + if (cl->fVersion == BTRIEVE_FILE_V3 && read_le16(cl->curRecAdr) == 0x0000) continue; for (i = ((cl->fVersion >= BTRIEVE_FILE_V4)? RECORD_HEADER_SIZE_V4 : RECORD_HEADER_SIZE_V3) ; i < cl->IFixedRecLen ; i++){ diff --git a/BtrieveFileSaverLib.h b/BtrieveFileSaverLib.h index 5a42f4f..04ef7d0 100644 --- a/BtrieveFileSaverLib.h +++ b/BtrieveFileSaverLib.h @@ -31,8 +31,9 @@ #include #endif -#include -#include +#include +#include +#include #pragma pack(push) #pragma pack(1) @@ -62,11 +63,11 @@ /* project defines */ #define MAX_SINGLE_INT_VAL 0xFF // single byte int max value #define MAX_SHORT_INT_VAL 0xFFFF // 2 byte int max value -#define MAX_LONG_INT_VAL 0xFFFFFFFF // 4 byte int max value - -#define PAGE_KICK_OFF 0xFFFF00FF // define to overcome the page id at three byte page header - -#define FCR_IDENT 0x00004346 // byte swaped 'FC' +#define MAX_LONG_INT_VAL UINT32_C(0xFFFFFFFF) // 4 byte int max value + +#define PAGE_KICK_OFF UINT32_C(0xFFFF00FF) // define to overcome the page id at three byte page header + +#define FCR_IDENT UINT32_C(0x00004346) // byte swaped 'FC' /* File type/feature flags */ @@ -125,8 +126,8 @@ /* macro definition */ -#define ReverseLongInt(x) ((x) << 16 | (x) >> 16) -#define VRPage(x) (long) (((long) (x).hi << 16) | ((x).mid << 8) | (x).lo) +#define ReverseLongInt(x) (((uint32_t)(x) << 16) | ((uint32_t)(x) >> 16)) +#define VRPage(x) (uint32_t) (((uint32_t) (x).hi << 16) | ((uint32_t)(x).mid << 8) | (x).lo) /* struct defines */ typedef struct @@ -137,16 +138,16 @@ typedef struct unsigned char frag; }VRECPTR; -typedef struct{ - unsigned long int pId; /* page id, note that it is 16Bit */ - unsigned long int offset; /* physical position within the file */ - unsigned short int indexA; /* sort order based on pID */ -}PAGE_LINK; - -typedef struct{ - unsigned long int PageId; - unsigned short int FCRUsageCount; - short int Version; +typedef struct{ + uint32_t pId; /* page id, note that it is 16Bit */ + unsigned long int offset; /* physical position within the file */ + unsigned short int indexA; /* sort order based on pID */ +}PAGE_LINK; + +typedef struct{ + uint32_t PageId; + unsigned short int FCRUsageCount; + short int Version; short int PageSize; char buffer1[12]; unsigned short int FixedRecordLength; /* 0 means compressed */ @@ -201,4 +202,4 @@ unsigned short int BF_OPEN (CLIENT_STRUCT *cl, char *fName); unsigned short int BF_GET_REC (CLIENT_STRUCT *cl, char *dataBuffer, unsigned long *dbLen); unsigned short int BF_CLOSE (CLIENT_STRUCT *cl); -#pragma pack(pop) \ No newline at end of file +#pragma pack(pop) From c33c91bc1668c72bff18f14e84a8a8835c7e7739 Mon Sep 17 00:00:00 2001 From: Gregory Morse Date: Sat, 11 Jul 2026 03:02:16 +0200 Subject: [PATCH 2/6] Avoid unsigned long wraparound for record counter --- BtrieveFileSaverLib.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/BtrieveFileSaverLib.c b/BtrieveFileSaverLib.c index 9ecd083..210c44d 100644 --- a/BtrieveFileSaverLib.c +++ b/BtrieveFileSaverLib.c @@ -729,9 +729,12 @@ unsigned short int BF_GET_REC (CLIENT_STRUCT *cl, char *dataBuffer, unsigned lon if (retval != NO_ERROR) return retval; - cl->curRecordId ++; - - if (cl->VarRecsAllowed){ + if (cl->curRecordId == MAX_LONG_INT_VAL) + cl->curRecordId = 0; + else + cl->curRecordId ++; + + if (cl->VarRecsAllowed){ retval = getVariableData (cl, dataBuffer, dbLen, cl->curRecAdr); }else{ if (dataBuffer && *dbLen > 0) From 0b671c0306a17fc15f94ed1a0ad517bfc7fbe108 Mon Sep 17 00:00:00 2001 From: Gregory Morse Date: Sun, 12 Jul 2026 22:22:34 +0200 Subject: [PATCH 3/6] Prevent Btrieve data-page traversal loops --- BtrieveFileSaverLib.c | 49 ++++++++++++++----------------------------- BtrieveFileSaverLib.h | 1 + 2 files changed, 17 insertions(+), 33 deletions(-) diff --git a/BtrieveFileSaverLib.c b/BtrieveFileSaverLib.c index 210c44d..c68777f 100644 --- a/BtrieveFileSaverLib.c +++ b/BtrieveFileSaverLib.c @@ -524,40 +524,23 @@ static long int lp_pp(CLIENT_STRUCT *cl, long int lp ) * Jump to the next available Data page and if non, return END_OF_FILE. * */ -unsigned short int getNextDataPage (CLIENT_STRUCT *cl) -{ - unsigned short int retval = NO_ERROR; - unsigned long int pOff; - unsigned long int cPId = cl->curDPageID; - - memset (cl->CUR_DPAGE, 0x00, cl->fPageSize); - - while ((getPageType (cl, cl->CUR_DPAGE)) != DAT_PAGE_ID /*&& cPId < cl->fNumPages-2*/){ - pOff = lp_pp (cl, cPId++); - - if (pOff > (cl->fNumPages * cl->fPageSize)) continue; - - if ((retval = readPageFromFile (cl, cl->CUR_DPAGE, pOff)) != NO_ERROR) return retval; - - if (cl->fVersion == BTRIEVE_FILE_V6 && read_le16(cl->CUR_DPAGE+2) != cPId -1){ - write_le32(cl->CUR_DPAGE, 0); +unsigned short int getNextDataPage (CLIENT_STRUCT *cl) +{ + while (cl->nextDataPageIndex < cl->numDATPages){ + PAGE_LINK *page = &cl->DATArr[cl->nextDataPageIndex++]; + + memset (cl->CUR_DPAGE, 0x00, cl->fPageSize); + if (readPageFromFile (cl, cl->CUR_DPAGE, page->offset) != NO_ERROR) + return IO_ERROR; + if (getPageType (cl, cl->CUR_DPAGE) != DAT_PAGE_ID) continue; - } - - if (cl->fVersion == BTRIEVE_FILE_V3){ - unsigned long usage = 0L; - ((unsigned char*)&usage)[2] = cl->CUR_DPAGE[4]; - ((unsigned char*)&usage)[1] = cl->CUR_DPAGE[6]; - ((unsigned char*)&usage)[0] = cl->CUR_DPAGE[7]; - if (usage == 0){ - memset (cl->CUR_DPAGE, 0x00 , 10); - continue; - } - } - } - cl->curDPageID = cPId; - return NO_ERROR; -} + + cl->curDPageID = page->pId; + return NO_ERROR; + } + + return END_OF_FILE; +} /* * diff --git a/BtrieveFileSaverLib.h b/BtrieveFileSaverLib.h index 04ef7d0..0554348 100644 --- a/BtrieveFileSaverLib.h +++ b/BtrieveFileSaverLib.h @@ -179,6 +179,7 @@ typedef struct{ unsigned short int IFixedRecLen; /* internal phys rec len */ char recHeaderSize; /* header size of each rec */ unsigned long int curDPageID; /* Id of the currently in use data page */ + unsigned long int nextDataPageIndex; /* next entry in the validated DATArr page table */ char *curRecAdr; /* currently used record adrress*/ unsigned long int curRecOff; /* Offset of the current record within the data page */ /* From 6d15b3ddba9b2edd3c77ac256ddfc03c89115a8c Mon Sep 17 00:00:00 2001 From: Gregory Morse Date: Sun, 12 Jul 2026 22:28:15 +0200 Subject: [PATCH 4/6] Preserve legacy page validation during bounded traversal --- BtrieveFileSaverLib.c | 83 +++++++++++++++++++++++++++++-------------- BtrieveFileSaverLib.h | 1 - 2 files changed, 56 insertions(+), 28 deletions(-) diff --git a/BtrieveFileSaverLib.c b/BtrieveFileSaverLib.c index c68777f..b35cb54 100644 --- a/BtrieveFileSaverLib.c +++ b/BtrieveFileSaverLib.c @@ -472,10 +472,10 @@ unsigned short int BF_OPEN (CLIENT_STRUCT *cl, char *fName) * a file offset, using the PAT for new format files. * * * \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -static long int lp_pp(CLIENT_STRUCT *cl, long int lp ) -{ - unsigned long int ret, pat1, pat2; - unsigned short int u1, u2, pppat; +static long int lp_pp(CLIENT_STRUCT *cl, long int lp ) +{ + unsigned long int ret, pat1, pat2; + unsigned short int u1, u2, pppat; if (cl->fVersion > BTRIEVE_FILE_V5){ ret = 2; // first PAT pair on 2, 3 @@ -488,10 +488,16 @@ static long int lp_pp(CLIENT_STRUCT *cl, long int lp ) pat1 = ret * (long)cl->fPageSize; // first PAT of pair pat2 = pat1 + (long)cl->fPageSize; // second right after it - fseek( cl->fHandle, pat1+4L, 0 ); // get both usage counts - fread( &u1, 2, 1, cl->fHandle ); - fseek( cl->fHandle, pat2+4L, 0 ); - fread( &u2, 2, 1, cl->fHandle ); + { + unsigned char rawUsage[2]; + + fseek( cl->fHandle, pat1+4L, 0 ); // get both usage counts + fread( rawUsage, sizeof(rawUsage), 1, cl->fHandle ); + u1 = read_le16(rawUsage); + fseek( cl->fHandle, pat2+4L, 0 ); + fread( rawUsage, sizeof(rawUsage), 1, cl->fHandle ); + u2 = read_le16(rawUsage); + } if( u1 > u2 ) // choose most recent one ret = pat1; else @@ -525,21 +531,44 @@ static long int lp_pp(CLIENT_STRUCT *cl, long int lp ) * */ unsigned short int getNextDataPage (CLIENT_STRUCT *cl) -{ - while (cl->nextDataPageIndex < cl->numDATPages){ - PAGE_LINK *page = &cl->DATArr[cl->nextDataPageIndex++]; +{ + unsigned short int retval = NO_ERROR; + long int pOff; + unsigned long int cPId = cl->curDPageID; - memset (cl->CUR_DPAGE, 0x00, cl->fPageSize); - if (readPageFromFile (cl, cl->CUR_DPAGE, page->offset) != NO_ERROR) - return IO_ERROR; - if (getPageType (cl, cl->CUR_DPAGE) != DAT_PAGE_ID) + memset (cl->CUR_DPAGE, 0x00, cl->fPageSize); + + while (getPageType (cl, cl->CUR_DPAGE) != DAT_PAGE_ID){ + uint64_t fileSize; + + if (cPId >= cl->fNumPages) + return END_OF_FILE; + + pOff = lp_pp (cl, cPId++); + fileSize = (uint64_t)cl->fNumPages * cl->fPageSize; + if (pOff < 0 || (uint64_t)pOff >= fileSize) continue; - cl->curDPageID = page->pId; - return NO_ERROR; - } + if ((retval = readPageFromFile (cl, cl->CUR_DPAGE, (unsigned long)pOff)) != NO_ERROR) + return retval; - return END_OF_FILE; + if (cl->fVersion == BTRIEVE_FILE_V6 && read_le16(cl->CUR_DPAGE + 2) != cPId - 1){ + write_le32(cl->CUR_DPAGE, 0); + continue; + } + + if (cl->fVersion == BTRIEVE_FILE_V3){ + uint32_t usage = ((uint32_t)(unsigned char)cl->CUR_DPAGE[4] << 16) + | ((uint32_t)(unsigned char)cl->CUR_DPAGE[6] << 8) + | (unsigned char)cl->CUR_DPAGE[7]; + if (usage == 0){ + memset (cl->CUR_DPAGE, 0x00, 10); + continue; + } + } + } + cl->curDPageID = cPId; + return NO_ERROR; } /* @@ -598,13 +627,13 @@ unsigned short int getVariableData (CLIENT_STRUCT *cl, char *dataBuffer, unsigne || (fread (cl->CUR_VPAGE, cl->fPageSize, 1, cl->fHandle)) != 1) return IO_ERROR; FragId = ((cl->fPageSize -1) >> 1) - numFrag; - FragOff = ((short*)cl->CUR_VPAGE)[FragId] & 0x7FFF; - - /* get to the end of the fragment */ - for(fEId = 1; ((short*)cl->CUR_VPAGE)[FragId - fEId] == -1; fEId++); - - /* calculate the length of the fragment */ - addLen = (((short*)cl->CUR_VPAGE)[ FragId - fEId] & 0x7FFF ) - FragOff - cl->VFragParam; + FragOff = read_le16(cl->CUR_VPAGE + (FragId * sizeof(uint16_t))) & 0x7FFF; + + /* get to the end of the fragment */ + for(fEId = 1; read_le16(cl->CUR_VPAGE + ((FragId - fEId) * sizeof(uint16_t))) == UINT16_MAX; fEId++); + + /* calculate the length of the fragment */ + addLen = (read_le16(cl->CUR_VPAGE + ((FragId - fEId) * sizeof(uint16_t))) & 0x7FFF) - FragOff - cl->VFragParam; if (addLen > cl->fPageSize * 2 || addLen == 0) break; @@ -616,7 +645,7 @@ unsigned short int getVariableData (CLIENT_STRUCT *cl, char *dataBuffer, unsigne numFrag = (unsigned char)*(cl->CUR_VPAGE + FragOff + 3); if (cl->fVersion < BTRIEVE_FILE_V6){ - if (((short*)cl->CUR_VPAGE)[ FragId] & 0x8000){ + if (read_le16(cl->CUR_VPAGE + (FragId * sizeof(uint16_t))) & 0x8000){ FragOff += sizeof (VRECPTR); addLen -= sizeof(VRECPTR); }else VPageId = PAGE_KICK_OFF; diff --git a/BtrieveFileSaverLib.h b/BtrieveFileSaverLib.h index 0554348..04ef7d0 100644 --- a/BtrieveFileSaverLib.h +++ b/BtrieveFileSaverLib.h @@ -179,7 +179,6 @@ typedef struct{ unsigned short int IFixedRecLen; /* internal phys rec len */ char recHeaderSize; /* header size of each rec */ unsigned long int curDPageID; /* Id of the currently in use data page */ - unsigned long int nextDataPageIndex; /* next entry in the validated DATArr page table */ char *curRecAdr; /* currently used record adrress*/ unsigned long int curRecOff; /* Offset of the current record within the data page */ /* From fad23d095ac6ca2bcf23bc96b4ca3c621253b093 Mon Sep 17 00:00:00 2001 From: Gregory Morse Date: Tue, 14 Jul 2026 01:47:19 +0200 Subject: [PATCH 5/6] Make Btrieve decoding portable across modern compilers --- BtrieveFileSaverLib.c | 369 +++++++++++++++++++++--------------------- 1 file changed, 184 insertions(+), 185 deletions(-) diff --git a/BtrieveFileSaverLib.c b/BtrieveFileSaverLib.c index b35cb54..c6c275f 100644 --- a/BtrieveFileSaverLib.c +++ b/BtrieveFileSaverLib.c @@ -68,6 +68,12 @@ ________________________________________________________________________________ #include "BtrieveFileSaverLib.h" +#if defined(_MSC_VER) && _MSC_VER >= 1950 +#define BTRIEVE_VS2026_NOINLINE __declspec(noinline) +#else +#define BTRIEVE_VS2026_NOINLINE +#endif + /* * * Function freeClient @@ -100,51 +106,51 @@ unsigned short int freeClient (CLIENT_STRUCT *cl, unsigned short int ErrorCode) * Just swap the bytes of a 32 bit int value * */ -static uint32_t byte_swap(uint32_t i ) -{ - return ((i>>16)&0xFFFF) | (i<<16); -} - -static uint16_t read_le16(const void *data) -{ - const unsigned char *bytes = (const unsigned char*)data; - return (uint16_t)(bytes[0] | ((uint16_t)bytes[1] << 8)); -} - -static uint32_t read_le32(const void *data) -{ - const unsigned char *bytes = (const unsigned char*)data; - return (uint32_t)bytes[0] | - ((uint32_t)bytes[1] << 8) | - ((uint32_t)bytes[2] << 16) | - ((uint32_t)bytes[3] << 24); -} - -static uint32_t read_word_swapped_u32(const void *data) -{ - return (uint32_t)byte_swap(read_le32(data)); -} - -static void write_le32(void *data, uint32_t value) -{ - unsigned char *bytes = (unsigned char*)data; - bytes[0] = (unsigned char)(value & 0xff); - bytes[1] = (unsigned char)((value >> 8) & 0xff); - bytes[2] = (unsigned char)((value >> 16) & 0xff); - bytes[3] = (unsigned char)((value >> 24) & 0xff); -} - -static uint32_t read_vrec_page_id_v5(const char *data) -{ - uint32_t value = (uint32_t)(unsigned char)data[0] | - ((uint32_t)(unsigned char)data[1] << 16) | - ((uint32_t)(unsigned char)data[2] << 24); - return value == PAGE_KICK_OFF ? value : byte_swap(value); -} - -/* -* -* Function getPageType +static uint32_t byte_swap(uint32_t i ) +{ + return ((i>>16)&0xFFFF) | (i<<16); +} + +static uint16_t read_le16(const void *data) +{ + const unsigned char *bytes = (const unsigned char*)data; + return (uint16_t)(bytes[0] | ((uint16_t)bytes[1] << 8)); +} + +static uint32_t read_le32(const void *data) +{ + const unsigned char *bytes = (const unsigned char*)data; + return (uint32_t)bytes[0] | + ((uint32_t)bytes[1] << 8) | + ((uint32_t)bytes[2] << 16) | + ((uint32_t)bytes[3] << 24); +} + +static uint32_t read_word_swapped_u32(const void *data) +{ + return (uint32_t)byte_swap(read_le32(data)); +} + +static void write_le32(void *data, uint32_t value) +{ + unsigned char *bytes = (unsigned char*)data; + bytes[0] = (unsigned char)(value & 0xff); + bytes[1] = (unsigned char)((value >> 8) & 0xff); + bytes[2] = (unsigned char)((value >> 16) & 0xff); + bytes[3] = (unsigned char)((value >> 24) & 0xff); +} + +static uint32_t read_vrec_page_id_v5(const char *data) +{ + uint32_t value = (uint32_t)(unsigned char)data[0] | + ((uint32_t)(unsigned char)data[1] << 16) | + ((uint32_t)(unsigned char)data[2] << 24); + return value == PAGE_KICK_OFF ? value : byte_swap(value); +} + +/* +* +* Function getPageType * * cl CLIENT_STRUCT pointer * tmpPage char pointer // the page to be identified @@ -154,26 +160,19 @@ static uint32_t read_vrec_page_id_v5(const char *data) * this way. * */ -char getPageType (CLIENT_STRUCT *cl, char *tmpPage) +BTRIEVE_VS2026_NOINLINE char getPageType (CLIENT_STRUCT *cl, char *tmpPage) { - switch (cl->fVersion){ - case BTRIEVE_FILE_V3: - case BTRIEVE_FILE_V4: - case BTRIEVE_FILE_V5:{ - if (read_le16(tmpPage + 4) & 0x8000) return DAT_PAGE_ID; - }break; - case BTRIEVE_FILE_V6: - case BTRIEVE_FILE_V61: - case BTRIEVE_FILE_V7:{ - return tmpPage[1]; - }break; - case BTRIEVE_FILE_V8: - case BTRIEVE_FILE_V9: -// case BTRIEVE_FILE_V95: - { - return tmpPage[4]; - }break; - } + const uint16_t majorVersion = (uint16_t)cl->fVersion >> 8; + + if (majorVersion >= 3 && majorVersion <= 5) + return (read_le16(tmpPage + 4) & 0x8000) ? DAT_PAGE_ID : 0x00; + + if (majorVersion == 6 || majorVersion == 7) + return tmpPage[1]; + + if (majorVersion == 8 || majorVersion == 9) + return tmpPage[4]; + return 0x00; } @@ -226,15 +225,15 @@ unsigned long int addPage (CLIENT_STRUCT *cl, char *tmpPage, char pageType) elementCnt = &cl->numVATPages; }else return NO_ERROR; - if (cl->fVersion >= BTRIEVE_FILE_V8){ - pId = read_le16(tmpPage); - pUsage = read_le16(tmpPage + 6); - }else{ - pId = read_le32(tmpPage); - pId &= PAGE_KICK_OFF; - pId = byte_swap(pId); - pUsage = read_le16(tmpPage + 4); - } + if (cl->fVersion >= BTRIEVE_FILE_V8){ + pId = read_le16(tmpPage); + pUsage = read_le16(tmpPage + 6); + }else{ + pId = read_le32(tmpPage); + pId &= PAGE_KICK_OFF; + pId = byte_swap(pId); + pUsage = read_le16(tmpPage + 4); + } /* check if you have allready a page using the same id but lower usage counter */ for (pCnt = 0; pCnt < *elementCnt; pCnt++){ @@ -315,12 +314,12 @@ unsigned short int readAllPagesFromFile (CLIENT_STRUCT *cl) char pType = 0x00; /* read page by page to check if there is a PAT page available */ - while ((pType = getNextPhysicalPage(cl, cl->CUR_DPAGE)) != 0x00) - addPage (cl, cl->CUR_DPAGE, pType); - /* reset the header of the data page container */ - write_le32(cl->CUR_DPAGE, 0); - return NO_ERROR; -} + while ((pType = getNextPhysicalPage(cl, cl->CUR_DPAGE)) != 0x00) + addPage (cl, cl->CUR_DPAGE, pType); + /* reset the header of the data page container */ + write_le32(cl->CUR_DPAGE, 0); + return NO_ERROR; +} /* * @@ -429,24 +428,24 @@ unsigned short int BF_OPEN (CLIENT_STRUCT *cl, char *fName) } if (fseek (cl->fHandle, cl->fPageSize, SEEK_SET) != NO_ERROR) return IO_ERROR; /* read the shadow page to validate both pairs and retrieve the number of records */ - if ((fread (&lcFCR_shadow, sizeof(FCR), 1, cl->fHandle)) != 1) return freeClient (cl, IO_ERROR); - if (lcFCR.FCRUsageCount > lcFCR_shadow.FCRUsageCount) - cl->numRecs = read_word_swapped_u32(lcFCR.numRecs); - else - cl->numRecs = read_word_swapped_u32(lcFCR_shadow.numRecs); - }else{ /* less then 6x format */ - cl->fPageSize = lcFCR.PageSize; - cl->VFragParam = VFRAG_CUT_V5; - cl->numRecs = read_word_swapped_u32(lcFCR.numRecs); - } + if ((fread (&lcFCR_shadow, sizeof(FCR), 1, cl->fHandle)) != 1) return freeClient (cl, IO_ERROR); + if (lcFCR.FCRUsageCount > lcFCR_shadow.FCRUsageCount) + cl->numRecs = read_word_swapped_u32(lcFCR.numRecs); + else + cl->numRecs = read_word_swapped_u32(lcFCR_shadow.numRecs); + }else{ /* less then 6x format */ + cl->fPageSize = lcFCR.PageSize; + cl->VFragParam = VFRAG_CUT_V5; + cl->numRecs = read_word_swapped_u32(lcFCR.numRecs); + } if (cl->fVersion >= BTRIEVE_FILE_V6 && cl->fVersion < BTRIEVE_FILE_V7){ cl->numPointerPerPat = (cl->fPageSize - PAT_PAGE_HEADER_SIZE_V6) / PAT_POINTER_SIZE_V6; } - /* allocate memory to hold the current data page */ - if ((cl->CUR_DPAGE = (char*) malloc (cl->fPageSize)) == NULL) return IO_ERROR; - else write_le32(cl->CUR_DPAGE, 0); + /* allocate memory to hold the current data page */ + if ((cl->CUR_DPAGE = (char*) malloc (cl->fPageSize)) == NULL) return IO_ERROR; + else write_le32(cl->CUR_DPAGE, 0); /* get the max number of pages */ fseek (cl->fHandle, 0, SEEK_END); @@ -458,10 +457,10 @@ unsigned short int BF_OPEN (CLIENT_STRUCT *cl, char *fName) sortPages (cl, DAT_PAGE_ID); /* if var-rec's, allocate memory to hold the current var-data page */ - if (cl->VarRecsAllowed){ - if ((cl->CUR_VPAGE = (char*) malloc (cl->fPageSize)) == NULL) return IO_ERROR; - else write_le32(cl->CUR_VPAGE, 0); - } + if (cl->VarRecsAllowed){ + if ((cl->CUR_VPAGE = (char*) malloc (cl->fPageSize)) == NULL) return IO_ERROR; + else write_le32(cl->CUR_VPAGE, 0); + } return NO_ERROR; } @@ -472,10 +471,10 @@ unsigned short int BF_OPEN (CLIENT_STRUCT *cl, char *fName) * a file offset, using the PAT for new format files. * * * \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -static long int lp_pp(CLIENT_STRUCT *cl, long int lp ) -{ - unsigned long int ret, pat1, pat2; - unsigned short int u1, u2, pppat; +static long int lp_pp(CLIENT_STRUCT *cl, long int lp ) +{ + unsigned long int ret, pat1, pat2; + unsigned short int u1, u2, pppat; if (cl->fVersion > BTRIEVE_FILE_V5){ ret = 2; // first PAT pair on 2, 3 @@ -488,16 +487,16 @@ static long int lp_pp(CLIENT_STRUCT *cl, long int lp ) pat1 = ret * (long)cl->fPageSize; // first PAT of pair pat2 = pat1 + (long)cl->fPageSize; // second right after it - { - unsigned char rawUsage[2]; - - fseek( cl->fHandle, pat1+4L, 0 ); // get both usage counts - fread( rawUsage, sizeof(rawUsage), 1, cl->fHandle ); - u1 = read_le16(rawUsage); - fseek( cl->fHandle, pat2+4L, 0 ); - fread( rawUsage, sizeof(rawUsage), 1, cl->fHandle ); - u2 = read_le16(rawUsage); - } + { + unsigned char rawUsage[2]; + + fseek( cl->fHandle, pat1+4L, 0 ); // get both usage counts + fread( rawUsage, sizeof(rawUsage), 1, cl->fHandle ); + u1 = read_le16(rawUsage); + fseek( cl->fHandle, pat2+4L, 0 ); + fread( rawUsage, sizeof(rawUsage), 1, cl->fHandle ); + u2 = read_le16(rawUsage); + } if( u1 > u2 ) // choose most recent one ret = pat1; else @@ -505,13 +504,13 @@ static long int lp_pp(CLIENT_STRUCT *cl, long int lp ) ret += (long)(( lp << 2 ) + 4L ); // position in PAT fseek( cl->fHandle, ret, 0 ); - { - unsigned char rawLp[4] = {0, 0, 0, 0}; - fread( rawLp, 4, 1, cl->fHandle ); // read it into LP - - lp = byte_swap( read_le32(rawLp) ); // and un-word-swap it - } - lp &= 0xFFFFFFL; + { + unsigned char rawLp[4] = {0, 0, 0, 0}; + fread( rawLp, 4, 1, cl->fHandle ); // read it into LP + + lp = byte_swap( read_le32(rawLp) ); // and un-word-swap it + } + lp &= 0xFFFFFFL; } if( lp == 0xFFFFFFL || lp == -1L ) // NULL pointer values @@ -530,46 +529,46 @@ static long int lp_pp(CLIENT_STRUCT *cl, long int lp ) * Jump to the next available Data page and if non, return END_OF_FILE. * */ -unsigned short int getNextDataPage (CLIENT_STRUCT *cl) -{ - unsigned short int retval = NO_ERROR; - long int pOff; - unsigned long int cPId = cl->curDPageID; - - memset (cl->CUR_DPAGE, 0x00, cl->fPageSize); - - while (getPageType (cl, cl->CUR_DPAGE) != DAT_PAGE_ID){ - uint64_t fileSize; - - if (cPId >= cl->fNumPages) - return END_OF_FILE; - - pOff = lp_pp (cl, cPId++); - fileSize = (uint64_t)cl->fNumPages * cl->fPageSize; - if (pOff < 0 || (uint64_t)pOff >= fileSize) - continue; - - if ((retval = readPageFromFile (cl, cl->CUR_DPAGE, (unsigned long)pOff)) != NO_ERROR) - return retval; - - if (cl->fVersion == BTRIEVE_FILE_V6 && read_le16(cl->CUR_DPAGE + 2) != cPId - 1){ - write_le32(cl->CUR_DPAGE, 0); - continue; - } - - if (cl->fVersion == BTRIEVE_FILE_V3){ - uint32_t usage = ((uint32_t)(unsigned char)cl->CUR_DPAGE[4] << 16) - | ((uint32_t)(unsigned char)cl->CUR_DPAGE[6] << 8) - | (unsigned char)cl->CUR_DPAGE[7]; - if (usage == 0){ - memset (cl->CUR_DPAGE, 0x00, 10); - continue; - } - } - } - cl->curDPageID = cPId; - return NO_ERROR; -} +unsigned short int getNextDataPage (CLIENT_STRUCT *cl) +{ + unsigned short int retval = NO_ERROR; + long int pOff; + unsigned long int cPId = cl->curDPageID; + + memset (cl->CUR_DPAGE, 0x00, cl->fPageSize); + + while (getPageType (cl, cl->CUR_DPAGE) != DAT_PAGE_ID){ + uint64_t fileSize; + + if (cPId >= cl->fNumPages) + return END_OF_FILE; + + pOff = lp_pp (cl, cPId++); + fileSize = (uint64_t)cl->fNumPages * cl->fPageSize; + if (pOff < 0 || (uint64_t)pOff >= fileSize) + continue; + + if ((retval = readPageFromFile (cl, cl->CUR_DPAGE, (unsigned long)pOff)) != NO_ERROR) + return retval; + + if (cl->fVersion == BTRIEVE_FILE_V6 && read_le16(cl->CUR_DPAGE + 2) != cPId - 1){ + write_le32(cl->CUR_DPAGE, 0); + continue; + } + + if (cl->fVersion == BTRIEVE_FILE_V3){ + uint32_t usage = ((uint32_t)(unsigned char)cl->CUR_DPAGE[4] << 16) + | ((uint32_t)(unsigned char)cl->CUR_DPAGE[6] << 8) + | (unsigned char)cl->CUR_DPAGE[7]; + if (usage == 0){ + memset (cl->CUR_DPAGE, 0x00, 10); + continue; + } + } + } + cl->curDPageID = cPId; + return NO_ERROR; +} /* * @@ -586,7 +585,7 @@ unsigned short int getVariableData (CLIENT_STRUCT *cl, char *dataBuffer, unsigne { unsigned short int numFrag = 0; unsigned short int retval = 0; - uint32_t VPageId = 0; + uint32_t VPageId = 0; unsigned short int FragId = 0; unsigned short int FragOff = 0; unsigned long int pCnt = 0L; @@ -598,13 +597,13 @@ unsigned short int getVariableData (CLIENT_STRUCT *cl, char *dataBuffer, unsigne if (dataBuffer && *dbLen > 0) memcpy (dataBuffer, curRecAdr + cl->recHeaderSize, (cl->FixRecLen > *dbLen) ? *dbLen : cl->FixRecLen); - if (cl->fVersion >= BTRIEVE_FILE_V8){ - VPageId = read_le16(curRecAdr + cl->FixRecLen + cl->recHeaderSize); - numFrag = read_le16(curRecAdr + cl->FixRecLen + 10); - }else{ - VPageId = read_vrec_page_id_v5(curRecAdr + cl->recHeaderSize + curLen); - numFrag = (unsigned char)*(curRecAdr + cl->recHeaderSize + curLen + 3); - } + if (cl->fVersion >= BTRIEVE_FILE_V8){ + VPageId = read_le16(curRecAdr + cl->FixRecLen + cl->recHeaderSize); + numFrag = read_le16(curRecAdr + cl->FixRecLen + 10); + }else{ + VPageId = read_vrec_page_id_v5(curRecAdr + cl->recHeaderSize + curLen); + numFrag = (unsigned char)*(curRecAdr + cl->recHeaderSize + curLen + 3); + } while (VPageId != PAGE_KICK_OFF && FragId != MAX_SHORT_INT_VAL){ /* get the VAT page offset within the file */ @@ -627,25 +626,25 @@ unsigned short int getVariableData (CLIENT_STRUCT *cl, char *dataBuffer, unsigne || (fread (cl->CUR_VPAGE, cl->fPageSize, 1, cl->fHandle)) != 1) return IO_ERROR; FragId = ((cl->fPageSize -1) >> 1) - numFrag; - FragOff = read_le16(cl->CUR_VPAGE + (FragId * sizeof(uint16_t))) & 0x7FFF; - - /* get to the end of the fragment */ + FragOff = read_le16(cl->CUR_VPAGE + (FragId * sizeof(uint16_t))) & 0x7FFF; + + /* get to the end of the fragment */ for(fEId = 1; read_le16(cl->CUR_VPAGE + ((FragId - fEId) * sizeof(uint16_t))) == UINT16_MAX; fEId++); - - /* calculate the length of the fragment */ - addLen = (read_le16(cl->CUR_VPAGE + ((FragId - fEId) * sizeof(uint16_t))) & 0x7FFF) - FragOff - cl->VFragParam; + + /* calculate the length of the fragment */ + addLen = (read_le16(cl->CUR_VPAGE + ((FragId - fEId) * sizeof(uint16_t))) & 0x7FFF) - FragOff - cl->VFragParam; if (addLen > cl->fPageSize * 2 || addLen == 0) break; if (cl->fVersion >= BTRIEVE_FILE_V8){ - VPageId = read_le16(&((char*)cl->CUR_VPAGE)[ FragOff]); - numFrag = read_le16(&((char*)cl->CUR_VPAGE)[ FragOff +4]); - }else{ - VPageId = read_vrec_page_id_v5(cl->CUR_VPAGE + FragOff); - numFrag = (unsigned char)*(cl->CUR_VPAGE + FragOff + 3); + VPageId = read_le16(&((char*)cl->CUR_VPAGE)[ FragOff]); + numFrag = read_le16(&((char*)cl->CUR_VPAGE)[ FragOff +4]); + }else{ + VPageId = read_vrec_page_id_v5(cl->CUR_VPAGE + FragOff); + numFrag = (unsigned char)*(cl->CUR_VPAGE + FragOff + 3); if (cl->fVersion < BTRIEVE_FILE_V6){ - if (read_le16(cl->CUR_VPAGE + (FragId * sizeof(uint16_t))) & 0x8000){ + if (read_le16(cl->CUR_VPAGE + (FragId * sizeof(uint16_t))) & 0x8000){ FragOff += sizeof (VRECPTR); addLen -= sizeof(VRECPTR); }else VPageId = PAGE_KICK_OFF; @@ -718,13 +717,13 @@ unsigned short int BF_GET_REC (CLIENT_STRUCT *cl, char *dataBuffer, unsigned lon if (cl->numRecs == 0L) return END_OF_FILE; while ((retval = getNextRecord (cl)) == NO_ERROR){ - if (cl->fVersion >= BTRIEVE_FILE_V6 && read_le16(cl->curRecAdr) != 0x0000){ + if (cl->fVersion >= BTRIEVE_FILE_V6 && read_le16(cl->curRecAdr) != 0x0000){ break; }else if (cl->fVersion < BTRIEVE_FILE_V6){ unsigned long int i = 0; char isEmpty = true; - if (cl->fVersion == BTRIEVE_FILE_V3 && read_le16(cl->curRecAdr) == 0x0000) continue; + if (cl->fVersion == BTRIEVE_FILE_V3 && read_le16(cl->curRecAdr) == 0x0000) continue; for (i = ((cl->fVersion >= BTRIEVE_FILE_V4)? RECORD_HEADER_SIZE_V4 : RECORD_HEADER_SIZE_V3) ; i < cl->IFixedRecLen ; i++){ @@ -741,12 +740,12 @@ unsigned short int BF_GET_REC (CLIENT_STRUCT *cl, char *dataBuffer, unsigned lon if (retval != NO_ERROR) return retval; - if (cl->curRecordId == MAX_LONG_INT_VAL) - cl->curRecordId = 0; - else - cl->curRecordId ++; - - if (cl->VarRecsAllowed){ + if (cl->curRecordId == MAX_LONG_INT_VAL) + cl->curRecordId = 0; + else + cl->curRecordId ++; + + if (cl->VarRecsAllowed){ retval = getVariableData (cl, dataBuffer, dbLen, cl->curRecAdr); }else{ if (dataBuffer && *dbLen > 0) From f4efcf8be73b83b33afe1556c6cc3e26b833c592 Mon Sep 17 00:00:00 2001 From: Gregory Morse Date: Tue, 14 Jul 2026 02:15:35 +0200 Subject: [PATCH 6/6] Preserve original page decoder under VS2026 --- BtrieveFileSaverLib.c | 53 ++++++++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/BtrieveFileSaverLib.c b/BtrieveFileSaverLib.c index c6c275f..a4e47e7 100644 --- a/BtrieveFileSaverLib.c +++ b/BtrieveFileSaverLib.c @@ -68,12 +68,6 @@ ________________________________________________________________________________ #include "BtrieveFileSaverLib.h" -#if defined(_MSC_VER) && _MSC_VER >= 1950 -#define BTRIEVE_VS2026_NOINLINE __declspec(noinline) -#else -#define BTRIEVE_VS2026_NOINLINE -#endif - /* * * Function freeClient @@ -160,21 +154,38 @@ static uint32_t read_vrec_page_id_v5(const char *data) * this way. * */ -BTRIEVE_VS2026_NOINLINE char getPageType (CLIENT_STRUCT *cl, char *tmpPage) -{ - const uint16_t majorVersion = (uint16_t)cl->fVersion >> 8; - - if (majorVersion >= 3 && majorVersion <= 5) - return (read_le16(tmpPage + 4) & 0x8000) ? DAT_PAGE_ID : 0x00; - - if (majorVersion == 6 || majorVersion == 7) - return tmpPage[1]; - - if (majorVersion == 8 || majorVersion == 9) - return tmpPage[4]; - - return 0x00; -} +#if defined(_MSC_VER) && _MSC_VER >= 1950 +/* + * MSVC 19.50's global optimizer drops the V3/V4/V5 arm from this sparse + * 16-bit switch in the complete library translation unit. Keep the original + * decoder and disable only that optimization pass for this function. The + * WineVDM golden fixtures exercise this path in release builds. + */ +#pragma optimize("g", off) +#endif +char getPageType (CLIENT_STRUCT *cl, char *tmpPage) +{ + switch (cl->fVersion){ + case BTRIEVE_FILE_V3: + case BTRIEVE_FILE_V4: + case BTRIEVE_FILE_V5:{ + if (read_le16(tmpPage + 4) & 0x8000) return DAT_PAGE_ID; + }break; + case BTRIEVE_FILE_V6: + case BTRIEVE_FILE_V61: + case BTRIEVE_FILE_V7:{ + return tmpPage[1]; + }break; + case BTRIEVE_FILE_V8: + case BTRIEVE_FILE_V9:{ + return tmpPage[4]; + }break; + } + return 0x00; +} +#if defined(_MSC_VER) && _MSC_VER >= 1950 +#pragma optimize("", on) +#endif /* *