/***************************************************************************/
/* DBASE.C - This is the module containing the code to read and write      */
/* dBASE files.                                                            */
/*                                                                         */
/* (C) Copyright 1993-96 - SYWARE, Inc. - All rights reserved              */
/***************************************************************************/

#include "precomp.h"
#include "wbemidl.h"
#include "dbase.h"
#include <stdio.h>
#include <time.h>

#define s_lstrcpy(a, b) lstrcpy((LPSTR) a, (LPCSTR) b)
#define s_lstrcat(a, b) lstrcat((LPSTR) a, (LPCSTR) b)
#define s_lstrlen(a) lstrlen((LPCSTR) a)
#define s_lstrcmp(a, b) lstrcmp((LPCSTR) a, (LPCSTR) b)

/***************************************************************************/
#define FIRST_RECORD 0xFFFFFFFF
#define LAST_RECORD 0xFFFFFFFE
/***************************************************************************/

SWORD FAR PASCAL dBaseExtendedOpen(LPUSTR name, BOOL fReadOnly,
                                   UWORD extraColumns, LPDBASEFILE FAR *lpf)
{
/*
    HFILE hf;
    UWORD columnCount;
    UWORD recordLen;
    HGLOBAL h;
    LPDBASEFILE f;
    UWORD i;
    UCHAR FAR * ptr;
    UCHAR fullname[DBASE_MAX_PATHNAME_SIZE+1];

    // Get the filename 
    s_lstrcpy((char*)fullname, name);
    ptr = fullname + s_lstrlen((char*)fullname);
    while ((ptr != fullname) && (*ptr != PATH_SEPARATOR_CHAR) && (*ptr != ':') &&
           (*ptr != '.'))
        ptr--;
    if (*ptr != '.')
        s_lstrcat((char*)fullname, ".dbf");

    // Open the file 
    if (fReadOnly)
        hf = _lopen((LPCSTR)fullname, OF_READ);
    else
        hf = _lopen((LPCSTR)fullname, OF_READWRITE);
    if (hf == HFILE_ERROR)
        return DBASE_ERR_TABLEACCESSERROR;

    //Figure out how many columns there are 
    if (HFILE_ERROR == _llseek(hf, DBASE_HEADER_SIZE_OFFSET, 0)) {
        _lclose(hf);
        return DBASE_ERR_CORRUPTFILE;
    }
    if (sizeof(UWORD) != _lread(hf, &columnCount, sizeof(UWORD))) {
        _lclose(hf);
        return DBASE_ERR_CORRUPTFILE;
    }
    columnCount = (columnCount - DBASE_HEADER_LENGTH - 1)/
                                DBASE_COLUMN_DESCR_LENGTH;

    // Get record size 
    if (HFILE_ERROR == _llseek(hf, DBASE_RECORD_LENGTH_OFFSET, 0)) {
        _lclose(hf);
        return DBASE_ERR_CORRUPTFILE;
    }
    if (sizeof(UWORD) != _lread(hf, &recordLen, sizeof(UWORD))) {
        _lclose(hf);
        return DBASE_ERR_CORRUPTFILE;
    }

    // Allocate space for handle 
    h = GlobalAlloc (GMEM_MOVEABLE, (sizeof(DBASEFILE) - sizeof(DBASECOL)) +
              (sizeof(DBASECOL) * (columnCount + extraColumns)) + recordLen);
    if (h == NULL || (f = (LPDBASEFILE)GlobalLock (h)) == NULL)
    {
        _lclose(hf);
        return DBASE_ERR_MEMALLOCFAIL;
    }

    // Read in the header 
    if (HFILE_ERROR == _llseek(hf, 0, 0)) {
        GlobalUnlock(h); 
        GlobalFree(h); 
        _lclose(hf);
        return DBASE_ERR_CORRUPTFILE;
    }
    if (DBASE_HEADER_LENGTH != _lread(hf, f, DBASE_HEADER_LENGTH)) {
        GlobalUnlock(h); 
        GlobalFree(h); 
        _lclose(hf);
        return DBASE_ERR_CORRUPTFILE;
    }
    if (f->encrypted != DBASE_NOT_ENCRYPTED) {
        GlobalUnlock(h); 
        GlobalFree(h); 
        _lclose(hf);
        return DBASE_ERR_CORRUPTFILE;
    }
    f->hf = hf;
    f->columnCount = columnCount;
    f->sortArray = NULL;
    f->currentRecord = 0;
    f->record = ((UCHAR FAR *) (&(f->column))) + 
                ((columnCount+extraColumns) * sizeof(DBASECOL));
    f->headerDirty = FALSE;
    f->recordDirty = FALSE;
    f->newRecord = FALSE;

    // Read in column definition 
    ptr = f->record + 1;
    for (i = 0; (i < columnCount); i++) {
        if (DBASE_COLUMN_DESCR_LENGTH != 
                   _lread(hf, &(f->column[i]), DBASE_COLUMN_DESCR_LENGTH)) {
            GlobalUnlock(h); 
            GlobalFree(h); 
            _lclose(hf);
            return DBASE_ERR_CORRUPTFILE;
        }
        OemToAnsiBuff((LPCSTR) f->column[i].name, f->column[i].name,
                                                 DBASE_COLUMN_NAME_SIZE);
        f->column[i].value = ptr;
        ptr += f->column[i].length;
    }

    *lpf = f;
*/
    return DBASE_ERR_SUCCESS;

}

/***************************************************************************/

SWORD FAR PASCAL dBaseFlushDirtyRecord(LPDBASEFILE f)
{
/*
    LONG offset;
    UCHAR c;
    UDWORD currentRecord;

    // Is there a dirty record? 
    if (f->recordDirty) {

        // Yes.  Write it back 
        f->recordDirty = FALSE;
        if (f->sortArray == NULL)
            currentRecord = f->currentRecord-1;
        else
            currentRecord = f->sortArray[f->currentRecord-1];
        offset = f->headerSize + (currentRecord * f->recordSize);
        if (HFILE_ERROR == _llseek(f->hf, offset, 0)) {
            f->newRecord = FALSE;
            f->currentRecord = 0;
            return DBASE_ERR_CORRUPTFILE;
        }
        AnsiToOemBuff((LPCSTR) f->record, f->record, f->recordSize);
        if (f->recordSize != _lwrite(f->hf, (LPCSTR) f->record, f->recordSize)) {
            f->newRecord = FALSE;
            f->currentRecord = 0;
            return DBASE_ERR_WRITEERROR;
        }

        // Was this a new record? 
        if (f->newRecord) {

            // Yes.  Write the end-of-data mark 
            f->newRecord = FALSE;
            c = DBASE_FILE_END;
            if (1 != _lwrite(f->hf, (LPCSTR) &c, 1)) {
                f->currentRecord = 0;
                return DBASE_ERR_WRITEERROR;
            }
        }

        // Write back the header 
        f->headerDirty = TRUE;
    }
*/
    return DBASE_ERR_SUCCESS;

}
/***************************************************************************/

