//====START_GENERATED_PROLOG======================================
//
//
//   COMPONENT_NAME: odutils
//
//   CLASSES: none
//
//   ORIGINS: 82,27
//
//
//   (C) COPYRIGHT International Business Machines Corp. 1995,1996
//   All Rights Reserved
//   Licensed Materials - Property of IBM
//   US Government Users Restricted Rights - Use, duplication or
//   disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
//   	
//   IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
//   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
//   PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR
//   CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
//   USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
//   OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
//   OR PERFORMANCE OF THIS SOFTWARE.
//
//====END_GENERATED_PROLOG========================================
//
// @(#) 1.23 nt/src/utils/PlfmFile.cpp, odutils, od96nt, odnt9712d 1/10/97 12:07:30 [ 3/21/97 16:52:35 ]
/*
	File:		PlfmFile.cpp

	Contains:	Implmentation for PlatformFile class

	Copyright:	© 1993-95 by Apple Computer, Inc., all rights reserved.

*/

#include <stdio.h>

#ifndef _ODTYPES_
#include "ODTypes.h"
#endif

#ifndef _PLFMFILE_
#include "PlfmFile.h"
#endif

#ifndef _ODMEMORY_
#include "ODMemory.h"
#endif

#ifndef _EXCEPT_
#include "Except.h"
#endif

#ifndef _PASCLSTR_
#include "PasclStr.h"
#endif

#ifndef _ITEXT_
#include "IText.h"
#endif

#include <time.h>

#ifndef __ODTYPESP__
#include <ODTypesP.h>
#endif

#ifndef __ODPAGTUN__
#include <ODPagTun.h>
#endif

#ifndef __STRING__
#include <string.h>
#endif

#ifndef __ERRORS__
#include <Errors.h>
#endif

#ifndef _UTILERRS_
#include <UtilErrs.h>
#endif

//==============================================================================
// Constants
//==============================================================================

//==============================================================================
// Scalar Types
//==============================================================================

//==============================================================================
// Local Classes
//==============================================================================

//==============================================================================
// Global Variables
//==============================================================================

//==============================================================================
// Function Prototype
//==============================================================================

//==============================================================================
// PlatformFile
//==============================================================================

const ODOSType kBogusOSType = 0;	// Default type/creator value until it's read

//------------------------------------------------------------------------------
// PlatformFile::IsEqualTo
//------------------------------------------------------------------------------
ODBoolean PlatformFile::IsEqualTo(PlatformFile* file)
{
	DWORD nameLen, errorCode;
	char *fullPathThis, *fullPathThat, *fnPart;
	int notFoundCount = 0;
	ODBoolean match, noMem = kODFalse;

	if (!Exists())
		notFoundCount = 1;
	
	if (!file->Exists())
		notFoundCount++;

	if (notFoundCount == 2)
		THROW(ERROR_FILE_NOT_FOUND);
		
	if (notFoundCount == 1)
		return(kODFalse);

	fullPathThis = (char *)malloc(kODMaxFileNameSize + 1);
	fullPathThat = (char *)malloc(kODMaxFileNameSize + 1);
	if (fullPathThis != NULL || fullPathThat != NULL)
	{
		nameLen = GetFullPathName((char *)fFileSpec.name, kODMaxFileNameSize + 1,
		                          fullPathThis, &fnPart);
		if (nameLen != 0)
			nameLen = GetFullPathName((char *)file->fFileSpec.name, kODMaxFileNameSize + 1,
			                          fullPathThat, &fnPart);
		if (nameLen != 0)
		{
			strlwr(fullPathThis);
			strlwr(fullPathThat);
			match = !strcmp(fullPathThis, fullPathThat);
		}
		else
			errorCode = GetLastError();
	}
	else
		noMem = kODTrue;

	if (fullPathThis)
		free(fullPathThis);
	if (fullPathThat)
		free(fullPathThat);

	if (noMem)
		THROW(kODErrOutOfMemory);

	if (nameLen == 0)
		THROW(errorCode);

	return(match);
}

//------------------------------------------------------------------------------
// PlatformFile::IsDirectory
//------------------------------------------------------------------------------
ODBoolean PlatformFile::IsDirectory()
{	
	DWORD attrs;

	attrs = ::GetFileAttributes((char *)fFileSpec.name);
	if (attrs == -1)
		THROW(GetLastError());

	return (attrs & FILE_ATTRIBUTE_DIRECTORY);
}

