/* --- Copyright University of Sussex 1997. All rights reserved. ----------
 * File:			C.win32/extern/src/file.c
 * Purpose:			Miscellaneous file operations
 * Author:			Robert John Duncan, Jun 14 1994 (see revisions)
 * Documentation:
 * Related Files:
 */

#include "popcore.h"


/***************************************************************************
*                                                                          *
*	Opening and Closing Files                                              *
*                                                                          *
***************************************************************************/

HANDLE pop_get_console_handle(BOOL output)
	/*	Return a handle to the console (if available) either the input or
		the active screen buffer depending on the OUTPUT flag
	*/
{
	return CreateFile(
		output ? TEXT("CONOUT$") : TEXT("CONIN$"),
		GENERIC_READ|GENERIC_WRITE,
		FILE_SHARE_READ|FILE_SHARE_WRITE,
		NULL,
		OPEN_EXISTING,
		0, NULL);
}

HANDLE pop_get_std_handle(DWORD fd, BOOL create)
	/*	Return a handle for the standard input, output or error device
		identfied by their conventional file descriptors (0, 1, 2).
		If the required device has not been set up and the CREATE flag
		is TRUE, create a console and return a handle to that instead.
	*/
{
	HANDLE handle;
	switch (fd) {
		case 0:
			handle = GetStdHandle(STD_INPUT_HANDLE);
			break;
		case 1:
			handle = GetStdHandle(STD_OUTPUT_HANDLE);
			break;
		case 2:
			handle = GetStdHandle(STD_ERROR_HANDLE);
			break;
		default:
			handle = INVALID_HANDLE_VALUE;
			create = FALSE;
			break;
	}
	if (handle == NULL) {
		/* this is what you get in a GUI app */
		handle = INVALID_HANDLE_VALUE;
	}
	if (handle == INVALID_HANDLE_VALUE && create) {
		handle = pop_get_console_handle(fd != 0);
		if (handle == INVALID_HANDLE_VALUE) {
			AllocConsole();
			handle = pop_get_console_handle(fd != 0);
		}
	}
	return handle;
}

BOOL pop_set_std_handle(DWORD fd, HANDLE handle)
	/*	Set the standard input, output or error device to the given HANDLE
	*/
{
	switch (fd) {
		case 0:
			return SetStdHandle(STD_INPUT_HANDLE, handle);
		case 1:
			return SetStdHandle(STD_OUTPUT_HANDLE, handle);
		case 2:
			return SetStdHandle(STD_ERROR_HANDLE, handle);
		default:
			return FALSE;
	}
}

HANDLE pop_create_file(
		TCHAR *name,
		DWORD access,
		DWORD share,
		LPSECURITY_ATTRIBUTES security,
		DWORD create,
		DWORD attributes,
		HANDLE template)
	/*	Open/Create a named file
	*/
{
	SECURITY_ATTRIBUTES inheritable;
	if (security) {
		/* for now, just read this as "make it inheritable" */
		ZeroMemory(&inheritable, sizeof inheritable);
		inheritable.nLength = sizeof inheritable;
		inheritable.bInheritHandle = TRUE;
		security = &inheritable;
	}
	return CreateFile(name, access, share, security, create, attributes,
		template);
}

BOOL pop_create_pipe(HANDLE *ihandle, HANDLE *ohandle)
	/*	Create an anonymous pipe
	*/
{
	return CreatePipe(ihandle, ohandle, NULL, 0);
}


/***************************************************************************
*                                                                          *
*	Obtaining File Information                                             *
*                                                                          *
***************************************************************************/

DWORD pop_get_file_size(HANDLE handle, DWORD *size_hi)
	/*	Get the size of a file identified by the given HANDLE; returns
		the low word of the size and sets SIZE_HI to the high word
	*/
{
	return GetFileSize(handle, size_hi);
}

DWORD pop_get_file_attributes(TCHAR *path)
	/*	Get the file attributes of the given PATH
	*/
{
	return GetFileAttributes(path);
}

DWORD pop_get_file_type(HANDLE handle)
	/*	Get the file type of the given HANDLE
	*/
{
	switch (GetFileType(handle)) {
		case FILE_TYPE_DISK:
			return POP_FILE_TYPE_DISK;
		case FILE_TYPE_CHAR: {
			DWORD mode;
			/* distinguish the console by trying to get the console mode */
			return GetConsoleMode(handle, &mode) ?
				POP_FILE_TYPE_CONSOLE : POP_FILE_TYPE_CHAR;
		}
		case FILE_TYPE_PIPE:
			return POP_FILE_TYPE_PIPE;
		case FILE_TYPE_UNKNOWN:
		default:
			return POP_FILE_TYPE_OTHER;
	}
}

DWORD pop_get_file_time(HANDLE handle)
	/*	Get the last modified time of a file in Poplog time
	*/
{
	DWORD time = 0;
	FILETIME last_write;
	if (GetFileTime(handle, NULL, NULL, &last_write)) {
		DWORD usecs = pop_time_from_file_time(&time, &last_write);
		if (usecs >= 500000) time++;
	}
	return time;
}