SWORD FAR PASCAL dBaseQuickSortKeyCompare(LPDBASEFILE f, UWORD icol,
                                          BOOL descending, UDWORD left,
                                          UDWORD right, BOOL FAR *result)

/* Is the key of the left greater than or equal the key of the right? */

{
/*
    SWORD err;
    UWORD size;
    UCHAR szLeft[255];
    UCHAR szRight[255];
    SDOUBLE dblLeft;
    SDOUBLE dblRight;
    BOOL nullLeft;
    BOOL nullRight;

    // Handle boundry conditions 
    if (left == LAST_RECORD) {
        *result = TRUE;
        return DBASE_ERR_SUCCESS;
    }
    if (right == FIRST_RECORD) {
        *result = TRUE;
        return DBASE_ERR_SUCCESS;
    }
    if (left == FIRST_RECORD) {
        *result = FALSE;
        return DBASE_ERR_SUCCESS;
    }
    if (right == LAST_RECORD) {
        *result = FALSE;
        return DBASE_ERR_SUCCESS;
    }

    // Position to the right record 
    if (HFILE_ERROR == _llseek(f->hf, f->headerSize + (right * f->recordSize),
                               0))
        return DBASE_ERR_CORRUPTFILE;

    // Read the right record 
    size = _lread(f->hf, f->record, f->recordSize);
    if (size != f->recordSize)
        return DBASE_ERR_CORRUPTFILE;
    OemToAnsiBuff((LPCSTR) f->record, (LPSTR) f->record, f->recordSize);
    *(f->record) = DBASE_RECORD_NOT_DELETED;

    // Get the right key 
    switch (f->column[icol-1].type) {
    case DBASE_CHAR:
    case DBASE_LOGICAL:
    case DBASE_DATE:
    case DBASE_MEMO:
        err = dBaseColumnCharVal(f, icol, 0, 255, szRight, &nullRight);
        break;
    case DBASE_NUMERIC:
    case DBASE_FLOAT:
        err = dBaseColumnNumVal(f, icol, &dblRight, &nullRight);
        break;
    }
    if (err != DBASE_ERR_SUCCESS)
        return err;

    // If right is null, return TRUE 
    if (nullRight) {
        *result = TRUE;
        return DBASE_ERR_SUCCESS;
    }

    // Position to the left record 
    if (HFILE_ERROR == _llseek(f->hf, f->headerSize + (left * f->recordSize),
                               0))
        return DBASE_ERR_CORRUPTFILE;

    // Read the left record 
    size = _lread(f->hf, f->record, f->recordSize);
    if (size != f->recordSize)
        return DBASE_ERR_CORRUPTFILE;
    OemToAnsiBuff((LPCSTR) f->record, (LPSTR) f->record, f->recordSize);
    *(f->record) = DBASE_RECORD_NOT_DELETED;

    // Get the left key 
    switch (f->column[icol-1].type) {
    case DBASE_CHAR:
    case DBASE_LOGICAL:
    case DBASE_DATE:
    case DBASE_MEMO:
        err = dBaseColumnCharVal(f, icol, 0, 255, szLeft, &nullLeft);
        break;
    case DBASE_NUMERIC:
    case DBASE_FLOAT:
        err = dBaseColumnNumVal(f, icol, &dblLeft, &nullLeft);
        break;
    }
    if (err != DBASE_ERR_SUCCESS)
        return err;

    // If left is null, return FALSE 
    if (nullRight) {
        *result = FALSE;
        return DBASE_ERR_SUCCESS;
    }

    // Compare the keys 
    switch (f->column[icol-1].type) {
    case DBASE_CHAR:
    case DBASE_LOGICAL:
    case DBASE_DATE:
    case DBASE_MEMO:
        if (!descending) {
            if (s_lstrcmp(szLeft, szRight) >= 0)
                *result = TRUE;
            else
                *result = FALSE;
        }
        else {
            if (s_lstrcmp(szLeft, szRight) <= 0)
                *result = TRUE;
            else
                *result = FALSE;
        }
        break;
    case DBASE_NUMERIC:
    case DBASE_FLOAT:
        if (!descending) {
            if (dblLeft >= dblRight)
                *result = TRUE;
            else
                *result = FALSE;
        }
        else {
            if (dblLeft <= dblRight)
                *result = TRUE;
            else
                *result = FALSE;
        }
        break;
    }
*/
    return DBASE_ERR_SUCCESS;

}
/***************************************************************************/

SWORD FAR PASCAL dBaseQuickSort(LPDBASEFILE f, UWORD icol, BOOL descending,
                                UDWORD FAR *sortArray, UDWORD left,
                                UDWORD right)
{
/*
    UDWORD i;
    UDWORD j;
    UDWORD temp;
    SWORD err;
    BOOL result;

    // Q1: 
    if (right <= left)
        return DBASE_ERR_SUCCESS;
        
    // Q2: 
    i = left;
    j = right+1;

    while (TRUE) {

        // Q3: 
        while (TRUE) {
            i++;
            err = dBaseQuickSortKeyCompare(f, icol, descending,
                                  sortArray[i], sortArray[left], &result);
            if (err != DBASE_ERR_SUCCESS)
                return err;
            if (result)
                break;
        }

        // Q4: 
        while (TRUE) {
            j--;
            err = dBaseQuickSortKeyCompare(f, icol, descending,
                                  sortArray[left], sortArray[j], &result);
            if (err != DBASE_ERR_SUCCESS)
                return err;
            if (result)
                break;
        }

        // Q5: 
        if (j <= i)
            break;

        // Q6: 
        temp = sortArray[j];
        sortArray[j] = sortArray[i];
        sortArray[i] = temp;
    }

    // Q5: 
    temp = sortArray[left];
    sortArray[left] = sortArray[j];
    sortArray[j] = temp;

    // Q7: 
    err = dBaseQuickSort(f, icol, descending, sortArray, left, j-1);
    if (err != DBASE_ERR_SUCCESS)
        return err;

    // Q8: 
    err = dBaseQuickSort(f, icol, descending, sortArray, j+1, right);
    if (err != DBASE_ERR_SUCCESS)
        return err;
*/
    return DBASE_ERR_SUCCESS;

}
/***************************************************************************/
/***************************************************************************/

