/*-------------------------------------------------------------------------

*******This source was originally compiled with Lattice C V-3.11**********

    These functions have been released into the public domain for
    personal use only.  If you find them of some use or would like to
    use them for commercial purposes please send a donation of
    $15.00 to the address below.  The money will be used to
    finance the BBS run by myself (LOGITEK).  Good luck and happy
    computing !

    Paul McDonald
    2200 Columbia Pike
    Apt. 917
    Arlington Va. 22204


---------------------------------------------------------------------------
    Name:     DBTOC

    Description:   This file contains the basic routines to read/write
                   dbase III files.  A good understanding of the functions
                   can be gained by studying the header file DBASE.H, which
                   defines the data structures used.

    Date                     Author                   Explanation
 ------------           -----------------        --------------------
    11/18/87            Paul McDonald              Original Author


----------------------------------------------------------------------------*/
#include "\lc\header\stdlib.h"
#include "\switch\c\dbase.h"

#define  NULL 0
#define  ERROR -1

/*---------------------------------------------------------------------------
    Name:     OPENDBF
    Description:   Opens a dbf file as r/w file, fills in the structure info
                   and returns a pointer to the DBF structure.
-----------------------------------------------------------------------------*/

DBF *opendbf(filename)

    char *filename;
{

    char *getmem();
    FIELD     *current;

    DBF  *tmp_ptr;
    long offset;
    int  cntr;


    /* attempt memory allocation for header struct */
    if(!(tmp_ptr = (DBF *)getmem(sizeof(DBF)))){
         return(NULL);
    }

     
    if( (tmp_ptr->fh = o_pen(filename,2) ) == ERROR)           /* attempt to open the file for read/write access */
         return(NULL);
    cntr = 0;

    while(filename[cntr])
         tmp_ptr->filename[cntr] = filename[cntr++];
    tmp_ptr->filename[cntr] = 0;

    offset = 0;         /* start from top of file */

    l_seek(tmp_ptr->fh,offset,0);      

    r_ead(tmp_ptr->fh,&(tmp_ptr->header),sizeof(DBF_HEAD)); /* fill in the header struct from the file */

    tmp_ptr->no_fields = ((tmp_ptr->header.head_len - 1)/32 - 1);

    /* allocate memory for the first field structure */

    if( ! ( tmp_ptr->fields = (FIELD *)getmem(sizeof(FIELD) ) ) )
         printstr(0,0,"ERROR ALLOCATING MEMORY !!!");                     

    r_ead(tmp_ptr->fh,tmp_ptr->fields,32);       /* read in the first field */
    tmp_ptr->fields->prev = (FIELD *)0L;         /* start the list */
    current = tmp_ptr->fields;                   /* current node on the list */
    current->offset = 0L;                        /* first field is at offset 0 */
    offset = current->fld_len;
                                                 /* allocate space for the fields actual value */
    if(current->type != 'D'){
         if( !(current->fld_val = getmem(current->fld_len + 1)))
              printstr(0,0,"ERROR ALLOCATING MEMORY !!");       
    }else{                                                       /* for dates allocate space for the slashes */
          if( !(current->fld_val = getmem(current->fld_len + 3)))
              printstr(0,0,"ERROR ALLOCATING MEMORY !!");       
    }
                                  /* make the rest of the list */
    for(cntr = 1; cntr < tmp_ptr->no_fields; ++cntr){
         if( ! ( current->next = (FIELD *)getmem( sizeof(FIELD) ) ) )
              printstr(0,0,"ERROR ALLOCATING MEMORY !!!");                     
         r_ead(tmp_ptr->fh,current->next,32);
         current->next->prev = current;
         current = current->next;
         current->offset = offset;          /* offset of this field from beg. of the record */
                                            /* allocate space for the fields actual value */
         if(current->type != 'D'){
              if( !(current->fld_val = getmem(current->fld_len + 1)))
                   printstr(0,0,"ERROR ALLOCATING MEMORY !!");      
         }else{                                                       /* for dates allocate space for the slashes */
              if( !(current->fld_val = getmem(current->fld_len + 3)))
                   printstr(0,0,"ERROR ALLOCATING MEMORY !!");       
         }

         offset += current->fld_len;        /* update next fields offset in record */
    }
    current->next = (FIELD *)0L;       /* end of list = NULL */

/* allocate memory for the record buffer and read the first record */
    if(!(tmp_ptr->record = getmem(tmp_ptr->header.rec_len)))
         printstr(0,0,"ERROR ALLOCATING MEMORY !!!");

    tmp_ptr->curr_rec = 0;
    tmp_ptr->updated = 0;         /* set the updated flag to false */
    return(tmp_ptr);

}