BOOL pop_get_file_id(
		HANDLE handle,
		DWORD *volume,
		DWORD *index_hi,
		DWORD *index_lo)
	/*	Get the unique 3-word file identfier (volume number + file index)
		for a given HANDLE
	*/
{
	BY_HANDLE_FILE_INFORMATION bhfi;

	if (!GetFileInformationByHandle(handle, &bhfi))
		return FALSE;
	*volume = bhfi.dwVolumeSerialNumber;
	*index_hi = bhfi.nFileIndexHigh;
	*index_lo = bhfi.nFileIndexLow;

	return TRUE;
}


/***************************************************************************
*                                                                          *
*	File and Directory Names                                               *
*                                                                          *
***************************************************************************/

DWORD pop_get_full_pathname(TCHAR *file, TCHAR *buffer, DWORD len)
	/*	Copies the full pathname of the given FILE to a BUFFER of length
		LEN; returns the number of characters in the path or zero for
		error
	*/
{
	TCHAR *ptr;
	return GetFullPathName(file, len, buffer, &ptr);
}

DWORD pop_get_current_directory(TCHAR *buffer, DWORD len)
	/*	Copies the full pathname of the current directory to a BUFFER
		of length LEN; returns the number of characters in the path
		or zero for error
	*/
{
	return GetCurrentDirectory(len, buffer);
}

BOOL pop_set_current_directory(TCHAR *path)
	/*	Sets the current directory to PATH
	*/
{
	return SetCurrentDirectory(path);
}

DWORD pop_start_file_search
	(TCHAR *pattern, TCHAR *buffer, DWORD len, DWORD *attrs, HANDLE *handle)
	/*	Copies to a BUFFER of length LEN the first filename which matches
		PATTERN; returns the number of characters in the name, including the
		terminating null, or 0 for error. File attributes are copied to the
		argument ATTRS while HANDLE is filled in with a handle which can be
		used in subsequent calls to pop_file_search.
	*/
{
	DWORD nchars;
	WIN32_FIND_DATA found;

	*handle = FindFirstFile(pattern, &found);
	if (*handle == INVALID_HANDLE_VALUE)
		nchars = 0;
	else
	{
		*attrs = found.dwFileAttributes;
		nchars = lstrlen(found.cFileName) + 1;
		CopyMemory(buffer, found.cFileName,
			(len < nchars ? len : nchars)*sizeof(TCHAR));
	}

	return nchars;
}

DWORD pop_file_search(HANDLE handle, TCHAR *buffer, DWORD len, DWORD *attrs)
	/*	Continue a file search started by pop_start_file_search
	*/
{
	DWORD nchars;
	WIN32_FIND_DATA found;

	if (FindNextFile(handle, &found))
	{
		*attrs = found.dwFileAttributes;
		nchars = lstrlen(found.cFileName) + 1;
		CopyMemory(buffer, found.cFileName,
			(len < nchars ? len : nchars)*sizeof(TCHAR));
	}
	else
		nchars = 0;

	return nchars;
}

BOOL pop_end_file_search(HANDLE handle)
	/*	Terminate a file search
	*/
{
	return FindClose(handle);
}


/***************************************************************************
*                                                                          *
*	Move, Copy, Delete, etc.                                               *
*                                                                          *
***************************************************************************/

BOOL pop_move_file(TCHAR *from_name, TCHAR *to_name, BOOL replace_existing) {
	if (!replace_existing)
		return MoveFile(from_name, to_name);
	else if (pop_os_version_info.dwPlatformId == VER_PLATFORM_WIN32_NT)
		return MoveFileEx(from_name, to_name, MOVEFILE_REPLACE_EXISTING|
											  MOVEFILE_COPY_ALLOWED);
	else
		return DeleteFile(to_name) && MoveFile(from_name, to_name);
}

BOOL pop_copy_file(TCHAR *from_name, TCHAR *to_name, BOOL replace_existing) {
	return CopyFile(from_name, to_name, !replace_existing);
}

BOOL pop_delete_file(TCHAR *name) {
	return DeleteFile(name);
}


/* --- Revision History ---------------------------------------------------
--- Robert Duncan, Jun 10 1997
		Modified pop_create_file to allow file handles to be inherited
--- Robert Duncan, Jan 24 1997
		Corrections for UNICODE compilation
--- Robert Duncan, Jun  4 1996
		Changed pop_move_file because MoveFileEx is not supported on
		Windows 95
--- Robert Duncan, May 24 1996
		pop_c*lose_handle moved to "handle.c"
--- Robert Duncan, Apr 19 1996
		Standard I/O handles now identified to Poplog as conventional 0,1,2.
		pop_get_std_handle changed to create a console window if requested.
		Added pop_get_console_handle.
--- Robert John Duncan, Jan  8 1996
		Changed pop_get_file_type to recognise console handles -- making
		pop_is_c*onsole redundant -- and file search functions to return all
		attributes.
 */