//------------------------------------------------------------------------------
// PlatformFile::Create
//------------------------------------------------------------------------------
void PlatformFile::Create(ODOSType creator, ODOSType fileType, ODScriptCode scriptCode)
{	
	
	DWORD access;
	DWORD shareMode = 0;
	access = GENERIC_READ;
	if (fPermission & kODFileWrite)
		access = GENERIC_READ | GENERIC_WRITE;

	if (fPermission & kODFileShareRead)
		shareMode = FILE_SHARE_READ;

	if (fPermission & kODFileShareWrite)
		shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
		
	fDataRefNum = CreateFile((char *)fFileSpec.name, access, shareMode, NULL,
	                         CREATE_NEW, 0, NULL);
	if (fDataRefNum == kODNoFileRefNum)
		THROW(GetLastError());

	fCreator = creator;
	fFileType = fileType;
	fScriptTag = scriptCode;

	Close();
}

//------------------------------------------------------------------------------
// PlatformFile::Exists
//------------------------------------------------------------------------------
ODBoolean PlatformFile::Exists()
{
	DWORD attrs;

	attrs = ::GetFileAttributes((char *)fFileSpec.name);
	if (attrs == -1)
	{
		DWORD error = GetLastError();
		if (error == ERROR_FILE_NOT_FOUND || error == ERROR_PATH_NOT_FOUND)
			attrs = 0;
		else
			THROW(error);
	}
	return (attrs &
	        (FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_DIRECTORY));
}

//------------------------------------------------------------------------------
// PlatformFile::Open
//------------------------------------------------------------------------------
void PlatformFile::Open()
{	
	if (fDataRefNum == kODNoFileRefNum)
	{
		DWORD access;
		DWORD shareMode = 0;
	
		access = GENERIC_READ;
		if (fPermission & kODFileWrite)
			access = GENERIC_READ | GENERIC_WRITE;
	
		if (fPermission & kODFileShareRead)
			shareMode = FILE_SHARE_READ;

		if (fPermission & kODFileShareWrite)
			shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
			
		fDataRefNum = CreateFile((char *)fFileSpec.name, access, shareMode, NULL,
		                         OPEN_EXISTING, 0, NULL);
		if (fDataRefNum == kODNoFileRefNum)
			THROW(GetLastError());

		fOpenPermission = fPermission;
	}
	else
		THROW(kODErrFileOpened);
}

//------------------------------------------------------------------------------
// PlatformFile::Close
//------------------------------------------------------------------------------
void PlatformFile::Close()
{	
	if (fDataRefNum != kODNoFileRefNum) {
		if (!CloseHandle(fDataRefNum))
			THROW(GetLastError());
		fDataRefNum = kODNoFileRefNum;
	}
	else
		THROW(kODErrFileClosed);
}

//------------------------------------------------------------------------------
// PlatformFile::Delete
//------------------------------------------------------------------------------
void PlatformFile::Delete()
{	
	if (!IsDirectory())
	{
		if (!DeleteFile((char *)fFileSpec.name))
			THROW(GetLastError());
	}
	else
	{
		if (!RemoveDirectory((char *)fFileSpec.name))
			THROW(GetLastError());
	}
}


//------------------------------------------------------------------------------
// PlatformFile::FlushVolume
//------------------------------------------------------------------------------
void PlatformFile::FlushVolume()
{	
	if (fDataRefNum != kODNoFileRefNum)
	{
		if (!FlushFileBuffers(fDataRefNum))
			THROW (GetLastError());
	}
}

//------------------------------------------------------------------------------
// PlatformFile::SetFilePos
//------------------------------------------------------------------------------
void PlatformFile::SetFilePos(ODSShort posMode, ODSLong posOff)
{	
	if (SetFilePointer(fDataRefNum, posOff, NULL, posMode) == -1)
	{
		// It's theortically possible for a return of -1 (0xFFFFFFFF) to be
		// valid
		DWORD rc = GetLastError();
		if (rc != 0)
			THROW(rc);
	}
}

//------------------------------------------------------------------------------
// PlatformFile::GetFilePos
//------------------------------------------------------------------------------
ODSLong PlatformFile::GetFilePos()
{
	ODSLong	posOff;
	
	// Obtain the current file pointer by making a zero-byte change
	// to the current pointer, and use the "previous" offset which
	// is  returned by SetFilePointer
	posOff = SetFilePointer(fDataRefNum, 0, NULL, FILE_CURRENT);
	if (posOff == -1)
	{
		// It's theortically possible for a return of -1 (0xFFFFFFFF) to be
		// valid
		DWORD rc = GetLastError();
		if (rc != 0)
			THROW(rc);
	}
	return posOff;
}

//------------------------------------------------------------------------------
// PlatformFile::Read
//------------------------------------------------------------------------------
void PlatformFile::Read(void* buffer, ODSLong* count)
{	
	DWORD bytesRead;
	ODSLong countRequested = *count;
	BOOL successful;
	successful = ReadFile(fDataRefNum, buffer, *count, &bytesRead, NULL);
	*count = bytesRead;
	if (!successful)
		THROW (GetLastError());
	else
		if (countRequested != bytesRead)
			THROW(kODErrEndOfFile);
}