/*-----------------------------------------------------------------------------
    Name:     READREC() 

    Description: - returns the given record in the DBF structure
                   will return 1 if successful, 0 if rec not in DBF.
                   -1 if read error;

    Syntax:   success = readrec(dbf_ptr,rec_num)
              DBF  *dbf_ptr;
              long rec_num;
------------------------------------------------------------------------------*/

int readrec(dbf_ptr,rec_num)

    DBF  *dbf_ptr;
    unsigned long rec_num;
{

long     offset;

    if(rec_num > dbf_ptr->header.no_recs)
         return(0);

    offset = dbf_ptr->header.head_len + ((rec_num - 1) * dbf_ptr->header.rec_len);

    l_seek(dbf_ptr->fh,offset,0);

    if( r_ead(dbf_ptr->fh,dbf_ptr->record,dbf_ptr->header.rec_len) < dbf_ptr->header.rec_len)
         return(-1);
    else
         readbuf(dbf_ptr);        /* read the i/o buffer and put the values
                                     in the indiv. field buffers */
    dbf_ptr->curr_rec = rec_num;

    return(1);
}

                                                                               
/*-----------------------------------------------------------------------------
    Name:     WRITEREC() 

    Description: - writes record that is contained in the current record
                   structure to the specified record.
                   0 if record not found in dbf
                   1 if successfull
                   -1 if write error.

    Syntax:   success = writerec(dbf_ptr,rec_num)
              DBF  *dbf_ptr;
              long rec_num;
------------------------------------------------------------------------------*/

int writerec(dbf_ptr,rec_num)

    DBF  *dbf_ptr;
    unsigned long rec_num;
{

long     offset;

    if(rec_num > dbf_ptr->header.no_recs || rec_num < 1)
         return(0);

    offset = dbf_ptr->header.head_len + ((rec_num - 1) * dbf_ptr->header.rec_len);
    l_seek(dbf_ptr->fh,offset,0);

    writebuf(dbf_ptr);                 /* write the buffers back to the x-fer area */

    if( w_rite(dbf_ptr->fh,dbf_ptr->record,dbf_ptr->header.rec_len) < dbf_ptr->header.rec_len)
         return(-1);
    dbf_ptr->curr_rec = rec_num;
    dbf_ptr->updated = 1;
    return(1);
}

                                                                               
/*----------------------------------------------------------------------------
    Name:     READBUF()

    Description:   Reads the record buffer and places the individual 
                   field values in each fields buffer area.
    Syntax:   readbuf(dbf_ptr);
              DBF  *dbf_ptr;
---------------------------------------------------------------------------*/
int readbuf(dbf_ptr)
    DBF  *dbf_ptr;
{

int cntr,
    buf_ptr,
    fld_buf;

FIELD    *work_ptr;

    work_ptr = dbf_ptr->fields;   /* get pointer to first field */
    buf_ptr = 1;                  /* start at the second char of the buffer,
                                     the first char is the logical deleted,
                                     character either '*' or ' ' */
    if(dbf_ptr->record[0] == '*')
         dbf_ptr->deleted = 1;
    else
         dbf_ptr->deleted = 0;

    for(cntr = 0; cntr< dbf_ptr->no_fields; ++cntr){
         fld_buf = 0;

         while(fld_buf < work_ptr->fld_len)
              work_ptr->fld_val[fld_buf++] = dbf_ptr->record[buf_ptr++];

         work_ptr->fld_val[fld_buf] = 0;         /* put NULL at end */

         work_ptr = work_ptr->next;              /* goto next field */

    }

    return(0);

}