SWORD FAR PASCAL dBaseCreate(LPUSTR name)
{
/*
    UCHAR FAR * ptr;
    DBASEFILE header;
    HFILE hf;
    int i;
    HANDLE h;
    UCHAR NEAR *fullname;
    UCHAR c;
    HANDLE ht;
    time_t *t;
    struct tm *ts;

    // Get the filename 
    h = LocalAlloc(LMEM_MOVEABLE, DBASE_MAX_PATHNAME_SIZE+1);
    fullname = (UCHAR NEAR *) LocalLock(h);
    lstrcpy(fullname, name);
    ptr = fullname + lstrlen(fullname);
    while ((ptr != fullname) && (*ptr != '\\') && (*ptr != ':') &&
           (*ptr != '.'))
        ptr--;
    if (*ptr != '.')
        lstrcat(fullname, ".dbf");

    // Open the file 
    hf = _lcreat(fullname, 0);
    if (hf == HFILE_ERROR) {
        LocalUnlock(h);
        LocalFree(h);
        return DBASE_ERR_CREATEERROR;
    }

    // Set up the header 
    header.flag = DBASE_3_FILE;
    ht = LocalAlloc(LMEM_MOVEABLE, sizeof(time_t));
    t = (time_t NEAR *) LocalLock(ht);
    time(t);
    ts = localtime(t);
    LocalUnlock(ht);
    LocalFree(ht);
    header.year = ts->tm_year;
    header.month = ts->tm_mon + 1;
    header.day = ts->tm_mday;
    header.recordCount = 0;
    header.headerSize = DBASE_HEADER_LENGTH + 1;
    header.recordSize = 1;
    for (i=0; (i < 2); i++)
        header.reserved1[i] = 0;
    header.transaction = 0;
    header.encrypted = DBASE_NOT_ENCRYPTED;
    for (i=0; (i < 12); i++)
        header.reserved2[i] = 0;
    header.mdx = DBASE_MDX_FLAG_OFF;
    for (i=0; (i < 3); i++)
        header.reserved3[i] = 0;

    // Write the header 
    if (DBASE_HEADER_LENGTH != _lwrite(hf, (LPSTR) &header,
                                       DBASE_HEADER_LENGTH)) {
        _lclose(hf);
        AnsiToOem(fullname, fullname);
#ifdef WIN32
        DeleteFile(fullname);
#else
        remove(fullname);
#endif
        LocalUnlock(h);
        LocalFree(h);
        return DBASE_ERR_WRITEERROR;
    }

    // Write the end-of-columns mark 
    c = DBASE_END_OF_COLUMNS;
    if (1 != _lwrite(hf, &c, 1)) {
        _lclose(hf);
        AnsiToOem(fullname, fullname);
#ifdef WIN32
        DeleteFile(fullname);
#else
        remove(fullname);
#endif
        LocalUnlock(h);
        LocalFree(h);
        return DBASE_ERR_WRITEERROR;
    }

    // Write the end-of-data mark 
    c = DBASE_FILE_END;
    if (1 != _lwrite(hf, &c, 1)) {
        _lclose(hf);
        AnsiToOem(fullname, fullname);
#ifdef WIN32
        DeleteFile(fullname);
#else
        remove(fullname);
#endif
        LocalUnlock(h);
        LocalFree(h);
        return DBASE_ERR_WRITEERROR;
    }

    // Close the file 
    if (HFILE_ERROR == _lclose(hf)) {
        AnsiToOem(fullname, fullname);
#ifdef WIN32
        DeleteFile(fullname);
#else
        remove(fullname);
#endif
        LocalUnlock(h);
        LocalFree(h);
        return DBASE_ERR_CLOSEERROR;
    }
    LocalUnlock(h);
    LocalFree(h);
*/
    return DBASE_ERR_SUCCESS;

}

/***************************************************************************/

