// Copyright (C) 1999-2005 Open Source Telecom Corporation.
//  
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software 
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
// 
// As a special exception, you may use this file as part of a free software
// library without restriction.  Specifically, if other files instantiate
// templates or use macros or inline functions from this file, or you compile
// this file and link it with other files to produce an executable, this
// file does not by itself cause the resulting executable to be covered by
// the GNU General Public License.  This exception does not however    
// invalidate any other reasons why the executable file might be covered by
// the GNU General Public License.    
//
// This exception applies only to the code released under the name GNU
// Common C++.  If you copy code from other releases into a copy of GNU
// Common C++, as the General Public License permits, the exception does
// not apply to the code that you add in this way.  To avoid misleading
// anyone as to the status of such modified files, you must delete
// this exception notice from them.
//
// If you write modifications of your own for GNU Common C++, it is your choice
// whether to permit this exception to apply to your modifications.
// If you do not wish that, delete this exception notice.
//

// needed for GNU/LINUX glibc otherwise pread/pwrite wont work

#ifdef	__linux__
#ifndef	_XOPEN_SOURCE
#define	_XOPEN_SOURCE 500
#endif
/*
 * on old glibc's, this has to be
 * defined explicitly
 */
#ifndef _XOPEN_SOURCE_EXTENDED
#define _XOPEN_SOURCE_EXTENDED
#endif
#endif

#include <cc++/config.h>
#include <cc++/export.h>
#include <cc++/exception.h>
#include <cc++/thread.h>
#include <cc++/file.h>
#include <cc++/process.h>
#include "private.h"

#ifdef	__BORLANDC__
#include <stdio.h>
#else
#include <cstdio>
#endif
#include <sys/stat.h>
#include <cerrno>

#ifndef WIN32

#ifdef	HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif

#ifdef	HAVE_SYS_FILE_H
#include <sys/file.h>
#endif

#ifdef  HAVE_SYS_LOCKF_H
#include <sys/lockf.h>
#endif

#ifdef	COMMON_AIX_FIXES
#undef	LOCK_EX
#undef	LOCK_SH
#endif

#ifdef  MACOSX
#define MISSING_LOCKF   
#endif

#ifndef	F_LOCK
#define	MISSING_LOCKF

enum
{
	F_ULOCK = 1,
	F_LOCK,
	F_TLOCK,
	F_TEST
};

#endif

#endif // ndef WIN32
		
#if defined(_OSF_SOURCE) && defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE > 1
#undef	LOCK_EX
#undef	LOCK_SH
#endif

#if 0
/*
 * not used anymore ? (hen)
 */
static const char *clearfile(const char *pathname)
{
	remove(pathname);
	return pathname;
}

static const char *clearfifo(const char *pathname, int mode)
{
	remove(pathname);
	mkfifo(pathname, mode);
	return pathname;
}
#endif

