/* Copyright (c)1997 by Holger.Veit@gmd.de
 * may be freely used with EMX
 * Many small changes towards a more Posix-like interface: 
 * (C) 2000 Arnd.H.Hanses@rz.ruhr-uni-bochum.de
 *
 * $Id: mm.c,v 1.3 2000/04/03 18:08:54 veit Exp $
 */

#define INCL_BASE
#define OS2_API32
#define INCL_DOSPROCESS
#define INCL_DOSMEMMGR
#include <os2.h>

#define _POSIX_SYNCHRONIZED_IO 
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <errno.h>
#include <sys/errnox.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>

struct _mm_mapTbl
{
	void*	addr;
	size_t	len;
	short	prot;
	short	flags;
	int	fildes;
	size_t	fsize;
	off_t	off;

	struct _mm_mapTbl *next;
};

/* root of the mapping list; this is common to all threads,
 * therefore its access is protected by a critical section (AH: DLL noop) */
	struct _mm_mapTbl *
__mm_mmapRoot = NULL;

void *
_mmap(void*addr, const size_t len, const int protection, 
	const int flags, int fildes, off_t off) 
{
 const ULONG prot = protection;

	if (flags < MAP_FILE || flags > (MAP_COPY|MAP_ANON) || prot < PROT_READ || 
			prot > (PROT_EXEC|PROT_WRITE|PROT_READ|PROT_GUARD) || len > MAP_MAX ||
			((flags & MAP_ANON) && fildes != (-1)) ) {
		errno = EINVAL;
		return(MAP_FAILED); 	/* -1 cast to void* */
	}
	if ( (flags & MAP_FIXED) && ((size_t)addr + len > 0x20000000 || /* outside range */
		fildes != (-1)) ) { /* fd -1 mandatory for non-regular */
		errno = EINVAL;
		return(MAP_FAILED);
	}
	if ( fildes != (-1) ) { /* Not mapping unassociated anonymous memory */
	 int htype;
		if ( ioctl(fildes, FGETHTYPE, &htype) ) { 	/* is fd okay? */
		/* errno = EBADF; */ 	/* set by ioctl */
			_dPuts("mm.c: ioctl() failed! \n");
			return(MAP_FAILED);
		}
		if (HT_ISDEV(htype)) { /* ttys, etc. */
			errno = ENODEV;
			return(MAP_FAILED);
		}
		if (htype != HT_FILE) {	/* no support for mapping such device yet */
			errno = ENXIO;
			return(MAP_FAILED);
		}
	_dPrintf("mm.c: Requested prot=<%2lu> \n",prot);
	} /* sanity checks passed; do the real work now */
	{
	/* allocate a data structure, must be done before DosQueryMem below! */
	 APIRET rc;
	 size_t fsize, cnt, newlen;	
	 struct _mm_mapTbl *
	newmap = (struct _mm_mapTbl*)malloc(sizeof(struct _mm_mapTbl));
	if (!newmap) 
		return(MAP_FAILED); /* malloc sets errno */

	/* check whether the requested page range is free */
	if (flags & MAP_FIXED) {
#ifndef NOTNOW
		/* MAP_FIXED is not supported in OS/2 for now */
		errno = EINVAL; 
		return(MAP_FAILED); /* fake request of forbidden area */
#else
		ULONG needed = len;
		ULONG flg;
		int pgmask;

		/* are page free? */
		rc = DosQueryMem(addr, &needed, &flg);
		if (rc) {
			errno = _rc2Errno(rc);
			return(MAP_FAILED);
		}
		if (needed < len || (flg & PAG_FREE) != PAG_FREE) {
			errno = ENOMEM;
			return(MAP_FAILED);
		}

		/* is 'address AND off' multiple of page size? */
		pgmask = 0xfff;
		if ((addr & pgmask) || (off & pgmask)) {
			errno = EINVAL;
			return(MAP_FAILED);
		}
#endif
	}
	/* duplicate file descriptor, because file may be closed later */
	fildes = dup(fildes);
	if (fildes < 0) 
		goto errexit;
	else {
	/* create memory for map, must allow write to fill it with data */
		size_t len1 = len;
		rc = DosAllocMem(&addr,len,prot|PAG_WRITE|PAG_COMMIT);
		if (rc) {
			errno = _rc2Errno(rc);
			goto errexit;
		}
		/* copy the file into the region */
		if ((fsize = lseek(fildes,0,2)) == (size_t)-1)
			return(MAP_FAILED);
		if ((size_t)off > fsize) 
			off = fsize;	/* cannot implicitly extend */
		if (lseek(fildes,off,0) < 0)
			goto errexit;
		if ((fsize-off) < len1) 
			len1 = fsize - off;	/* cannot implicitly extend */

		if ((newlen=read(fildes,addr,len1)) == (size_t)-1) {
printf("err12 newlen=%ld fildes=%d, addr=%lx, len=%ld errno=%d\n",
			newlen,fildes,(long)addr,len1,errno);
			goto errexit;
		}
		/* set the protection to the requested type */
		rc = DosSetMem(addr,len,prot);
		if (rc) {
			errno = _rc2Errno(rc);
			return(MAP_FAILED);
		}
	}
	/* store the data */
	newmap->addr = addr;
	newmap->len = newlen;
	newmap->prot = prot;
	newmap->flags = flags;
	newmap->fildes = fildes;
	newmap->off = off;
	newmap->fsize = fsize;

	/* add the map into the list of mmaps */
	cnt = 10;
	while (DosEnterCritSec() && --cnt > 0);
	if (cnt==0) {
printf("err13\n");
		errno = EAGAIN;
		free(newmap);
		return(MAP_FAILED);
	}
	newmap->next = __mm_mmapRoot;
	__mm_mmapRoot = newmap;
	DosExitCritSec(); /* errno = 0 forbidden in C-lib */
	return(addr);

errexit:
	free(newmap);
	return(MAP_FAILED);
	}
}

	extern int
