/***
*open.c - file open
*
*       Copyright (c) 1989-1997, Microsoft Corporation. All rights reserved.
*
*Purpose:
*       defines _open() and _sopen() - open or create a file
*
*******************************************************************************/

#ifndef _MAC


#include <cruntime.h>
#include <oscalls.h>
#include <msdos.h>
#include <errno.h>
#include <fcntl.h>
#include <internal.h>
#include <io.h>
#include <share.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys\types.h>
#include <sys\stat.h>
#include <mtdll.h>
#include <stdarg.h>
#include <tchar.h>

/***
*int _open(path, flag, pmode) - open or create a file
*
*Purpose:
*       Opens the file and prepares for subsequent reading or writing.
*       the flag argument specifies how to open the file:
*         _O_APPEND -   reposition file ptr to end before every write
*         _O_BINARY -   open in binary mode
*         _O_CREAT -    create a new file* no effect if file already exists
*         _O_EXCL -     return error if file exists, only use with O_CREAT
*         _O_RDONLY -   open for reading only
*         _O_RDWR -     open for reading and writing
*         _O_TEXT -     open in text mode
*         _O_TRUNC -    open and truncate to 0 length (must have write permission)
*         _O_WRONLY -   open for writing only
*         _O_NOINHERIT -handle will not be inherited by child processes.
*       exactly one of _O_RDONLY, _O_WRONLY, _O_RDWR must be given
*
*       The pmode argument is only required when _O_CREAT is specified.  Its
*       flag settings:
*         _S_IWRITE -   writing permitted
*         _S_IREAD -    reading permitted
*         _S_IREAD | _S_IWRITE - both reading and writing permitted
*       The current file-permission maks is applied to pmode before
*       setting the permission (see umask).
*
*       The oflag and mode parameter have different meanings under DOS. See
*       the A_xxx attributes in msdos.inc
*
*       Note, the _creat() function also uses this function but setting up the
*       correct arguments and calling _open(). _creat() sets the __creat_flag
*       to 1 prior to calling _open() so _open() can return correctly. _open()
*       returns the file handle in eax in this case.
*
*Entry:
*       _TSCHAR *path - file name
*       int flag - flags for _open()
*       int pmode - permission mode for new files
*
*Exit:
*       returns file handle of open file if successful
*       returns -1 (and sets errno) if fails
*
*Exceptions:
*
*******************************************************************************/

int __cdecl _topen (
        const _TSCHAR *path,
        int oflag,
        ...
        )
{
        va_list ap;
        int pmode;

        va_start(ap, oflag);
        pmode = va_arg(ap, int);
        va_end(ap);

        /* default sharing mode is DENY NONE */
        return _tsopen(path, oflag, _SH_DENYNO, pmode);
}

/***
*int _sopen(path, oflag, shflag, pmode) - opne a file with sharing
*
*Purpose:
*       Opens the file with possible file sharing.
*       shflag defines the sharing flags:
*         _SH_COMPAT -  set compatability mode
*         _SH_DENYRW -  deny read and write access to the file
*         _SH_DENYWR -  deny write access to the file
*         _SH_DENYRD -  deny read access to the file
*         _SH_DENYNO -  permit read and write access
*
*       Other flags are the same as _open().
*
*       SOPEN is the routine used when file sharing is desired.
*
*Entry:
*       _TSCHAR *path - file to open
*       int oflag -     open flag
*       int shflag -    sharing flag
*       int pmode -     permission mode (needed only when creating file)
*
*Exit:
*       returns file handle for the opened file
*       returns -1 and sets errno if fails.
*
*Exceptions:
*
*******************************************************************************/

