/********************************************************************
 *                                                                  *
 *  MODULE    :  WVFILE.C                                           *
 *                                                                  *
 *  PURPOSE   : This file contains interface functions that allow   *
 *              WinVN to communicate with the local file system.    *
 *                                                                  *
 *******************************************************************/

/*
 * $Id: wvfile.c 1.16 1997/02/06 17:26:06 dumoulin Exp $
 */

#include <windows.h>
#include <windowsx.h>
#include "wvglob.h"
#include "winvn.h"
#pragma hdrstop
#include <io.h>
#include <ctype.h>
#include <conio.h>
#include <stdio.h>
#include <fcntl.h>          /* _O_ constant definitions */
#include <sys\types.h>
//#include <sys\stat.h>       /* _S_ constant definitions */
#include <malloc.h>
#include <errno.h>

#ifndef INVALID_SET_FILE_POINTER
#define INVALID_SET_FILE_POINTER (DWORD)-1
#endif

/*--- function MRROpenFile --------------------------------------------
 *
 *  Perform the same function as Windows' OpenFile, but also
 *  create an instance of a structure that keeps track of file-related
 *  information (most importantly, a file buffer so I don't have to
 *  do 1-byte system reads).
 *
 *    Entry    FileName    is the file name of the file to open.
 *             Mode        is the mode under which to open the file.
 *
 *    Exit     MRRFile     points to a dynamically-allocated structure
 *                         containing info about the file.
 *             Returns a handle to the file; NULL if failure.
 */
int MRROpenFile (char *FileName, int Mode, TypMRRFile **MRRFile)
{

	HANDLE hMyFile;


	if (!FileName || !*FileName)
	{
		SetLastError(ERROR_FILE_NOT_FOUND);
		return 0;
	}
	
	
	TypMRRFile *MyMRRFile = (TypMRRFile *) GlobalAllocPtr (GMEM_FIXED, sizeof (TypMRRFile));
	


	if (MyMRRFile == NULL)
	{
		SetLastError(ERROR_OUTOFMEMORY);
		return 0;
	}

	MyMRRFile->bufidx = 0;
	MyMRRFile->bytesread = 0;
	MyMRRFile->eofflag = FALSE;
	MyMRRFile->mode = Mode;


	if (Mode == OF_WRITE && GetFileAttributes(FileName) == -1)
		Mode = OF_CREATE;
		
	
	hMyFile = CreateFile(FileName, GENERIC_READ | (Mode == OF_READ ? 0 : GENERIC_WRITE), FILE_SHARE_READ, NULL, 
					(Mode == OF_CREATE ? OPEN_ALWAYS : OPEN_EXISTING), 
					FILE_ATTRIBUTE_NORMAL, NULL);
	
	if (hMyFile == INVALID_HANDLE_VALUE )
	{
		GlobalFreePtr (MyMRRFile);
		return (0);
	}
	else 
	{
		MyMRRFile->hFile = hMyFile;
	}

	*MRRFile = MyMRRFile;

	return (hMyFile != INVALID_HANDLE_VALUE ? 1 : 0);

}

/*--- function MRRCloseFile --------------------------------------------
 *
 *  Perform the same function as the close function, but also
 *  deallocate the structure allocated by MRROpenFile.
 *
 *    Entry    MRRFile  points to a structure describing the file.
 */
DWORD MRRCloseFile (TypMRRFile *MRRFile)
{
	DWORD dwError = NO_ERROR;
	if (MRRFile->mode == OF_WRITE || MRRFile->mode == OF_CREATE) 
	{
		DWORD dwBytes;
		
		if (MRRFile->bufidx)
			if (!WriteFile (MRRFile->hFile, MRRFile->buf, MRRFile->bufidx, &dwBytes, NULL) ||
				dwBytes != MRRFile->bufidx)
			{
				dwError = GetLastError();	
			}

	}

	CloseHandle(MRRFile->hFile);

	GlobalFreePtr (MRRFile);

	return dwError;
}

/*--- function MRRReadLine ---------------------------------------------
 *
 *  Read in a line from a file, very much like "fgets".
 *  Lines are assumed to be terminated by CR/LF (except that this
 *  is optional for the last line in a file).
 *
 *  No CR, LF, or zero byte is placed in the user's buffer or
 *  counted as a data byte in the returned count.
 *
 *    Entry    MRRFile  points to a structure describing the file.
 *             Linebuf  is the place to put the line.
 *             Len      is the length of Linebuf.
 *
 *    Exit     Linebuf  contains a line.
 *             Returns number of characters read.  0 means an empty line;
 *              -1 means EOF.
 */