_munmap(void *addr, const size_t len)
{
	struct _mm_mapTbl*p = __mm_mmapRoot, *p1 = __mm_mmapRoot;

	if (!len || (((ULONG)addr) & 0xfff)!=0) {
		errno = EINVAL; return -1;
	}
	while (p) {
		if ((size_t)addr >= (size_t)p->addr && 
				(size_t)addr <= ((size_t)p->addr+p->len)) {
		 int cnt = 10;
			/* sync changes in case of shared pages */
			if (p->flags & MAP_SHARED)
				msync((void*)p->addr,p->len, MS_SYNC);
			/* unlink the map element */
			while (DosEnterCritSec() && --cnt > 0);
			if (cnt==0) {
				errno = EAGAIN;
				return -1;
			}
			if (p1 == __mm_mmapRoot) {
				/* first element */
				__mm_mmapRoot = p->next;
			} else {
				p1->next = p->next;
			}
			DosExitCritSec();

			/* close file */
			close(p->fildes);

			/* free memory */
			DosFreeMem((void*)p->addr);
			free(p);

			return 0;
		}
		p1 = p;
		p = p->next;
	}
	errno = EINVAL;
	return -1;
}

	extern int
_msync(__const__ void *addr, size_t len, int dummy_flag)
{
 struct _mm_mapTbl *p = __mm_mmapRoot;

	while (p) {
		/* check whether the address is in a mmap region */
		if ((size_t)addr >= (size_t)p->addr &&
				(size_t)addr <= ( (size_t)p->addr + p->len) &&
		    /* and whether it is a shared map */
		    (p->flags & (MAP_SHARED|MAP_COPY) ) &&
		    /* and we should write */
		    (p->prot & PROT_WRITE)) {
		   
			long base = (long)addr - (long)p->addr + p->off;
			size_t sz = len > (p->fsize-p->off) ? p->fsize-p->off : len;

			if (lseek(p->fildes,base,0) < 0)
				return -1;
			if (write(p->fildes,addr,sz) < 0)
				return -1;
			return 0;
		}
		p = p->next;
	}
	return 0;
}

extern int madvise(void*,size_t,int); /* stub fn */
	extern int
madvise(void *addr, size_t len, int beh)
{
	/* This function is not implemented yet */
	if (!addr) { errno = EFAULT; return(-1);}
	if (len <= (size_t)-1 || beh > 5
				|| beh < 0) { errno = EINVAL; return(-1);}
	return 0;
}

	extern void *
mmap(void*a,const size_t l,const int p,const int f,int fd,off_t o) 
{return _mmap(a,l,p,f,fd,o);}
	extern int
msync(const void*a,size_t l,int d){return _msync(a,l,d);}
	extern int
munmap(void *A,size_t l) {return _munmap(A,l);}

