home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #20 / NN_1992_20.iso / spool / comp / lang / c / 13461 < prev    next >
Encoding:
Text File  |  1992-09-10  |  34.4 KB  |  1,278 lines

  1. Xref: sparky comp.lang.c:13461 comp.lang.c++:13475
  2. Newsgroups: comp.lang.c,comp.lang.c++
  3. Path: sparky!uunet!mnemosyne.cs.du.edu!arrakis!thor
  4. From: thor@arrakis.denver.co.us (Robert B. Hood)
  5. Subject: cBASE - Part 1
  6. Message-ID: <1992Sep10.134151.8451@arrakis.denver.co.us>
  7. Summary: A dBASE III+ Class in C++
  8. Reply-To: thor@arrakis.denver.co.us
  9. Organization: Bob's Programming Paradise, Lakewood, CO, USA
  10. Date: Thu, 10 Sep 1992 13:41:51 GMT
  11. Lines: 1265
  12.  
  13. I have received over 20 `me too's in two days, so I guess that
  14. warrants a post.  Here is the C++ class to access and modify
  15. dBASE III+ .DBF files.  It is still under construction (doesn't
  16. do indexing or currently handle memo fields), but it is a start.
  17.  
  18. I will also include two test programs (sans databases) that actually
  19. use the class.  This article contains CBASE.CPP, and subsequent
  20. posts will contains the header file and test programs.
  21.  
  22. Enjoy!
  23.  
  24. ------------------------- CBASE.CPP -----------------------------
  25. // cBASE - A dBASE III+ class for accessing and manipulating .DBF files.
  26. //                Copyright (C) 1992 Bob Hood
  27. //
  28. // Author      : Bob Hood (thor@arrakis.denver.co.us)
  29. // Last Update : 08.01.92
  30. // Header Files: cBASE.HPP
  31. // Comments    : Class is still under construction, so use at your own risk.
  32. //
  33. //               You are free to copy and distribute this code as long as
  34. //               you do not charge a fee for it, and the Copyright notice
  35. //               above remains intact.
  36.  
  37. //  -- `To Do' list --
  38. // 1. lock database header area when updating
  39. // 2. deal with memo fields
  40. // 3. implement a linked-list quick sort routine (for indexing)
  41. // 4. create generic area locking routine by locking (record number +
  42. //    1,000,000 or so).  This is how the big boys (Clipper, dBASE, etc.)
  43. //    allow you to view a record even though another user has it locked!
  44.  
  45. #include    <stdio.h>
  46. #include    <iostream.h>
  47. #include    <conio.h>
  48. #include    <stdlib.h>
  49. #include    <fcntl.h>
  50. #include    <io.h>
  51. #include    <alloc.h>
  52. #include    <string.h>
  53. #include    <mem.h>
  54. #include    <dos.h>
  55.  
  56. #include    "cbase.hpp"
  57.  
  58. #define DB_LOCK     0x00
  59. #define DB_UNLOCK   0x01
  60.  
  61. //---------------------------------------------------------------------------
  62. // constructor...
  63. //---------------------------------------------------------------------------
  64. dBASE::dBASE()
  65. {
  66.     data.db_name[0]    = '\0';
  67.     data.database      = -1;
  68.  
  69.     data.field_rec          = NULL;
  70.     data.record_hold        = NULL;
  71.     data.index.index_chain  = NULL;
  72.     data.relation_list      = NULL;
  73.     data.field_data         = NULL;
  74.  
  75.     db_err                  = NO_PROBLEM;
  76.     net_err                 = NO_PROBLEM;
  77. }
  78.  
  79. //---------------------------------------------------------------------------
  80. int dBASE::activate(char *db_name,char *alias)
  81. {
  82.     if(data.database != -1) return(ALREADY_OPEN_ERROR);
  83.  
  84.     data.database = open(db_name,O_RDWR|O_BINARY);
  85.  
  86.     if(data.database != -1)
  87.     {
  88.         strcpy(data.db_name,db_name);
  89.         strcpy(data.alias,alias);
  90.  
  91.         eof_met         = FALSE;
  92.         bof_met         = FALSE;
  93.         update          = FALSE;
  94.         file_locked     = FALSE;
  95.         record_locked   = FALSE;
  96.     }
  97.     else
  98.         return(DOS_OPEN_ERROR);
  99.  
  100.     return(getStructure());
  101. }
  102.  
  103. //---------------------------------------------------------------------------
  104. int dBASE::deactivate(void)
  105. {
  106.     INDEX_CHAIN     *hold1,*hold2;
  107.  
  108.     if(data.database == -1) return(NO_DB_OPEN_ERROR);
  109.  
  110.     // flush any changed data contained in the record buffer...
  111.  
  112.     if(update)
  113.     {
  114.         if(record_locked || file_locked)
  115.         {
  116.             f2b();              // return the linked list to a stream
  117.             writeRecord();      // and put the data back in the file
  118.             if(record_locked) rUnLock();
  119.         }
  120.         else
  121.             cout << "ERROR: Record #" << currRecord() << " not locked -- cannot update";
  122.     }
  123.  
  124.     // ...and shut that puppy down!
  125.  
  126.     if(file_locked) fUnLock();
  127.  
  128.     close(data.database);
  129.  
  130.     if(data.field_rec != NULL)      free(data.field_rec);
  131.     if(data.record_hold != NULL)    free(data.record_hold);
  132.     if(data.index.index_chain != NULL) killIndex();
  133.     if(data.relation_list != NULL)  free(data.relation_list);
  134.  
  135.     data.db_name[0]    = '\0';
  136.     data.alias[0]      = '\0';
  137.     data.database      = -1;
  138.     data.field_rec     = NULL;
  139.     data.record_hold   = NULL;
  140.     data.relation_list = NULL;
  141.  
  142.     eof_met            = FALSE;
  143.     bof_met            = FALSE;
  144.     update             = FALSE;
  145.     file_locked        = FALSE;
  146.     record_locked      = FALSE;
  147.  
  148.     db_err             = NO_PROBLEM;
  149.  
  150.     return(db_err);
  151. }
  152.  
  153. //---------------------------------------------------------------------------
  154. int dBASE::getStructure(void)
  155. {
  156.     int         x,y;
  157.     int         num_fields;
  158.     int         more_fields;
  159.     long        rec_pos;
  160.     FIELD_REC   hold_field;
  161.  
  162.     if(data.database == -1) return(NO_DB_OPEN_ERROR);
  163.  
  164.     lseek(data.database,0L,SEEK_END);   // jump to the end..
  165.     dbSize = tell(data.database);       // see how long this file is...
  166.  
  167.     lseek(data.database,0L,SEEK_SET);   // rewind the file
  168.  
  169.     x = read(data.database,(char *)&data.dbf_head,sizeof(DBF_HEAD));
  170.  
  171.     if(x != sizeof(DBF_HEAD)) return(DOS_READ_ERROR);
  172.  
  173.     rec_pos = tell(data.database);      // position of the start of the field list
  174.     data.field_list_offset = rec_pos;
  175.     num_fields = 0;
  176.  
  177.     for(x = 0,y = 0;x < 2;x++)          // two passes
  178.     {
  179.         lseek(data.database,rec_pos,SEEK_SET);   // move to start of field list
  180.  
  181.         more_fields = TRUE;
  182.         while(more_fields)
  183.         {
  184.             more_fields = (read(data.database,(char *)&hold_field,sizeof(FIELD_REC)) == sizeof(FIELD_REC));
  185.  
  186.             if(more_fields) more_fields = (hold_field.field_name[0] != 0x0D);
  187.             if(more_fields)
  188.             {
  189.                 if(x == 1)
  190.                 {
  191.                     memmove((void *)&data.field_rec[y],(void *)&hold_field,
  192.                             sizeof(FIELD_REC));
  193.                     ++y;
  194.                 }
  195.                 else
  196.                     ++num_fields;
  197.             }
  198.         }
  199.  
  200.         if(x == 0)      // first pass, create an array large enough to hold list
  201.         {
  202.             data.num_fields = num_fields;
  203.             data.field_rec = (FIELD_REC *)calloc(num_fields,sizeof(FIELD_REC));
  204.         }
  205.     }
  206.  
  207.     // the record pointer should be positioned @ the first data record in this
  208.     // database.  save it in the area structure
  209.  
  210.     data.data_records_offset = tell(data.database);
  211.     data.current_record = 1L;
  212.  
  213.     data.record_hold = (void *)malloc(data.dbf_head.rec_size);
  214.  
  215.     // position the record pointer @ the first record, and read it into
  216.     // the 'record_hold' area...
  217.  
  218.     gotoRecord(data.current_record);
  219.  
  220.     return(NO_PROBLEM);
  221. }
  222.  
  223. //---------------------------------------------------------------------------
  224. int dBASE::gotoRecord(LONG rec_number,int lockIt)
  225. {
  226.     int         x,y;
  227.     int         num_fields;
  228.     int         more_fields;
  229.     LONG        rec_pos;
  230.  
  231.     // see if there is a database currently open in this area
  232.  
  233.     if(data.database == -1) return(NO_DB_OPEN_ERROR);
  234.  
  235.     // do we have data sitting in the `data.record_hold' buffer?
  236.  
  237.     if(update)
  238.     {
  239.         if(!record_locked) return(RECORD_NOT_LOCKED_ERROR);
  240.  
  241.         f2b();              // return the linked list to a stream
  242.         writeRecord();      // and put the data back in the file
  243.     }
  244.  
  245.     x = NO_PROBLEM;
  246.  
  247.     if(record_locked && !file_locked) rUnLock();    // unlock the record
  248.  
  249.     // see if the requested record number is greater than those contained
  250.     // in the database file...
  251.  
  252.     if(rec_number > data.dbf_head.last_rec) return(INVALID_RECORD_ERROR);
  253.  
  254.     // position the file pointer at the first record in the database
  255.  
  256.     lseek(data.database,(long)data.dbf_head.data_offset,SEEK_SET);
  257.  
  258.     // calculate the records offset from here...
  259.  
  260.     rec_pos = (rec_number - 1) * data.dbf_head.rec_size;
  261.  
  262.     // now position to that record
  263.  
  264.     lseek(data.database,rec_pos,SEEK_CUR);
  265.  
  266.     // and update the area record pointer
  267.  
  268.     data.current_record = rec_number;
  269.  
  270.     if(lockIt && !file_locked)
  271.     {
  272.         if(!rLock()) x = db_err;
  273.     }
  274.  
  275.     // read the current record into 'record_hold' for this area
  276.  
  277.     read(data.database,data.record_hold,data.dbf_head.rec_size);
  278.  
  279.     // put us back to the start of this record in case we do a writeRecord()
  280.  
  281.     lseek(data.database,-data.dbf_head.rec_size,SEEK_CUR);
  282.  
  283.     // convert the stream of characters into a linked list of fields
  284.  
  285.     b2f();  // gets fields to linked list, and sets `update' to FALSE
  286.  
  287.     // make the new linked list publicly available
  288.  
  289.     field_list = data.field_data;
  290.  
  291.     return(x);
  292. }
  293.  
  294. //---------------------------------------------------------------------------
  295. int dBASE::goTop(void)
  296. {
  297.     return(gotoRecord(1L));
  298. }
  299.  
  300. //---------------------------------------------------------------------------
  301. int dBASE::goBottom(void)
  302. {
  303.     return(gotoRecord(data.dbf_head.last_rec));
  304. }
  305.  
  306. //---------------------------------------------------------------------------
  307. void *dBASE::getRecord(void)
  308. {
  309.     // see if there is a database currently open in this area
  310.  
  311.     if(data.database == -1)
  312.     {
  313.         db_err = NO_DB_OPEN_ERROR;
  314.         return(NULL);
  315.     }
  316.  
  317.     // return a pointer to the data currently being held in `record_hold'
  318.  
  319.     return((void *)data.record_hold);
  320. }
  321.  
  322. //---------------------------------------------------------------------------
  323. int dBASE::getRecordLen(void)
  324. {
  325.     // see if there is a database currently open in this area
  326.  
  327.     if(data.database == -1) return(NO_DB_OPEN_ERROR);
  328.  
  329.     // return the record len of the current database
  330.  
  331.     return(data.dbf_head.rec_size);
  332. }
  333.  
  334. //---------------------------------------------------------------------------
  335. int dBASE::writeRecord(void)
  336. {
  337.     // write the current record in `record_hold' (was converted with `f2b()'
  338.     // in `gotoRecord()' above
  339.  
  340.     write(data.database,data.record_hold,data.dbf_head.rec_size);
  341.  
  342.     // put us back to the start of this record
  343.  
  344.     lseek(data.database,-data.dbf_head.rec_size,SEEK_CUR);
  345.  
  346.     return(NO_PROBLEM);
  347. }
  348.  
  349. //---------------------------------------------------------------------------
  350. LONG dBASE::lastRecord(void)
  351. {
  352.     if(data.database == -1)
  353.     {
  354.         db_err = NO_DB_OPEN_ERROR;
  355.         return(0L);
  356.     }
  357.  
  358.     // return the record len of the current database
  359.  
  360.     return(data.dbf_head.last_rec);
  361. }
  362.  
  363. //---------------------------------------------------------------------------
  364. LONG dBASE::currRecord(void)
  365. {
  366.     if(data.database == -1)
  367.     {
  368.         db_err = NO_DB_OPEN_ERROR;
  369.         return(0L);
  370.     }
  371.  
  372.     // return the record len of the current database
  373.  
  374.     return(data.current_record);
  375. }
  376.  
  377. //---------------------------------------------------------------------------
  378. int dBASE::deleteRecord(void)
  379. {
  380.     char    *buff;
  381.  
  382.     if(data.database == -1) return(NO_DB_OPEN_ERROR);
  383.  
  384.     if(!record_locked) return(RECORD_NOT_LOCKED_ERROR);
  385.  
  386.     buff = (char *)getRecord();
  387.     *buff = '*';
  388.     data.deleted = TRUE;
  389.  
  390.     update = TRUE;
  391.  
  392.     return(NO_PROBLEM);
  393. }
  394.  
  395. //---------------------------------------------------------------------------
  396. int dBASE::recallRecord(void)
  397. {
  398.     char    *buff;
  399.  
  400.     if(data.database == -1) return(NO_DB_OPEN_ERROR);
  401.  
  402.     buff = (char *)getRecord();
  403.     *buff = ' ';
  404.     data.deleted = FALSE;
  405.  
  406.     update = TRUE;
  407.  
  408.     return(NO_PROBLEM);
  409. }
  410.  
  411. //---------------------------------------------------------------------------
  412. void dBASE::listInfo(void)
  413. {
  414.     int     x;
  415.  
  416.     // see if there is a database currently open in this area
  417.  
  418.     if(data.database == -1) return;
  419.  
  420.     printf("\n*** Structure of database \"%s\"\n\n",data.db_name);
  421.     printf("Last update %2d/%2d/%2d\n",
  422.             data.dbf_head.last_update[1],    // month
  423.             data.dbf_head.last_update[2],    // day
  424.             data.dbf_head.last_update[0]);   // year
  425.     printf("Data offset %d\n",data.dbf_head.data_offset);
  426.     printf("Record size %d\n",data.dbf_head.rec_size);
  427.  
  428.     printf("Number of records %lu\n\n",data.dbf_head.last_rec);
  429.     printf("NAME        TYPE LEN DEC\n\n");
  430.  
  431.     x = 0;
  432.     while(x < data.num_fields)
  433.     {
  434.         switch(data.field_rec[x].field_type)
  435.         {
  436.             case 'N':   printf("%-11s %-4c %3d %3d\n",
  437.                                 data.field_rec[x].field_name,
  438.                                 data.field_rec[x].field_type,
  439.                                 data.field_rec[x].len_info.num_size.len,
  440.                                 data.field_rec[x].len_info.num_size.dec);
  441.                         break;
  442.  
  443.             default:    printf("%-11s %-4c %3d\n",
  444.                                 data.field_rec[x].field_name,
  445.                                 data.field_rec[x].field_type,
  446.                                 data.field_rec[x].len_info.char_len);
  447.                         break;
  448.         }
  449.  
  450.         ++x;
  451.     }
  452. }
  453.  
  454. //---------------------------------------------------------------------------
  455. void dBASE::grab(char *from, char *to, char *dest)
  456. {
  457.     int     x;
  458.  
  459.     x = 0;
  460.     while(from <= to)
  461.     {
  462.         dest[x] = *from;
  463.         ++from;
  464.         ++x;
  465.     }
  466.     dest[x] = '\0';
  467. }
  468.  
  469. //---------------------------------------------------------------------------
  470. char *dBASE::alias(void)
  471. {
  472.     // see if there is a database currently open in this area
  473.  
  474.     if(data.database == -1)
  475.     {
  476.         db_err = NO_DB_OPEN_ERROR;
  477.         return(NULL);
  478.     }
  479.  
  480.     // return the record len of the current database
  481.  
  482.     return(data.alias);
  483. }
  484.  
  485. //---------------------------------------------------------------------------
  486. int dBASE::newRecord(int lockIt)
  487. {
  488.     int         x,y;
  489.     long        rec_pos;
  490.     char        *holdfield;
  491.  
  492.     if(data.database == -1) return(NO_DB_OPEN_ERROR);
  493.  
  494.     lseek(data.database,0L,SEEK_END);   // go to bottom of file
  495.  
  496.     holdfield = (char *)malloc(data.dbf_head.rec_size);
  497.  
  498.     memset(holdfield,' ',data.dbf_head.rec_size);
  499.  
  500.     x = write(data.database,holdfield,data.dbf_head.rec_size);
  501.     free(holdfield);
  502.  
  503.     if(x != data.dbf_head.rec_size) return(DOS_WRITE_ERROR);
  504.  
  505.     dbSize += x;                // update the size pointer...
  506.  
  507.     // dynamically update the database header
  508.  
  509.     lseek(data.database,0L,SEEK_SET);   // rewind the file
  510.  
  511.     x = read(data.database,(char *)&data.dbf_head,sizeof(DBF_HEAD));
  512.     if(x != sizeof(DBF_HEAD)) return(DOS_READ_ERROR);
  513.  
  514.     ++data.dbf_head.last_rec;   // increment the number of records by one
  515.  
  516.     // update the `last_update' field <<-- needs to be implemented
  517.  
  518.     lseek(data.database,0L,SEEK_SET);   // rewind the file
  519.     if(lock(data.database,0L,sizeof(DBF_HEAD),DB_LOCK))
  520.     {
  521.         write(data.database,(char *)&data.dbf_head,sizeof(DBF_HEAD));
  522.         lock(data.database,0L,sizeof(DBF_HEAD),DB_UNLOCK);
  523.     }
  524.     else
  525.         return(RECORD_LOCK_FAILED_ERROR);
  526.  
  527.     // is there a `file_locked' condition?
  528.  
  529.     if(file_locked)
  530.     {
  531.         // yes, expand the current lock to include the new record
  532.  
  533.         dbSize += data.dbf_head.rec_size;
  534.  
  535.         if(!lock(data.database,0L,dbSize,DB_LOCK))
  536.             return(FILE_LOCK_FAILED_ERROR);
  537.  
  538.         lockIt = FALSE;     // record is already implicitly locked,
  539.                             // so don't be redundant
  540.     }
  541.  
  542.     gotoRecord(data.dbf_head.last_rec);     // re-position to the new record
  543.  
  544.     x = NO_PROBLEM;
  545.  
  546.     if(lockIt && !rLock()) x = db_err;
  547.  
  548.     return(x);
  549. }
  550.  
  551. //---------------------------------------------------------------------------
  552. int dBASE::b2f(void)
  553. {
  554.     int         x,y,z;
  555.     int         left,right;
  556.     int         year,month,day;
  557.     char        logical;
  558.     char        text[256];
  559.     char        format[25];
  560.     char        *buff;
  561.     char        *hold;
  562.     float       number;
  563.     long        long_number;
  564.     FIELD_DATA  *new_field;
  565.  
  566.     if(data.database == -1) return(NO_DB_OPEN_ERROR);
  567.  
  568.     buff = (char *)getRecord();
  569.     y = getRecordLen();
  570.  
  571.     data.deleted = (*buff == '*') ? TRUE : FALSE;
  572.  
  573.     ++buff;
  574.  
  575.     clearDBFields();        // release the linked list of field data
  576.  
  577.     x = 0;
  578.     while(x < data.num_fields)
  579.     {
  580.         switch(data.field_rec[x].field_type)
  581.         {
  582.             case 'D':   grab(buff,buff+3,text);
  583.                         year = atoi(text);
  584.  
  585.                         grab(buff+4,buff+5,text);
  586.                         month = atoi(text);
  587.  
  588.                         grab(buff+6,buff+7,text);
  589.                         day = atoi(text);
  590.  
  591.                         buff += 8;
  592.  
  593.                         if((new_field = newDBField()) == NULL)
  594.                         {
  595.                             ++x;
  596.                             continue;
  597.                         }
  598.  
  599.                         new_field->field_type = 'D';
  600.                         new_field->data.date.month = month;
  601.                         new_field->data.date.day   = day;
  602.                         new_field->data.date.year  = year;
  603.  
  604.                         break;
  605.  
  606.             case 'C':   y = data.field_rec[x].len_info.char_len - 1;
  607.                         grab(buff,buff+y,text);
  608.  
  609.                         buff += y;
  610.                         ++buff;
  611.  
  612.                         if((new_field = newDBField()) == NULL)
  613.                         {
  614.                             ++x;
  615.                             continue;
  616.                         }
  617.  
  618.                         new_field->field_type = 'C';
  619.                         strcpy(new_field->data.text,text);
  620.  
  621.                         break;
  622.  
  623.             case 'L':   logical = *buff;
  624.                         ++buff;
  625.  
  626.                         if((new_field = newDBField()) == NULL)
  627.                         {
  628.                             ++x;
  629.                             continue;
  630.                         }
  631.  
  632.                         new_field->field_type = 'L';
  633.                         new_field->data.boolean = logical;
  634.  
  635.                         break;
  636.  
  637.             case 'M':   grab(buff,buff+9,text);
  638.                         buff += 10;
  639.                         break;
  640.  
  641.             case 'N':   y = data.field_rec[x].len_info.num_size.len - 1;
  642.                         grab(buff,buff+y,text);
  643.  
  644.                         buff += y;
  645.                         ++buff;
  646.  
  647.                         z = data.field_rec[x].len_info.num_size.dec;
  648.  
  649.                         if((new_field = newDBField()) == NULL)
  650.                         {
  651.                             ++x;
  652.                             continue;
  653.                         }
  654.  
  655.                         if(z > 0)
  656.                         {
  657.                             sprintf(format,"%%%3d.%03df",y+1,z);
  658.                             sscanf(text,"%f",&number);
  659.  
  660.                             new_field->field_type = 'F';
  661.                             new_field->data.float_number.number = number;
  662.                             strcpy(new_field->data.float_number.format,format);
  663.                         }
  664.                         else
  665.                         {
  666.                             long_number = atol(text);
  667.  
  668.                             new_field->field_type = 'N';
  669.                             new_field->data.long_number = long_number;
  670.                         }
  671.                         break;
  672.         }
  673.  
  674.         ++x;
  675.     }
  676.  
  677.     update = FALSE;
  678.  
  679.     return(NO_PROBLEM);
  680. }
  681.  
  682. //---------------------------------------------------------------------------
  683. FIELD_DATA *dBASE::newDBField(void)
  684. {
  685.     FIELD_DATA  *this_field;
  686.     FIELD_DATA  *new_field;
  687.  
  688.     new_field = (FIELD_DATA *) malloc(sizeof(FIELD_DATA));
  689.     if(new_field == NULL) return(NULL);
  690.  
  691.     if(data.field_data == NULL)
  692.         data.field_data = new_field;
  693.     else
  694.     {
  695.         this_field = data.field_data;
  696.         while(this_field->next != NULL) this_field = this_field->next;
  697.         this_field->next = new_field;
  698.     }
  699.  
  700.     new_field->next = NULL;
  701.     return(new_field);
  702. }
  703.  
  704. //---------------------------------------------------------------------------
  705. void dBASE::clearDBFields(void)
  706. {
  707.     FIELD_DATA  *this_field;
  708.     FIELD_DATA  *that_field;
  709.  
  710.     // clear out an previous data
  711.     if(data.field_data != NULL)
  712.     {
  713.         this_field = data.field_data;
  714.         while(this_field != NULL)
  715.         {
  716.             that_field = this_field->next;
  717.             free(this_field);
  718.             this_field = that_field;
  719.         }
  720.         data.field_data = NULL;
  721.     }
  722. }
  723.  
  724. //---------------------------------------------------------------------------
  725. int dBASE::f2b(void)
  726. {
  727.     int         x,y,z;
  728.     int         left,right;
  729.     int         year,month,day;
  730.     int         logical;
  731.     char        text[256];
  732.     char        *buff;
  733.     char        *hold;
  734.     float       number;
  735.     long        long_number;
  736.     FIELD_DATA  *this_field;
  737.  
  738.     if(data.database == -1) return(NO_DB_OPEN_ERROR);
  739.  
  740.     buff = (char *)getRecord();
  741.     ++buff;                 // skip over the "deleted" marker
  742.  
  743.     y = getRecordLen();
  744.  
  745.     this_field = data.field_data;
  746.  
  747.     while(this_field != NULL)
  748.     {
  749.         switch(this_field->field_type)
  750.         {
  751.             case 'D':   *text = 0;
  752.                         sprintf(hold,"%04d",this_field->data.date.year);
  753.                         strcat(text,hold);
  754.                         sprintf(hold,"%02d",this_field->data.date.month);
  755.                         strcat(text,hold);
  756.                         sprintf(hold,"%02d",this_field->data.date.day);
  757.                         strcat(text,hold);
  758.  
  759.                         hold = text;
  760.                         while(*hold != 0) *buff++ = *hold++;
  761.  
  762.                         break;
  763.  
  764.             case 'C':   hold = this_field->data.text;
  765.                         while(*hold != 0) *buff++ = *hold++;
  766.                         break;
  767.  
  768.             case 'L':   *buff = this_field->data.boolean;
  769.                         ++buff;
  770.                         break;
  771.  
  772.             case 'M':
  773.                         break;
  774.  
  775.             case 'F':   sprintf(text,this_field->data.float_number.format,
  776.                                      this_field->data.float_number.number);
  777.                         hold = text;
  778.                         while(*hold != 0) *buff++ = *hold++;
  779.                         break;
  780.  
  781.             case 'N':   sprintf(text,"%ld",this_field->data.long_number);
  782.                         hold = text;
  783.                         while(*hold != 0) *buff++ = *hold++;
  784.                         break;
  785.         }
  786.  
  787.         this_field = this_field->next;
  788.     }
  789.  
  790.     buff = (char *)getRecord();
  791.     ++buff;
  792.     putchar('(');
  793.     for(z = 0;z < y;z++) putchar(*buff++);
  794.     putchar(')');
  795.     getch();
  796.  
  797.     return(NO_PROBLEM);
  798. }
  799.  
  800. //---------------------------------------------------------------------------
  801. void dBASE::printDBFields(void)
  802. {
  803.     FIELD_DATA  *this_field;
  804.  
  805.     if(data.database == -1) return;
  806.  
  807.     this_field = data.field_data;
  808.  
  809.     while(this_field != NULL)
  810.     {
  811.         switch(this_field->field_type)
  812.         {
  813.             case 'D':   printf("\t%02d/%02d/%04d\n",
  814.                                 this_field->data.date.month,
  815.                                 this_field->data.date.day,
  816.                                 this_field->data.date.year);
  817.                         break;
  818.  
  819.             case 'C':   printf("\t%s\n",this_field->data.text);
  820.                         break;
  821.  
  822.             case 'L':   printf("\t%c\n",this_field->data.boolean);
  823.                         break;
  824.  
  825.             case 'M':
  826.                         break;
  827.  
  828.             case 'F':   printf("\t");
  829.                         printf(this_field->data.float_number.format,
  830.                                this_field->data.float_number.number);
  831.                         printf("\n");
  832.                         break;
  833.  
  834.             case 'N':   printf("\t%ld\n",this_field->data.long_number);
  835.                         break;
  836.         }
  837.  
  838.         this_field = this_field->next;
  839.     }
  840. }
  841.  
  842. //---------------------------------------------------------------------------
  843. void dBASE::skip(LONG num)
  844. {
  845.     LONG    hold;
  846.  
  847.     if(data.database == -1) return;
  848.  
  849.     eof_met = FALSE;
  850.     bof_met = FALSE;
  851.  
  852.     if(data.index.index_chain != NULL)  // we have an index active
  853.     {
  854.         if(num < 0)     // we're skipping backward
  855.         {
  856.             hold = num;
  857.             while(hold)
  858.             {
  859.                 if(data.index.current_record->prev == NULL)     // @ BOF
  860.                 {
  861.                     hold = data.index.current_record->record;
  862.                     bof_met = TRUE;
  863.                     break;
  864.                 }
  865.  
  866.                 --hold;
  867.                 data.index.current_record = data.index.current_record->prev;
  868.             }
  869.             if(!bof_met) hold = data.index.current_record->record;
  870.         }
  871.         else            // we're skipping forward
  872.         {
  873.             hold = num;
  874.             while(hold)
  875.             {
  876.                 if(data.index.current_record->next == NULL)     // @ EOF
  877.                 {
  878.                     hold = data.index.current_record->record;
  879.                     eof_met = TRUE;
  880.                     break;
  881.                 }
  882.  
  883.                 --hold;
  884.                 data.index.current_record = data.index.current_record->next;
  885.             }
  886.             if(!eof_met) hold = data.index.current_record->record;
  887.         }
  888.     }
  889.     else
  890.     {
  891.         hold = data.current_record + num;
  892.  
  893.         if(hold > data.dbf_head.last_rec)
  894.         {
  895.             eof_met = TRUE;
  896.             hold = data.dbf_head.last_rec;
  897.         }
  898.         else if(hold < 1)
  899.         {
  900.             bof_met = TRUE;
  901.             hold = 1;
  902.         }
  903.     }
  904.  
  905.     data.current_record = hold;
  906.     gotoRecord(data.current_record);
  907. }
  908.  
  909. //---------------------------------------------------------------------------
  910. int dBASE::eof(void)
  911. {
  912.     if(data.database == -1) return(NO_DB_OPEN_ERROR);
  913.  
  914.     return(eof_met);
  915. }
  916.  
  917. //---------------------------------------------------------------------------
  918. int dBASE::bof(void)
  919. {
  920.     if(data.database == -1) return(NO_DB_OPEN_ERROR);
  921.  
  922.     return(bof_met);
  923. }
  924.  
  925. //---------------------------------------------------------------------------
  926. // this function is a front-end to indexOn(int) which locates the field
  927. // indicated by its name, and then forwards that field's # to the real
  928. // routine.
  929. //---------------------------------------------------------------------------
  930. int dBASE::indexOn(char *fieldName)
  931. {
  932.     int     x;
  933.  
  934.     // see if there is a database currently open in this area
  935.  
  936.     if(data.database == -1) return(NO_DB_OPEN_ERROR);
  937.  
  938.     x = 0;
  939.     while(x < data.num_fields)
  940.     {
  941.         if(stricmp(data.field_rec[x].field_name,fieldName) == 0) break;
  942.         ++x;
  943.     }
  944.  
  945.     // see if we located it...
  946.     if(x == data.num_fields) return(INVALID_FIELD_NAME_ERROR);
  947.  
  948.     return(indexOn(x + 1));     // convert x to 1-based indexing
  949. }
  950.  
  951. //---------------------------------------------------------------------------
  952. int dBASE::indexOn(int fieldNumber)     // fieldNumber is 1-based (1234...)
  953. {
  954.     INDEX_CHAIN *this_link,*new_link;
  955.     FIELD_DATA  *this_field;
  956.     FIELD_REC   *this_rec;
  957.     int     x,first;
  958.  
  959.     // see if there is a database currently open in this area
  960.  
  961.     if(data.database == -1) return(NO_DB_OPEN_ERROR);
  962.     if(fieldNumber < 1 || fieldNumber > data.num_fields)
  963.         return(INVALID_FIELD_NUM_ERROR);
  964.  
  965.     // is there an index currently active?
  966.  
  967.     if(data.index.index_chain != NULL) killIndex();     // keel it... 8^)
  968.  
  969.     // build a linked list of FIELD DATA/RECORD NUMBER pairs that will
  970.     // act as our index.
  971.  
  972.     data.index.index_field = x - 1;
  973.  
  974.     gotoRecord(1L);
  975.  
  976.     while(!eof())
  977.     {
  978.         new_link = (INDEX_CHAIN *)malloc(sizeof(INDEX_CHAIN));
  979.  
  980.         if(data.index.index_chain == NULL)
  981.             data.index.index_chain = new_link;
  982.         else
  983.         {
  984.             this_link->next = new_link;
  985.             new_link->prev = this_link;
  986.         }
  987.  
  988.         this_link = new_link;
  989.         this_link->next = NULL;
  990.  
  991.         // locate the field data
  992.         x = 0;
  993.         this_field = data.field_data;
  994.         while(x < fieldNumber)
  995.         {
  996.             this_field = this_field->next;
  997.             ++x;
  998.         }
  999.  
  1000.         switch(this_field->field_type)
  1001.         {
  1002.             case 'D':   sprintf(this_link->key.textKey,"%02d/%02d/%04d",
  1003.                                 this_field->data.date.month,
  1004.                                 this_field->data.date.day,
  1005.                                 this_field->data.date.year);
  1006.                         break;
  1007.  
  1008.             case 'C':   strcpy(this_link->key.textKey,this_field->data.text);
  1009.                         break;
  1010.  
  1011.             case 'L':   this_link->key.logicalKey = this_field->data.boolean == 'F' ? FALSE : TRUE;
  1012.                         break;
  1013.  
  1014.             case 'M':
  1015.                         break;
  1016.  
  1017.             case 'F':   this_link->key.floatKey = this_field->data.float_number.number;
  1018.                         break;
  1019.  
  1020.             case 'N':   this_link->key.longKey = this_field->data.long_number;
  1021.                         break;
  1022.         }
  1023.         this_link->record = currRecord();
  1024.  
  1025.         skip();
  1026.     }
  1027.  
  1028.     // since the other routines check for an active index, all we have left
  1029.     // to do is sort it...
  1030. }
  1031.  
  1032. //---------------------------------------------------------------------------
  1033. void dBASE::killIndex(void)
  1034. {
  1035.     INDEX_CHAIN *this_link;
  1036.     INDEX_CHAIN *that_link;
  1037.  
  1038.     // clear out any previous index
  1039.  
  1040.     if(data.index.index_chain != NULL)
  1041.     {
  1042.         this_link = data.index.index_chain;
  1043.         while(this_link != NULL)
  1044.         {
  1045.             that_link = this_link->next;
  1046.             free(this_link);
  1047.             this_link = that_link;
  1048.         }
  1049.  
  1050.         data.index.index_chain    = NULL;
  1051.         data.index.current_record = NULL;
  1052.     }
  1053. }
  1054.  
  1055. //---------------------------------------------------------------------------
  1056. int dBASE::fLock(void)
  1057. {
  1058.     // ask the network to lock this file...
  1059.  
  1060.     file_locked = lock(data.database,0L,dbSize,DB_LOCK);
  1061.  
  1062.     if(!file_locked)
  1063.         db_err = FILE_LOCK_FAILED_ERROR;
  1064.     else
  1065.         record_locked = TRUE;   // locked file means locked records
  1066.  
  1067.     return(file_locked);
  1068. }
  1069.  
  1070. //---------------------------------------------------------------------------
  1071. int dBASE::fUnLock(void)
  1072. {
  1073.     // ask the network to unlock this file...
  1074.  
  1075.     file_locked = !lock(data.database,0L,dbSize,DB_UNLOCK);
  1076.  
  1077.     if(file_locked)
  1078.         db_err = FILE_UNLOCK_FAILED_ERROR;
  1079.     else
  1080.         record_locked = FALSE;   // unlocked file means unlocked records
  1081.  
  1082.     return(!file_locked);
  1083. }
  1084.  
  1085. //---------------------------------------------------------------------------
  1086. int dBASE::rLock(void)
  1087. {
  1088.     long        pos;
  1089.  
  1090.     // ask the network to lock this record...
  1091.  
  1092.     // see ::lock() for an explanation of this mechanism.
  1093.  
  1094.     pos = tell(data.database);
  1095.     record_locked = lock(data.database,pos,data.dbf_head.rec_size,DB_LOCK);
  1096.  
  1097.     if(!record_locked) db_err = RECORD_LOCK_FAILED_ERROR;
  1098.  
  1099.     return(record_locked);
  1100. }
  1101.  
  1102. //---------------------------------------------------------------------------
  1103. int dBASE::rUnLock(void)
  1104. {
  1105.     long        pos;
  1106.  
  1107.     // ask the network to unlock this record...
  1108.  
  1109.     pos = tell(data.database);
  1110.     record_locked = !lock(data.database,pos,data.dbf_head.rec_size,DB_UNLOCK);
  1111.  
  1112.     if(record_locked) db_err = RECORD_UNLOCK_FAILED_ERROR;
  1113.  
  1114.     return(!record_locked);
  1115. }
  1116.  
  1117. //---------------------------------------------------------------------------
  1118. int dBASE::lock(int file,long offset,long length,int function)
  1119. {
  1120.     union       REGS xr;
  1121.     int         result;
  1122.  
  1123.     net_err = -1;
  1124.  
  1125.     // if `length' is 0L, what the hell are they trying to lock?!
  1126.  
  1127.     if(length == 0L) return(FALSE);
  1128.  
  1129.     // we use the DOS 21h/5Ch function (Lock/Unlock File Access) to
  1130.     // allow locking compatibility with networks that support this interrupt
  1131.     // (such as Novell NetWare, LANtastic, and Banyan Vines).
  1132.  
  1133.     // AL = [0 to lock/1 to unlock]
  1134.     // BX = File Handle
  1135.     // CX = High word of offset
  1136.     // DX = Low word of offset
  1137.     // SI = High word of length
  1138.     // DI = Low word of length
  1139.     // If CF = 1, AX = error code
  1140.  
  1141.     xr.h.ah = 0x5C;
  1142.     xr.h.al = function;     // lock/unlock request
  1143.     xr.x.bx = file;         // file handle
  1144.  
  1145.     // starting offset in `file'
  1146.  
  1147.     if(offset != 0L)
  1148.     {
  1149.         xr.x.cx = (INT)((offset & 0x7FFF0000) >> 8);
  1150.         xr.x.dx = (INT)(offset & 0x0000FFFF);
  1151.     }
  1152.     else    // skip the contortions for a 0L value
  1153.     {
  1154.         xr.x.cx = 0x0000;
  1155.         xr.x.dx = 0x0000;
  1156.     }
  1157.  
  1158.     // number of bytes in `file' to lock
  1159.  
  1160.     xr.x.si = (INT)((length & 0x7FFF0000) >> 8);
  1161.     xr.x.di = (INT)(length & 0x0000FFFF);
  1162.  
  1163.     int86(0x21,&xr,&xr);
  1164.  
  1165.     // we will receive an error code of 0x0001 (Invalid Function) if file/record
  1166.     // locking is not appropriate or possible for the current medium (such as
  1167.     // a file residing on a local hard drive without SHARE loaded).  In such
  1168.     // an instance, simply act as though the file/record is locked.
  1169.  
  1170.     if(xr.x.cflag && xr.x.ax != 0x0001)
  1171.     {
  1172.         net_err = xr.x.ax;
  1173.         return(FALSE);
  1174.     }
  1175.     else
  1176.         return(TRUE);
  1177. }
  1178.  
  1179. //---------------------------------------------------------------------------
  1180. int dBASE::getDBErr(void)
  1181. {
  1182.     int     hold;
  1183.  
  1184.     hold = db_err;
  1185.     db_err = NO_PROBLEM;        // reset the error holder on each call
  1186.     return(hold);
  1187. }
  1188.  
  1189. //---------------------------------------------------------------------------
  1190. int dBASE::getNetErr(void)
  1191. {
  1192.     int     hold;
  1193.  
  1194.     hold = net_err;
  1195.     net_err = NO_PROBLEM;       // reset the error holder on each call
  1196.     return(hold);
  1197. }
  1198.  
  1199. //---------------------------------------------------------------------------
  1200. // this function is a front-end to getField(int) which locates the field
  1201. // indicated by its name, and then forwards that field's # to the real
  1202. // routine.
  1203. //---------------------------------------------------------------------------
  1204. FIELD_DATA *dBASE::getField(char *fieldName)
  1205. {
  1206.     int     x;
  1207.  
  1208.     // see if there is a database currently open in this area
  1209.  
  1210.     if(data.database == -1)
  1211.     {
  1212.         db_err = NO_DB_OPEN_ERROR;
  1213.         return(NULL);
  1214.     }
  1215.  
  1216.     x = 0;
  1217.     while(x < data.num_fields)
  1218.     {
  1219.         if(stricmp(data.field_rec[x].field_name,fieldName) == 0) break;
  1220.         ++x;
  1221.     }
  1222.  
  1223.     // see if we located it...
  1224.     if(x == data.num_fields)
  1225.     {
  1226.         db_err = INVALID_FIELD_NAME_ERROR;
  1227.         return(NULL);
  1228.     }
  1229.  
  1230.     return(getField(x + 1));     // convert x to 1-based indexing
  1231. }
  1232.  
  1233. //---------------------------------------------------------------------------
  1234. FIELD_DATA *dBASE::getField(int fieldNumber)     // fieldNumber is 1-based (1234...)
  1235. {
  1236.     FIELD_DATA  *this_field;
  1237.     int     x,first;
  1238.  
  1239.     // see if there is a database currently open in this area
  1240.  
  1241.     if(data.database == -1)
  1242.     {
  1243.         db_err = NO_DB_OPEN_ERROR;
  1244.         return(NULL);
  1245.     }
  1246.  
  1247.     if(fieldNumber < 1 || fieldNumber > data.num_fields)
  1248.     {
  1249.         db_err = INVALID_FIELD_NUM_ERROR;
  1250.         return(NULL);
  1251.     }
  1252.  
  1253.     this_field = data.field_data;
  1254.     x = fieldNumber - 1;
  1255.  
  1256.     while(this_field != NULL && x)
  1257.     {
  1258.         this_field = this_field->next;
  1259.         --x;
  1260.     }
  1261.  
  1262.     if(this_field == NULL)      // shouldn't occur, but here for robustness
  1263.     {
  1264.         db_err = INVALID_FIELD_NUM_ERROR;
  1265.         return(NULL);
  1266.     }
  1267.  
  1268.     return(this_field);
  1269. }
  1270.  
  1271. ---------------------------- end CBASE.CPP ----------------------------
  1272.  
  1273.  
  1274. -- 
  1275. Bob Hood    thor@arrakis.denver.co.us     H: 303-980-8392  W: 303-632-2180
  1276. ---------------------------------------------------------------------------
  1277. When people are free to do as they please, they usually imitate each other.
  1278.