int MRRReadLine (TypMRRFile *MRRFile, char *Linebuf, int Len)
{
	int BytesReturned = 0;
	unsigned char ch;

	/* If we hit the EOF while reading last time, we might not have   */
	/* had to return an EOF indication then--but we certainly do now. */

	if (MRRFile->eofflag)
		return (-1);

	/* Read bytes until we exhaust the user's buffer,                 */
	/* empty our own internal buffer,                                 */
	/* or hit a CR (which hopefully belongs to a CR/LF pair).         */

readlp:;
	while (Len && MRRFile->bufidx < MRRFile->bytesread &&
	/*   (ch = MRRFile->buf[MRRFile->bufidx]) != '\r' && ch != '\n')  */
	//ishido         (ch = MRRFile->buf[MRRFile->bufidx]) >= ' ') 
			(!iscntrl( (ch = MRRFile->buf[MRRFile->bufidx]))) ) 
	{
		*(Linebuf++) = ch;
		BytesReturned++;
		(MRRFile->bufidx)++;
		Len--;
	}

	/* If we emptied our own internal buffer, fill 'er up again       */
	/* from the file.  If the read hits EOF, return the user's        */
	/* data now (indicating EOF if we never got any data bytes        */
	/* else go back up and continue taking from the buffer.           */

	if (MRRFile->bufidx >= MRRFile->bytesread) 
	{
		DWORD dwRead = 0;		
		MRRFile->bufidx = 0;

		if (ReadFile (MRRFile->hFile, MRRFile->buf, BUFSIZE, &dwRead, NULL))
			MRRFile->bytesread = dwRead;

		if (MRRFile->bytesread > 0) 
		{
			goto readlp;
		}
		else 
		{
			MRRFile->eofflag = TRUE;
			if (!BytesReturned)
				BytesReturned--;
			goto endit;
		}
	}

	/* If we reach here, we either filled the user's buffer or        */
	/* hit a CR.  No EOF was encountered.                             */
	/* Either way, we must now skip to the beginning of the next      */
	/* line.  This means skipping to the next LF.  Since in most      */
	/* cases the user does specify a big enough buffer, in most       */
	/* cases all we are doing here is reading up the next character   */
	/* (assuming it's a LF).                                          */
	/* All data that should go in the user's buffer is there by now.  */

skipLF:;

	while ( MRRFile->bufidx < MRRFile->bytesread && 
		    MRRFile->buf[MRRFile->bufidx] != '\n' ) 
	{
		(MRRFile->bufidx)++;
	}

	/* We either found the LineFeed we were looking for, or hit       */
	/* the end of our internal buffer.  If the latter, fill 'er       */
	/* up and try again.                                              */

	if (MRRFile->bufidx >= MRRFile->bytesread) 
	{
		DWORD dwRead;
		MRRFile->bufidx = 0;
		MRRFile->bytesread = 0;

		if (ReadFile (MRRFile->hFile, MRRFile->buf, BUFSIZE, &dwRead, NULL))
			MRRFile->bytesread = dwRead;


		if (MRRFile->bytesread > 0) 
		{
			goto skipLF;
		}
		else 
		{
			MRRFile->eofflag = TRUE;
			goto endit;
		}
	}

	/* The buffer pointer is now pointing at the LF.  Advance         */
	/* it by one so we'll get the first character of the next         */
	/* line next time.                                                */
	/* If this takes us past the end of the buffer, no problem.       */


	if (MRRFile->buf[MRRFile->bufidx] == '\n')
		(MRRFile->bufidx)++;

endit:;
	return (BytesReturned);
}


/*--- function MRRReadInternalBuff ---------------------------------------------
 *
 *  Fill the internal MRRFile buffer.  Adjust the bufidx and bytesread
 *   counters accordingly.  Append '\0' at EOF.
 *
 *    Entry    MRRFile  points to a structure describing the file.
 *             Linebuf  is the place to put the line.
 *             Len      is the length of Linebuf.
 *
 *    Exit     Linebuf  contains a line.
 *
 *    Returns:  0 if success;
 *             -1 if EOF.
 */
