home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 18 REXX / 18-REXX.zip / blt2rx_o.zip / c_src.zip / bd_rixu.c < prev    next >
C/C++ Source or Header  |  1996-10-13  |  23KB  |  742 lines

  1.  
  2. /* bd_rixu.c -  4-Oct-1996 Cornel Huth
  3.  * This module is called by bd_main.c
  4.  * REINDEX_XB, with custom-sort compare
  5.  */
  6.  
  7. #include "platform.h"
  8.  
  9. // external to this module
  10.  
  11. void PutMsg(CHAR *strg);
  12. void GetMsg(CHAR *strg);
  13.  
  14. extern CHAR *collateTable;
  15.  
  16. #if FOR_WINDOWS == 1
  17.  void DoWinThing(int waitFlag);
  18. #endif
  19.  
  20. // public in this module
  21.  
  22. int bd_rixu(void);
  23.  
  24. void __cdecl CallbackRixU(CALLBACKPACK *CBP); // *CBP on stack; caller OR callee may clean stack
  25. void APIENTRY ThreadRixU(ACCESSPACK *AP);     // OS-called thread routine
  26.  
  27. LONG rezRixU=0;  // thread reindex return code
  28. TID tidRixU;     // reindex thread ID  (OS/2=TID, Win32=DWORD)
  29. HANDLE hRixU;    // reindex thread's handle (Win32 only)
  30. LONG recs2addU;  // records to add en-masse (also used in CallbackRixU())
  31.  
  32. // GO
  33.  
  34. int bd_rixu(void) {
  35.  
  36.  void BuildFieldListRixU(FIELDDESCTYPE fieldList[]);
  37.  
  38.  /*
  39.  * This module is similar to bd_rix.c, except that it uses custom compare
  40.  * function.  It can be noticably slower than the instrinsic Bullet sort
  41.  * functions, but the extended capabilities more than make up for that.
  42.  * The function itself, UserRixUfp(), is located near the end of this source
  43.  */
  44.  
  45.  LONG APIENTRY UserRixUfp(PVOID fp1, PVOID fp2, LONG count, ULONG handle);
  46.  
  47.  
  48. #pragma pack(1)
  49.  
  50.  ACCESSPACK AP;
  51.  DOSFILEPACK DFP;
  52.  CREATEDATAPACK CDP;
  53.  CREATEINDEXPACK CIP;
  54.  HANDLEPACK HP;
  55.  OPENPACK OP;
  56.  QUERYSETPACK QSP;
  57.  STATINDEXPACK SIP;   // for separate thread reindex percentage done
  58.  
  59.  struct EmpRecType {
  60.   CHAR tag;              // record tag, init to SPACE, * means deleted
  61.   CHAR empID[9];         // SSN (not 0T string)
  62.   CHAR empLN[16];        // last name
  63.   CHAR empFN[16];        // first name
  64.   CHAR empSalary[10];    // salary "1234567.90"
  65.   CHAR empDept[6];       // department assigned
  66.  }; // 58 bytes
  67.  struct EmpRecType EmpRec;
  68.  
  69. #pragma pack()
  70.  
  71.  time_t startTime, endTime;
  72.  int display = 0;                // display results or not flag
  73.  int thread = 1;                 // dispatch reindex in a thread flag
  74.  int lastNameVar;                // used to construct on-the-fly data record...
  75.  int lastFourVar;                // ...that is unique
  76.  
  77.  double baseSalary = 30000.50;   // all make at least this
  78.  double bonus = 0.00;            // but all get varying bonus amounts
  79.  
  80.  LONG rez;                       // return value from Bullet
  81.  
  82.  CHAR nameIX3[] = ".\\$bd_rixu.ix3";// name of index file created
  83.  ULONG indexID=0;                // handle of index file
  84.  CHAR keyExpression[128];        // key expression string buffer (159 max)
  85.  CHAR keyBuffer[68];             // buffer used to store/receive key values
  86.  
  87.  CHAR nameData[] = ".\\$bd_rixu.dbf"; // name of data file created
  88.  ULONG dataID=0;                 // handle of data file
  89.  FIELDDESCTYPE fieldList[5];     // 5 fields used in data record
  90.  
  91.  LONG i;                 // counter
  92.  CHAR tmpStr[128];       // misc stuff, non-Bullet related
  93.  CHAR putStr[128];
  94.  ULONG tmpU;
  95.  
  96.  
  97.  SIP.func = 0; // init+ref it for DOSX32 compile
  98.  
  99.  srand((unsigned)time(0));       // see generator
  100.  
  101.  memset(fieldList,0,sizeof(fieldList));  // init unused bytes to 0 (required)
  102.  BuildFieldListRixU(fieldList);
  103.  
  104.  // Use 512K for workspace in reindex module (min is 48K, max is whatever you want)
  105.  #define NEW_XBUFF_SIZE (512*1024)
  106.  
  107.  QSP.func = SET_SYSVARS_XB;
  108.  QSP.item = REINDEX_BUFFER_SIZE;
  109.  QSP.itemValue = NEW_XBUFF_SIZE;
  110.  rez = BULLET(&QSP);
  111.  if (rez) {
  112.     sprintf(putStr,"Failed SET_SYSVARS_XB #%d (reindex buffer size) call.  Err: %d\n",QSP.item,rez);
  113.     PutMsg(putStr);
  114.     goto Abend;
  115.  }
  116.  
  117.  tmpU = QSP.itemValue;              // 0=default, or 144KB total buffer space
  118.  if (tmpU == 0) tmpU = (144*1024);
  119.  sprintf(putStr,"Bullet reindex tmp buffer was %d KB, now %d KB\n", tmpU/1024,
  120.                                                                      NEW_XBUFF_SIZE/1024);
  121.  PutMsg(putStr);
  122.  
  123.  // Delete previous files from any previous run (disregard any error return)
  124.  
  125.  DFP.func = DELETE_FILE_DOS;
  126.  DFP.filenamePtr = nameData;
  127.  rez = BULLET(&DFP);
  128.  DFP.filenamePtr = nameIX3;
  129.  rez = BULLET(&DFP);
  130.  
  131.  
  132.  // Create the data file, a standard DBF (ID=3) as defined in fieldList above.
  133.  
  134.  CDP.func = CREATE_DATA_XB;
  135.  CDP.filenamePtr = nameData;
  136.  CDP.noFields = 5;
  137.  CDP.fieldListPtr = fieldList;
  138.  CDP.fileID = 0x03;
  139.  rez = BULLET(&CDP);
  140.  if (rez) {
  141.     sprintf(putStr,"Failed data file create.  Err: %d\n",rez);
  142.     PutMsg(putStr);
  143.     goto Abend;
  144.  }
  145.  
  146.  
  147.  // Open the data file (required before creating an index file for it)
  148.  
  149.  OP.func = OPEN_DATA_XB;
  150.  OP.filenamePtr = nameData;
  151.  OP.asMode = READWRITE | DENYNONE;
  152.  rez = BULLET(&OP);
  153.  if (rez) {
  154.     sprintf(putStr,"Failed data file open.  Err: %d\n",rez);
  155.     PutMsg(putStr);
  156.     goto Abend;
  157.  }
  158.  dataID = OP.handle;
  159.  
  160.  // Set the custom User sort-cmp function #10
  161.  // before creating/opening the index that uses it
  162.  
  163.  QSP.func = SET_SYSVARS_XB;
  164.  QSP.item = 10;                      // user sort function #10
  165.  QSP.itemValue = (LONG) &UserRixUfp; // function pointer to custom sort-compare
  166.  rez = BULLET(&QSP);
  167.  if (rez) {
  168.     sprintf(putStr,"Failed SET_SYSVARS_XB #%d (custom-sort set) call.  Err: %d\n",QSP.item,rez);
  169.     PutMsg(putStr);
  170.     goto Abend;
  171.  }
  172.  
  173.  
  174.  // Create an index file for the data file opened above.
  175.  // This example uses the salary field as the sort (floating-point)
  176.  
  177.  strcpy(keyExpression,"SALARY");
  178.  
  179.  CIP.func = CREATE_INDEX_XB;
  180.  CIP.filenamePtr = nameIX3;
  181.  CIP.keyExpPtr = keyExpression;
  182.  CIP.xbLink = dataID;            // the handle of the data file
  183.  
  184.  // '10' below is user sortFunction 10 (may be 10 to 19)
  185.  
  186.  CIP.sortFunction = 10 | DUPS_ALLOWED | SORT_SET; // user sort w/duplicates allowed
  187.  
  188.  CIP.codePage = CODEPAGE;
  189.  CIP.countryCode = CTRYCODE;
  190.  CIP.collatePtr = collateTable;
  191.  CIP.nodeSize = 512;             // 512-byte node size (or 1024, 2048 bytes)
  192.  rez = BULLET(&CIP);
  193.  if (rez) {
  194.     sprintf(putStr,"Failed index file create.  Err: %d\n",rez);
  195.     PutMsg(putStr);
  196.     goto Abend;
  197.  }
  198.  
  199.  
  200.  // Open the index file (what we just created above).
  201.  // As with the index-create, the index-open requires the handle of the data
  202.  // file which this index file indexes.
  203.  
  204.  OP.func = OPEN_INDEX_XB;
  205.  OP.filenamePtr = nameIX3;
  206.  OP.asMode = READWRITE | DENYNONE;
  207.  OP.xbLink = dataID;
  208.  rez = BULLET(&OP);
  209.  if (rez) {
  210.     sprintf(putStr,"Failed index file open.  Err: %d\n",rez);
  211.     PutMsg(putStr);
  212.     goto Abend;
  213.  }
  214.  indexID = OP.handle;
  215.  
  216.  
  217.  PutMsg("Display all data accessed (slower results)? (y/N) ");
  218.  GetMsg(tmpStr);
  219.  if (*tmpStr=='y') display = 1;
  220.  
  221. #if PLATFORM == ON_OS2 || PLATFORM == ON_WIN32 && FOR_WINDOWS == 0
  222.   PutMsg("  Dispatch REINDEX_XB in a separate thread? (Y/n) ");
  223.   GetMsg(tmpStr);
  224.   if (*tmpStr=='n') thread = 0;
  225. #else
  226.   thread = 0;
  227. #endif
  228.  
  229.  
  230.  PutMsg("  How many records do you want for this test run? ");
  231.  GetMsg(tmpStr);
  232.  recs2addU = atol(tmpStr);
  233.  if (recs2addU < 1) recs2addU = 1;       // would you rather end the test?
  234.  if (recs2addU > 9999999) recs2addU = 1; // why wait around for 10M?
  235.  
  236.  
  237.  // Add the data records, which are created here, on-the-fly, varying enough
  238.  // to make unique records.  Not that this matters in this example (it does
  239.  // in bd_rix.c) since the key is SALARY, and it does permit duplicates.
  240.  
  241.  lastFourVar = 1;                        // 0001 start (as used in strncpy() below)
  242.  lastNameVar = 0;                        // 0000 start
  243.  
  244.  EmpRec.tag = ' ';                       // set to not-deleted
  245.  strncpy(EmpRec.empID,"465990000",9);    // only changing last 4 in test
  246.  strcpy(EmpRec.empLN,"0000LastNameNum"); // only changing first 4 in test
  247.  strcpy(EmpRec.empFN,"YourFirstName");   // everyone has this first name!
  248.  strncpy(EmpRec.empSalary,"      1.00",10);
  249.  strcpy(EmpRec.empDept,"MIS");           // everyone works for MIS!
  250.  
  251.  sprintf(putStr,"    Adding %d records...  ",recs2addU);
  252.  PutMsg(putStr);
  253.  
  254.  time(&startTime);
  255.  
  256.  AP.func = ADD_RECORD_XB;
  257.  AP.handle = dataID;
  258.  AP.recPtr = &EmpRec;
  259.  for (i = 1; i <= recs2addU; i++) {
  260.  
  261.     bonus = (float) rand();
  262.     sprintf(tmpStr,"%10.2f",baseSalary+bonus);
  263.     strncpy(EmpRec.empSalary,tmpStr,10);
  264.  
  265.     sprintf(tmpStr,"%4.4i",lastFourVar++);
  266.     strncpy(EmpRec.empID+5,tmpStr,4);
  267.     rez = BULLET(&AP);
  268.     if (rez) {
  269.        sprintf(putStr,"Failed while adding record %d.  Err: %d\n",i,rez);
  270.        PutMsg(putStr);
  271.        goto Abend;
  272.     }
  273.     if (lastFourVar > 9999) {                    // changes every 10,000 adds
  274.        lastFourVar = 0;
  275.        sprintf(tmpStr,"%4.4i",++lastNameVar);
  276.        strncpy(EmpRec.empLN,tmpStr,4);           // update first 4 of empLN
  277.     }
  278.  
  279. #if FOR_WINDOWS == 1                   // deal with message queue every now and then
  280.     if (i % Q_ADD == 0) DoWinThing(0); // 0=no waiting around
  281. #endif
  282.  
  283.  }
  284.  time(&endTime);
  285.  sprintf(putStr,"took %u secs.\n",(endTime - startTime));
  286.  PutMsg(putStr);
  287.  
  288.  
  289.  // Reindex
  290.  
  291.  sprintf(putStr,"Reindexing %d records...  \r",recs2addU);
  292.  PutMsg(putStr);
  293.  
  294.  time(&startTime);
  295.  
  296.  AP.func = REINDEX_XB;           // this is all there is to reindexing even...
  297.  AP.handle = indexID;            // ...million-record databases
  298.  AP.keyPtr = keyBuffer;          // if dup key error...(see manual for details)
  299.  AP.nextPtr = NULL;              // just this one index file
  300.  
  301.  if (thread) {
  302.  
  303. #if PLATFORM == ON_DOSX32
  304.  
  305.     PutMsg(" Threads are not supported on this OS\n");
  306.     goto Abend;
  307.  
  308. #elif PLATFORM == ON_OS2
  309.  
  310.     rez = DosCreateThread(&tidRixU,
  311.                           (PFNTHREAD) &ThreadRixU,
  312.                           (ULONG) &AP,
  313.                           CREATE_READY | STACK_SPARSE,
  314.                           32768);  // min is 8K stack
  315.     if (rez) {
  316.        sprintf(putStr,"Could not start reindex thread.  Err: %d\n",rez);
  317.        PutMsg(putStr);
  318.        goto Abend;
  319.     }
  320.  
  321.     DosSleep(32);  // wait a bit to let reindex code set progress to non-zero
  322.  
  323.     // could also use a callback routine to put out a reindex % number
  324.  
  325.     SIP.func = STAT_INDEX_XB;
  326.     SIP.handle = indexID;
  327.     rez = BULLET(&SIP);
  328.     while (rez==0) {
  329.        DosSleep(100);
  330.        sprintf(putStr,"Reindexing %d records... %3.3u%%\r",recs2addU,SIP.progress);
  331.        PutMsg(putStr);
  332.        rez = BULLET(&SIP);
  333.        if (SIP.progress==0) {
  334.           sprintf(putStr,"Reindexing %d records...  ",recs2addU);
  335.           PutMsg(putStr);
  336.           break;
  337.        }
  338.     }
  339.     if (rez) {
  340.        sprintf(putStr,"Failed progress check.  Err: %d\n",rez);
  341.        PutMsg(putStr);
  342.     } // continue
  343.  
  344.     // Can actually get here _before_ the reindex thread fully exits, but
  345.     // won't get here until SIP.progress is back to 0.
  346.  
  347.     time(&endTime);
  348.  
  349.     // It's likely that the thread has exited completely by now, but if
  350.     // it was blocked after setting SIP.progress to 0 (hung up in the cache,
  351.     // etc.), then it's possible to get here well before Bullet has exited.
  352.     // Since calling Bullet while Bullet is busy (for most routines) results
  353.     // in a rc=640 being returned (mutex timeout), just call this to wait
  354.     // for the thread to exit completely.  Note that this has only been seen
  355.     // under Win95 (done, but blocked), but this little routine won't hurt.
  356.     // Ignore any error, which there will be if the thread has indeed exited.
  357.     // Another option is to use a non-0 mutex-timeout value, via SET_SYSVARS_XB.
  358.  
  359.     rez = DosWaitThread(&tidRixU,DCWW_WAIT);
  360.     // ignore error (such as "invalid thread ID")
  361.  
  362.     DosSleep(1); // allow the reindex thread to exit BULLET() (and set rez)
  363.  
  364.     rez = rezRixU;          // get return code set by ThreadRix()
  365.     if (rez) {              // rez is already AP.stat
  366.        sprintf(putStr,"Failed reindex.  Err: %d\n",rez);
  367.        PutMsg(putStr);
  368.        goto Abend;
  369.     }
  370.     sprintf(putStr,"took %u secs.\n",(endTime - startTime));
  371.     PutMsg(putStr);
  372.  
  373. #elif PLATFORM == ON_WIN32
  374.  
  375.     hRixU = CreateThread(NULL,
  376.                          32768,               // 8KB min.
  377.                          (LPTHREAD_START_ROUTINE) &ThreadRixU,
  378.                          (PACCESSPACK) &AP,
  379.                          0,                   // immediate start
  380.                          &tidRixU);
  381.  
  382.  
  383.     if (hRixU==0) {
  384.        rez = GetLastError();
  385.        sprintf(putStr,"Could not start reindex thread.  Err: %d\n",rez);
  386.        PutMsg(putStr);
  387.        goto Abend;
  388.     }
  389.  
  390.     Sleep(32);   // wait a bit to let reindex code set progress to non-zero
  391.  
  392.     SIP.func = STAT_INDEX_XB;
  393.     SIP.handle = indexID;
  394.     rez = BULLET(&SIP);
  395.     while (rez==0) {
  396.        Sleep(100);
  397.        sprintf(putStr,"Reindexing %d records... %3.3u%%\r",recs2addU,SIP.progress);
  398.        PutMsg(putStr);
  399.        rez = BULLET(&SIP);
  400.        if (SIP.progress==0) {
  401.           sprintf(putStr,"Reindexing %d records...  ",recs2addU);
  402.           PutMsg(putStr);
  403.           break;
  404.        }
  405.     }
  406.     if (rez) {
  407.        sprintf(putStr,"Failed progress check.  Err: %d\n",rez);
  408.        PutMsg(putStr);
  409.     } // continue
  410.  
  411.  
  412.     // Can actually get here _before_ the reindex thread fully exits, but
  413.     // won't get here until SIP.progress is back to 0.
  414.  
  415.     time(&endTime);
  416.  
  417.     // It's likely that the thread has exited completely by now, but if
  418.     // it was blocked after setting SIP.progress to 0 (hung up in the cache,
  419.     // etc.), then it's possible to get here well before Bullet has exited.
  420.     // Since calling Bullet while Bullet is busy (for most routines) results
  421.     // in a rc=640 being returned (mutex timeout), just call this to wait
  422.     // for the thread to exit completely.  Note that this has only been seen
  423.     // under Win95 (done, but blocked), but this little routine won't hurt.
  424.     // Ignore any error, which there will be if the thread has indeed exited.
  425.     // Another option is to use a non-0 mutex-timeout value, via SET_SYSVARS_XB.
  426.     // Error code 640 is documented in BULLET_2.H.
  427.  
  428.     rez = WaitForSingleObject(hRixU,10*1000); // 10 secs plenty of flush time
  429.     // ignore error
  430.  
  431.     Sleep(32);   // allow the reindex thread to exit BULLET() (and set rez)
  432.                  // why 32ms?  Only because OS/2 min std resolution is 32ms
  433.  
  434.     rez = rezRixU;          // get return code set by ReindexThread9()
  435.     if (rez) {              // rez is already AP.stat
  436.        sprintf(putStr,"Failed reindex.  Err: %d\n",rez);
  437.        PutMsg(putStr);
  438.        goto Abend;
  439.     }
  440.     sprintf(putStr,"took %u secs.\n",(endTime - startTime));
  441.     PutMsg(putStr);
  442.  
  443. #endif
  444.  
  445.  }
  446.  else {
  447.  
  448.     // no separate thread, so use callback (especially useful for DOSX32 and Win32s)
  449.  
  450.     QSP.func = SET_SYSVARS_XB;
  451.     QSP.item = CALLBACK_PTR;
  452.     QSP.itemValue = (ULONG) &CallbackRixU;
  453.     rez = BULLET(&QSP);
  454.     if (rez) {
  455.        sprintf(putStr,"Failed SET_SYSVARS_XB #%d (callback address) call.  Err: %d\n",QSP.item,rez);
  456.        PutMsg(putStr);
  457.        goto Abend;
  458.     }
  459.  
  460.     // now make actual REINDEX_XB call, set up above
  461.  
  462.     rez = BULLET(&AP);       // rez=0 if okay, or =1 if pack failed with ecode in AP.stat
  463.     if (rez) {
  464.        rez = AP.stat;
  465.        sprintf(putStr,"Failed reindex.  Err: %d\n",rez);
  466.        PutMsg(putStr);
  467.        goto Abend;
  468.     }
  469.     time(&endTime);
  470.     sprintf(putStr,"Reindexing %d records...  took %u secs.\n",recs2addU,(endTime - startTime));
  471.     PutMsg(putStr);
  472.  
  473.     // turn off callback
  474.  
  475.     QSP.func = SET_SYSVARS_XB;
  476.     QSP.item = CALLBACK_PTR;
  477.     QSP.itemValue = 0;
  478.     rez = BULLET(&QSP);
  479.     if (rez) {
  480.        sprintf(putStr,"Failed SET_SYSVARS_XB #%d (callback clear) call.  Err: %d\n",QSP.item,rez);
  481.        PutMsg(putStr);
  482.        goto Abend;
  483.     }
  484.  
  485.  }
  486.  
  487.  
  488.  // Get key data
  489.  
  490.  memset(keyBuffer,0,sizeof(keyBuffer));
  491.  sprintf(putStr," Accessing %d keys...     ",recs2addU);
  492.  PutMsg(putStr);
  493.  if (display) PutMsg("\n");
  494.  time(&startTime);
  495.  AP.func = FIRST_KEY_XB;
  496.  AP.handle = indexID;
  497.  AP.keyPtr = keyBuffer;
  498.  rez = BULLET(&AP);
  499.  i=0;
  500.  while (rez==0) {
  501.     if (display) {
  502.        sprintf(putStr,"%s  %9.9lu\r", keyBuffer, AP.recNo);
  503.        PutMsg(putStr);
  504.     }
  505.     i++;
  506.     AP.func = NEXT_KEY_XB;
  507.     rez = BULLET(&AP);
  508.  
  509. #if FOR_WINDOWS == 1                   // deal with message queue every now and then
  510.     if (i % Q_KEY == 0) DoWinThing(0); // 0=no waiting around
  511. #endif
  512.  
  513.  }
  514.  time(&endTime);
  515.  if (display) PutMsg("\n...");
  516.  
  517.  // expected rez is EXB_END_OF_FILE
  518.  
  519.  if (rez == EXB_END_OF_FILE) rez=0;
  520.  if (rez) {
  521.     sprintf(putStr,"Failed KEY access.  Err: %d\n",rez);
  522.     PutMsg(putStr);
  523.     goto Abend;
  524.  }
  525.  sprintf(putStr,"took %u secs. for %d keys\n",(endTime - startTime),i);
  526.  PutMsg(putStr);
  527.  
  528.  
  529.  // Get key and record data
  530.  
  531.  sprintf(putStr," Accessing %d keys+recs...",recs2addU);
  532.  PutMsg(putStr);
  533.  if (display) PutMsg("\n");
  534.  time(&startTime);
  535.  
  536.  AP.func = GET_FIRST_XB;
  537.  AP.handle = indexID;
  538.  AP.keyPtr = keyBuffer;
  539.  AP.recPtr = &EmpRec;
  540.  rez = BULLET(&AP);
  541.  i=0;
  542.  while (rez==0) {
  543.     if (display) {
  544.        sprintf(putStr,"%s  %9.9lu   %s\r", keyBuffer, AP.recNo, &EmpRec); // partial show
  545.        PutMsg(putStr);
  546.     }
  547.     i++;
  548.     AP.func = GET_NEXT_XB;
  549.     rez = BULLET(&AP);
  550.  
  551. #if FOR_WINDOWS == 1                   // deal with message queue every now and then
  552.     if (i % Q_GET == 0) DoWinThing(0); // 0=no waiting around
  553. #endif
  554.  
  555.  }
  556.  time(&endTime);
  557.  if (display) PutMsg("\n...");
  558.  
  559.  // expected rez is EXB_END_OF_FILE
  560.  
  561.  if (rez == EXB_END_OF_FILE) rez=0;
  562.  if (rez) {
  563.     sprintf(putStr,"Failed GET access.  Err: %d\n",rez);
  564.     PutMsg(putStr);
  565.     goto Abend;
  566.  }
  567.  sprintf(putStr,"took %u secs. for %d keys & records\n",(endTime - startTime),i);
  568.  PutMsg(putStr);
  569.  
  570.  
  571.  // Fatal errors above come straight to here
  572. Abend:
  573.  
  574.  // Close files
  575.  
  576.  if (indexID) {
  577.     HP.func = CLOSE_INDEX_XB;
  578.     HP.handle = indexID;
  579.     rez = BULLET(&HP);
  580.     if (rez) {
  581.        sprintf(putStr,"Failed index file close.  Err: %d\n",rez);
  582.        PutMsg(putStr);
  583.     }
  584.  }
  585.  
  586.  // Unlikely the above could fail, considering how far it has gotten so far!
  587.  // But logic says that we want to continue closing other open files...
  588.  
  589.  if (dataID) {
  590.     HP.func = CLOSE_DATA_XB;
  591.     HP.handle = dataID;
  592.     rez = BULLET(&HP);
  593.     if (rez) {
  594.        sprintf(putStr,"Failed data file close.  Err: %d\n",rez);
  595.        PutMsg(putStr);
  596.     }
  597.  }
  598.  return rez;  // module exit
  599. }
  600.  
  601.  
  602. ////////////////////
  603. // Support Routines
  604. ////////////////////
  605.  
  606. #if PLATFORM == ON_OS2 || PLATFORM == ON_WIN32
  607.  
  608. // -------------------------
  609. // Reindex thread, thread #2
  610.  
  611. void APIENTRY ThreadRixU(ACCESSPACK *AP) {
  612.  
  613.  rezRixU = BULLET(AP);
  614.  
  615.  // Reindex is a xaction-list routine and so must check AP.stat for any
  616.  // error code -- rez as returned from the Bullet call is the list item
  617.  // that failed.  Since this example has but the single item, rez=1 on
  618.  // failure, with the error code in AP.stat.
  619.  
  620.  if (rezRixU) rezRixU = AP->stat;
  621. }
  622. #endif
  623.  
  624.  
  625. // ----------------------------
  626. // Custom Sort-Compare Function
  627.  
  628. LONG APIENTRY UserRixUfp(PVOID fp1, PVOID fp2, LONG count, ULONG handle) {
  629.  
  630.  // High value for this compare, including a maxed-out enumerator (0xFFFF)
  631.  // this must be 'static' declared since a pointer to it is returned
  632.  // by this function for the special case.
  633.  
  634.  static CHAR highValuesFP[13]="9999999.99\xFF\xFF";
  635.  
  636.  
  637.  if ((count==0) | (handle==0)) PutMsg("count or handle is 0\n");    // won't happen
  638.  
  639.  // If there is not a Build-Key routine to convert the DBF data field from
  640.  // ASCII numbers to a binary IEEE float (or if you don't want to use IEEE
  641.  // fp key values), then that conversion can be done right here, in the
  642.  // compare.  The 10-digit ASCII numbers (same as from the DBF data record)
  643.  // are passed as a string, in the form as shown in highValuesFP.  Also,
  644.  // count is passed, though it is already known to be 12 in this example
  645.  // (10+2 bytes of the enumerator since DUPS_ALLOWED).  Handle is not used,
  646.  // but could be used to find out if duplicates are allowed if this wasn't
  647.  // know prior.
  648.  
  649.  // never should only one pointer be null -- either both are or neither
  650.  
  651.  if ((fp1!=NULL) & (fp2!=NULL)) {
  652.  
  653.     double tfp1 = atof((CHAR *)fp1);
  654.     double tfp2 = atof((CHAR *)fp2);
  655.     if (tfp1 > tfp2) return 1;
  656.     if (tfp1 < tfp2) return -1;
  657.     return 0;
  658.  }
  659.  else {
  660.     return (ULONG)&highValuesFP[0];
  661.  }
  662. }
  663.  
  664.  
  665. //------------------------------------
  666. // Init field list items for data file
  667.  
  668. void BuildFieldListRixU(FIELDDESCTYPE fieldList[]) {
  669.  
  670.  strcpy(fieldList[0].fieldName, "SSN");  // field names must be upper-case
  671.  fieldList[0].fieldType = 'C';           // field types must be upper-case
  672.  fieldList[0].fieldLen = 9;
  673.  fieldList[0].fieldDC = 0;
  674.  
  675.  strcpy(fieldList[1].fieldName, "LNAME");
  676.  fieldList[1].fieldType = 'C';
  677.  fieldList[1].fieldLen = 16;
  678.  fieldList[1].fieldDC = 0;
  679.  
  680.  strcpy(fieldList[2].fieldName, "FNAME");
  681.  fieldList[2].fieldType = 'C';
  682.  fieldList[2].fieldLen = 16;
  683.  fieldList[2].fieldDC = 0;
  684.  
  685.  strcpy(fieldList[3].fieldName, "SALARY");
  686.  fieldList[3].fieldType = 'N';
  687.  fieldList[3].fieldLen = 10;      // "1234567.90"
  688.  fieldList[3].fieldDC = 2;
  689.  
  690.  strcpy(fieldList[4].fieldName, "DEPT");
  691.  fieldList[4].fieldType = 'C';
  692.  fieldList[4].fieldLen = 6;
  693.  fieldList[4].fieldDC = 0;
  694.  return;
  695. }
  696.  
  697.  
  698. // --------------------------------
  699. // Callback for reindex (no thread)
  700. // This must get parm from stack
  701. // Stack clean up may be caller OR callee
  702. // CBP->callMode == 0 (0=reindex; 1=pack records)
  703. // CBP->data1 is 1 to 99 when working (it starts
  704. // at 1), and is 0 when finished (same as SxP.progress)
  705.  
  706. void __cdecl CallbackRixU(CALLBACKPACK *CBP) {
  707.  
  708.  static ULONG pct=0;
  709.  CHAR putStr[128];
  710.  
  711.  if (CBP->data1 > pct) {   // this logic prevents final 0% (all done) from showing
  712.     // W10a fails to carry-forward %% in sprintf so percent sign doesn't display
  713.     pct = CBP->data1;
  714.     sprintf(putStr,"Reindexing %d records... %3.3u%%\r",recs2addU,pct);
  715.     PutMsg(putStr);
  716.  }
  717.  
  718.  // when reindex is complete data1 is always set to 0
  719.  // take the opportunity to re-init the static pct var
  720.  // else it'll stay at its last value
  721.  
  722.  if (CBP->data1 == 0) pct=0;
  723.  
  724.  return;
  725.  
  726.  // could also get % this way, but why if already in CBP->data1
  727.  
  728. #if 0
  729.  STATINDEXPACK SIP;
  730.  
  731.  SIP.func = STAT_INDEX_XB;
  732.  SIP.handle = indexID;
  733.  rez = BULLET(&SIP);
  734.  if (SIP.progress != 0) {
  735.     sprintf(putStr,"Reindexing %d records... %3.3u%%\r",recs2addU,SIP.progress);
  736.     PutMsg(putStr);
  737.  }
  738.  return;
  739. #endif
  740.  
  741. }
  742.