/****************************************************************************
*
*                   SciTech OS Portability Manager Library
*
*  ========================================================================
*
*    The contents of this file are subject to the SciTech MGL Public
*    License Version 1.0 (the "License"); you may not use this file
*    except in compliance with the License. You may obtain a copy of
*    the License at http://www.scitechsoft.com/mgl-license.txt
*
*    Software distributed under the License is distributed on an
*    "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
*    implied. See the License for the specific language governing
*    rights and limitations under the License.
*
*    The Original Code is Copyright (C) 1991-1998 SciTech Software, Inc.
*
*    The Initial Developer of the Original Code is SciTech Software, Inc.
*    All Rights Reserved.
*
*  ========================================================================
*
* Language:     ANSI C
* Environment:  32-bit OS/2 VDD
*
* Description:  C library compatible I/O functions for use within a VDD.
*
****************************************************************************/

#include "pmapi.h"
#include "vddfile.h"

/*------------------------ Main Code Implementation -----------------------*/

#define EOF -1

/* NB: none of the file VDHs are available during the DOS session          */
/* initialzation context!                                                  */

/* Macros for Open/Close APIs to allow using this module in both VDDs and  */
/* normal OS/2 applications. Unfortunately VDHRead/Write/Seek don't map to */
/* their Dos* counterparts so cleanly.                                     */
#ifdef __OS2_VDD__
#define _OS2Open    VDHOpen
#define _OS2Close   VDHClose
#else
#define _OS2Open    DosOpen
#define _OS2Close   DosClose
#endif

/****************************************************************************
REMARKS:
VDD implementation of the ANSI C fopen function.
****************************************************************************/
FILE * fopen(
    const char *filename,
    const char *mode)
{
    FILE    *f = PM_malloc(sizeof(FILE));
    long    oldpos;
    ULONG   rc, ulAction;
    ULONG   omode, oflags;

    if (f != NULL) {
        f->offset = 0;
        f->text = (mode[1] == 't' || mode[2] == 't');
        f->writemode = (mode[0] == 'w') || (mode[0] == 'a');
        f->unputc = EOF;
        f->endp = f->buf + sizeof(f->buf);
        f->curp = f->startp = f->buf;

        if (mode[0] == 'r') {
            #ifdef __OS2_VDD__
            omode  = VDHOPEN_ACCESS_READONLY | VDHOPEN_SHARE_DENYNONE;
            oflags = VDHOPEN_ACTION_OPEN_IF_EXISTS | VDHOPEN_ACTION_FAIL_IF_NEW;
            #else
            omode  = OPEN_ACCESS_READONLY | OPEN_SHARE_DENYNONE;
            oflags = OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW;
            #endif
            }
        else if (mode[0] == 'w') {
            #ifdef __OS2_VDD__
            omode  = VDHOPEN_ACCESS_WRITEONLY | VDHOPEN_SHARE_DENYWRITE;
            oflags = VDHOPEN_ACTION_REPLACE_IF_EXISTS | VDHOPEN_ACTION_CREATE_IF_NEW;
            #else
            omode  = OPEN_ACCESS_WRITEONLY | OPEN_SHARE_DENYWRITE;
            oflags = OPEN_ACTION_REPLACE_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW;
            #endif
            }
        else {
            #ifdef __OS2_VDD__
            omode  = VDHOPEN_ACCESS_READWRITE | VDHOPEN_SHARE_DENYWRITE;
            oflags = VDHOPEN_ACTION_OPEN_IF_EXISTS | VDHOPEN_ACTION_CREATE_IF_NEW;
            #else
            omode  = OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYWRITE;
            oflags = OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW;
            #endif
            }
        rc = _OS2Open((PSZ)filename, (PHFILE)&f->handle, &ulAction, 0, VDHOPEN_FILE_NORMAL, oflags, omode, NULL);
        if (rc != 0) {
            PM_free(f);
            return NULL;
            }

        #ifdef __OS2_VDD__
        f->filesize = VDHSeek((HFILE)f->handle, 0, VDHSK_END_OF_FILE);
        #else
        rc = DosSetFilePtr((HFILE)f->handle, 0, FILE_END, &f->filesize);
        #endif

        if (mode[0] == 'a')
            fseek(f,0,2);
    }
    return f;
}

/****************************************************************************
REMARKS:
VDD implementation of the ANSI C fread function. Note that unlike Windows VxDs,
OS/2 VDDs are not limited to 64K reads or writes.
****************************************************************************/
size_t fread(
    void *ptr,
    size_t size,
    size_t n,
    FILE *f)
{
    char    *buf = ptr;
    int     bytes,readbytes,totalbytes = 0;

    /* First copy any data already read into our buffer */
    if ((bytes = (f->curp - f->startp)) > 0) {
        memcpy(buf,f->curp,bytes);
        f->startp = f->curp = f->buf;
        buf += bytes;
        totalbytes += bytes;
        bytes = (size * n) - bytes;
        }
    else
        bytes = size * n;
    if (bytes) {
        #ifdef __OS2_VDD__
        readbytes = VDHRead((HFILE)f->handle, buf, bytes);
        #else
        DosRead((HFILE)f->handle, buf, bytes, &readbytes);
        #endif
        totalbytes += readbytes;
        f->offset += readbytes;
        }
    return totalbytes / size;
}