int MRRReadInternalBuff (TypMRRFile *MRRFile)
{
	int nToRead;
	int nRet;

	if(MRRFile->bufidx)
	{
		if (MRRFile->bytesread  <= MRRFile->bufidx)
		{
			MRRFile->bytesread = 0;	
		}
		else
		{
			MRRFile->bytesread -= MRRFile->bufidx;
			MoveBytes(	(void far*) (MRRFile->buf + MRRFile->bufidx),
						(void far*) MRRFile->buf,
						MRRFile->bytesread);
		}
		MRRFile->bufidx = 0;
	}

	if (MRRFile->bytesread < 0) //read past buffer means no data in buffer MDS
		*MRRFile->buf = 0;

	if(MRRFile->eofflag)
		return (-1);

	if(nToRead = sizeof(MRRFile->buf) - MRRFile->bytesread)
	{
		
		DWORD dwRead = 0;
		if (ReadFile (MRRFile->hFile, MRRFile->buf + MRRFile->bytesread, nToRead, &dwRead, NULL))
			nRet = dwRead;
		else
			nRet = HFILE_ERROR;

		if(nRet == HFILE_ERROR)
		{
			DEBUG_BREAK;
			MRRFile->eofflag = TRUE;
			return -1;
		}

		MRRFile->bytesread += nRet;
		if(nRet < nToRead)
		{
			MRRFile->buf[MRRFile->bytesread++] = '\0';
			MRRFile->eofflag = TRUE;
			return -1;
		}
	}
	return 0;
}

/*--- function MRRWriteLine ---------------------------------------------
 *
 *  Write out a line of text, followed by a CR and LF.
 *
 *    Entry    MRRFile  points to a structure describing the file.
 *             LineBuf  points to line buffer to write out.
 *             Len      is the number of bytes to write.
 */
DWORD MRRWriteLine (TypMRRFile *MRRFile, char far *LineBuf, unsigned int Len)
{
	unsigned int BytesToCopy;
	DWORD dwError = NO_ERROR;
	int needsNewline = strcmp (LineBuf, "\r\n");
	
	do 
	{
		BytesToCopy = Len < (BUFSIZE - (unsigned int) MRRFile->bufidx) ?
		Len : BUFSIZE - (unsigned int) MRRFile->bufidx;

		MoveBytes (LineBuf, (char far *) (MRRFile->buf + MRRFile->bufidx), BytesToCopy);
		MRRFile->bufidx += BytesToCopy;
		LineBuf += BytesToCopy;
		Len -= BytesToCopy;
		
		if (MRRFile->bufidx >= BUFSIZE) 
		{

			DWORD dwWrote;
			BOOL bOK = WriteFile(MRRFile->hFile, (LPBYTE) MRRFile->buf, BUFSIZE, &dwWrote, NULL);
			if (!bOK ||	dwWrote != BUFSIZE)
			{
				dwError = GetLastError();
				needsNewline = FALSE; //no more writing
				//write failed
				break;
			}
			MRRFile->bufidx = 0;
		}
	} while (Len > 0);

	if (needsNewline) 
	{
		dwError = MRRWriteLine (MRRFile, "\r\n", 2);
	}
	return (dwError);
}


/*--- function MRRWriteBuff ---------------------------------------------
 *
 *  Write a buffered line, NOT followed by a CR and LF.
 *
 *    Entry    MRRFile  points to a structure describing the file.
 *             LineBuf  points to line buffer to write out.
 *             Len      is the number of bytes to write.
 */
DWORD MRRWriteBuff(TypMRRFile *MRRFile, char far *LineBuf, unsigned int Len)
{
	unsigned int BytesToCopy;
	DWORD dwError = NO_ERROR;
	do
	{
		BytesToCopy = Len < (BUFSIZE - (unsigned int) MRRFile->bufidx) ?
					  Len : BUFSIZE - (unsigned int) MRRFile->bufidx;

		MoveBytes (LineBuf, (char far *) (MRRFile->buf + MRRFile->bufidx), BytesToCopy);
		MRRFile->bufidx += BytesToCopy;
		LineBuf += BytesToCopy;
		Len -= BytesToCopy;
		if (MRRFile->bufidx >= BUFSIZE)
		{

			DWORD dwWrote;
			BOOL bOK = WriteFile(MRRFile->hFile, MRRFile->buf, BUFSIZE, &dwWrote, NULL);
			if (!bOK ||	dwWrote != BUFSIZE)
			
			{
				dwError = GetLastError();
				break;
			}
			MRRFile->bufidx = 0;
		}
	} while (Len > 0);

	return (dwError);
}

/*-- function MRRWriteDocument -----------------------------------------
 *
 *  Write out an entire document to disk.
 *
 *  Entry   Document    points to a document.
 *          Offset      is the number of bytes to skip at the beginning
 *                      of the line (between the end of the structure
 *                      described in TypLine and the beginning of text).
 *                      In most cases this will be zero.
 *          szFileName  points to the file name to save to.
 *          Append      is TRUE iff we should append to the file.
 *
 *    Returns TRUE iff we wrote the file OK.
 */