#ifdef	CCXX_NAMESPACES
namespace ost {
#endif

RandomFile::RandomFile(const char *name) : Mutex(name) {
#ifdef WIN32
	fd = INVALID_HANDLE_VALUE;
	// immediate is not defined on Win32 
#else
	fd = -1;
	flags.immediate = false;
#endif
	flags.thrown = flags.initial = flags.temp = false;
	flags.count = 0;
	pathname = NULL;
}

RandomFile::RandomFile(const RandomFile &rf) : Mutex() {
	// first, `dup'-licate the file descriptor/handle
#ifdef WIN32
	HANDLE pidHandle = GetCurrentProcess();
	HANDLE dupHandle;

	if(rf.fd != INVALID_HANDLE_VALUE)
	{
		if(!DuplicateHandle(pidHandle, rf.fd, pidHandle, &dupHandle, 0, FALSE, DUPLICATE_SAME_ACCESS))
			fd = INVALID_HANDLE_VALUE;
		else
			fd = dupHandle;
	}
	else
		fd = INVALID_HANDLE_VALUE;

#else
	if(rf.fd > -1)
		fd = dup(rf.fd);
	else
		fd = -1;

#endif	

	flags = rf.flags;
	flags.count = 0;

	if(rf.pathname)
		pathname = newString(rf.pathname);		
	else
		pathname = NULL;
}

RandomFile::~RandomFile()
{
	final();
}

File::Error RandomFile::restart(void)
{
	return errOpenFailed;
}

File::Attr RandomFile::initialize(void)
{
	return attrPublic;
}

void RandomFile::final(void)
{
#ifdef WIN32
	if(fd != INVALID_HANDLE_VALUE)
	{
		CloseHandle(fd);
		if(flags.temp && pathname)
			DeleteFile(pathname);
	}

#else
	if(fd > -1)
	{
		close(fd);
		if(flags.temp && pathname)
			remove(pathname);
	}
#endif

	if(pathname)
	{
		delString(pathname);
		pathname = NULL;
	}

#ifdef WIN32
	fd = INVALID_HANDLE_VALUE;
#else
	fd = -1;
#endif
	flags.count = 0;
	flags.initial = false;
}

RandomFile::Error RandomFile::error(Error id, char *str)
{
	errstr = str;
	errid = id;
	if(!flags.thrown)
	{
		flags.thrown = true;
#ifdef	CCXX_EXCEPTIONS
		if(Thread::getException() == Thread::throwObject)
			throw(this);
#ifdef	COMMON_STD_EXCEPTION
		else if(Thread::getException() == Thread::throwException)
		{
			if(!str)
				str = "";
			throw FileException(str);
		}
#endif
#endif
	}
	return id;
}

bool RandomFile::initial(void)
{
	bool init;

#ifdef WIN32
	if(fd == INVALID_HANDLE_VALUE)
#else
	if(fd < 0)
#endif		
		return false;

	enterMutex();
	init = flags.initial;
	flags.initial = false;

	if(!init)
	{
		leaveMutex();
		return false;
	}

#ifdef WIN32
	Attr access = initialize();
	if(access == attrInvalid)
	{
		CloseHandle(fd);
		if(pathname)
			DeleteFile(pathname);
		fd = INVALID_HANDLE_VALUE;
		leaveMutex();
		error(errInitFailed);
		return false;
	}

#else
	int mode = (int)initialize();
	if(!mode)
	{
		close(fd);
		fd = -1;
		if(pathname)
			remove(pathname);
		leaveMutex();
		error(errInitFailed);
		return false;
	}
	fchmod(fd, mode);
#endif
	
	leaveMutex();
	return init;
}

#ifndef WIN32
RandomFile::Error RandomFile::setCompletion(Complete mode)
{
	long flag = fcntl(fd, F_GETFL);

	if(fd < 0)
		return errNotOpened;

	flags.immediate = false;
#ifdef O_SYNC
	flag &= ~(O_SYNC | O_NDELAY);
#else
	flag &= ~O_NDELAY;
#endif
	switch(mode)
	{
	case completionImmediate:
#ifdef O_SYNC
		flag |= O_SYNC;
#endif
		flags.immediate = true;
		break;

	case completionDelayed:
		flag |= O_NDELAY;

	//completionDeferred: ? (hen)
	case completionDeferred:
		break;
	}
	fcntl(fd, F_SETFL, flag);
	return errSuccess;
}
#endif

off_t RandomFile::getCapacity(void)
{
	off_t eof, pos = 0;
#ifdef WIN32
	if(!fd)
#else
	if(fd < 0)
#endif
		return 0;

	enterMutex();
#ifdef WIN32
	pos = SetFilePointer(fd, 0l, NULL, FILE_CURRENT);
	eof = SetFilePointer(fd, 0l, NULL, FILE_END);
	SetFilePointer(fd, pos, NULL, FILE_BEGIN);
#else
	lseek(fd, pos, SEEK_SET);
	pos = lseek(fd, 0l, SEEK_CUR);
	eof = lseek(fd, 0l, SEEK_END);
#endif
	leaveMutex();
	return eof;
}

bool RandomFile::operator!(void)
{
#ifdef WIN32
	return fd == INVALID_HANDLE_VALUE;
#else
	if(fd < 0)
		return true;

	return false;
#endif
}

ThreadFile::ThreadFile(const char *path) : RandomFile(path)
{
	first = NULL;
	open(path);
}

ThreadFile::~ThreadFile()
{
	final();
	fcb_t *next;
	while(first)
	{
		next = first->next;
		delete first;
		first = next;
	}
}

ThreadFile::Error ThreadFile::restart(void)
{
	return open(pathname);
}

ThreadFile::Error ThreadFile::open(const char *path)
{
#ifdef WIN32
	if(fd != INVALID_HANDLE_VALUE)
#else
	if(fd > -1)
#endif
		final();


	if(path != pathname)
	{
		if(pathname)
			delString(pathname);
		pathname = newString(path);
	}

	flags.initial = false;

#ifdef WIN32
	fd = CreateFile(pathname, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, NULL);
	if(fd == INVALID_HANDLE_VALUE)
	{
		flags.initial = true;
		fd = CreateFile(pathname, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, NULL);
	}
	if(fd == INVALID_HANDLE_VALUE)
		return errOpenFailed;

#else
	fd = ::open(pathname, O_RDWR);
	if(fd < 0)
	{
		flags.initial = true;
		fd = ::open(pathname, O_CREAT | O_RDWR | O_TRUNC,
			    (int)attrPrivate);
	}
	if(fd < 0)
		return error(errOpenFailed);

#ifdef	LOCK_EX
	if(flock(fd, LOCK_EX | LOCK_NB))
	{
		close(fd);
		fd = -1;
		return error(errOpenInUse);
	}
#endif
#endif // WIN32

	return errSuccess;
}

ThreadFile::fcb_t *ThreadFile::getFCB(void)
{
	fcb_t *fcb = (fcb_t *)state.getKey();

	if(!fcb)
	{
		fcb = new fcb_t;
		fcb->next = first;
		first = fcb;
		fcb->address = NULL;
		fcb->len = 0;
		fcb->pos = 0;
		state.setKey(fcb);
	}
	return fcb;
}

ThreadFile::Error ThreadFile::fetch(caddr_t address, ccxx_size_t len, off_t pos)
{
	fcb_t *fcb = getFCB();
#ifdef WIN32
	Thread::Cancel save;
	if(fd == INVALID_HANDLE_VALUE)
#else
	if(fd < 0)
#endif
		return errNotOpened;

	if(address)
		fcb->address = address;

	if(len)
		fcb->len = len;

	if(pos != -1)
		fcb->pos = pos;


#ifdef WIN32
	enterMutex();
	save = Thread::enterCancel();
	SetFilePointer(fd, fcb->pos, NULL, FILE_BEGIN);
	DWORD count;
	if(!ReadFile(fd, fcb->address, fcb->len, &count, NULL))
	{
		Thread::exitCancel(save);
		leaveMutex();
		return errReadFailure;
	}
	Thread::exitCancel(save);
	leaveMutex();
	if(count < fcb->len)
		return errReadIncomplete;

	return errSuccess;

#else
#ifdef	HAVE_PREAD_PWRITE
	int io = ::pread(fd, fcb->address, fcb->len, fcb->pos);
#else
	enterMutex();
	lseek(fd, fcb->pos, SEEK_SET);
	int io = ::read(fd, fcb->address, fcb->len);
	leaveMutex();
#endif

	if((size_t) io == fcb->len)
		return errSuccess;

	if(io > -1)
		return errReadIncomplete;

	switch(errno)
	{
	case EINTR:
		return errReadInterrupted;
	default:
		return errReadFailure;
	}
#endif // WIN32
}

ThreadFile::Error ThreadFile::update(caddr_t address, ccxx_size_t len, off_t pos)
{
	fcb_t *fcb = getFCB();
#ifdef WIN32
	if(fd == INVALID_HANDLE_VALUE)
#else
	if(fd < 0)
#endif
		return errNotOpened;


	if(address)
		fcb->address = address;

	if(len)
		fcb->len = len;

	if(pos != -1)
		fcb->pos = pos;

#ifdef WIN32
	enterMutex();
	Thread::Cancel save = Thread::enterCancel();
	SetFilePointer(fd, fcb->pos, NULL, FILE_BEGIN);
	DWORD count;
	if(!WriteFile(fd, fcb->address, fcb->len, &count, NULL))
	{
		Thread::exitCancel(save);
		leaveMutex();
		return errWriteFailure;
	}
	Thread::exitCancel(save);
	leaveMutex();
	if(count < fcb->len)
		return errWriteIncomplete;

	return errSuccess;

#else
#ifdef	HAVE_PREAD_PWRITE
	int io = ::pwrite(fd, fcb->address, fcb->len, fcb->pos);
#else
	enterMutex();
	lseek(fd, fcb->pos, SEEK_SET);
	int io = ::write(fd, fcb->address, fcb->len);
	leaveMutex();
#endif

	if((size_t) io == fcb->len)
		return errSuccess;

	if(io > -1)
		return errWriteIncomplete;

	switch(errno)
	{
	case EINTR:
		return errWriteInterrupted;
	default:
		return errWriteFailure;
	}
#endif //WIN32
}

ThreadFile::Error ThreadFile::append(caddr_t address, ccxx_size_t len)
{
	fcb_t *fcb = getFCB();
#ifdef WIN32
	if(fd == INVALID_HANDLE_VALUE)
#else
	if(fd < 0)
#endif
		return errNotOpened;

	if(address)
		fcb->address = address;

	if(len)
		fcb->len = len;

	enterMutex();

#ifdef WIN32
	Thread::Cancel save = Thread::enterCancel();
	fcb->pos = SetFilePointer(fd, 0l, NULL, FILE_END);
	DWORD count;
	if(!WriteFile(fd, fcb->address, fcb->len, &count, NULL))
	{
		Thread::exitCancel(save);
		leaveMutex();
		return errWriteFailure;
	}
	Thread::exitCancel(save);
	leaveMutex();
	if(count < fcb->len)
		return errWriteIncomplete;

	return errSuccess;

#else
	fcb->pos = lseek(fd, 0l, SEEK_END);
	int io = ::write(fd, fcb->address, fcb->len);
	leaveMutex();

	if((size_t) io == fcb->len)
		return errSuccess;

	if(io > -1)
		return errWriteIncomplete;

	switch(errno)
	{
	case EINTR:
		return errWriteInterrupted;
	default:
		return errWriteFailure;
	}
#endif // WIN32
}

off_t ThreadFile::getPosition(void)
{
	fcb_t *fcb = getFCB();

	return fcb->pos;
}

bool ThreadFile::operator++(void)
{
	off_t eof;
	fcb_t *fcb = getFCB();

	fcb->pos += fcb->len;
	enterMutex();
#ifdef WIN32
	eof = SetFilePointer(fd, 0l, NULL, FILE_END);
#else
	eof = lseek(fd, 0l, SEEK_END);
#endif
	leaveMutex();

	if(fcb->pos >= eof)
	{
		fcb->pos = eof;
		return true;
	}
	return false;
}

bool ThreadFile::operator--(void)
{
	fcb_t *fcb = getFCB();

	fcb->pos -= fcb->len;
	if(fcb->pos <= 0)
	{
		fcb->pos = 0;
		return true;
	}
	return false;
}

SharedFile::SharedFile(const char *path) :
RandomFile(path)
{
	fcb.address = NULL;
	fcb.len = 0;
	fcb.pos = 0;
	open(path);
}

SharedFile::SharedFile(const SharedFile &sh) :
RandomFile(sh)
{
}

SharedFile::~SharedFile()
{
	final();
}

SharedFile::Error SharedFile::open(const char *path)
{
#ifdef WIN32
	if(fd != INVALID_HANDLE_VALUE)
#else
	if(fd > -1)
#endif
		final();

	if(path != pathname)
	{
		if(pathname)
			delString(pathname);
		pathname = newString(path);
	}

	flags.initial = false;

#ifdef WIN32
	fd = CreateFile(pathname, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, NULL);
	if(fd == INVALID_HANDLE_VALUE)
	{
		flags.initial = true;
		fd = CreateFile(pathname, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, NULL);
	}
	if(fd == INVALID_HANDLE_VALUE)
		return errOpenFailed;

	return errSuccess;

#else
	fd = ::open(pathname, O_RDWR);
	if(fd < 0)
	{
		flags.initial = true;
		fd = ::open(pathname, O_CREAT | O_RDWR | O_TRUNC,
			    (int)attrPrivate);
	}
	if(fd < 0)
		return error(errOpenFailed);

#ifdef	LOCK_SH
	if(flock(fd, LOCK_SH | LOCK_NB))
	{
		close(fd);
		fd = -1;
		return error(errOpenInUse);
	}
#endif
	return errSuccess;
#endif // WIN32
}

SharedFile::Error SharedFile::fetch(caddr_t address, ccxx_size_t len, off_t pos)
{
#ifdef WIN32
	if(fd == INVALID_HANDLE_VALUE)
#else
	if(fd < 0)
#endif
		return errNotOpened;

	enterMutex();
	if(address)
		fcb.address = address;

	if(len)
		fcb.len = len;

	if(pos != -1)
		fcb.pos = pos;

#ifdef WIN32
	Thread::Cancel save = Thread::enterCancel();
	OVERLAPPED over;
	SetFilePointer(fd, fcb.pos, NULL, FILE_BEGIN);
	over.hEvent = 0;
	over.Offset = fcb.pos;
	over.OffsetHigh = 0;
	LockFileEx(fd, LOCKFILE_EXCLUSIVE_LOCK, 0, fcb.len, 0, &over);
	DWORD count;
	if(!ReadFile(fd, fcb.address, fcb.len, &count, NULL))
	{
		Thread::exitCancel(save);
		leaveMutex();
		return errReadFailure;
	}
	Thread::exitCancel(save);
	leaveMutex();
	if(count < fcb.len)
		return errReadIncomplete;

	return errSuccess;

#else
	lseek(fd, fcb.pos, SEEK_SET);
	lockf(fd, F_LOCK, fcb.len);
	int io = ::read(fd, fcb.address, fcb.len);
	leaveMutex();

	if((size_t) io == fcb.len)
		return errSuccess;

	if(io > -1)
		return errReadIncomplete;

	switch(errno)
	{
	case EINTR:
		return errReadInterrupted;
	default:
		return errReadFailure;
	}
#endif
}

#ifndef WIN32
SharedFile::Error SharedFile::clear(ccxx_size_t len, off_t pos)
{
	if(fd < 0)
		return errNotOpened;

	enterMutex();
	if(len)
		fcb.len = len;

	if(pos != -1)
		fcb.pos = pos;

	lseek(fd, fcb.pos, SEEK_SET);
	lockf(fd, F_ULOCK, fcb.len);
	leaveMutex();
	return errSuccess;
}
#endif // ndef WIN32

SharedFile::Error SharedFile::update(caddr_t address, ccxx_size_t len, off_t pos)
{
#ifdef WIN32	
	if(fd == INVALID_HANDLE_VALUE)
#else
	if(fd < 0)
#endif
		return errNotOpened;

	enterMutex();
	if(address)
		fcb.address = address;

	if(len)
		fcb.len = len;

	if(pos != -1)
		fcb.pos = pos;

#ifdef WIN32
	Thread::Cancel save = Thread::enterCancel();
	OVERLAPPED over;
	SetFilePointer(fd, fcb.pos, NULL, FILE_BEGIN);
	over.hEvent = 0;
	over.Offset = pos;
	over.OffsetHigh = 0;
	DWORD count;
	if(!WriteFile(fd, fcb.address, fcb.len, &count, NULL))
	{
		SetFilePointer(fd, fcb.pos, NULL, FILE_CURRENT);
		UnlockFileEx(fd, 0, len, 0, &over);
		Thread::exitCancel(save);
		leaveMutex();
		return errWriteFailure;
	}
	SetFilePointer(fd, fcb.pos, NULL, FILE_CURRENT);
	UnlockFileEx(fd, 0, len, 0, &over);
	Thread::exitCancel(save);
	leaveMutex();
	if(count < fcb.len)
		return errWriteIncomplete;

	return errSuccess;

#else
	lseek(fd, fcb.pos, SEEK_SET);
	int io = ::write(fd, fcb.address, fcb.len);
	lockf(fd, F_ULOCK, fcb.len);
	leaveMutex();

	if((size_t) io == fcb.len)
		return errSuccess;

	if(io > -1)
		return errWriteIncomplete;

	switch(errno)
	{
	case EINTR:
		return errWriteInterrupted;
	default:
		return errWriteFailure;
	}
#endif // WIN32
}

SharedFile::Error SharedFile::append(caddr_t address, ccxx_size_t len)
{
#ifdef WIN32
	if(fd == INVALID_HANDLE_VALUE)
#else
	if(fd < 0)
#endif
		return errNotOpened;

	enterMutex();
	if(address)
		fcb.address = address;

	if(len)
		fcb.len = len;

#ifdef WIN32
	Thread::Cancel save = Thread::enterCancel();
	fcb.pos = SetFilePointer(fd, 0l, NULL, FILE_END);
	OVERLAPPED over;
	over.hEvent = 0;
	over.Offset = fcb.pos;
	over.OffsetHigh = 0;
	LONG eof = fcb.pos;
	LockFileEx(fd, LOCKFILE_EXCLUSIVE_LOCK, 0, 0x7fffffff, 0, &over);
	fcb.pos = SetFilePointer(fd, 0l, NULL, FILE_END);
	DWORD count;
	if(!WriteFile(fd, fcb.address, fcb.len, &count, NULL))
	{
		SetFilePointer(fd, eof, NULL, FILE_CURRENT);
		Thread::exitCancel(save);
		UnlockFileEx(fd, 0, 0x7fffffff, 0, &over);
		Thread::exitCancel(save);
		leaveMutex();
		return errWriteFailure;
	}
	SetFilePointer(fd, eof, NULL, FILE_CURRENT);
	UnlockFileEx(fd, 0, 0x7fffffff, 0, &over);
	Thread::exitCancel(save);
	leaveMutex();
	if(count < fcb.len)
		return errWriteIncomplete;

	return errSuccess;

#else
	fcb.pos = lseek(fd, 0l, SEEK_END);
	lockf(fd, F_LOCK, -1);
	fcb.pos = lseek(fd, 0l, SEEK_END);
	int io = ::write(fd, fcb.address, fcb.len);
	lseek(fd, fcb.pos, SEEK_SET);
	lockf(fd, F_ULOCK, -1);
	leaveMutex();

	if((size_t) io == fcb.len)
		return errSuccess;

	if(io > -1)
		return errWriteIncomplete;

	switch(errno)
	{
	case EINTR:
		return errWriteInterrupted;
	default:
		return errWriteFailure;
	}
#endif // WIN32
}

off_t SharedFile::getPosition(void)
{
	return fcb.pos;
}

bool SharedFile::operator++(void)
{
	off_t eof;

	enterMutex();
	fcb.pos += fcb.len;
#ifdef WIN32
	eof = SetFilePointer(fd, 0l, NULL, FILE_END);
#else
	eof = lseek(fd, 0l, SEEK_END);
#endif

	if(fcb.pos >= eof)
	{
		fcb.pos = eof;
		leaveMutex();
		return true;
	}
	leaveMutex();
	return false;
}

bool SharedFile::operator--(void)
{
	enterMutex();
	fcb.pos -= fcb.len;
	if(fcb.pos <= 0)
	{
		fcb.pos = 0;
		leaveMutex();
		return true;
	}
	leaveMutex();
	return false;
}

size_t MappedFile::pageAligned(size_t size)
{
	size_t pages = size / Process::getPageSize();

	if(size % Process::getPageSize())
		++pages;

	return pages * Process::getPageSize();
}

#ifdef WIN32

static void makemapname(const char *source, char *target)
{
	unsigned count = 60;
	while(*source && count--)
	{
		if(*source == '/' || *source == '\\')
			*(target++) = '_';
		else
			*(target++) = toupper(*source);
		++source;
	}
	*target = 0;
}

MappedFile::MappedFile(const char *fname, Access mode, size_t size) :
RandomFile(fname)
{
	DWORD share, page;
	map = INVALID_HANDLE_VALUE;
	fcb.address = NULL;

        switch(mode)
        {
        case accessReadOnly:
                share = FILE_SHARE_READ;  
                page = PAGE_READONLY;
                prot = FILE_MAP_READ;
                break;   
        case accessWriteOnly:
                share = FILE_SHARE_WRITE;
                page = PAGE_WRITECOPY;
                prot = FILE_MAP_COPY;
                break;
        case accessReadWrite:
                share = FILE_SHARE_READ|FILE_SHARE_WRITE;
                page = PAGE_READWRITE;
                prot = FILE_MAP_WRITE;
        }
	fd = CreateFile(pathname, mode, share, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, NULL); 
	if(fd == INVALID_HANDLE_VALUE)
	{
		error(errOpenFailed);
		return;
	}
	SetFilePointer(fd, (LONG)size, 0, FILE_BEGIN);
	SetEndOfFile(fd);
	makemapname(fname, mapname);
	map = CreateFileMapping(fd, NULL, page, 0, 0, mapname);
	if(!map)
		error(errMapFailed);
	fcb.address = MapViewOfFile(map, prot, 0, 0, size);
	fcb.len = (ccxx_size_t)size;
	fcb.pos = 0;
	if(!fcb.address)
		error(errMapFailed);
}

MappedFile::MappedFile(const char *fname, Access mode) :
RandomFile(fname)
{
	DWORD share, page;
	map = INVALID_HANDLE_VALUE;
	fcb.address = NULL;

	switch(mode)
	{
	case accessReadOnly:
		share = FILE_SHARE_READ;
		page = PAGE_READONLY;
		prot = FILE_MAP_READ;
		break;
	case accessWriteOnly:
		share = FILE_SHARE_WRITE;
		page = PAGE_WRITECOPY;
		prot = FILE_MAP_COPY;
		break;
	case accessReadWrite:
		share = FILE_SHARE_READ|FILE_SHARE_WRITE;
		page = PAGE_READWRITE;
		prot = FILE_MAP_WRITE;
	}
	fd = CreateFile(pathname, mode, share, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, NULL);
	if(fd == INVALID_HANDLE_VALUE)
	{
		error(errOpenFailed);
		return;
	}
	makemapname(fname, mapname);
	map = CreateFileMapping(fd, NULL, page, 0, 0, mapname);
	if(!map)
		error(errMapFailed);
}

MappedFile::MappedFile(const char *fname, pos_t pos, size_t len, Access mode) :
RandomFile(fname)
{
	DWORD share, page;
	map = INVALID_HANDLE_VALUE;
	fcb.address = NULL;

	switch(mode)
	{
	case accessReadOnly:
		share = FILE_SHARE_READ;
		page = PAGE_READONLY;
		prot = FILE_MAP_READ;
		break;
	case accessWriteOnly:
		share = FILE_SHARE_WRITE;
		page = PAGE_WRITECOPY;
		prot = FILE_MAP_COPY;
		break;
	case accessReadWrite:
		share = FILE_SHARE_READ|FILE_SHARE_WRITE;
		page = PAGE_READWRITE;
		prot = FILE_MAP_WRITE;
	}
	fd = CreateFile(pathname, mode, share, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, NULL);
	if(fd == INVALID_HANDLE_VALUE)
	{
		error(errOpenFailed);
		return;
	}
	makemapname(fname, mapname);
	map = CreateFileMapping(fd, NULL, page, 0, 0, mapname);
	if(!map)
	{
		error(errMapFailed);
		return;
	}
	fcb.address = MapViewOfFile(map, prot, 0, pos, len);
	fcb.len = (ccxx_size_t)len;
	fcb.pos = pos;
	if(!fcb.address)
		error(errMapFailed);
}

MappedFile::~MappedFile()
{
	if(fcb.address)
	{
		unlock();
		UnmapViewOfFile(fcb.address);
	}

	if(map != INVALID_HANDLE_VALUE)
		CloseHandle(map);

	final();
}

void MappedFile::sync(void)
{
}

void MappedFile::sync(caddr_t address, size_t len)
{
}

void MappedFile::release(caddr_t address, size_t len)
{
	if(fcb.address)
	{
		unlock();
		UnmapViewOfFile(fcb.address);
	}
	fcb.address = NULL;
}

caddr_t MappedFile::fetch(off_t pos, size_t len)
{
	if(fcb.address)
	{
		unlock();
		UnmapViewOfFile(fcb.address);
	}

	fcb.address = MapViewOfFile(map, prot, 0, pos, len);
	fcb.len = (ccxx_size_t)len;
	fcb.pos = pos;
	if(!fcb.address)
		error(errMapFailed);

	return fcb.address;
}

void MappedFile::update(size_t offset, size_t len)
{
}

void MappedFile::update(caddr_t address, size_t len)
{
}

bool MappedFile::lock(void)
{
	unlock();
	if(VirtualLock(fcb.address, fcb.len))
		fcb.locked = true;
	return fcb.locked;
}

void MappedFile::unlock(void)
{
	if(!fcb.address)
		fcb.locked = false;

	if(!fcb.locked)
		return;

	VirtualUnlock(fcb.address, fcb.len);
	fcb.locked = false;
}

#else
#ifdef	HAVE_MLOCK
MappedFile::MappedFile(const char *fname, Access mode) :
RandomFile(fname)
{
	fd = open(fname, (int)mode);
	if(fd < 0 && mode != accessReadOnly)
		fd = ::open(pathname, O_CREAT | O_RDWR | O_TRUNC,
			    (int)attrPrivate);

	if(fd < 0)
	{
		error(errOpenFailed);
		return;
	}

	switch(mode)
	{
	case O_RDONLY:
		prot = PROT_READ;
		break;
	case O_WRONLY:
		prot = PROT_WRITE;
		break;
	default:
		prot = PROT_READ | PROT_WRITE;
	}
}

MappedFile::MappedFile(const char *fname, Access mode, size_t size) :
RandomFile(fname)
{
	fd = open(fname, (int)mode | O_CREAT, 0660);

	if(fd < 0)
	{
		error(errOpenFailed);
		return;
	}

	switch(mode)
	{
	case O_RDONLY:
		prot = PROT_READ;
		break;
	case O_WRONLY:
		prot = PROT_WRITE;
		break;
	default:
		prot = PROT_READ | PROT_WRITE;
	}

	enterMutex();
	lseek(fd, size, SEEK_SET);
	fcb.address = (caddr_t)mmap(NULL, size, prot, MAP_SHARED, fd, 0);
	fcb.len = size;
	fcb.pos = 0;
	leaveMutex();
	if((caddr_t)(fcb.address) == (caddr_t)(MAP_FAILED))
	{
		close(fd);
		fd = -1;
		error(errMapFailed);
	}
}

MappedFile::MappedFile(const char *fname, pos_t pos, size_t len, Access mode) :
RandomFile(fname)
{
	fd = open(fname, (int)mode);
	if(fd < 0)
	{
		error(errOpenFailed);
		return;
	}

	switch(mode)
	{
	case O_RDONLY:
		prot = PROT_READ;
		break;
	case O_WRONLY:
		prot = PROT_WRITE;
		break;
	default:
		prot = PROT_READ | PROT_WRITE;
	}

	enterMutex();
	lseek(fd, pos + len, SEEK_SET);
	fcb.address = (caddr_t)mmap(NULL, len, prot, MAP_SHARED, fd, pos);
	fcb.len = len;
	fcb.pos = pos;
	leaveMutex();
	if((caddr_t)(fcb.address) == (caddr_t)(MAP_FAILED))
	{
		close(fd);
		fd = -1;
		error(errMapFailed);
	}
}

MappedFile::~MappedFile()
{
	unlock();
	final();
}

void MappedFile::sync(void)
{
	msync(fcb.address, fcb.len, MS_SYNC);
}

void MappedFile::release(caddr_t address, size_t len)
{
	enterMutex();
	if(address)
		fcb.address = address;

	if(len)
		fcb.len = len;

	if(fcb.locked)
		unlock();

	munmap(fcb.address, fcb.len);
	leaveMutex();
}

caddr_t MappedFile::fetch(off_t pos, size_t len)
{
	enterMutex();
	unlock();
	fcb.len = len;
	fcb.pos = pos;
	lseek(fd, fcb.pos + len, SEEK_SET);
	fcb.address = (caddr_t)mmap(NULL, len, prot, MAP_SHARED, fd, pos);
	leaveMutex();
	return fcb.address;
}

bool MappedFile::lock(void)
{
	unlock();
	if(!mlock(fcb.address, fcb.len))
		fcb.locked = true;
	return fcb.locked;
}

void MappedFile::unlock(void)
{
	if(!fcb.address)
		fcb.locked = false;

	if(!fcb.locked)
		return;

	munlock(fcb.address, fcb.len);
	fcb.locked = false;
}

void MappedFile::update(size_t offset, size_t len)
{
	int mode = MS_ASYNC;
	caddr_t address;

	if(flags.immediate)
		mode = MS_SYNC;

	enterMutex();
	address = fcb.address;
	address += offset;
	if(!len)
		len = fcb.len;
	leaveMutex();
	msync(address, len, mode);
}

void MappedFile::update(caddr_t address, size_t len)
{
	int mode = MS_ASYNC;
	if(flags.immediate)
		mode = MS_SYNC;

	msync(address, len, mode);
}
#endif
#endif // ndef WIN32

time_t lastAccessed(const char *path)
{
#ifdef	WIN32
	__int64 ts;
	WIN32_FILE_ATTRIBUTE_DATA ino;

	if(!GetFileAttributesEx(path, GetFileExInfoStandard, &ino))
		return 0;

	ts = ((__int64)ino.ftLastAccessTime.dwHighDateTime << 32) +
		ino.ftLastAccessTime.dwLowDateTime;

	ts -= (SECS_BETWEEN_EPOCHS * SECS_TO_100NS);
        ts /= SECS_TO_100NS;

        return(time_t)ts;
#else
	struct stat ino;

	if(stat(path, &ino))
		return 0;

	return ino.st_atime;
#endif
}

time_t lastModified(const char *path)
{
#ifdef	WIN32
	__int64 ts;
	WIN32_FILE_ATTRIBUTE_DATA ino;

	if(!GetFileAttributesEx(path, GetFileExInfoStandard, &ino))
		return 0;

	ts = ((__int64)ino.ftLastWriteTime.dwHighDateTime << 32) +
		ino.ftLastWriteTime.dwLowDateTime;

	ts -= (SECS_BETWEEN_EPOCHS * SECS_TO_100NS);
	ts /= SECS_TO_100NS;

	return(time_t)ts;
#else
	struct stat ino;
	
	if(stat(path, &ino))
		return 0;

	return ino.st_mtime;
#endif
}

bool isDir(const char *path)
{
#ifdef WIN32
	DWORD attr = GetFileAttributes(path);
	if(attr == (DWORD)~0l)
		return false;

	if(attr & FILE_ATTRIBUTE_DIRECTORY)
		return true;

	return false;

#else
	struct stat ino;

	if(stat(path, &ino))
		return false;

	if(S_ISDIR(ino.st_mode))
		return true;

	return false;
#endif // WIN32
}

bool isFile(const char *path)
{
#ifdef WIN32
	DWORD attr = GetFileAttributes(path);
	if(attr == (DWORD)~0l)
		return false;

	if(attr & FILE_ATTRIBUTE_DIRECTORY)
		return false;

	return true;

#else
	struct stat ino;

	if(stat(path, &ino))
		return false;

	if(S_ISREG(ino.st_mode))
		return true;

	return false;
#endif // WIN32
}

#ifndef WIN32
// the Win32 version is given in line in the header
bool isDevice(const char *path)
{
	struct stat ino;

	if(stat(path, &ino))
		return false;

	if(S_ISCHR(ino.st_mode))
		return true;

	return false;
}
#endif

bool canAccess(const char *path)
{
#ifdef WIN32
	DWORD attr = GetFileAttributes(path);
	if(attr == (DWORD)~0l)
		return false;

	if(attr & FILE_ATTRIBUTE_SYSTEM)
		return false;

	if(attr & FILE_ATTRIBUTE_HIDDEN)
		return false;

	return true;

#else
	if(!access(path, R_OK))
		return true;

	return false;

#endif
}

bool canModify(const char *path)
{
#ifdef WIN32
	DWORD attr = GetFileAttributes(path);

	if(!canAccess(path))
		return false;

	if(attr & FILE_ATTRIBUTE_READONLY)
		return false;

	if(attr & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_NORMAL))
		return true;