int __cdecl _tsopen (
        const _TSCHAR *path,
        int oflag,
        int shflag,
        ...
        )
{

        int fh;                         /* handle of opened file */
        int filepos;                    /* length of file - 1 */
        _TSCHAR ch;                     /* character at end of file */
        char fileflags;                 /* _osfile flags */
        va_list ap;                     /* variable argument (pmode) */
        int pmode;
        HANDLE osfh;                    /* OS handle of opened file */
        DWORD fileaccess;               /* OS file access (requested) */
        DWORD fileshare;                /* OS file sharing mode */
        DWORD filecreate;               /* OS method of opening/creating */
        DWORD fileattrib;               /* OS file attribute flags */
        DWORD isdev;                    /* device indicator in low byte */
        SECURITY_ATTRIBUTES SecurityAttributes;

        SecurityAttributes.nLength = sizeof( SecurityAttributes );
        SecurityAttributes.lpSecurityDescriptor = NULL;

        if (oflag & _O_NOINHERIT) {
            SecurityAttributes.bInheritHandle = FALSE;
            fileflags = FNOINHERIT;
        }
        else {
            SecurityAttributes.bInheritHandle = TRUE;
            fileflags = 0;
        }

        /* figure out binary/text mode */
        if ((oflag & _O_BINARY) == 0)
            if (oflag & _O_TEXT)
                fileflags |= FTEXT;
            else if (_fmode != _O_BINARY)   /* check default mode */
                fileflags |= FTEXT;

        /*
         * decode the access flags
         */
        switch( oflag & (_O_RDONLY | _O_WRONLY | _O_RDWR) ) {

            case _O_RDONLY:         /* read access */
                    fileaccess = GENERIC_READ;
                    break;
            case _O_WRONLY:         /* write access */
                    fileaccess = GENERIC_WRITE;
                    break;
            case _O_RDWR:           /* read and write access */
                    fileaccess = GENERIC_READ | GENERIC_WRITE;
                    break;
            default:                /* error, bad oflag */
                    errno = EINVAL;
                    _doserrno = 0L; /* not an OS error */
                    return -1;
        }

        /*
         * decode sharing flags
         */
        switch ( shflag ) {

            case _SH_DENYRW:        /* exclusive access */
                fileshare = 0L;
                break;

            case _SH_DENYWR:        /* share read access */
                fileshare = FILE_SHARE_READ;
                break;

            case _SH_DENYRD:        /* share write access */
                fileshare = FILE_SHARE_WRITE;
                break;

            case _SH_DENYNO:        /* share read and write access */
                fileshare = FILE_SHARE_READ | FILE_SHARE_WRITE;
                break;

            default:                /* error, bad shflag */
                errno = EINVAL;
                _doserrno = 0L; /* not an OS error */
                return -1;
        }

        /*
         * decode open/create method flags
         */
        switch ( oflag & (_O_CREAT | _O_EXCL | _O_TRUNC) ) {
            case 0:
            case _O_EXCL:                   // ignore EXCL w/o CREAT
                filecreate = OPEN_EXISTING;
                break;

            case _O_CREAT:
                filecreate = OPEN_ALWAYS;
                break;

            case _O_CREAT | _O_EXCL:
            case _O_CREAT | _O_TRUNC | _O_EXCL:
                filecreate = CREATE_NEW;
                break;

            case _O_TRUNC:
            case _O_TRUNC | _O_EXCL:        // ignore EXCL w/o CREAT
                filecreate = TRUNCATE_EXISTING;
                break;

            case _O_CREAT | _O_TRUNC:
                filecreate = CREATE_ALWAYS;
                break;

            default:
                // this can't happen ... all cases are covered
                errno = EINVAL;
                _doserrno = 0L;
                return -1;
        }

        /*
         * decode file attribute flags if _O_CREAT was specified
         */
        fileattrib = FILE_ATTRIBUTE_NORMAL;     /* default */

        if ( oflag & _O_CREAT ) {
                /*
                 * set up variable argument list stuff
                 */
                va_start(ap, shflag);
                pmode = va_arg(ap, int);
                va_end(ap);

                if ( !((pmode & ~_umaskval) & _S_IWRITE) )
                        fileattrib = FILE_ATTRIBUTE_READONLY;
        }

        /*
         * Set temporary file (delete-on-close) attribute if requested.
         */
        if ( oflag & _O_TEMPORARY ) {
            fileattrib |= FILE_FLAG_DELETE_ON_CLOSE;
            fileaccess |= DELETE;
        }

        /*
         * Set temporary file (delay-flush-to-disk) attribute if requested.
         */
        if ( oflag & _O_SHORT_LIVED )
            fileattrib |= FILE_ATTRIBUTE_TEMPORARY;

        /*
         * Set sequential or random access attribute if requested.
         */
        if ( oflag & _O_SEQUENTIAL )
            fileattrib |= FILE_FLAG_SEQUENTIAL_SCAN;
        else if ( oflag & _O_RANDOM )
            fileattrib |= FILE_FLAG_RANDOM_ACCESS;

        /*
         * get an available handle.
         *
         * multi-thread note: the returned handle is locked!
         */
        if ( (fh = _alloc_osfhnd()) == -1 ) {
            errno = EMFILE;         /* too many open files */
            _doserrno = 0L;         /* not an OS error */
            return -1;              /* return error to caller */
        }

        /*
         * try to open/create the file
         */
        if ( (osfh = CreateFile( (LPTSTR)path,
                                 fileaccess,
                                 fileshare,
                                 &SecurityAttributes,
                                 filecreate,
                                 fileattrib,
                                 NULL ))
             == (HANDLE)0xffffffff )
        {
            /*
             * OS call to open/create file failed! map the error, release
             * the lock, and return -1. note that it's not necessary to
             * call _free_osfhnd (it hasn't been used yet).
             */
            _dosmaperr(GetLastError());     /* map error */
            _unlock_fh(fh);
            return -1;                      /* return error to caller */
        }

        /* find out what type of file (file/device/pipe) */
        if ( (isdev = GetFileType(osfh)) == FILE_TYPE_UNKNOWN ) {
            CloseHandle(osfh);
            _dosmaperr(GetLastError());     /* map error */
            _unlock_fh(fh);
            return -1;
        }

        /* is isdev value to set flags */
        if (isdev == FILE_TYPE_CHAR)
            fileflags |= FDEV;
        else if (isdev == FILE_TYPE_PIPE)
            fileflags |= FPIPE;

        /*
         * the file is open. now, set the info in _osfhnd array
         */
        _set_osfhnd(fh, (long)osfh);

        /*
         * mark the handle as open. store flags gathered so far in _osfile
         * array.
         */
        fileflags |= FOPEN;
        _osfile(fh) = fileflags;

        if ( !(fileflags & (FDEV|FPIPE)) && (fileflags & FTEXT) &&
             (oflag & _O_RDWR) )
        {
            /* We have a text mode file.  If it ends in CTRL-Z, we wish to
               remove the CTRL-Z character, so that appending will work.
               We do this by seeking to the end of file, reading the last
               byte, and shortening the file if it is a CTRL-Z. */

            if ((filepos = _lseek_lk(fh, -1, SEEK_END)) == -1) {
                /* OS error -- should ignore negative seek error,
                   since that means we had a zero-length file. */
                if (_doserrno != ERROR_NEGATIVE_SEEK) {
                    _close(fh);
                    _unlock_fh(fh);
                    return -1;
                }
            }
            else {
                /* Seek was OK, read the last char in file. The last
                   char is a CTRL-Z if and only if _read returns 0
                   and ch ends up with a CTRL-Z. */
                ch = 0;
                if (_read_lk(fh, &ch, 1) == 0 && ch == 26) {
                    /* read was OK and we got CTRL-Z! Wipe it
                       out! */
                    if (_chsize_lk(fh,filepos) == -1)
                    {
                        _close(fh);
                        _unlock_fh(fh);
                        return -1;
                    }
                }

                /* now rewind the file to the beginning */
                if ((filepos = _lseek_lk(fh, 0, SEEK_SET)) == -1) {
                    _close(fh);
                    _unlock_fh(fh);
                    return -1;
                }
            }
        }

        /*
         * Set FAPPEND flag if appropriate. Don't do this for devices or pipes.
         */
        if ( !(fileflags & (FDEV|FPIPE)) && (oflag & _O_APPEND) )
            _osfile(fh) |= FAPPEND;

        _unlock_fh(fh);                 /* unlock handle */

        return fh;                      /* return handle */
}