SWORD FAR PASCAL dBaseAddColumn(LPUSTR name, LPUSTR colName, UCHAR type,
                               UCHAR length, UCHAR scale)
{
/*
    LPDBASEFILE f;
    LPSTR from;
    int i;
    LONG offset;
    UCHAR c;
    SWORD err;

    // Check the column name 
    if (lstrlen(colName) > DBASE_COLUMN_NAME_SIZE)
        return DBASE_ERR_NOTSUPPORTED;

    // Check the length and scale 
    switch (type) {
    case DBASE_CHAR:
        if ((length == 0) || (length > 254))
            return DBASE_ERR_NOTSUPPORTED;
        scale = 0;
        break;
    case DBASE_NUMERIC:
    case DBASE_FLOAT:
        if ((length == 0) || (length > 19))
            return DBASE_ERR_NOTSUPPORTED;
        if (length == 1) {
            if (scale != 0)
                return DBASE_ERR_NOTSUPPORTED;
        }
        else if (((UCHAR) (scale+2)) > length)
            return DBASE_ERR_NOTSUPPORTED;
        break;
    case DBASE_LOGICAL:
        length = 1;
        scale = 0;
        break;
    case DBASE_DATE:
        length = 8;
        scale = 0;
        break;
    case DBASE_MEMO:
        return DBASE_ERR_NOTSUPPORTED;
    default:
        return DBASE_ERR_NOTSUPPORTED;
    }

    // Open the file 
    err = dBaseExtendedOpen(name, FALSE, 1, &f) ;
    if (err != DBASE_ERR_SUCCESS)
        return err;

    if (f->recordCount != 0) {
        dBaseClose(f);
        return DBASE_ERR_NOTSUPPORTED;
    }

    // Fill in the new entry 
    from = colName;
    for (i=0; (i < DBASE_COLUMN_NAME_SIZE); i++) {
        f->column[f->columnCount].name[i] = *from;
        if (*from != 0)
            from++;
    }
    f->column[f->columnCount].type = type;
    for (i=0; (i < 4); i++)
        f->column[f->columnCount].reserved1[i] = 0;
    f->column[f->columnCount].length = length;
    f->column[f->columnCount].scale = scale;
    for (i=0; (i < 2); i++)
        f->column[f->columnCount].reserved2[i] = 0;
    f->column[f->columnCount].workarea = 0;
    for (i=0; (i < 10); i++)
        f->column[f->columnCount].reserved3[i] = 0;
    f->column[f->columnCount].mdx = DBASE_MDX_FLAG_OFF;
    f->column[f->columnCount].value = NULL;

    // Write the new column information 
    offset = DBASE_HEADER_LENGTH + (f->columnCount*DBASE_COLUMN_DESCR_LENGTH);
    if (HFILE_ERROR == _llseek(f->hf, offset, 0)) {
        dBaseClose(f);
        return DBASE_ERR_CORRUPTFILE;
    }
    AnsiToOemBuff(f->column[f->columnCount].name, 
                  f->column[f->columnCount].name, DBASE_COLUMN_NAME_SIZE);
    if (DBASE_COLUMN_DESCR_LENGTH != _lwrite(f->hf, (LPSTR)
                &(f->column[f->columnCount]), DBASE_COLUMN_DESCR_LENGTH)) {
        dBaseClose(f);
        return DBASE_ERR_WRITEERROR;
    }

    // Adust header information 
    (f->columnCount)++;
    f->headerSize += DBASE_COLUMN_DESCR_LENGTH;
    f->recordSize += length;
    if (type == DBASE_FLOAT)
        f->flag = DBASE_4_FILE;

    // Put in end-of-columns mark 
    c = DBASE_END_OF_COLUMNS;
    if (1 != _lwrite(f->hf, &c, 1)) {
        dBaseClose(f);
        return DBASE_ERR_WRITEERROR;
    }

    // Put in end-of-data mark 
    c = DBASE_FILE_END;
    if (1 != _lwrite(f->hf, &c, 1)) {
        dBaseClose(f);
        return DBASE_ERR_WRITEERROR;
    }

    // Close the file 
    f->headerDirty = TRUE;
    err = dBaseClose(f);
    if (err != DBASE_ERR_SUCCESS)
        return err;
*/
    return DBASE_ERR_SUCCESS;

}

/***************************************************************************/

SWORD FAR PASCAL dBaseOpen(LPUSTR name, BOOL fReadOnly, LPDBASEFILE FAR *lpf)
{
    return dBaseExtendedOpen(name, fReadOnly, 0, lpf);
}

/***************************************************************************/

SWORD FAR PASCAL dBaseColumnCount(LPDBASEFILE f, UWORD FAR *count)
{
//  *count = f->columnCount;
    return DBASE_ERR_SUCCESS;
}

/***************************************************************************/

SWORD FAR PASCAL dBaseColumnName(LPDBASEFILE f, UWORD icol, LPUSTR name)
{
/*
    int i;

    if ((icol > f->columnCount) || (icol <= 0))
        return DBASE_ERR_NOSUCHCOLUMN;

    for (i = 0; i < DBASE_COLUMN_NAME_SIZE; i++)
        name[i] = f->column[icol-1].name[i];
    name[DBASE_COLUMN_NAME_SIZE] = '\0';
*/
    return DBASE_ERR_SUCCESS;
}

/***************************************************************************/

SWORD FAR PASCAL dBaseColumnType(LPDBASEFILE f, UWORD icol, UCHAR FAR *type)
{
/*
    if ((icol > f->columnCount) || (icol <= 0))
        return DBASE_ERR_NOSUCHCOLUMN;

    *type = f->column[icol-1].type;
*/
    return DBASE_ERR_SUCCESS;
}

/***************************************************************************/

SWORD FAR PASCAL dBaseColumnLength(LPDBASEFILE f, UWORD icol, UCHAR FAR *len)
{
/*
    if ((icol > f->columnCount) || (icol <= 0))
        return DBASE_ERR_NOSUCHCOLUMN;

    *len = f->column[icol-1].length;
*/
    return DBASE_ERR_SUCCESS;
}

/***************************************************************************/

SWORD FAR PASCAL dBaseColumnScale(LPDBASEFILE f, UWORD icol, UCHAR FAR *scale)
{
/*
    if ((icol > f->columnCount) || (icol <= 0))
        return DBASE_ERR_NOSUCHCOLUMN;

    *scale = f->column[icol-1].scale;
*/
    return DBASE_ERR_SUCCESS;
}

/***************************************************************************/

SWORD FAR PASCAL dBaseSort(LPDBASEFILE f, UWORD icol, BOOL descending)
{
/*
    SWORD err;
    HANDLE h;
    UDWORD FAR *sortArray;
    UDWORD idx;

    // Make sure column is really there 
    if ((icol > f->columnCount) || (icol < 0))
        return DBASE_ERR_NOSUCHCOLUMN;

    // Write back any pending writes 
    err = dBaseFlushDirtyRecord(f);
    if (err != DBASE_ERR_SUCCESS)
        return err;

    // Remove sort order (if any) 
    if (f->sortArray != NULL) {
        GlobalUnlock(GlobalPtrHandle (f->sortArray));
        GlobalFree(GlobalPtrHandle(f->sortArray));
        f->sortArray = NULL;
    }

    // Is there a new sort? 
    if (icol != 0) {

        // Yes.  Allocate space for new sort 
        h = GlobalAlloc (GMEM_MOVEABLE, (sizeof(UDWORD) * (f->recordCount + 2)));
        if (h == NULL || (sortArray = (UDWORD FAR *)GlobalLock (h)) == NULL)
            return DBASE_ERR_MEMALLOCFAIL;

        // Fill in the sort array 
        sortArray[0] = FIRST_RECORD;
        for (idx = 1; idx < f->recordCount+1; idx++)
            sortArray[idx] = idx-1;
        sortArray[f->recordCount+1] = LAST_RECORD;

        // Sort the records 
        f->currentRecord = 1;
        err = dBaseQuickSort(f, icol, descending, sortArray, 1, f->recordCount);
        if (err != DBASE_ERR_SUCCESS) {
            f->currentRecord = 0;
            GlobalUnlock(h);
            GlobalFree(h);
            return err;
        }
        f->currentRecord = 0;

        // Move the items into place 
        for (idx = 0; idx < f->recordCount; idx++)
            sortArray[idx] = sortArray[idx+1];
        sortArray[f->recordCount] = f->recordCount;
        f->sortArray = sortArray;
    }

    // Position before record 
    f->currentRecord = 0;
*/
    return DBASE_ERR_SUCCESS;
}