/*----------------------------------------------------------------------------
    Name:     WRITEBUF()

    Description:   Writes the record buffer and places the individual 
                   field values in the record buffer.
    Syntax:   writebuf(dbf_ptr);
              DBF  *dbf_ptr;
---------------------------------------------------------------------------*/
int writebuf(dbf_ptr)
    DBF  *dbf_ptr;
{

int cntr,
    buf_ptr,
    fld_buf;
char     null_flg;

FIELD    *work_ptr;

    work_ptr = dbf_ptr->fields;   /* get pointer to first field */
    buf_ptr = 1;                  /* start at the second character, first is
                                     the deletion marker */

    for(cntr = 0; cntr< dbf_ptr->no_fields; ++cntr){
         fld_buf = 0;
         null_flg = 0;            /* if end of valid data in fld buff is hit */

         while(fld_buf < work_ptr->fld_len){
              if(!work_ptr->fld_val[fld_buf])
                   null_flg = 1;
              if(!null_flg)
                   dbf_ptr->record[buf_ptr++] = work_ptr->fld_val[fld_buf++];
              else
                   dbf_ptr->record[buf_ptr++] = ' ';  /* pad with spaces */
         }

         work_ptr = work_ptr->next;              /* goto next field */

    }

return(0);

}


/*--------------------------------------------------------------------------
    Name:     APPEND()

    Description:   Adds a record to a dbase file, the entire record is filled
                   with spaces to initialize it.
    Syntax:   int  append(dbf_ptr)
                   DBF  *dbf_ptr;
              returns   1 - successful
                        -1 - disk i/o
---------------------------------------------------------------------------*/
int append(dbf_ptr)

    DBF  *dbf_ptr;
{

unsigned int cntr;
long     offset;
char     eof_mark;

    eof_mark = 26;

    for(cntr=0; cntr < dbf_ptr->header.rec_len;++cntr)
         dbf_ptr->record[cntr] = 0x20;                /* fill with spaces */

    offset = dbf_ptr->header.head_len + ((dbf_ptr->header.no_recs) * dbf_ptr->header.rec_len);
    l_seek(dbf_ptr->fh,offset,0);


    if( (w_rite(dbf_ptr->fh,dbf_ptr->record,dbf_ptr->header.rec_len)) < dbf_ptr->header.rec_len)
         return(-1);    /* error writing to disk */

    ++(dbf_ptr->header.no_recs);
    dbf_ptr->curr_rec = dbf_ptr->header.no_recs;

    offset += (dbf_ptr->header.rec_len);
    l_seek(dbf_ptr->fh,offset,0);           /* goto new EOF */
    w_rite(dbf_ptr->fh,&eof_mark,1);        /* put eof marker at eof */

    readrec(dbf_ptr,dbf_ptr->curr_rec);     /* read in the new record */

    return(1);     /* success */

}
/*---------------------------------------------------------------------------
    Name:     CLOSEDBF()

    Description:   Closes the dbf file updateing the header info.

    Syntax:   closedbf(dbf_ptr);
              DBF  *dbf_ptr;
----------------------------------------------------------------------------*/
int closedbf(dbf_ptr)

    DBF  *dbf_ptr;
{
    
FIELD    *tmpfield,
         *workptr;
unsigned int  packdate,
              sysdate();


    if(dbf_ptr->updated){
         packdate = sysdate();         /* get the system date */
         dbf_ptr->header.dd = packdate & 0x001f;           /* update file date */
         dbf_ptr->header.mm = (packdate >> 5) & 0x000f;
         dbf_ptr->header.yy = (packdate >> 9) + 80;
    }

    l_seek(dbf_ptr->fh,(long)0,0);     /* go to BOF */
                                       /* write updated header to disk */

    w_rite(dbf_ptr->fh,&(dbf_ptr->header),sizeof(DBF_HEAD));

    c_lose(dbf_ptr->fh);

    tmpfield = dbf_ptr->fields;

    workptr = tmpfield;
    while(workptr){
         workptr = tmpfield->next;
         rlsmem(tmpfield->fld_val,tmpfield->fld_len + 1);
         rlsmem((char *)tmpfield,sizeof(FIELD));
         tmpfield = workptr;
    }

    rlsmem(dbf_ptr->record,dbf_ptr->header.rec_len);
    rlsmem((char *)dbf_ptr,sizeof(DBF));

    return(0);
}