#else  /* _MAC */


#include <cruntime.h>
#include <internal.h>
#include <string.h>
#include <memory.h>
#include <msdos.h>
#include <errno.h>
#include <fcntl.h>
#include <macos\files.h>
#include <string.h>
#include <macos\errors.h>
#include <macos\types.h>
#include <io.h>
#include <share.h>
#include <stdlib.h>
#include <sys\types.h>
#include <sys\stat.h>
#include <stdarg.h>

/* define the entry in terminator table */

#pragma data_seg(".CRT$XTX")

_PVFV pendlowio = _endlowio;

#pragma data_seg()

/* Mac definitions for ioDenyModes */

#define MAC_PERMRD 0x0001
#define MAC_PERMWR 0x0002
#define MAC_DENYRD 0x0010
#define MAC_DENYWR 0x0020


/***
*int _open(path, flag, pmode) - open or create a file
*
*Purpose:
*       Opens the file and prepares for subsequent reading or writing.
*       the flag argument specifies how to open the file:
*         _O_APPEND -   reposition file ptr to end before every write
*         _O_BINARY -   open in binary mode
*         _O_CREAT -    create a new file* no effect if file already exists
*         _O_EXCL -     return error if file exists, only use with O_CREAT
*         _O_RDONLY -   open for reading only
*         _O_RDWR -     open for reading and writing
*         _O_TEXT -     open in text mode
*         _O_TRUNC -    open and truncate to 0 length (must have write permission)
*         _O_WRONLY -   open for writing only
*       exactly one of _O_RDONLY, _O_WRONLY, _O_RDWR must be given
*
*       The pmode argument is only required when _O_CREAT is specified.  Its
*       flag settings:
*         _S_IWRITE -   writing permitted
*         _S_IREAD -    reading permitted
*         _S_IREAD | _S_IWRITE - both reading and writing permitted
*       The current file-permission masks is applied to pmode before
*       setting the permission (see umask).
*
*       Note, the _creat() function also uses this function but setting up the
*       correct arguments and calling _open().
*
*Entry:
*       char *path - file name
*       int flag - flags for _open()
*       int pmode - permission mode for new files
*
*Exit:
*       returns file handle of open file if successful
*       returns -1 (and sets errno) if fails
*
*Exceptions:
*
*******************************************************************************/