/***************************************************************************/

SWORD FAR PASCAL dBaseReadFirst(LPDBASEFILE f)
{
/*
    LONG offset;
    SWORD err;
    UWORD size;
    UDWORD currentRecord;

    // Write back any pending writes 
    err = dBaseFlushDirtyRecord(f);
    if (err != DBASE_ERR_SUCCESS)
        return err;

    // Position to the first record 
    if (f->sortArray == NULL)
        currentRecord = 0;
    else
        currentRecord = f->sortArray[0];
    offset = f->headerSize + (currentRecord * f->recordSize);
    if (HFILE_ERROR == _llseek(f->hf, offset, 0))
        return DBASE_ERR_CORRUPTFILE;

    // Read the record 
    size = _lread(f->hf, f->record, f->recordSize);
    if (size == 1) {
        if (*(f->record) == DBASE_FILE_END)
            return DBASE_ERR_NODATAFOUND;
        else
            return DBASE_ERR_CORRUPTFILE;
    }
    if (size != f->recordSize)
        return DBASE_ERR_CORRUPTFILE;
    f->currentRecord = 1;

    // If this record is deleted, return next record 
    if (*(f->record) == DBASE_RECORD_DELETED)
        return dBaseReadNext(f);

    // Return record 
    OemToAnsiBuff(f->record, f->record, f->recordSize);
*/
    return DBASE_ERR_SUCCESS;
}

/***************************************************************************/

SWORD FAR PASCAL dBaseReadNext(LPDBASEFILE f)
{
/*
    LONG offset;
    SWORD err;
    UWORD size;
    UDWORD currentRecord;

    // Write back any pending writes 
    err = dBaseFlushDirtyRecord(f);
    if (err != DBASE_ERR_SUCCESS)
        return err;

    // Position to the next record 
    if (f->sortArray == NULL)
        currentRecord = f->currentRecord;
    else
        currentRecord = f->sortArray[f->currentRecord];
    offset = f->headerSize + (currentRecord * f->recordSize);
    if (HFILE_ERROR == _llseek(f->hf, offset, 0))
        return DBASE_ERR_CORRUPTFILE;

    // Read the record 
    size = _lread(f->hf, f->record, f->recordSize);
    if (size == 1) {
        if (*(f->record) == DBASE_FILE_END)
            return DBASE_ERR_NODATAFOUND;
        else
            return DBASE_ERR_CORRUPTFILE;
    }
    if (size != f->recordSize)
        return DBASE_ERR_CORRUPTFILE;
    f->currentRecord++;

    // If this record is deleted, return next record 
    if (*(f->record) == DBASE_RECORD_DELETED)
        return dBaseReadNext(f);

    // Return record 
    OemToAnsiBuff(f->record, f->record, f->recordSize);
*/
    return DBASE_ERR_SUCCESS;
}

/***************************************************************************/

SWORD FAR PASCAL dBaseColumnCharVal(LPDBASEFILE f, UWORD icol, UDWORD offset,
                                   SDWORD bufsize, UCHAR FAR *val,
                                   BOOL FAR *isNull)
{
/*

    LPDBASECOL lpCol;
    UCHAR FAR *fromPtr;
    UCHAR FAR *toPtr;
    UCHAR i;
    UWORD year;
    UWORD month;
    UWORD day;
*/
    SWORD err;
/*
    // Make sure data is really there 
    if ((icol > f->columnCount) || (icol <= 0))
        return DBASE_ERR_NOSUCHCOLUMN;
    if (f->currentRecord == 0)
        return DBASE_ERR_NODATAFOUND;
    if (*(f->record) == DBASE_RECORD_DELETED)
        return DBASE_ERR_NODATAFOUND;

    // Get the data 
    lpCol = &(f->column[icol-1]);
    fromPtr = lpCol->value;
*/
    err = DBASE_ERR_SUCCESS;
/*
    switch (lpCol->type) {
    case DBASE_CHAR:
        toPtr = val;
        *isNull = TRUE;
        if (bufsize <= ((SDWORD) lpCol->length)) {
            err = DBASE_ERR_TRUNCATION;
        }
        else {
            bufsize = lpCol->length;
        }
        for (i=0; i < (UCHAR) bufsize; i++) {
            if (*fromPtr != ' ')
                *isNull = FALSE;
            if (toPtr != NULL) {
                *toPtr = *fromPtr;
                toPtr++;
            }
            fromPtr++;
        }
        if (toPtr != NULL)
            *toPtr = '\0';
        break;
    case DBASE_FLOAT:
    case DBASE_NUMERIC:
        i = lpCol->length;
        while ((*fromPtr == ' ') && (i > 0)) {
            fromPtr++;
            i--;
        }
        toPtr = val;
        *isNull = TRUE;
        for (;i > 0; i--) {
            if (toPtr != NULL) {
                *toPtr = *fromPtr;
                toPtr++;
            }
            fromPtr++;
            *isNull = FALSE;
        }
        if (toPtr != NULL)
            *toPtr = '\0';
        break;
    case DBASE_DATE:
        if (*fromPtr == ' ')
            *isNull = TRUE;
        else {
            *isNull = FALSE;
            year = ((fromPtr[0] & 0x0F) * 1000) +
               ((fromPtr[1] & 0x0F) * 100) +
               ((fromPtr[2] & 0x0F) * 10) +
                (fromPtr[3] & 0x0F);
            month = ((fromPtr[4] & 0x0F) * 10) + (fromPtr[5] & 0x0F);
            day = ((fromPtr[6] & 0x0F) * 10) + (fromPtr[7] & 0x0F);
            if (val != NULL)
                wsprintf(val, "%04d-%02d-%02d", year, month, day);
        }
        break;
    case DBASE_LOGICAL:
        switch (*fromPtr) {
        case 'Y':
        case 'y':
        case 'T':
        case 't':
        case '1':
            if (val != NULL)
                lstrcpy(val, "1");
            *isNull = FALSE;
            break;
        case 'N':
        case 'n':
        case 'F':
        case 'f':
        case '0':
            if (val != NULL)
                lstrcpy(val, "0");
            *isNull = FALSE;
            break;
        case '?':
        case ' ':
            *isNull = TRUE;
            break;
        }
        break;
    case DBASE_MEMO:
        *isNull = TRUE;
        break;
    }
    if ((*isNull) && (val != NULL))
        lstrcpy(val, "");
*/
    return err;
}