/*-----------------------------------------------------------------------------
    Name:     DELETE()

    Description:   Logically deletes the given record

    Syntax:        success = delete(dbf_ptr,rec_num)
              DBF  *dbf_ptr;
              long rec_num;
              int  success   - 1 = successful
                               0 = record not found
                             (-1) = File i/o error
---------------------------------------------------------------------------*/
int delete(dbf_ptr,rec_num)

    DBF  *dbf_ptr;
    long rec_num;
{

long     offset;

    if(dbf_ptr->header.no_recs < rec_num)
         return(0);

    dbf_ptr->curr_rec = rec_num;
    dbf_ptr->deleted = 1;

    offset = dbf_ptr->header.head_len + ((rec_num - 1) * dbf_ptr->header.rec_len);
    l_seek(dbf_ptr->fh,offset,0);      /* seek to the record */

    if(w_rite(dbf_ptr->fh,"*",1) != 1)      /* an asterisk logically deletes the record */
         return(-1);    /* must be a file i/o error */
    else
         return(1);     /* it worked */

}

/*-----------------------------------------------------------------------------
    Name:     UNDELETE()

    Description:   Logically undeletes the given record

    Syntax:        success = undelete(dbf_ptr,rec_num)
              DBF  *dbf_ptr;
              long rec_num;
              int  success   - 1 = successful
                               0 = record not found
                             (-1) = File i/o error
---------------------------------------------------------------------------*/
int undelete(dbf_ptr,rec_num)

    DBF  *dbf_ptr;
    long rec_num;
{

long     offset;

    if(dbf_ptr->header.no_recs < rec_num)
         return(0);

    dbf_ptr->curr_rec = rec_num;       /* this becomes the curr record */
    dbf_ptr->deleted = 0;              /* reset deleted flag */

    offset = dbf_ptr->header.head_len + ((rec_num - 1) * dbf_ptr->header.rec_len);
    l_seek(dbf_ptr->fh,offset,0);      /* seek to the record */

    if(w_rite(dbf_ptr->fh," ",1) != 1)      /* clear the asterisk */
         return(-1);    /* must be a file i/o error */
    else
         return(1);     /* it worked */

}

/*---------------------------------------------------------------------------
    Name:     MAKHEAD()

    Description:   This function creates the first part of the dbf's header
                   on disk, subsequent calls to ADDFIELD() will allow you to
                   customize the rest of the header to your needs.
----------------------------------------------------------------------------*/
DBF  *makhead(filename)

    char *filename;
{

    char *getmem();
    DBF  *tmp_ptr;
    long offset;
    int  cntr;
    unsigned int   packdate,
                   sysdate();

    /* attempt memory allocation for header struct */

    if(!(tmp_ptr = (DBF *)getmem(sizeof(DBF)))){
         return(NULL);
    }
    if( (tmp_ptr->fh = c_reate(filename,0) ) == ERROR)           /* create file with NO special attributes */
         return(NULL);
    cntr = 0;
    while(filename[cntr])
         tmp_ptr->filename[cntr] = filename[cntr++];
    tmp_ptr->filename[cntr] = 0;

    tmp_ptr->header.vers = 3;
    tmp_ptr->header.no_recs = 0;
    tmp_ptr->header.head_len = sizeof(DBF_HEAD);
    tmp_ptr->header.rec_len = 0;       /* account for logical deletion char */
    for(cntr=0; cntr<20; ++cntr)
         tmp_ptr->header.reserved[cntr] = 0;     /* clear out the reserved area */

    packdate = sysdate();         /* get the system date */
    tmp_ptr->header.dd = packdate & 0x001f;
    tmp_ptr->header.mm = (packdate >> 5) & 0x000f;
    tmp_ptr->header.yy = (packdate >> 9) + 80;

    offset = 0;                        /* write the structure to disk */
    l_seek(tmp_ptr->fh,offset,0);
    w_rite(tmp_ptr->fh,&(tmp_ptr->header),sizeof(DBF_HEAD));

    tmp_ptr->fields = NULL;
    tmp_ptr->curr_rec = 0;
    tmp_ptr->record = NULL;       /* no record buffer allocated yet */
    tmp_ptr->no_fields = 0;

    return(tmp_ptr);

}