int __cdecl _open (
        const char *path,
        int oflag,
        ...
        )
{
        va_list ap;

        va_start(ap, oflag);
        /* default sharing mode is DENY NONE */
        return _sopen(path, oflag, _SH_DENYNO, va_arg(ap, int));
}

/***
*void __mopen(stpath, fh, ioPermssn, ioDenyModes) - MAC open a file with sharing
*
*Purpose:
*       Worker routine to open a file on the MAC.  It only opens the file.
*  If local open fails it will try AppleShare oepn.
*
*Entry:
*       char *stpath -  file to open (Pascal string)
*       int fh - file handle to use
*       int ioPermssn - persmission modes flags
*       int ioDenyModes -       Deny mode flags
*
*Exit:
*       returns TRUE if successful and sets errno & _osfhnd[fh] &
*                       _osfile[fh] if successful
*
*Exceptions:
*
*******************************************************************************/

int __cdecl __mopen (
        char *stpath,
        int fh,
        int ioPermssn,
        int ioDenyModes
        )
{
        HParamBlockRec parm;
        OSErr osErr;
        char sz[256];
        char buf[10];
        char *pch;

        //if starts with full path, test the availability of the volume
        memcpy(sz, stpath, (*stpath+1));
        _p2cstr(sz);

        if (*sz != ':' && (pch = strchr(sz, ':')) != NULL)
        {
                *(pch+1) = '\0';
                _c2pstr(sz);
                memset(&parm, 0, sizeof(HParamBlockRec));
                memset(buf, 0, 10);
                parm.ioParam.ioNamePtr = sz;
                parm.ioParam.ioBuffer = buf;
                parm.ioParam.ioReqCount = 6;
                osErr = PBHGetVolParmsSync(&parm);

                if (!osErr)
                {
                        if (!(buf[4]&0x8000))
                        {
                                parm.ioParam.ioNamePtr = stpath;
                                parm.ioParam.ioVRefNum = 0;
                                goto local;
                        }
                }
                else
                {
                        _dosmaperr(osErr);
                        return osErr;
                }
        }

        /* try to open the file using Appleshare calls*/
        parm.ioParam.ioNamePtr = stpath;
        parm.ioParam.ioVRefNum = 0;
        parm.accessParam.ioDenyModes = (unsigned char)ioDenyModes;
        _osperm[fh] = (unsigned char)ioDenyModes;
        parm.fileParam.ioDirID = 0;
        parm.ioParam.ioMisc = NULL;
        osErr = PBHOpenDenySync(&parm);

        if (osErr == paramErr)
        {
local:
                /* Try local open */
                parm.ioParam.ioPermssn = ioPermssn;
                _osperm[fh] = (unsigned char)ioPermssn;
                osErr = PBHOpenDFSync(&parm);
        }

        if (!osErr)
        {
                _osfile[fh] |= FOPEN;
                _osfhnd[fh] = parm.ioParam.ioRefNum;
        }
        else
        {
                _dosmaperr(osErr);
        }

        return osErr;
}