/***************************************************************************/

SWORD FAR PASCAL dBaseColumnNumVal(LPDBASEFILE f, UWORD icol,
                                  SDOUBLE FAR *val, BOOL FAR *isNull)
{
/*
    LPDBASECOL lpCol;
    UCHAR i;
    double d;
    BOOL neg;
    UCHAR FAR *fromPtr;
    BOOL foundDecimalPoint;
    short scale;
    BOOL negexp;
    short exp;

    // Make sure data is really there 
    if ((icol > f->columnCount) || (icol <= 0))
        return DBASE_ERR_NOSUCHCOLUMN;
    if (f->currentRecord == 0)
        return DBASE_ERR_NODATAFOUND;
    if (*(f->record) == DBASE_RECORD_DELETED)
        return DBASE_ERR_NODATAFOUND;

    // Get the data 
    lpCol = &(f->column[icol-1]);
    fromPtr = lpCol->value;
    switch (lpCol->type) {
    case DBASE_CHAR:
    case DBASE_FLOAT:
    case DBASE_NUMERIC:
        *isNull = TRUE;
        for (i=0; i < lpCol->length; i++) {
            if (*fromPtr != ' ')
                break;
            fromPtr++;
        }

        neg = FALSE;
        if (i < lpCol->length) {
            if (*fromPtr == '-') {
                neg = TRUE;
                fromPtr++;
                i++;
            }
        }

        d = 0.0;
        scale = 0;
        foundDecimalPoint = FALSE;
        for (;i < lpCol->length; i++) {
            if (!foundDecimalPoint && (*fromPtr == '.'))
                foundDecimalPoint = TRUE;
            else {
                if ((*fromPtr == 'E') || (*fromPtr == 'e')) {
                    fromPtr++;
                    i++;
                    if (i < lpCol->length) { 
                        if (*fromPtr == '-') {
                            negexp = TRUE;
                            fromPtr++;
                            i++;
                        }
                        else if (*fromPtr == '+') {
                            negexp = FALSE;
                            fromPtr++;
                            i++;
                        }
                        else
                            negexp = FALSE;
                    }
                    else
                        negexp = FALSE;
                    exp = 0;
                    for (;i < lpCol->length; i++) {
                        if ((*fromPtr < '0') || (*fromPtr > '9'))
                           return DBASE_ERR_CONVERSIONERROR;
                        exp = (exp * 10) + (*fromPtr - '0');
                        fromPtr++;
                    }
                    if (negexp)
                        scale = scale + exp;
                    else
                        scale = scale - exp;
                    break;
                }
                if ((*fromPtr < '0') || (*fromPtr > '9'))
                    return DBASE_ERR_CONVERSIONERROR;
                d = (d * 10) + (*fromPtr - '0');
                *isNull = FALSE;
                if (foundDecimalPoint)
                    scale++;
            }
            fromPtr++;
        }

        for (; (0 < scale); scale--)
            d /= 10;
        for (; (0 > scale); scale++)
            d *= 10;

        if (val != NULL) {
            if (neg)
                *val = -d;
            else
                *val = d;
        }
        break;
    case DBASE_DATE:
        return DBASE_ERR_CONVERSIONERROR;
    case DBASE_LOGICAL:
        switch (*fromPtr) {
        case 'Y':
        case 'y':
        case 'T':
        case 't':
        case '1':
            if (val != NULL)
                *val = 1.0;
            *isNull = FALSE;
            break;
        case 'N':
        case 'n':
        case 'F':
        case 'f':
        case '0':
            if (val != NULL)
                *val = 0.0;
            *isNull = FALSE;
            break;
        case '?':
        case ' ':
            *isNull = TRUE;
            break;
        }
        break;
    case DBASE_MEMO:
        *isNull = TRUE;
        break;
    }
    if ((val != NULL) && (*isNull))
        *val = 0.0;
*/
    return DBASE_ERR_SUCCESS;
}

/***************************************************************************/

SWORD FAR PASCAL dBaseAddRecord(LPDBASEFILE f)
{
/*
    UWORD i;
    UCHAR FAR * ptr;
    SWORD err;

    // Write dirty record 
    err = dBaseFlushDirtyRecord(f);
    if (err != DBASE_ERR_SUCCESS)
        return err;

    // Remove sort order (if any) 
    if (f->sortArray != NULL) {
        GlobalUnlock(GlobalPtrHandle(f->sortArray));
        GlobalFree(GlobalPtrHandle(f->sortArray));
        f->sortArray = NULL;
    }

    // Create null record 
    ptr = f->record;
    for (i=0; i < f->recordSize; i++) {
        *ptr = ' ';
        ptr++;
    }
    f->recordDirty = TRUE;
    f->newRecord = TRUE;

    // Increment record count 
    f->recordCount++;
    f->headerDirty = TRUE;

    // Reset current record 
    f->currentRecord = f->recordCount;
*/  
    return DBASE_ERR_SUCCESS;
}

/***************************************************************************/