	return false;

#else
	if(!access(path, W_OK | R_OK))
		return true;

	return false;
#endif
}

#ifdef	WIN32

const char *File::getExtension(const char *path)
{
        const char *cp = strrchr(path, '\\');
        if(!cp)
                cp = strrchr(path, '/');
        if(!cp)
                cp = strrchr(path, ':');


	if(cp)
		++cp;
	else
		cp = path;

	if(*cp == '.')
		return "";

	cp = strrchr(cp, '.');

	if(!cp)
		cp = "";

	return cp;
}

const char *File::getFilename(const char *path)
{
	const char *cp = strrchr(path, '\\');
	if(cp)
		return ++cp;

	cp = strrchr(path, '/');
	if(cp)
		return ++cp;

	cp = strrchr(path, ':');
	if(cp)
		++cp;

	return path;
}

char *File::getFilename(const char *path, char *buffer, size_t size)
{
	const char *cp = strrchr(path, '\\');
	if(!cp)
		cp = strrchr(path, '/');
	if(!cp)
		cp = strrchr(path, ':');

	if(cp)
		snprintf(buffer, size, "%s", ++cp);
	else
		snprintf(buffer, size, "%s", path);

	return buffer;
}

char *File::getDirname(const char *path, char *buffer, size_t size)
{
	size_t len;
        const char *cp = strrchr(path, '\\');

	snprintf(buffer, size, "%s", path);

        if(!cp)
                cp = strrchr(path, '/');
        if(!cp)
                cp = strrchr(path, ':');

	if(!cp)
		return buffer;

	if(cp)
		len = cp - path;

	if(len >= size)
		len = size - 1;
	buffer[len] = 0;
	return buffer;
}