/***
*int _sopen(path, oflag, shflag, pmode) - open a file with sharing
*
*Purpose:
*       Opens the file with possible file sharing.
*       shflag defines the sharing flags:
*         _SH_DENYRW -  deny read and write access to the file
*         _SH_DENYNO -  permit read and write access
*
*       Other flags are the same as _open().
*
*       SOPEN is the routine used when file sharing is desired.
*
*Entry:
*       char *path - file to open (C string)
*       int oflag -     open flag
*       int shflag -    sharing flag
*       int pmode -     permission mode (needed only when creating file)
*
*Exit:
*       returns file handle for the opened file
*       returns -1 and sets errno if fails.
*
*Exceptions:
*
*******************************************************************************/

int _cdecl _sopen (
        const char *path,
        int oflag,
        int shflag,
        ...
        )
{

        int fh;         /* handle of opened file */
        OSErr osErr = 0;
        unsigned char ioPermssn;
        short int ioDenyModes;
        ParamBlockRec parm;
        int pmode;
        va_list ap;                     /* variable argument (pmode) */
        char lpath[256];

        if (!*path)
        {
                errno = ENOENT;
                return -1;
        }

        strcpy(lpath,path);
        _c2pstr(lpath);

        /* get a file handle*/
        for (fh = 0; fh <_nfile; fh++)
        {
                if (!(_osfile[fh] & FOPEN))
                {
                        break;
                }
        }

        if (fh >= _nfile)
        {
                errno = EMFILE;
                _macerrno = 0;
                return -1;
        }

        _osfile[fh] = 0;
                _osfileflags[fh] = 0;

        /* figure out binary/text mode */

        switch (oflag  & (_O_BINARY | _O_TEXT))
        {
                case _O_BINARY:
                        break;

                case _O_TEXT:
                        _osfile[fh] = (unsigned char)FTEXT;
                        break;

                case _O_TEXT | _O_BINARY:
                        errno = EINVAL;
                        return -1;

                default:
                        if (_fmode != _O_BINARY)
                        {
                                _osfile[fh] = (unsigned char)FTEXT;
                        }
                        break;
        }

        /* figure out read/write modes */

        switch (oflag & (_O_RDWR | _O_RDONLY | _O_WRONLY))
        {
                case _O_RDONLY:
                        ioPermssn = fsRdPerm;
                        _osfile[fh] |= FRDONLY;
                        if (oflag & _O_TRUNC)
                        {
                                errno = EINVAL;
                                return -1;
                        }
                        ioDenyModes = MAC_PERMRD;
                        break;

                case _O_WRONLY:
                        ioPermssn = fsRdWrShPerm;
                        _osfile[fh] |= FWRONLY;
                        ioDenyModes = MAC_PERMWR;
                        break;

                case _O_RDWR:
                        ioPermssn  = fsRdWrPerm;
                        ioDenyModes = MAC_PERMRD | MAC_PERMWR;
                        break;

                default:
                        errno = EINVAL;
                        return -1;
        }

        switch (shflag)
        {
                case _SH_DENYRD:
                        ioDenyModes |= MAC_DENYRD;
                        break;

                case _SH_DENYWR:
                        ioDenyModes |= MAC_DENYWR;
                        break;

                case _SH_DENYRW:
                        ioDenyModes |= MAC_DENYRD | MAC_DENYWR;
                        break;

                case _SH_DENYNO:
                        if (ioPermssn == fsRdWrPerm)
                        {
                                ioPermssn = fsRdWrShPerm;
                        }
                        break;

                default:
                        errno = EINVAL;
                        return -1;
        }

        if (!(oflag & _O_CREAT && oflag & _O_EXCL))
        {
                /* try to open the file */
                if (!__mopen(lpath, fh, ioPermssn, ioDenyModes))
                {
                        oflag &= ~_O_CREAT;     /*file open - no need to create*/
                }
        }

        /* Didn't work try creating the file if requested */
        if (oflag & _O_CREAT)
        {
                /* reset errno from mopen, since we can try create*/
                errno = 0;
                va_start(ap, shflag);
                pmode = va_arg(ap, int);
                pmode &= ~_umaskval;
                if (!(pmode & (_S_IREAD | _S_IWRITE)))
                {
                        errno = EINVAL;
                        return -1;
                }
                parm.fileParam.ioNamePtr = lpath;
                parm.fileParam.ioVRefNum = 0;
                osErr = PBCreateSync(&parm);
                if (!osErr)
                {
                        parm.fileParam.ioFDirIndex = 0;
                        PBGetFInfoSync(&parm);
                        parm.fileParam.ioFlFndrInfo.fdType = (_osfile[fh] & FTEXT ? 'TEXT' : '    ');
                        parm.fileParam.ioFlFndrInfo.fdCreator = '    ';
                        PBSetFInfoSync(&parm);
                }
                else
                {
                        if (osErr == dupFNErr && oflag & _O_EXCL)
                        {
                                errno = EEXIST; /*special case normally returns EACCES*/
                                _macerrno = osErr;
                        }
                        else
                        {
                                _dosmaperr(osErr);
                        }
                        return -1;
                }
                if (osErr = __mopen(lpath, fh, ioPermssn, ioDenyModes))
                {
                        _dosmaperr(osErr);
                        return -1;
                }
                else if (!(pmode & _S_IWRITE))
                {
                        PBSetFLockSync(&parm);
                }
        }
        if (!(_osfile[fh] & FOPEN))
        {
                goto ErrExit;
        }

        parm.ioParam.ioRefNum = _osfhnd[fh];

        /* Truncate file */
        if (oflag & _O_TRUNC)
        {
                parm.ioParam.ioMisc = 0;
                osErr = PBSetEOFSync(&parm);
                if (osErr)
                {
                        _dosmaperr(osErr);
                        goto ErrExit;
                }
        }

        /* get vol reference */
        parm.volumeParam.ioVolIndex = -1;
        parm.ioParam.ioNamePtr = lpath;
        parm.ioParam.ioVRefNum = 0;
        osErr = PBGetVInfoSync(&parm);
        if (osErr)
        {
                _dosmaperr(osErr);
                goto ErrExit;
        }
        _osVRefNum[fh] = parm.volumeParam.ioVRefNum;

        if (oflag & _O_APPEND)
        {
                _osfile[fh] |= FAPPEND;
        }

        if (oflag & _O_TEMPORARY)
        {
                _osfileflags[fh] |= FTEMP;
        }
        return fh;                      /* return handle */


ErrExit:
        if (_osfile[fh] & FOPEN)
        {
                PBCloseSync(&parm);
                _osfile[fh] = 0;
        }
        return -1;
}


#endif  /* _MAC */
