/***
*lseeki64.c - change file position
*
*       Copyright (c) 1994-1997, Microsoft Corporation. All rights reserved.
*
*Purpose:
*       defines _lseeki64() - move the file pointer
*
*******************************************************************************/

#include <cruntime.h>
#include <oscalls.h>
#include <mtdll.h>
#include <io.h>
#include <internal.h>
#include <stdlib.h>
#include <errno.h>
#include <msdos.h>
#include <stdio.h>

/*
 * Convenient union for accessing the upper and lower 32-bits of a 64-bit
 * integer.
 */
typedef union doubleint {
        __int64 bigint;
        struct {
            unsigned long lowerhalf;
            long upperhalf;
        } twoints;
} DINT;


/***
*__int64 _lseeki64( fh, pos, mthd ) - move the file pointer
*
*Purpose:
*       Moves the file pointer associated with fh to a new position. The new
*       position is pos bytes (pos may be negative) away from the origin
*       specified by mthd.
*
*       If mthd == SEEK_SET, the origin in the beginning of file
*       If mthd == SEEK_CUR, the origin is the current file pointer position
*       If mthd == SEEK_END, the origin is the end of the file
*
*       Multi-thread:
*       _lseeki64()    = locks/unlocks the file
*       _lseeki64_lk() = does NOT lock/unlock the file (it is assumed that
*                        the caller has the aquired the file lock, if needed).
*
*Entry:
*       int     fh   - file handle to move file pointer on
*       __int64 pos  - position to move to, relative to origin
*       int     mthd - specifies the origin pos is relative to (see above)
*
*Exit:
*       returns the offset, in bytes, of the new position from the beginning
*       of the file.
*       returns -1i64 (and sets errno) if fails.
*       Note that seeking beyond the end of the file is not an error.
*       (although seeking before the beginning is.)
*
*Exceptions:
*
*******************************************************************************/

#ifdef _MT

__int64 __cdecl _lseeki64 (
        int fh,
        __int64 pos,
        int mthd
        )
{
        __int64 r;

        /* validate fh */

        if ( ((unsigned)fh >= (unsigned)_nhandle) ||
             !(_osfile(fh) & FOPEN) )
        {
                /* bad file handle */
                errno = EBADF;
                _doserrno = 0;          /* not OS error */
                return( -1i64 );
        }

        _lock_fh( fh );                 /* lock file handle */
        r = _lseeki64_lk( fh, pos, mthd ); /* seek */
        _unlock_fh( fh );               /* unlock file handle */

        return( r );
}


/***
*__int64 _lseeki64_lk( fh, pos, mthd ) - move the file pointer
*
*Purpose:
*       Non-locking version of _lseeki64 for internal use only.
*
*Entry:
*
*Exit:
*
*Exceptions:
*
*******************************************************************************/

__int64 __cdecl _lseeki64_lk (
        int fh,
        __int64 pos,
        int mthd
        )
{
        DINT newpos;                    /* new file position */
        unsigned long errcode;          /* error code from API call */
        HANDLE osHandle;        /* o.s. handle value */

#else  /* _MT */

__int64 __cdecl _lseeki64 (
        int fh,
        __int64 pos,
        int mthd
        )
{
        DINT newpos;                    /* new file position */
        unsigned long errcode;          /* error code from API call */
        HANDLE osHandle;        /* o.s. handle value */

        /* validate fh */

        if ( ((unsigned)fh >= (unsigned)_nhandle) ||
             !(_osfile(fh) & FOPEN) )
         {
                /* bad file handle */
                errno = EBADF;
                _doserrno = 0;          /* not OS error */
                return( -1i64 );
        }

#endif  /* _MT */

        newpos.bigint = pos;

        /* tell OS to seek */

#if SEEK_SET != FILE_BEGIN || SEEK_CUR != FILE_CURRENT || SEEK_END != FILE_END
    #error Xenix and Win32 seek constants not compatible
#endif  /* SEEK_SET != FILE_BEGIN || SEEK_CUR != FILE_CURRENT || SEEK_END != FILE_END */

        if ((osHandle = (HANDLE)_get_osfhandle(fh)) == (HANDLE)-1)
        {
            errno = EBADF;
                return( -1i64 );
        }

        if ( ((newpos.twoints.lowerhalf =
               SetFilePointer( osHandle,
                               newpos.twoints.lowerhalf,
                               &(newpos.twoints.upperhalf),
                               mthd )) == -1L) &&
             ((errcode = GetLastError()) != NO_ERROR) )
        {
                _dosmaperr( errcode );
                return( -1i64 );
        }

        _osfile(fh) &= ~FEOFLAG;        /* clear the ctrl-z flag on the file */
        return( newpos.bigint );        /* return */
}