//------------------------------------------------------------------------------
// PlatformFile::Write
//------------------------------------------------------------------------------
void PlatformFile::Write(const void* buffer, ODSLong* count)
{	
	DWORD bytesWritten;
	ODSLong countRequested = *count;
	BOOL successful;
	successful = WriteFile(fDataRefNum, buffer, *count, &bytesWritten, NULL);
	*count = bytesWritten;
	if (!successful)
		THROW (GetLastError());
}

//------------------------------------------------------------------------------
// PlatformFile::GetEndOfFile
//------------------------------------------------------------------------------
ODSLong PlatformFile::GetEndOfFile()
{	
	ODSLong	length;
	
	DWORD rc;
	length = GetFileSize(fDataRefNum, NULL);
	rc = GetLastError();
	if (rc != NO_ERROR)
		THROW(rc);
	return length;
}

//------------------------------------------------------------------------------
// PlatformFile::SetEndOfFile
//------------------------------------------------------------------------------
void PlatformFile::SetEndOfFile(ODSLong length)
{	
	DWORD prevOffset;
	DWORD rc = NO_ERROR;
	// Set file pointer to end of file and save previous offset
	prevOffset = SetFilePointer(fDataRefNum, length, NULL, FILE_BEGIN);
	if (prevOffset == -1)
	{
		// It's theortically possible for a return of -1 (0xFFFFFFFF) to be
		// valid
		DWORD rc = GetLastError();
		if (rc != 0)
			THROW(rc);
	}

	// Set end of file file pointer offset
	if (!::SetEndOfFile(fDataRefNum))
		rc = GetLastError();

	// Reset the file pointer to saved value.  If error ocurred and
	// no error had occurred up to this point, get the return code 
	// for the THROW.
	if (prevOffset < length)
	{
		if (SetFilePointer(fDataRefNum, prevOffset, NULL, FILE_BEGIN) == -1 &&
			rc == NO_ERROR)
			rc = GetLastError();
	}

	// If error occurred, throw exception
	if (rc != NO_ERROR)
		THROW(rc);
}

//------------------------------------------------------------------------------
// PlatformFile::MoveRename
//------------------------------------------------------------------------------
void PlatformFile::MoveRename( ODFileSpec* newSpec, ODBoolean isDuplicate )
{
	DWORD error;

	if (isDuplicate && !DeleteFile((char *)newSpec->name))
	{
		error = GetLastError();
		if (error != ERROR_FILE_NOT_FOUND)
			THROW(error);
	}

	if (!MoveFile((char *)fFileSpec.name, (char *)newSpec->name))
		THROW(GetLastError());

	fFileSpec = *newSpec;
}	// MoveRename()

//------------------------------------------------------------------------------
// PlatformFile::Rename
//		This routine will not yet delete a file that will prevent the
//			renaming (as Move() does above.)
//------------------------------------------------------------------------------
void PlatformFile::Rename( ODFilePathname newName )
{
	if (!MoveFile((char *)fFileSpec.name, (char *)newName))
	{
		THROW(GetLastError());
	}
	strcpy ((char *)fFileSpec.name, (char *)newName);
}

//------------------------------------------------------------------------------
// PlatformFile::GetFileModDate
//		This routine will set the file modification date to the input parameter
//------------------------------------------------------------------------------
ODTime PlatformFile::GetFileModDate()
{
	DWORD error = 0;
	ODTime t;
	ODFilePermission savedPerm;

	FILETIME timeLastWrite;
	BOOL successful, closeFile = FALSE;

	// Note:  This function can be, and is, called when the file is
	//        not open.
	try
	{
		if (fDataRefNum == kODNoFileRefNum)
		{
			ODFilePermission savedPerm = fPermission;
			SetPermission(kODFileRead);
			Open();
			SetPermission(savedPerm);
			closeFile = TRUE;
		}
	}
	catch (...)
	{
		SetPermission(savedPerm);
		throw;
	}

	successful = GetFileTime(fDataRefNum, NULL, NULL, &timeLastWrite);

	if (!successful)
		error = GetLastError();

	try
	{
		if (closeFile)
			Close();
	}
	catch(...)
	{
		// Only rethrow the error if we haven't already experienced an error
		if (!error)
			throw;
	}

	if (!error)
		error = ConvertFromFileTime(&timeLastWrite, &t);

	if (error)
		THROW(error);

	return t;
}