#else

const char *File::getExtension(const char *path)
{
	const char *cp = strrchr(path, '/');
	if(cp)
		++cp;
	else
		cp = path;

	if(*cp == '.')
		return "";

	cp = strrchr(cp, '.');
	if(cp)
		return cp;
	return "";
}

char *File::getDirname(const char *path, char *buffer, size_t size)
{
        unsigned len;
        const char *cp = strrchr(path, '/');

        snprintf(buffer, size, "%s", path);

        if(!cp)
                return buffer;

        if(cp)
                len = cp - path;

        if(len >= size)
                len = size - 1;
        buffer[len] = 0;
        return buffer;
}

const char *File::getFilename(const char *path)
{
	const char *cp = strrchr(path, '/');

	if(cp)
		return ++cp;

	return path;
}

char *File::getFilename(const char *path, char *buffer, size_t size)
{
	const char *cp = strrchr(path, '/');

	if(cp)
		snprintf(buffer, size, "%s", ++cp);
	else
		snprintf(buffer, size, "%s", path);

	return buffer;
}

#endif

#ifdef	HAVE_REALPATH

char *File::getRealpath(const char *path, char *buffer, size_t size)
{
	char temp[PATH_MAX];
	setString(buffer, size, ".");
	if(!realpath(path, temp))
		return NULL;
	if(strlen(temp) >= size)
		return NULL;
	setString(buffer, size, temp);
	return buffer;
}