/****************************************************************************
REMARKS:
VDD implementation of the ANSI C fwrite function.
****************************************************************************/
size_t fwrite(
    void *ptr,
    size_t size,
    size_t n,
    FILE *f)
{
    char        *buf = ptr;
    int         bytes,writtenbytes,totalbytes = 0;

    /* Flush anything already in the buffer */
    if (!f->writemode)
        return 0;
    fflush(f);
    bytes = size * n;
    #ifdef __OS2_VDD__
    writtenbytes = VDHWrite((HFILE)f->handle, buf, bytes);
    #else
    DosWrite((HFILE)f->handle, buf, bytes, &writtenbytes);
    #endif
    totalbytes += writtenbytes;
    f->offset += writtenbytes;
    if (f->offset > f->filesize)
        f->filesize = f->offset;
    return totalbytes / size;
}

/****************************************************************************
REMARKS:
VxD implementation of the ANSI C fflush function.
****************************************************************************/
int fflush(
    FILE *f)
{
    ULONG     bytes;

    /* First copy any data already written into our buffer */
    if (f->writemode && (bytes = (f->curp - f->startp)) > 0) {
        #ifdef __OS2_VDD__
        bytes = VDHWrite((HFILE)f->handle, f->startp, bytes);
        #else
        DosWrite((HFILE)f->handle, f->startp, bytes, &bytes);
        #endif
        f->offset += bytes;
        if (f->offset > f->filesize)
            f->filesize = f->offset;
        f->startp = f->curp = f->buf;
        }
    return 0;
}

/****************************************************************************
REMARKS:
VDD implementation of the ANSI C fseek function.
****************************************************************************/
int fseek(
    FILE *f,
    long int offset,
    int whence)
{
    fflush(f);

    if (whence == 0)
        f->offset = offset;
    else if (whence == 1)
        f->offset += offset;
    else if (whence == 2)
        f->offset = f->filesize + offset;

    #ifdef __OS2_VDD__
    VDHSeek((HFILE)f->handle, f->offset, VDHSK_ABSOLUTE);
    #else
    DosSetFilePtr((HFILE)f->handle, f->offset, FILE_BEGIN, NULL);
    #endif

    return 0;
}

/****************************************************************************
REMARKS:
VDD implementation of the ANSI C ftell function.
****************************************************************************/
long ftell(
    FILE *f)
{
    long    offset;

    offset = (f->curp - f->startp);
    offset += f->offset;
    return offset;
}

/****************************************************************************
REMARKS:
VDD implementation of the ANSI C feof function.
****************************************************************************/
int feof(
    FILE *f)
{
    return (f->offset == f->filesize);
}

/****************************************************************************
REMARKS:
Read a single character from the input file buffer, including translation
of the character in text transation modes.
****************************************************************************/
static int __getc(
    FILE *f)
{
    int c;

    if (f->unputc != EOF) {
        c = f->unputc;
        f->unputc = EOF;
        }
    else {
        if (f->startp == f->curp) {
            int bytes = fread(f->buf,1,sizeof(f->buf),f);
            if (bytes == 0)
                return EOF;
            f->curp = f->startp + bytes;
            }
        c = *f->startp++;
        if (f->text && c == '\r') {
            int nc = __getc(f);
            if (nc != '\n')
                f->unputc = nc;
            }
        }
    return c;
}

/****************************************************************************
REMARKS:
Write a single character from to input buffer, including translation of the
character in text transation modes.
****************************************************************************/
static int __putc(int c,FILE *f)
{
    int count = 1;
    if (f->text && c == '\n') {
        __putc('\r',f);
        count = 2;
        }
    if (f->curp == f->endp)
        fflush(f);
    *f->curp++ = c;
    return count;
}

/****************************************************************************
REMARKS:
VxD implementation of the ANSI C fgets function.
****************************************************************************/
char *fgets(
    char *s,
    int n,
    FILE *f)
{
    int c = 0;
    char *cs;

    cs = s;
    while (--n > 0 && (c = __getc(f)) != EOF) {
        *cs++ = c;
        if (c == '\n')
            break;
        }
    if (c == EOF && cs == s)
        return NULL;
    *cs = '\0';
    return s;
}

/****************************************************************************
REMARKS:
VxD implementation of the ANSI C fputs function.
****************************************************************************/
int fputs(
    const char *s,
    FILE *f)
{
    int r = 0;
    int c;

    while ((c = *s++) != 0)
        r = __putc(c, f);
    return r;
}

/****************************************************************************
REMARKS:
VxD implementation of the ANSI C fclose function.
****************************************************************************/
int fclose(
    FILE *f)
{
    fflush(f);
    _OS2Close((HFILE)f->handle);
    PM_free(f);
    return 0;
}
