// BDE - (C) Copyright 1995 by Borland International #include "dbfseek.h" #include "macro.h" #define szCURRENTMODULE "DBFSEEK" //===================================================================== // This BDE example provides Paradox for Windows ver. 5.0 ObjectPAL users // a means of doing a seek similar to the dBASE seek of MAINTAINED // expression indexes on dBASE tables for retrieving record numbers. The // ObjectPAL procedures created are: DbfIdxSeek and DbfGetIdxExpr. // // Expressions tested in an MDX file: // // LTRIM(STR(MONTH(DATES)))+"/"+LTRIM(STR(DAY(DATES)))+"/" // +SUBSTR(LTRIM(STR(YEAR(DATES))),3,2) // LTRIM(FIRST_NAME) // LTRIM(FIRST_NAME)-LTRIM(LAST_NAME) // FIRST_NAME (a character field) // FIRST_NAME-LAST_NAME // FLOATS (a float field) // NUMBERS (a number field) // RECNO() // RTRIM(FIRST_NAME) + DTOS(DATES) // Indexes on concatenation of character and date field. The date // field is converted to characters in format YYYYMMDD. // i.e. - Jerry19950518. // SQRT(NUMBERS) // LTRIM(STR(FLOATS)) // SUBSTR(FIRST_NAME,2,3) // // Notes: // // DbfIdxSeek returns a record number. A return of 0 means that the record // was not found. // // DbfGetIdxExpr is just for viewing an expression string. // // The EXACT setting, when set to FALSE, will find records with partial // values in character expressions. EXACT set to TRUE sets NEAR to FALSE. // // EXACT: 0 = FALSE, 1 = TRUE // // The NEAR setting, after an unsuccessful dbfSEEK, positions the record // pointer at the record in the indexed table immediately after the position // at which the value searched for would have been found. // // NEAR: 0 = FALSE, 1 = TRUE // // Character expressions are case sensitive except for soundex which must // sound like the key value for a match to occur. // // Use no commas in values seeking NUMERIC expressions. // // "Sounds like" can be used if SOUNDEX is the only function used // and is at the beginning of the expression. All field names and // other words must be contained within its perentheses i.e. - // SOUNDEX(FIRST_NAME) // SOUNDEX(FIRST_NAME-LAST_NAME) // // DATE fields can be used directly if the field name is at the beginning // of the expression and is used alone i.e. - // DATES (a date field) // // // This is how the Paradox Uses block should appear: // // Uses dbfseek // // DbfIdxSeek(TableDir cptr, TableName cptr, // IndexFileNameWithExtension cptr, // IndexTagName cptr, KeyValue cptr, // Exact cword, Near cword, // returnStringBuffer cptr)cdouble // DbfGetIdxExpr(TableDir cptr, TableName cptr, // IndexFileNameWithExtension cptr, // IndexTagName cptr, ReturnValue cptr) // // endUses // // // And some typical methods: // // Var // returnStringBuffer String // endVar // // // method open(var eventInfo Event) // // if eventInfo.isPreFilter() then // ;// This code executes for each object on the form: // // else // ;// This code executes only for the form: // // ; requires a 129 byte buffer: // returnStringBuffer = space(129) ; VERY IMPORTANT! or GPF // // endif // // endmethod // // // method pushButton(var eventInfo Event) // // var exact, near smallint endvar // exact = 0 // near = 0 // RecNoReturn = DbfIdxSeek(workingdir(), "test.dbf", // "test.mdx", "sometag", // "somevalue", exact, near, returnStringBuffer) // if RecNoReturn > 0 then // TableFrameName.moveToRecNo(RecNoReturn) // else // TableFrameName.moveToRecNo(1) // endif // message(returnStringBuffer) ; for errors // // endmethod // // // method pushButton(var eventInfo Event) // // DbfGetIdxExpr(workingdir(), "test.dbf", "test.mdx", // "sometag", returnStringBuffer) // view(returnStringBuffer) // // endmethod // //===================================================================== //===================================================================== // Function: // DbfIdxSeek(pCHAR pTblDir, pCHAR pTblName, pCHAR pIdxName, // pCHAR pIdxTag, pCHAR pKeyVal, pCHAR pszRetBuffer) // // Description: // This example shows how to seek for a key value in a dBASE // maintained key expression. // //===================================================================== DFLOAT FAR WINAPI _export DbfIdxSeek (pCHAR pTblDir, pCHAR pTblName, pCHAR pIdxName, pCHAR pIdxTag, pCHAR pKeyVal, int bExact, int bNear, pCHAR pszRetBuffer) { CHAR szTblType[] = szDBASE; // table type (dBASE) UINT16 i = 0; // for loops UINT16 iKeyValLen = 0; // for total key bytes received UINT16 iKeyValEven = 1; // for total used key bytes div_t x; // for calcing total used key bytes FMTBcd Bcd; CURProps TblProps; IDXDesc *pIdxDesc = NULL; DBIResult rslt = NULL; UINT32 retVal = 0; hDBIDb hDb = NULL; hDBICur hCur = NULL; pRECProps precProps = NULL; char *ptok = NULL; UINT16 iFldNum = 0; DFLOAT fKeyVal = 0; pFLDDesc pFldDesc = NULL; char *ptrFunc, c = '('; UINT16 iIndexSeqNumber = 0; pBYTE pBuf = NULL; // Pointer to the record buffer pBYTE pKeyBuf = NULL; // Pointer to the key buffer BOOL bIsNegative = 0; BOOL bExpFunc = 0; BOOL bHasNoFld = 0; BOOL bBlank = 0; BOOL bBlankDate = 0; UINT16 ptr = 0; UINT16 iFldType = 0; DBIDATE dateD = NULL; UINT16 uDay = NULL; // Day portion of date UINT16 uMon = NULL; // Month portion of date INT16 iYear = NULL; // Year portion of date char szYear[5]; // buffer for year char szPlaces[2] = "4"; char *szKeyBuf1 = NULL; char *szKeyBuf2 = NULL; char *szKeyBuf3 = NULL; char *szKeyBuf4 = NULL; char *szFField = NULL; // buffer for first exp. field name UINT16 uDecLen = 0; // Decimal length if (bExact == 1) { bNear = 0; } // initialize buffer for error string memset(pszRetBuffer, 0, (DBIMAXMSGLEN + 1) * sizeof(char)); // do nothing if a key value is not sent CHKERR_NODISPLAY(!strcmp(pKeyVal, "")) iKeyValLen = strlen(&pKeyVal[0]); if (pKeyVal[0] == '-') // for negative numbers { bIsNegative = TRUE; } precProps = ((pRECProps) malloc (sizeof(RECProps))); if (precProps == NULL) { GetString(1, pszRetBuffer); //"Out of Memory." CLEANUP_NODISPLAY; } // open a database in the current session and get a database handle CHKERR_CLEANUP(DbiOpenDatabase(NULL, NULL, dbiREADONLY, dbiOPENSHARED, NULL, 0, NULL, NULL, &hDb)); // set the current directory CHKERR_CLEANUP(DbiSetDirectory(hDb, pTblDir)); // associate a cursor handle with an opened table CHKERR_CLEANUP(DbiOpenTable(hDb, pTblName, NULL, pIdxName, pIdxTag, 0, dbiREADONLY, dbiOPENSHARED, xltFIELD, FALSE, NULL, &hCur)); CHKERR_CLEANUP(DbiGetCursorProps(hCur, &TblProps)); if (strcmp(szTblType, TblProps.szTableType)) { GetString(7, pszRetBuffer); //"Only dBASE tables are supported." CLEANUP_NODISPLAY; } pFldDesc = (pFLDDesc) malloc (sizeof(FLDDesc) * TblProps.iFields); if (pFldDesc == NULL) { GetString(1, pszRetBuffer); //"Out of Memory." CLEANUP_NODISPLAY; } CHKERR_CLEANUP(DbiGetFieldDescs(hCur, pFldDesc)); pIdxDesc = (IDXDesc*) malloc (TblProps.iIndexes * sizeof(IDXDesc)); if (pIdxDesc == NULL) { GetString(1, pszRetBuffer); //"Out of Memory." CLEANUP_NODISPLAY; } CHKERR_CLEANUP(DbiGetIndexDescs(hCur, pIdxDesc)); pBuf = (pBYTE) malloc (TblProps.iRecBufSize); if (pBuf == NULL) { GetString(1, pszRetBuffer); //"Out of Memory." CLEANUP_NODISPLAY; } CHKERR_CLEANUP(DbiInitRecord(hCur, pBuf)); CHKERR_CLEANUP(DbiGetIndexSeqNo(hCur, pIdxName, pIdxTag, NULL, &iIndexSeqNumber)); if (pIdxDesc[iIndexSeqNumber - 1].bMaintained == 0) { GetString(3, pszRetBuffer); //"Only maintained indexes are supported." CLEANUP_NODISPLAY; } pKeyBuf = (pBYTE) malloc((pIdxDesc[iIndexSeqNumber - 1].iKeyLen + 1) * sizeof (BYTE)); szKeyBuf1 = (char*) malloc((pIdxDesc[iIndexSeqNumber - 1].iKeyLen + 1) * sizeof (char)); szKeyBuf2 = (char*) malloc((pIdxDesc[iIndexSeqNumber - 1].iKeyLen + 1) * sizeof (char)); szKeyBuf3 = (char*) malloc((pIdxDesc[iIndexSeqNumber - 1].iKeyLen + 1) * sizeof (char)); szKeyBuf4 = (char*) malloc((pIdxDesc[iIndexSeqNumber - 1].iKeyLen + 1) * sizeof (char)); // initialize buffer for first exp. field name szFField = (char*) malloc((strlen(pIdxDesc[iIndexSeqNumber - 1].szKeyExp) + 1) * sizeof (char)); if ((szFField == NULL) || (pKeyBuf == NULL) || (szKeyBuf1 == NULL) || (szKeyBuf2 == NULL) || (szKeyBuf3 == NULL) || (szKeyBuf4 == NULL)) { GetString(1, pszRetBuffer); //"Out of Memory." CLEANUP_NODISPLAY; } // does the expression contain soundex ptrFunc = strchr(pIdxDesc[iIndexSeqNumber - 1].szKeyExp, c); if (ptrFunc) { ptr = strncmpi("soundex", pIdxDesc[iIndexSeqNumber - 1].szKeyExp, 7); if (ptr == 0) { bExpFunc = 1; } // get first field name and type disregarding function ptok = strtok(pIdxDesc[iIndexSeqNumber - 1].szKeyExp, "("); ptok = strtok('\0', " ()-+/*"); if (ptok) { strcpy(szFField, ptok); } else { // has no field name in the expression bHasNoFld = 1; } } else { // has no function, then assuming expression has a field name ptok = strtok(pIdxDesc[iIndexSeqNumber - 1].szKeyExp, " -+/*"); if (ptok) { strcpy(szFField, ptok); } else { bHasNoFld = 1; } } if (bHasNoFld == 0) { for (i = 0; i < TblProps.iFields; i++) { if (!strcmp(pFldDesc[i].szName, ptok)) { iFldType = pFldDesc[i].iFldType; iFldNum = pFldDesc[i].iFldNum; break; } } } if (iFldType == fldDATE) { // set max input to key buffer if (iKeyValLen > pIdxDesc[iIndexSeqNumber - 1].iKeyLen + 3) { GetString(4, pszRetBuffer); //"Key length exceeded." MessageBeep(-1); CLEANUP_NODISPLAY; } if (pKeyVal[0] == ' ') { uMon = 12; uDay = 31; iYear = 9999; bBlankDate = 1; bNear = 1; } else { ptok = strtok(pKeyVal, " /"); if (ptok) { uMon = atoi(ptok); } for (i = 0; i < 2; i++) { ptok = strtok('\0', "/"); if ((i == 0)&&(ptok)) { uDay = atoi(ptok); } if ((i == 1)&&(ptok)) { // assumption: dates entered > 51 and <100 = 1950 otherwise // it = 2050 unless 4 digit year is entered if (atoi(ptok) > 50 && atoi(ptok) < 100) { if (atoi(ptok) > 50) { strcpy(szYear, "19"); strcat(szYear, ptok); iYear = atoi(szYear); } else { strcpy(szYear, "20"); strcat(szYear, ptok); iYear = atoi(szYear); } } else { strcpy(szYear, ptok); iYear = atoi(szYear); } } } } CHKERR_CLEANUP(DbiDateEncode(uMon, uDay, iYear, &dateD)); CHKERR_CLEANUP(DbiPutField(hCur, iFldNum, pBuf, (pBYTE) &dateD)); CHKERR_CLEANUP(DbiExtractKey(hCur, pBuf, (pBYTE) pKeyBuf)); if (bNear == 1) { CHKERR_CLEANUP(DbiSetToKey(hCur, keySEARCHGEQ, TRUE, 0, 0, pKeyBuf)); } else { CHKERR_CLEANUP(DbiSetToKey(hCur, keySEARCHEQ, TRUE, 0, 0, pKeyBuf)); } CHKERR_CLEANUP(DbiGetNextRecord(hCur, dbiNOLOCK, pBuf, precProps)); CHKERR_CLEANUP(DbiGetField(hCur, iFldNum, pBuf, NULL, &bBlank)); if (bBlank) { // if set to near and the indate is larger than any key value, // force an end of table (message only), otherwise a blank // record gets found. if (bNear == 1 && bBlankDate == 0) { GetString(9, pszRetBuffer); //"At end of table." CLEANUP_NODISPLAY; } } retVal = precProps[0].iPhyRecNum; } else { if (pIdxDesc[iIndexSeqNumber - 1].iKeyExpType == fldDBCHAR) { // set key buffer for char expression type memset(pKeyBuf, ' ', pIdxDesc[iIndexSeqNumber - 1]. iKeyLen + 1 * sizeof (BYTE)); if (bExpFunc == 0) { // set max input to key buffer if (iKeyValLen > pIdxDesc[iIndexSeqNumber - 1].iKeyLen) { GetString(4, pszRetBuffer); //"Key length exceeded." MessageBeep(-1); CLEANUP_NODISPLAY; } memcpy((pCHAR)pKeyBuf, pKeyVal, iKeyValLen); if (bExact == 0 && bNear == 0) { CHKERR_CLEANUP(DbiGetRecordForKey(hCur, TRUE, 0, iKeyValLen, (pBYTE) pKeyBuf, NULL)); CHKERR_CLEANUP(DbiGetRecord(hCur, dbiNOLOCK, NULL, precProps)); } if (bExact == 1) { CHKERR_CLEANUP(DbiGetRecordForKey(hCur, TRUE, 0, 0, (pBYTE) pKeyBuf, NULL)); CHKERR_CLEANUP(DbiGetRecord(hCur, dbiNOLOCK, NULL, precProps)); } if (bNear == 1) { CHKERR_CLEANUP(DbiSetToKey(hCur, keySEARCHGEQ, TRUE, 0, 0, pKeyBuf)); CHKERR_CLEANUP(DbiGetNextRecord(hCur, dbiNOLOCK, pBuf, precProps)); } } if (bExpFunc == 1) // for soundex { // if first char in pKeyVal is not a letter, fail // (because DbiGetRecordForKey won't). if (!((pKeyVal[0] > '@') && (pKeyVal[0] < '[')) && !((pKeyVal[0] > '`') && (pKeyVal[0] < '{'))) { GetString(5, pszRetBuffer); //"Invalid character." MessageBeep(-1); CLEANUP_NODISPLAY; } CHKERR_CLEANUP(DbiPutField(hCur, iFldNum, pBuf, (pBYTE) pKeyVal)); CHKERR_CLEANUP(DbiExtractKey(hCur, pBuf, (pBYTE) pKeyBuf)); CHKERR_CLEANUP(DbiGetRecordForKey(hCur, TRUE, 0, 0, (pBYTE) pKeyBuf, NULL)); CHKERR_CLEANUP(DbiGetRecord(hCur, dbiNOLOCK, NULL, precProps)); } retVal = precProps[0].iPhyRecNum; } if (pIdxDesc[iIndexSeqNumber - 1].iKeyExpType == fldDBKEYBCD) { // can't start with a space if (pKeyVal[0] == ' ') { CLEANUP_NODISPLAY; } if (strchr(&pKeyVal[0], ',')) { GetString(6, pszRetBuffer); //"Use no commas." MessageBeep(-1); CLEANUP_NODISPLAY; } // set max for input to key buffer if (iKeyValLen > (pIdxDesc[iIndexSeqNumber - 1].iKeyLen) + 3) { GetString(4, pszRetBuffer); //"Key length exceeded." MessageBeep(-1); CLEANUP_NODISPLAY; } memset(pKeyBuf, '\0', pIdxDesc[iIndexSeqNumber - 1]. iKeyLen * sizeof (BYTE)); memset(szKeyBuf1, '\0', pIdxDesc[iIndexSeqNumber - 1]. iKeyLen * sizeof (char)); // OK, now for some decimal and negative number control... if (pKeyVal[0] == '.' || (pKeyVal[0] == '-' && pKeyVal[1] == '.')) { GetString(8, pszRetBuffer); //"Enter 0 or -0 first." MessageBeep(-1); CLEANUP_NODISPLAY; } ptok = strtok(pKeyVal, " ."); strcpy(szKeyBuf4, ptok); iKeyValLen = strlen(szKeyBuf4); if (ptok) { ptok = strtok(NULL, "."); if (ptok) { uDecLen = strlen(&ptok[0]); strcat(szKeyBuf4, ptok); i = 0; if (szKeyBuf4[0] == '0') { while (szKeyBuf4[i] == '0') { i++; } strcpy(szKeyBuf2, &szKeyBuf4[i]); } else { szKeyBuf2 = strdup(szKeyBuf4); iKeyValLen = strlen(szKeyBuf2); } } else { szKeyBuf2 = strdup(szKeyBuf4); } } if (bIsNegative) { if (strlen(&pKeyVal[0]) > 1) { ptok = strtok(szKeyBuf2, " -"); strcpy(szKeyBuf3, ptok); iKeyValLen = strlen(szKeyBuf3); fKeyVal = atof(szKeyBuf3); } else { CLEANUP_NODISPLAY; } } else { fKeyVal = atof(szKeyBuf2); } if (fKeyVal == 0) { CLEANUP_NODISPLAY; } // make total key bytes used an even number. x = div(iKeyValLen, 2); if (iKeyValLen > 1) { iKeyValEven = iKeyValLen; } if (x.rem == 1) { iKeyValEven = iKeyValLen + 1; } // setup first key buffer byte szPlaces[0] = szPlaces[0] + (char)(iKeyValLen - uDecLen); memmove(szKeyBuf1, szPlaces, 1); // setup second key buffer byte if (bIsNegative) { szPlaces[0] = (((iKeyValLen - 1) * 4) + 129); } else { szPlaces[0] = ((iKeyValLen * 4) + 1); } memmove(&szKeyBuf1[1], &szPlaces[0], 1); CHKERR_CLEANUP(DbiBcdFromFloat(&fKeyVal, iKeyValLen + 2, 2, &Bcd)); if (iKeyValLen > 1) { iKeyValLen--; } for (i = 0; i < iKeyValLen; i++) { memmove(&szKeyBuf1[i + 2], &Bcd.iFraction[i], 1); } memcpy((pCHAR)pKeyBuf, szKeyBuf1, (iKeyValEven / 2) + 2); if (bNear == 1) { CHKERR_CLEANUP(DbiSetToKey(hCur, keySEARCHGEQ, TRUE, 0, 0, pKeyBuf)); } else { CHKERR_CLEANUP(DbiSetToKey(hCur, keySEARCHEQ, TRUE, 0, 0, pKeyBuf)); } CHKERR_CLEANUP(DbiGetNextRecord(hCur, dbiNOLOCK, pBuf, precProps)); retVal = precProps[0].iPhyRecNum; } } CleanUp: if (hCur) { DbiCloseCursor(&hCur); } if (hDb) { DbiCloseDatabase(&hDb); } if (precProps) { free(precProps); } if (pBuf) { free(pBuf); } if (pKeyBuf) { free(pKeyBuf); } if (pIdxDesc) { free(pIdxDesc); } if (pFldDesc) { free(pFldDesc); } if (szKeyBuf1) { free(szKeyBuf1); } if (szKeyBuf2) { free(szKeyBuf2); } if (szKeyBuf3) { free(szKeyBuf3); } if (szKeyBuf4) { free(szKeyBuf4); } if (szFField) { free(szFField); } return retVal; } //===================================================================== // Function: // DbfGetIdxExpr(pCHAR pTblDir, pCHAR pTblName, pCHAR pIdxName, // pCHAR pIdxTag, pCHAR pszRetBuffer) // // Description: // This example shows how to return an expression string and // other information about an MDX file. // //===================================================================== void FAR WINAPI _export DbfGetIdxExpr (pCHAR pTblDir, pCHAR pTblName, pCHAR pIdxName, pCHAR pIdxTag, pCHAR pszRetBuffer) { CHAR szTblType[] = szDBASE; DBIResult rslt; CURProps TblProps; UINT16 iIndexSeqNumber; hDBIDb hDb = NULL; hDBICur hCur = NULL; IDXDesc *pIdxDesc = NULL; // initialize buffer for error string memset(pszRetBuffer, 0, (DBIMAXMSGLEN + 1) * sizeof(char)); // open a database in the current session and get a database handle CHKERR_CLEANUP(DbiOpenDatabase(NULL, NULL, dbiREADONLY, dbiOPENSHARED, NULL, 0, NULL, NULL, &hDb)); // set the current directory CHKERR_CLEANUP(DbiSetDirectory(hDb, pTblDir)); // associate a cursor handle with an opened table CHKERR_CLEANUP(DbiOpenTable(hDb, pTblName, NULL, pIdxName, pIdxTag, 0, dbiREADONLY, dbiOPENSHARED, xltFIELD, FALSE, NULL, &hCur)); CHKERR_CLEANUP(DbiGetCursorProps(hCur, &TblProps)); if (strcmp(szTblType, TblProps.szTableType)) { GetString(7, pszRetBuffer); //"Only dBASE tables are supported." CLEANUP_NODISPLAY; } pIdxDesc = (IDXDesc*) malloc (TblProps.iIndexes * sizeof(IDXDesc)); if (pIdxDesc == NULL) { GetString(1, pszRetBuffer); //"Out of Memory." CLEANUP_NODISPLAY; } CHKERR_CLEANUP(DbiGetIndexDescs(hCur, pIdxDesc)); CHKERR_CLEANUP(DbiGetIndexSeqNo(hCur, pIdxName, pIdxTag, NULL, &iIndexSeqNumber)); if (pIdxDesc[iIndexSeqNumber - 1].bMaintained == 0) { GetString(3, pszRetBuffer); //"Only maintained indexes are supported." CLEANUP_NODISPLAY; } strcpy(pszRetBuffer, pIdxDesc[iIndexSeqNumber - 1].szKeyExp); CleanUp: if (hCur) { DbiCloseCursor(&hCur); } if (hDb) { DbiCloseDatabase(&hDb); } if (pIdxDesc) { free(pIdxDesc); } } //===================================================================== // Function: // DBIError(DBIResult retVal, pCHAR pszRetBuffer) // // Input: // retVal - BDE return // pszRetBuffer - Buffer to contain the error string // // Return: a DBIResult value. // // Description: // This function gets information of where an error occurred. //===================================================================== DBIResult DBIError (DBIResult retVal, pCHAR pszRetBuffer) { if (retVal == DBIERR_NONE) { return retVal; } if (retVal == DBIERR_CANTFINDODAPI) { GetString(2, pszRetBuffer); //"Cannot find IDAPI files: Check path." CLEANUP_DISPLAYERR(pszRetBuffer); } else { DbiGetErrorString(retVal, pszRetBuffer); } CleanUp: return retVal; } //===================================================================== // Function: // GetString(WORD iStringID, pCHAR pszRetBuffer) // // Input: // iStringID - Identifier for the string // pszRetBuffer - Buffer to contain the error string. // (Allocated by the calling function) // // Description: // Load a string from DBFSEEK.DLL. //===================================================================== void GetString (WORD iStringID, pCHAR pszRetBuffer) { if (pszRetBuffer) { LoadString(GetModuleHandle(szCURRENTMODULE), iStringID, pszRetBuffer, DBIMAXMSGLEN - 1); } } #pragma argsused BOOL WINAPI LibMain (HINSTANCE hInstance, WORD wDataSeg, WORD cbHeapSize, LPSTR lpCmdLine) { if (cbHeapSize != 0) { UnlockData(0); } return TRUE; } int WINAPI WEP (int nSystemExit) { switch (nSystemExit) { case WEP_SYSTEM_EXIT: break; case WEP_FREE_DLL: break; } return 1; }