#else

#ifdef  WIN323
static char *getFile(char *path)
{
        char *cp = strchr(path, '\\');
        if(!cp)
                cp = strchr(path, '/');
        if(!cp)
                cp = strchr(path, ':');
        return cp;
}
#else
static char *getFile(char *path)
{
	return strchr(path, '/');
}
#endif

char *File::getRealpath(const char *path, char *buffer, size_t size)
{
	if(size > PATH_MAX)
		size = PATH_MAX;

	unsigned symlinks = 0;
#if !defined(DYNAMCIC_LOCAL_ARRAYS)
	char left[PATH_MAX];
#else
	char left[size];
#endif
	size_t left_len, buffer_len;

#ifdef	WIN32
	if(path[1] == ':')
#else
	if(path[0] == '/')
#endif
	{
		buffer[0] = '/';
		buffer[1] = 0;
		if(!path[1])
			return buffer;

		buffer_len = 1;
		snprintf(left, size, "%s", path + 1);
		left_len = strlen(left);
  	}
	else
	{
		if(!Dir::getPrefix(buffer, size))
		{
			snprintf(buffer, size, "%s", ".");
			return NULL;
		}
		buffer_len = strlen(buffer);
		snprintf(left, size, "%s", path);
		left_len = strlen(left);
	}

	if(left_len >= size || buffer_len >= size)
		return NULL;

	while(left_len > 0)
	{
#ifdef	HAVE_LSTAT
		struct stat ino;
#endif
#if !defined(DYNAMIC_LOCAL_ARRAYS)
		char next_token[PATH_MAX];
#else
		char next_token[size];
#endif
		char *p;
		const char *s = (p = getFile(left)) ? p : left + left_len;

		memmove(next_token, left, s - left);
		left_len -= s - left;
		if(p != NULL)
			memmove(left, s + 1, left_len + 1);

		next_token[s - left] = 0;
		if(buffer[buffer_len - 1] != '/')
		{
			if(buffer_len +1 >= size)
				return NULL;

			buffer[buffer_len++] = '/';
			buffer[buffer_len] = 0;
		}
		if(!next_token[0])
			continue;
		else if(!strcmp(next_token, "."))
			continue;
		else if(!strcmp(next_token, ".."))
		{
			if(buffer_len > 1)
			{
				char *q;
				buffer[buffer_len - 1] = 0;
#ifdef	WIN32
				q = strrchr(buffer, '\\');
				if(!q)
					q = strrchr(buffer, '/');
				if(!q)
					q = strchr(buffer, ':');
#else
				q = strrchr(buffer, '/');
#endif
				*q = 0;
				buffer_len = q - buffer;
			}
			continue;
		}
		snprintf(next_token, size, "%s", buffer);
		buffer_len = strlen(buffer);
		if(buffer_len >= size)
			return NULL;

#ifndef	HAVE_LSTAT
		if(!isFile(buffer) && !isDir(buffer))
			return buffer;

		continue;
#else
		if(lstat(buffer, &ino) < 0)
		{
			if(errno == ENOENT && !p)
				return buffer;

			return NULL;
		}

		if((ino.st_mode & S_IFLNK) == S_IFLNK)
		{
                	char symlink[size];
                        int slen;

                        if (symlinks++ > MAXSYMLINKS)
				return NULL;

                        slen = readlink(buffer, symlink, size);

                        if (slen < 0)
                                return NULL;
                        symlink[slen] = 0;

                        if (symlink[0] == '/') 
			{
                                buffer[1] = 0;
                                buffer_len = 1;
                        } else if (buffer_len > 1) 
			{
                                char *q;

				buffer[buffer_len - 1] = 0;
                                q = strrchr(buffer, '/');
                                *q = 0;
                                buffer_len = q - buffer;
                        }
                        if (symlink[slen - 1] != '/' && p) 
			{
                                if (slen >= size)
					return NULL;

                                symlink[slen] = '/';
                                symlink[slen + 1] = 0;
                        }
                        if(p)
			{
				snprintf(symlink, size, "%s", left);
				left_len = strlen(symlink);
			}
			if(left_len >= size)
				return NULL;
			snprintf(left, size, "%s", symlink);
			left_len = strlen(left);
		}
#endif

	}

#ifdef	WIN32
        if(buffer_len > 1 && buffer[buffer_len - 1] == '\\')
                buffer[buffer_len - 1] = 0;
        else if(buffer_len > 1 && buffer[buffer_len - 1] == '/')
                buffer[buffer_len - 1] = 0;
#else
	if(buffer_len > 1 && buffer[buffer_len - 1] == '/')
		buffer[buffer_len - 1] = 0;
#endif
	return buffer;
}

#endif

#ifdef	CCXX_NAMESPACES
}
#endif

/** EMACS **
 * Local variables:
 * mode: c++
 * c-basic-offset: 8
 * End:
 */