SWORD FAR PASCAL dBaseSetColumnCharVal(LPDBASEFILE f, UWORD icol,
                                      UCHAR FAR *val, SDWORD size)
{
/*
    LPDBASECOL lpCol;
    UCHAR FAR *fromPtr;
    UCHAR FAR *toPtr;
    UCHAR FAR *decimalLocation;
    UCHAR FAR *valStart;
    UCHAR FAR *valEnd;
    UCHAR FAR *ptr;
    UCHAR length;
    UCHAR i;
    SWORD extraZeros;
    BOOL negNeeded;
    BOOL foundExp;
    SWORD err;

    // Make sure data is really there 
    if ((icol > f->columnCount) || (icol <= 0))
        return DBASE_ERR_NOSUCHCOLUMN;
    if (f->currentRecord == 0)
        return DBASE_ERR_NODATAFOUND;
    if (*(f->record) == DBASE_RECORD_DELETED)
        return DBASE_ERR_NODATAFOUND;

    // Set value 
    lpCol = &(f->column[icol-1]);
    toPtr = lpCol->value;
    err = DBASE_ERR_SUCCESS;
    switch (lpCol->type) {
    case DBASE_CHAR:
        fromPtr = val;
        for (i=0; i < lpCol->length; i++) {
            if (((SDWORD) i) == size)
                break;
            *toPtr = *fromPtr;
            toPtr++;
            fromPtr++;
        }
        for (; i < lpCol->length; i++) {
            *toPtr = ' ';
            toPtr++;
        }
        if (((SDWORD) lpCol->length) < size)
            err = DBASE_ERR_TRUNCATION;
        break;
    case DBASE_FLOAT:
    case DBASE_NUMERIC:

        // If a zero length string is specifed, point to a zero 
        if ((lpCol->scale == 0) && (size == 0)) {
            val = "0";
            size = 1;
        }
        
        // Point at start of value 
        length = lpCol->length - 1;
        valStart = val;
        if (*valStart == '-') {
            valStart++;
        }

        // Make sure all characters are legal and find decimal point 
        foundExp = FALSE;
        decimalLocation = NULL;
        fromPtr = valStart;
        i = 0;
        while (((SDWORD) i) < size) {
            if (*fromPtr == '\0')
                break;
            else if (*fromPtr == '.') {
                if ((decimalLocation != NULL) || (foundExp))
                    return DBASE_ERR_CONVERSIONERROR;
                decimalLocation = fromPtr;
            }
            else if ((*fromPtr == 'E') || (*fromPtr == 'e')) {
                if (foundExp)
                    return DBASE_ERR_CONVERSIONERROR;
                foundExp = TRUE;
                if ((*fromPtr == '-') || (*fromPtr == '+')) {
                    fromPtr++;
                    i++;
                }
            }
            else if (!(IsCharAlphaNumeric(*fromPtr)) || IsCharAlpha(*fromPtr))
                return DBASE_ERR_CONVERSIONERROR;
            fromPtr++;
            i++;
        }

        // Scientific notation? 
        if (foundExp) {

            // Yes.  Error if not enough room for value 
            if (size > (SDWORD) lpCol->length)
                return DBASE_ERR_CONVERSIONERROR;

            // Copy the value 
            for (i=0; (SWORD) i < (SWORD) lpCol->length - lstrlen(val); i++) {
                *toPtr = ' ';
                toPtr++;
            }
            fromPtr = val;
            while (*fromPtr) {
                *toPtr = *fromPtr;
                toPtr++;
                fromPtr++;
            }
            break;
        }

        // Find end of value 
        valEnd = fromPtr-1;
        if (decimalLocation == valEnd) {
            valEnd--;
            decimalLocation = NULL;
        }

        // Truncate extra characters at end 
        if (decimalLocation != NULL) {
            if (lpCol->scale == 0) {
                valEnd = decimalLocation-1;
                decimalLocation = NULL;
            }
            else {
                if ((SWORD) lpCol->scale < (valEnd - decimalLocation)) {
                    valEnd = decimalLocation + lpCol->scale;
                }
            }
        }

        // Figure out how many extra zeros need to be added at end 
        if (lpCol->scale == 0)
            extraZeros = 0;
        else if (decimalLocation == NULL)
            extraZeros = lpCol->scale + 1;
        else
            extraZeros = lpCol->scale - (valEnd - decimalLocation);

        // Shave off extra characters in front 
        while ((SWORD) length < (valEnd - valStart) + 1 + extraZeros)
            valStart++;

        // Put in leading spaces 
        for (i=0; 
             (SWORD) i < (length - (valEnd - valStart + 1) - extraZeros); 
             i++) {
            *toPtr = ' ';
            toPtr++;
        }

        // Put in sign if needed 
        negNeeded = FALSE;
        if (*val == '-') {
            for (ptr = valStart; ptr <= valEnd; ptr++) {
                if ((*ptr != '0') && (*ptr != '.')) {
                    negNeeded = TRUE;
                    break;
                }
            }
        }
        if (negNeeded)
            *toPtr = '-';
        else
            *toPtr = ' ';
        toPtr++;

        // Put in value 
        while (valStart <= valEnd) {
            *toPtr = *valStart;
            toPtr++;
            valStart++;
        }

        // Put in required trailing zeros 
        if (extraZeros == lpCol->scale + 1) {
            *toPtr = '.';
            toPtr++;
            extraZeros--;
        }
        while (extraZeros > 0) {
            *toPtr = '0';
            toPtr++;
            extraZeros--;
        }
        break;

    case DBASE_DATE:
        // Make sure value is legal 
        if (size != 10)
            return DBASE_ERR_CONVERSIONERROR;
        for (i=0; i < 10; i++) {
            switch (i) {
            case 0:
            case 1:
            case 2:
            case 3:
            case 6:
            case 9:
                if (!(IsCharAlphaNumeric(val[i])) || IsCharAlpha(val[i]))
                    return DBASE_ERR_CONVERSIONERROR;
                break;
            case 4:
            case 7:
                if (val[i] != '-')
                    return DBASE_ERR_CONVERSIONERROR;
                break;
            case 5:
                if ((val[i] < '0') || (val[i] > '1'))
                    return DBASE_ERR_CONVERSIONERROR;
                break;
            case 8:
                if ((val[i] < '0') && (val[i] > '3'))
                    return DBASE_ERR_CONVERSIONERROR;
                if ((val[i] == '3') && (val[i+1] > '1'))
                    return DBASE_ERR_CONVERSIONERROR;
                break;
            }
        }

        // Copy the value 
        for (i=0; i < 4; i++) {
            *toPtr = val[i];
            toPtr++;
        }
        for (i=5; i < 7; i++) {
            *toPtr = val[i];
            toPtr++;
        }
        for (i=8; i < 10; i++) {
            *toPtr = val[i];
            toPtr++;
        }
        break;
    case DBASE_LOGICAL:
        if (size != 1)
            return DBASE_ERR_CONVERSIONERROR;
        switch (*val) {
        case 'Y':
        case 'y':
        case 'T':
        case 't':
        case '1':
            *toPtr = 'Y';
            break;
        case 'N':
        case 'n':
        case 'F':
        case 'f':
        case '0':
            *toPtr = 'N';
            break;
        case '?':
        case ' ':
            *toPtr = '?';
            break;
        default:
            return DBASE_ERR_CONVERSIONERROR;
        }
        break;
    case DBASE_MEMO:
        break;
    }
    f->recordDirty = TRUE;
*/
    return DBASE_ERR_SUCCESS;
}