//------------------------------------------------------------------------------
// PlatformFile::SetFileModDate
//		This routine will set the file modification date to the input parameter
//------------------------------------------------------------------------------
void PlatformFile::SetFileModDate(ODTime date)
{
	DWORD error;
	ODFilePermission savedPerm;
	FILETIME timeLastWrite;
	BOOL successful, closeFile = FALSE, reopenFile = FALSE;

	// Note:  This function can be, and is, called when the file is
	//        not open.  Code exists to also handle files open R/O.
	if (fDataRefNum != kODNoFileRefNum && !(fOpenPermission & kODFileWrite))
	{
		Close();
		reopenFile = TRUE;
	}

	try
	{
		if (fDataRefNum == kODNoFileRefNum)
		{
			savedPerm = fPermission;
			SetPermission(kODFileRead | kODFileWrite);
			Open();
			SetPermission(savedPerm);
			closeFile = TRUE;
		}
	}
	catch (...)
	{
		SetPermission(savedPerm);
		throw;
	}


	error = ConvertToFileTime(date, &timeLastWrite);

	if (!error)
	{
		successful = SetFileTime(fDataRefNum, NULL, NULL, &timeLastWrite);

		if (!successful)
			error = GetLastError();
	}

	try
	{
		if (closeFile)
			Close();
		if (reopenFile)
			Open();
	}
	catch(...)
	{
		// Only rethrow the error if we haven't already experienced an error
		if (!error)
			throw;
	}

	if (error)
		THROW(error);
}

DWORD PlatformFile::ConvertFromFileTime(LPFILETIME fileTime, ODTime *odTime)
{
	DWORD error = 0;
	BOOL successful;
	SYSTEMTIME sysTime;
	struct tm tmCLib;

	successful = FileTimeToSystemTime(fileTime, &sysTime);
	if (successful)
	{
		tmCLib.tm_year = sysTime.wYear - 1900;
		tmCLib.tm_mon = sysTime.wMonth - 1;
		tmCLib.tm_mday = sysTime.wDay;
		tmCLib.tm_hour = sysTime.wHour;
		tmCLib.tm_min = sysTime.wMinute;
		tmCLib.tm_sec = sysTime.wSecond;
		tmCLib.tm_isdst = -1;

		*odTime = (ODTime) mktime(&tmCLib);
	}
	else
		error = GetLastError();

	return error;
}

DWORD PlatformFile::ConvertToFileTime(ODTime odTime, LPFILETIME fileTime)
{
	DWORD error = 0;
	BOOL successful;
	SYSTEMTIME sysTime;
	struct tm *tmCLib;

	tmCLib = localtime((time_t *)&odTime);

	if (tmCLib)
	{
		sysTime.wYear = tmCLib->tm_year + 1900;
		sysTime.wMonth = tmCLib->tm_mon + 1;
		sysTime.wDay = tmCLib->tm_mday;
		sysTime.wHour = tmCLib->tm_hour;
		sysTime.wMinute = tmCLib->tm_min;
		sysTime.wSecond = tmCLib->tm_sec;
		sysTime.wMilliseconds = 0;        // Garbage >999 will cause failure

		successful = SystemTimeToFileTime(&sysTime, fileTime);
		if (!successful)
			error = GetLastError();
	}
	else
		error = kODErrValueOutOfRange;

	return error;
}

void PlatformFile::GetFileAttributes(ODFileAttrFlags *fAttr )
{
	DWORD attrs;

	attrs = ::GetFileAttributes((char *)fFileSpec.name);

	if (attrs == -1)
		THROW(GetLastError());
		
	*fAttr = 0;

	if (attrs & FILE_ATTRIBUTE_READONLY)
		*fAttr |= kODFileAttrReadOnly;
	else
	{
		ODFilePermission  savedPermission = fPermission;
		fPermission = kODFileRead | kODFileWrite;

		try
		{
			// It's possible that the filesystem is R/O (e.g. a filesystem
			// mounted R/O, or a diskette with write-protect tab set).  The
			// file itself may show up as having RW permissions, but a write
			// operation will not be allowed.  Please see defect 24848 for
			// more info.  It shouldn't be necessary, but the base OD code
			// can't handle Save operations that fail--you don't get a 2nd
			// chance and you'll lose your changes.  We detect the situation
			// a priori to avoid failed Save's.
			Open();
			ODSLong eof = GetEndOfFile();
			try
			{
				SetEndOfFile(eof);
			}
			catch (ODException &xcpt)
			{
				if (xcpt.error == ERROR_ACCESS_DENIED ||
				    xcpt.error == ERROR_WRITE_PROTECT)
					*fAttr |= kODFileAttrReadOnly;
				else
				{
					Close();
					throw;
				}
			}
			Close();
			fPermission = savedPermission;
		}
		catch (ODException &xcpt)
		{
			fPermission = savedPermission;
			throw;
		}
	}
}