/*-----------------------------------------------------------------------------
    Name:     ADDFIELD()

    Description:   Adds a field descriptor structure to the structure
                   already written to disk.  This function ONLY works
                   for files that have NO records in them.
    Syntax:   int  addfield(dbf_ptr,fieldname,type,length,dec_cnt)
                   DBF  *dbf_ptr;
                   char *fieldname,
                        type;
                   int  length,
                        dec_cnt;
         returns 1 - successful
                 0 - improper field type
                -1 - file i/o error, or memory error


    Note:     This function has NOT been tested !!

------------------------------------------------------------------------------*/
int addfield(dbf_ptr,fieldname,type,length,dec_cnt)

    DBF  *dbf_ptr;
    char *fieldname,
         type;
    unsigned int   length,
                   dec_cnt;

{

    char *getmem(),
         cr_ret[2];
    FIELD     *tempptr,
              *workptr;
    long offset;
    int  cntr;
    unsigned int   packdate,
                   sysdate();

                                     /* allocate memory for the field descriptor */
     if(!(tempptr = (FIELD *)getmem(sizeof(FIELD)))){
              return(NULL);
     }

    for(cntr = 0; cntr < 11 && fieldname[cntr]; ++cntr)
         tempptr->name[cntr] = fieldname[cntr];
    if(cntr < 10)
         while(cntr < 12)                   /* padd with nulls */
              tempptr->name[cntr++] = 0;
                                            /* check for proper field type */
    if(type != 'C' && type != 'D' && type != 'N'  && type != 'L'){
         rlsmem((char *)tempptr,sizeof(FIELD));
         return(0);
    }

    tempptr->type = type;
    tempptr->next = NULL;
    tempptr->prev = NULL;
    tempptr->fld_len = length;
    tempptr->decimals = dec_cnt;
    tempptr->offset = 0;
    tempptr->fld_val = 0;

    if(dbf_ptr->no_fields ==0)
         offset = 0;
    else
         offset = -2;              /* write over the CR and EOF marker at the end of the field space */

         l_seek(dbf_ptr->fh,offset,2);
                                       /* write the field descriptor to disk */

    if( (w_rite(dbf_ptr->fh,tempptr,sizeof(FIELD))) != sizeof(FIELD) )
         return(-1);                                  /* error writing to file */
                                                      /* allocate space for
                                                      actual field value */
    if( !(tempptr->fld_val = getmem(length + 1)))
         return(-1);
    tempptr->offset = dbf_ptr->header.rec_len+1;      /* offset of field in the record */

    dbf_ptr->header.head_len += sizeof(FIELD);
    if(dbf_ptr->record)      /* release current rec buffer if allocated */
         rlsmem(dbf_ptr->record,dbf_ptr->header.rec_len); 
    dbf_ptr->header.rec_len += length;      /* update header info */
    ++dbf_ptr->no_fields;
                                       /* allocate memory for a new rec buffer */
    if(!(dbf_ptr->record = getmem(dbf_ptr->header.rec_len)))
         return(-1);         /* out of memory !!! */

    if(!dbf_ptr->fields){              /* add this field descriptor to the linked list */
         dbf_ptr->fields = tempptr;
         tempptr->next = NULL;
         tempptr->prev = NULL;
    }else{
         workptr = dbf_ptr->fields;
         while(workptr->next)
              workptr = workptr->next;

         workptr->next = tempptr;
         tempptr->prev = workptr;
         tempptr->next = NULL;
    }     

    packdate = sysdate();         /* get the system date */
    dbf_ptr->header.dd = packdate & 0x001f;           /* update file date */
    dbf_ptr->header.mm = (packdate >> 5) & 0x000f;
    dbf_ptr->header.yy = (packdate >> 9) + 80;

    l_seek(dbf_ptr->fh,offset,0);      /* goto top of file and rewrite header info */

    if( (w_rite(dbf_ptr->fh,&(dbf_ptr->header),sizeof(DBF_HEAD))) != sizeof(DBF_HEAD) )
         return(-1);
    
    cr_ret[0] = 13;          /* CR marks as a field delimeter */
    cr_ret[1] = 26;          /* EOF marker */
    offset = 0;
    l_seek(dbf_ptr->fh,offset,2);      /* goto EOF */
    if( (w_rite(dbf_ptr->fh,cr_ret,2)) != 2  ) 
              return(-1);   

    return(1);

}
/*---------------------------------------------------------------------------
    Name:     ZAP()

    Description:   Deletes all records in a dbf file

    Syntax:   int  zap(dbf_ptr)
              DBF  *dbf_ptr;
              returns   1 - successful
                       -1 - file i/o error
---------------------------------------------------------------------------*/
int  zap(dbf_ptr)
    DBF  *dbf_ptr;
{

    long offset;
    unsigned int   packdate,
                   sysdate(),
                   tmpoff;

    FIELD     *workptr,
              *tmpprev,
              *tmpnext;
    char      *tmpval,
              cr_ret[2];

    char nulls[4];
    int  cntr;

    for(cntr=0; cntr<4; ++cntr)        /* bytes to write at end of each field descriptor */
         *(nulls+cntr) = NULL;

    if(c_lose(dbf_ptr->fh))          /* close the file */
         return(-1);

    if(_del(dbf_ptr->filename))
         return(-1);              /* delete the file */

    if( ( dbf_ptr->fh = c_reate(dbf_ptr->filename,0)) < 0)
         return(-1);

    dbf_ptr->curr_rec = 0;
    dbf_ptr->header.no_recs = 0;

    packdate = sysdate();         /* get the system date */
    dbf_ptr->header.dd = packdate & 0x001f;           /* update file date */
    dbf_ptr->header.mm = (packdate >> 5) & 0x000f;
    dbf_ptr->header.yy = (packdate >> 9) + 80;

    offset = 0;
    l_seek(dbf_ptr->fh,offset,0);      /* goto top of file and rewrite header info */

    if( (w_rite(dbf_ptr->fh,&(dbf_ptr->header),sizeof(DBF_HEAD))) != sizeof(DBF_HEAD) )
         return(-1);



    workptr = dbf_ptr->fields;         /* pointer to list of field descriptors */

    for(cntr=0;cntr<dbf_ptr->no_fields; ++cntr){
         offset = 0;
         l_seek(dbf_ptr->fh,offset,2);      /* goto EOF */
         tmpprev = workptr->prev;
         tmpnext = workptr->next;
         tmpoff = workptr->offset;
         tmpval = workptr->fld_val;

         workptr->prev = NULL;
         workptr->next = NULL;         /* clear these areas, as they are reserved */
         workptr->offset = 0;
         workptr->fld_val = 0;

         if( (w_rite(dbf_ptr->fh,workptr,(sizeof(FIELD))) ) < (sizeof(FIELD)) ) 
              return(-1);

         workptr->prev = tmpprev;
         workptr->next = tmpnext;
         workptr->offset = tmpoff;          /* reset the pointers,values */
         workptr->fld_val = tmpval;

         workptr=workptr->next;
    }
    cr_ret[0] = 13;          /* CR marks as a field delimeter */
    cr_ret[1] = 26;          /* EOF marker */
    offset = 0;
    l_seek(dbf_ptr->fh,offset,2);      /* goto EOF */
    if( (w_rite(dbf_ptr->fh,cr_ret,2)) != 2  ) 
              return(-1);   

    return(1);
}