/***************************************************************************/

SWORD FAR PASCAL dBaseSetColumnNull(LPDBASEFILE f, UWORD icol)
{
/*
    UCHAR i;
    LPDBASECOL lpCol;

    // Make sure data is really there 
    if ((icol > f->columnCount) || (icol <= 0))
        return DBASE_ERR_NOSUCHCOLUMN;
    if (f->currentRecord == 0)
        return DBASE_ERR_NODATAFOUND;
    if (*(f->record) == DBASE_RECORD_DELETED)
        return DBASE_ERR_NODATAFOUND;

    // Set value 
    lpCol = &(f->column[icol-1]);
    if (lpCol->type == DBASE_MEMO)
        return DBASE_ERR_NOTSUPPORTED;
    for (i=0; (i < f->column[icol-1].length); i++)
        f->column[icol-1].value[i] = ' ';
    f->recordDirty = TRUE;
*/
    return DBASE_ERR_SUCCESS;
}
/***************************************************************************/

SWORD FAR PASCAL dBaseDeleteRecord(LPDBASEFILE f)
{
/*
    if (f->currentRecord == 0)
        return DBASE_ERR_NODATAFOUND;
    if (*(f->record) == DBASE_RECORD_DELETED)
        return DBASE_ERR_NODATAFOUND;

    if (f->newRecord) {
        f->recordDirty = FALSE;
        f->newRecord = FALSE;
        f->recordCount--;
        f->headerDirty = TRUE;
        f->currentRecord = 0;
    }
    else {
        *(f->record) = DBASE_RECORD_DELETED;
        f->recordDirty = TRUE;
    }
*/  
    return DBASE_ERR_SUCCESS;
}
/***************************************************************************/
SWORD FAR PASCAL dBaseGetBookmark(LPDBASEFILE f, UDWORD far *b)
{
/*
    // Make sure data is really there 
    if (f->currentRecord == 0)
        return DBASE_ERR_NODATAFOUND;
    if (*(f->record) == DBASE_RECORD_DELETED)
        return DBASE_ERR_NODATAFOUND;

    if (f->sortArray == NULL)
        *b = f->currentRecord-1;
    else
        *b = f->sortArray[f->currentRecord-1];
*/
    return DBASE_ERR_SUCCESS;
}
/***************************************************************************/
SWORD FAR PASCAL dBasePosition(LPDBASEFILE f, UDWORD b)
{
/*
    LONG offset;
    SWORD err;
    UWORD size;

    // Write back any pending writes 
    err = dBaseFlushDirtyRecord(f);
    if (err != DBASE_ERR_SUCCESS)
        return err;

    // Position to the specified record 
    offset = f->headerSize + (b * f->recordSize);
    if (HFILE_ERROR == _llseek(f->hf, offset, 0))
        return DBASE_ERR_CORRUPTFILE;

    // Read the record 
    size = _lread(f->hf, f->record, f->recordSize);
    if (size == 1) {
        if (*(f->record) == DBASE_FILE_END)
            return DBASE_ERR_NODATAFOUND;
        else
            return DBASE_ERR_CORRUPTFILE;
    }
    if (size != f->recordSize)
        return DBASE_ERR_CORRUPTFILE;
    f->currentRecord = b+1;

    // If this record is deleted, return error 
    if (*(f->record) == DBASE_RECORD_DELETED)
        return DBASE_ERR_NODATAFOUND;

    // Return record 
    OemToAnsiBuff(f->record, f->record, f->recordSize);
*/
    return DBASE_ERR_SUCCESS;
}
/***************************************************************************/

SWORD FAR PASCAL dBaseClose(LPDBASEFILE f)
{
/*
    SWORD err;
    HANDLE ht;
    time_t *t;
    struct tm *ts;

    if (f->headerDirty) {
        ht = LocalAlloc(LMEM_MOVEABLE, sizeof(time_t));
        t = (time_t NEAR *) LocalLock(ht);
        time(t);
        ts = localtime(t);
        LocalUnlock(ht);
        LocalFree(ht);
        f->year = ts->tm_year;
        f->month = ts->tm_mon + 1;
        f->day = ts->tm_mday;
        if (HFILE_ERROR == _llseek(f->hf, 0, 0))
            return DBASE_ERR_CORRUPTFILE; 
        if (DBASE_HEADER_LENGTH != _lwrite(f->hf, (LPSTR) f, DBASE_HEADER_LENGTH))
            return DBASE_ERR_WRITEERROR;
    }
    err = dBaseFlushDirtyRecord(f);
    if (err != DBASE_ERR_SUCCESS)
        return err;
    if (HFILE_ERROR == _lclose(f->hf))
        return DBASE_ERR_CLOSEERROR;
    if (f->sortArray != NULL) {
        GlobalUnlock(GlobalPtrHandle(f->sortArray));
        GlobalFree(GlobalPtrHandle(f->sortArray));
    }
    GlobalUnlock(GlobalPtrHandle(f));
    GlobalFree(GlobalPtrHandle(f));
*/
    return DBASE_ERR_SUCCESS;
}

/***************************************************************************/

SWORD FAR PASCAL dBaseDestroy(LPUSTR name)
{
/*
    UCHAR FAR * ptr;
    HANDLE h;
    UCHAR NEAR *fullname;

    // Get the filename 
    h = LocalAlloc(LMEM_MOVEABLE, DBASE_MAX_PATHNAME_SIZE+1);
    fullname = (UCHAR NEAR *) LocalLock(h);
    lstrcpy(fullname, name);
    ptr = fullname + lstrlen(fullname);
    while ((ptr != fullname) && (*ptr != '\\') && (*ptr != ':') &&
           (*ptr != '.'))
        ptr--;
    if (*ptr != '.')
        lstrcat(fullname, ".dbf");

    AnsiToOem(fullname, fullname);
#ifdef WIN32
    DeleteFile(fullname);
#else
    remove(fullname);
#endif
    LocalUnlock(h);
    LocalFree(h);
*/
    return DBASE_ERR_SUCCESS;
}
/***************************************************************************/