DWORD MRRWriteDocument (TypDoc *Document, int Offset, char *szFileName, BOOL Append)
{
	TypMRRFile *MRRFile;
	long lHigh = 0;
	DWORD dwLow;
	DWORD dwError = NO_ERROR;

	TypBlock far *BlockPtr;
	TypLine far *LinePtr;
	int mode, len;
	char temp[MAXHEADERLINE], dateStr[MAXINTERNALLINE];

	if (!szFileName || !*szFileName)
		return ERROR_FILE_NOT_FOUND;

	HCURSOR hSaveCursor = SetCursor (hHourGlass);

	if (Append) 
		mode = OF_WRITE;
	else 
		mode = OF_CREATE;

	MRROpenFile (szFileName, mode, &MRRFile);
	dwError = GetLastError();

	if (MRRFile->hFile != INVALID_HANDLE_VALUE && 
		(!Append ||	
			( (dwLow = SetFilePointer (MRRFile->hFile, 0L, &lHigh, FILE_END)) != INVALID_SET_FILE_POINTER ||
			  GetLastError() == NO_ERROR ) ) )

	{

		/* first write a mailbox header (jsc) */
		dwError = MRRWriteLine (MRRFile, "\r\n", 2);
		if (dwError == NO_ERROR)
		{
			GetMailboxDate (dateStr, MAXINTERNALLINE - 1);
			len = _snprintf (temp, MAXHEADERLINE, "From %s %s",
				(*MailAddress) ? MailAddress : "Unknown", dateStr);

			dwError = MRRWriteLine (MRRFile, temp, len);
			if (dwError == NO_ERROR)
			{

				LockLine (Document->hFirstBlock, sizeof (TypBlock), (TypLineID) 0L, &BlockPtr, &LinePtr);
				
				while (dwError == NO_ERROR && LinePtr->length != END_OF_BLOCK) 
				{
					dwError = MRRWriteLine (MRRFile, ((char far *) LinePtr) + Offset + sizeof (TypLine),
						lstrlen (((char far *) LinePtr) + sizeof (TypLine) + Offset));
					NextLine (&BlockPtr, &LinePtr);
				}
				
				// make sure end with blank line
				if (dwError == NO_ERROR)
					dwError = MRRWriteLine (MRRFile, "\r\n", 2);

				GlobalUnlock (BlockPtr->hCurBlock);
			}
		}
		MRRCloseFile (MRRFile);
	}

	if (hSaveCursor)
		SetCursor (hSaveCursor);
	return (dwError);
}


/* Copies one file to another (both specified by path). Dynamically
 * allocates memory for the file buffer. Prompts before overwriting
 * existing file. Returns TRUE if successful, otherwise FALSE.
 */

#ifndef _WIN32
BOOL CopyFile(char *source, char *target, BOOL bFailIfExists)
{
    char *buf;
    int hsource, htarget;
    unsigned count = 0xff00;

    /* Open source file and create target, overwriting if necessary. */
    if( (hsource = _open( source, _O_BINARY | _O_RDONLY )) == - 1 )
        return FALSE;
    htarget = _open( target, _O_BINARY | _O_WRONLY | _O_CREAT | _O_EXCL,
                             _S_IREAD | _S_IWRITE );
    if(errno == EEXIST)
        if (bFailIfExists)
            return FALSE;
        else
            htarget = _open( target, _O_BINARY | _O_WRONLY | _O_CREAT |
                                     _O_TRUNC, _S_IREAD | _S_IWRITE );
    if( htarget == -1 )
        return FALSE;

    if( (unsigned)_filelength( hsource ) < count )
        count = (int)_filelength( hsource );

    /* Dynamically allocate a large file buffer. If there's not enough
     * memory for it, find the largest amount available on the near heap
     * and allocate that. This can't fail, no matter what the memory model.
     */
    if( (buf = (char *)malloc( (size_t)count )) == NULL )
    {

        count = _memmax();
        if( (buf = (char *)malloc( (size_t)count )) == NULL )
            return FALSE;
    }

    /* Read-write until there's nothing left. */
    while( !_eof( hsource ) )
    {
        /* Read and write input. */
        if( (count = _read( hsource, buf, count )) == -1 )
            return FALSE;
        if( (count = _write( htarget, buf, count )) == - 1 )
            return FALSE;
    }

    /* Close files and release memory. */
    _close( hsource );
    _close( htarget );
    free( buf );
    return TRUE;
}
#endif

/*
 * Local Variables:
 * tab-width: 2
 * end:
 */
