/**************************************************************\
**                                                            **
**  AMIGA module implementation, for SAS/C version 6.58       **
**                                                            **
**  Made by Irmen de Jong (ijong@gak.nl)                      **
**                                                            **
**  27-mar-96: Added a lot of AmiTCP functions!               **
**   2-apr-96: Many small fixes & enhancements.               **
**  11-apr-96: Totally rewritten the environment handling.    **
**             Now creates 4 separate dictionaries.           **
**             Fixed link(), added symlink() and readlink().  **
**  29-may-96: Added filenote() and fullpath()                **
**  11-jun-96: Moved filenote() to doslib/SetComment          **
**  12-jun-96: Removed execv                                  **
**  29-Aug-96: fixed getcwd(), some minor errno fixes         **
**  26-Dec-96: upgraded to 1.4: added putenv(), remove()      **
**             fixed mkdir: default protbits (0777)           **
**   6-Apr-97: fixed bug in readlink (lock was incorrect)     **
**   6-Nov-97: added uname()                                  **
**  12-Jan-98: upgraded to 1.5: fixed includes, new names,    **
**             uses new Amiga/.../unixemul.c                  **
**  28-Mar-98: fixed buffer overflow bug in convertenviron()  **
**                                                            **
**  Adapted from posixmodule.c; implements as much of the     **
**  functionality of this module as possible.                 **
**                                                            **
**                                                            **
**  TO DO: Implement execv(e) and threads (if possible).      **
**                                                            **
**  NOTE: Don't forget __io2errno conversion!!!!!!!!!!!!!!!!  **
**                                                            **
\**************************************************************/


#include "Python.h"
#include "osdefs.h"

#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stat.h>
#include <dos.h>
#include <sys/types.h>
#include <proto/dos.h>
#include <proto/exec.h>
#include <dos/dosextens.h>
#include <dos/var.h>
#include <dos/dostags.h>
#include <exec/execbase.h>

#include "mytime.h"     /* For clock_t on some systems */

#ifdef HAVE_UTIME_H
#include <utime.h>
#endif

#include <dirent.h>
#define NAMLEN(dirent) strlen((dirent)->d_name)

#ifdef AMITCP
#include <clib/netlib_protos.h>
#endif

/* Various compilers have only certain posix functions */
/* XXX Gosh I wish these were all moved into config.h */
/* These defines are for SAS/C on Amiga */
/* #define HAVE_EXECV      1 */
#define HAVE_SYSTEM     1
/* #define HAVE_WAIT       1 */
/* #define HAVE_KILL       1 */
#define HAVE_PIPE       1
#define HAVE_POPEN      1
/* #define HAVE_FORK       1 */
#define HAVE_GETEGID    1
#define HAVE_GETEUID    1
#define HAVE_GETGID     1
/* #define HAVE_GETPPID    1 */
#define HAVE_GETUID     1
#define HAVE_OPENDIR    1


/* Return a dictionary corresponding to the AmigaDOS environment table. */
/* That is, scan ENV: for global environment variables.                 */
/* The local shell environment variables are put into another table.    */

static int
convertenviron(PyObject **glob, PyObject **loc,
			   PyObject **both, PyObject **aliases)
{
	BPTR dlok;
	struct FileInfoBlock __aligned fib;
	PyObject *v;
	char *dynbuf;
	struct LocalVar *lvar;
	struct List *localvars;

	*glob=PyDict_New();
	*loc=PyDict_New();
	*both=PyDict_New();
	*aliases=PyDict_New();

	if(!*glob || !*loc || !*both || !*aliases)
	{
		if(*glob) Py_DECREF(*glob);
		if(*loc) Py_DECREF(*loc);
		if(*both) Py_DECREF(*both);
		if(*aliases) Py_DECREF(*aliases);
		return 0;
	}

	/* Read global vars from ENV: */
	/* Put them in 'glob' and in 'both'. */

	if(dlok=Lock("ENV:",ACCESS_READ))
	{
		if(Examine(dlok,&fib))
		{
			while(ExNext(dlok,&fib))
			{
				if(fib.fib_DirEntryType<0)
				{
					if(dynbuf=malloc(fib.fib_Size+1))
					{
						int len=GetVar(fib.fib_FileName,dynbuf,fib.fib_Size+1,GVF_GLOBAL_ONLY);
						if(len>=0 && (v=PyString_FromString(dynbuf)))
						{
							PyDict_SetItemString(*glob,fib.fib_FileName,v);
							PyDict_SetItemString(*both,fib.fib_FileName,v);
							Py_DECREF(v);
						}
						free(dynbuf);
					}
				}
			}
		}
	}

	if(dlok) UnLock(dlok);

	/* Scan the local shell environment, including "RC" and "Result2"!   */
	/* Put shell vars in 'loc' and 'both', and aliases in 'aliases'. */
	/* Because of the fact that the inserting of local vars into 'both' */
	/* happens AFTER the insertion of global vars, the formor overwrite */
	/* the latter, and thus have higher priority (as it should be). */

	localvars = (struct List*) &((struct Process*)FindTask(0))->pr_LocalVars;

	if(!IsListEmpty(localvars))
	{
		lvar = (struct LocalVar*) localvars->lh_Head;
		do {
			if(dynbuf=malloc(lvar->lv_Len+1))
			{
				strncpy(dynbuf,lvar->lv_Value,lvar->lv_Len);
				dynbuf[lvar->lv_Len]=0;

				if(v=PyString_FromString(dynbuf))
				{
					if(lvar->lv_Node.ln_Type==LV_VAR)
					{
						PyDict_SetItemString(*loc,lvar->lv_Node.ln_Name,v);
						PyDict_SetItemString(*both,lvar->lv_Node.ln_Name,v);
					}
					else if(lvar->lv_Node.ln_Type==LV_ALIAS)
						PyDict_SetItemString(*aliases,lvar->lv_Node.ln_Name,v);

					Py_DECREF(v);
				}
				free(dynbuf);
			}
		} while((lvar=(struct LocalVar*)lvar->lv_Node.ln_Succ)->lv_Node.ln_Succ);
	}


	return 1;
}


static PyObject *AmigaError; /* Exception amiga.error */

/* Set a Amiga-specific error from errno, and return NULL */

static PyObject * amiga_error(void)
{
	return PyErr_SetFromErrno(AmigaError);
}


/* AMIGA generic methods */

static PyObject *
amiga_1str(PyObject *args, int (*func)(const char *))
{
	char *path1;
	int res;
	if (!PyArg_Parse(args, "s", &path1))
		return NULL;
	Py_BEGIN_ALLOW_THREADS
	res = (*func)(path1);
	Py_END_ALLOW_THREADS
	if (res < 0)
		return amiga_error();
	Py_INCREF(Py_None);
	return Py_None;
}

static PyObject *
amiga_2str(PyObject *args, int (*func)(const char *, const char *))
{
	char *path1, *path2;
	int res;
	if (!PyArg_Parse(args, "(ss)", &path1, &path2))
		return NULL;
	Py_BEGIN_ALLOW_THREADS
	res = (*func)(path1, path2);
	Py_END_ALLOW_THREADS
	if (res < 0)
		return amiga_error();
	Py_INCREF(Py_None);
	return Py_None;
}

static PyObject *
amiga_strint(PyObject *args, int (*func)(const char *, int))
{
	char *path;
	int i;
	int res;
	if (!PyArg_Parse(args, "(si)", &path, &i))
		return NULL;
	Py_BEGIN_ALLOW_THREADS
	res = (*func)(path, i);
	Py_END_ALLOW_THREADS
	if (res < 0)
		return amiga_error();
	Py_INCREF(Py_None);
	return Py_None;
}

static PyObject *
amiga_strintint(PyObject *args, int (*func)(const char *, int, int))
{
	char *path;
	int i,i2;
	int res;
	if (!PyArg_Parse(args, "(sii)", &path, &i, &i2))
		return NULL;
	Py_BEGIN_ALLOW_THREADS
	res = (*func)(path, i, i2);
	Py_END_ALLOW_THREADS
	if (res < 0)
		return amiga_error();
	Py_INCREF(Py_None);
	return Py_None;
}

static PyObject *
amiga_do_stat(PyObject *self, PyObject *args, int (*statfunc)(const char *, struct stat *))
{
	struct stat st;
	char *path;
	int res;
	if (!PyArg_Parse(args, "s", &path))
		return NULL;
	Py_BEGIN_ALLOW_THREADS
	res = (*statfunc)(path, &st);
	Py_END_ALLOW_THREADS
	if (res != 0)
		return amiga_error();
	return Py_BuildValue("(llllllllll)",
			(long)st.st_mode,
			(long)st.st_ino,
			(long)st.st_dev,
			(long)st.st_nlink,
			(long)st.st_uid,
			(long)st.st_gid,
			(long)st.st_size,
			(long)st.st_atime,
			(long)st.st_mtime,
			(long)st.st_ctime);
}


/* AMIGA methods */

static PyObject *
amiga_chdir(PyObject *self, PyObject *args)
{
	return amiga_1str(args, chdir);
}

static PyObject *
amiga_chmod(PyObject *self, PyObject *args)
{
	return amiga_strint(args, chmod);
}

#ifdef HAVE_CHOWN
static PyObject *
amiga_chown(PyObject *self, PyObject *args)
{
	return amiga_strintint(args, chown);
}
#endif /* HAVE_CHOWN */

#ifdef HAVE_GETCWD
static PyObject *
amiga_getcwd(PyObject *self, PyObject *args)
{
	char buf[MAXPATHLEN];
	char *res;
	if (!PyArg_Parse(args,""))
			return NULL;
	Py_BEGIN_ALLOW_THREADS
	res = getcwd(buf, sizeof buf);
	Py_END_ALLOW_THREADS
	if (res == NULL)
			return amiga_error();
	return PyString_FromString(buf);
}
#endif

#ifdef HAVE_LINK
static PyObject *
amiga_link(PyObject *self, PyObject *args)
{
	return amiga_2str(args, link);
}
#endif

static PyObject *
amiga_listdir(PyObject *self, PyObject *args)
{
	BPTR dlok;
	char *name;
	struct FileInfoBlock __aligned fib;
	PyObject *d;

	if (!PyArg_Parse(args, "s", &name)) return NULL;

	if ((d = PyList_New(0)) == NULL) return NULL;

	if(dlok=Lock(name,ACCESS_READ))
	{
		if(Examine(dlok,&fib))
		{
			while(ExNext(dlok,&fib))
			{
				PyObject *v = PyString_FromString(fib.fib_FileName);
				if(v==NULL)
				{
					Py_DECREF(d); d=NULL; break;
				}

				if(PyList_Append(d,v)!=0)
				{
					Py_DECREF(v); Py_DECREF(d); d=NULL; break;
				}
				Py_DECREF(v);
			}
		}
		UnLock(dlok);
	}

	if(IoErr()==ERROR_NO_MORE_ENTRIES) return d;

	Py_DECREF(d);
	errno=__io2errno(_OSERR=IoErr());
	return amiga_error();
}

static PyObject *
amiga_mkdir(PyObject *self, PyObject *args)
{
	int res;
	char *path;
	int mode = 0777;
	if (!PyArg_ParseTuple(args, "s|i", &path, &mode)) return NULL;
	Py_BEGIN_ALLOW_THREADS
	res = my_mkdir(path, mode);
	Py_END_ALLOW_THREADS
	if (res < 0) return amiga_error();
	Py_INCREF(Py_None); return Py_None;
}

static PyObject *
amiga_rename(PyObject *self, PyObject *args)
{
	return amiga_2str(args, rename);
}

static PyObject *
amiga_rmdir(PyObject *self, PyObject *args)
{
	return amiga_1str(args, rmdir);
}

static PyObject *
amiga_stat(PyObject *self, PyObject *args)
{
	return amiga_do_stat(self, args, stat);
}

#ifdef HAVE_SYSTEM
static PyObject *
amiga_system(PyObject *self, PyObject *args)
{
	char *command;
	long sts;
	if (!PyArg_Parse(args, "s", &command))
		return NULL;
	Py_BEGIN_ALLOW_THREADS
	sts = system(command);
	Py_END_ALLOW_THREADS
	return PyInt_FromLong(sts);
}
#endif

#ifdef AMITCP
static PyObject *
amiga_umask(PyObject *self, PyObject *args)
{
	int i;
	if (!checkusergrouplib()) return NULL;
	if (!PyArg_Parse(args,"i",&i))
		return NULL;
	i = umask(i);
	if (i < 0)
		return amiga_error();
	return PyInt_FromLong((long)i);
}
#endif

#ifdef HAVE_UNAME
static PyObject *
amiga_uname(PyObject *self, PyObject *args)
{
        struct utsname u;
        int res;
        if (!PyArg_NoArgs(args))
                return NULL;
        Py_BEGIN_ALLOW_THREADS
        res = uname(&u);
        Py_END_ALLOW_THREADS
        if (res < 0)
                return amiga_error();
        return Py_BuildValue("(sssss)",
                             u.sysname,
                             u.nodename,
                             u.release,
                             u.version,
                             u.machine);
}
#endif

static PyObject *
amiga_unlink(PyObject *self, PyObject *args)
{
	return amiga_1str(args, unlink);
}

#ifdef AMITCP
static PyObject *
amiga_utime(PyObject *self, PyObject *args)
{
	char *path;
	long atime, mtime;
	int res;

#ifdef HAVE_UTIME_H
	struct utimbuf buf;
#define ATIME buf.actime
#define MTIME buf.modtime
#define UTIME_ARG &buf
#else /* HAVE_UTIME_H */
	time_t buf[2];
#define ATIME buf[0]
#define MTIME buf[1]
#define UTIME_ARG buf
#endif /* HAVE_UTIME_H */

	if (!PyArg_Parse(args, "(s(ll))", &path, &atime, &mtime))
		return NULL;
	ATIME = atime;
	MTIME = mtime;
	Py_BEGIN_ALLOW_THREADS
	res = utime(path, UTIME_ARG);
	Py_END_ALLOW_THREADS
	if (res < 0)
		return amiga_error();
	Py_INCREF(Py_None);
	return Py_None;
#undef UTIME_ARG
#undef ATIME
#undef MTIME
}
#endif


/* Process operations */

/* XXX Removed _exit. You are VERY STUPID if you used this. (2-apr-96) */

/* XXX Removed execv. You must use system/exit combination instead. */
/*     Maybe one day I'll implement a REAL execv ?? */



#ifdef HAVE_GETEGID
static PyObject *
amiga_getegid(PyObject *self, PyObject *args)
{
	if (!checkusergrouplib()) return NULL;
	if (!PyArg_Parse(args,""))
		return NULL;
	return PyInt_FromLong((long)getegid());
}
#endif

#ifdef HAVE_GETEUID
static PyObject *
amiga_geteuid(PyObject *self, PyObject *args)
{
	if (!checkusergrouplib()) return NULL;
	if (!PyArg_Parse(args,""))
		return NULL;
	return PyInt_FromLong((long)geteuid());
}
#endif

#ifdef HAVE_GETGID
static PyObject *
amiga_getgid(PyObject *self, PyObject *args)
{
	if (!checkusergrouplib()) return NULL;
	if (!PyArg_Parse(args,""))
		return NULL;
	return PyInt_FromLong((long)getgid());
}
#endif

static PyObject *
amiga_getpid(PyObject *self, PyObject *args)
{
	if (!PyArg_Parse(args,""))
		return NULL;
	return PyInt_FromLong((long)FindTask(0));
}

#ifdef HAVE_GETPGRP
static PyObject *
amiga_getpgrp(PyObject *self, PyObject *args)
{
	if (!checkusergrouplib()) return NULL;
	if (!PyArg_Parse(args,""))
		return NULL;
#ifdef GETPGRP_HAVE_ARG
	return PyInt_FromLong((long)getpgrp(0));
#else /* GETPGRP_HAVE_ARG */
	return PyInt_FromLong((long)getpgrp());
#endif /* GETPGRP_HAVE_ARG */
}
#endif /* HAVE_GETPGRP */

#ifdef HAVE_SETPGRP
static PyObject *
amiga_setpgrp(PyObject *self, PyObject *args)
{
	if (!checkusergrouplib()) return NULL;
	if (!PyArg_Parse(args,""))
		return NULL;
#ifdef SETPGRP_HAVE_ARG
	if (setpgrp(0, 0) < 0)
#else /* SETPGRP_HAVE_ARG */
	if (setpgrp() < 0)
#endif /* SETPGRP_HAVE_ARG */
		return amiga_error();
	Py_INCREF(Py_None);
	return Py_None;
}

#endif /* HAVE_SETPGRP */

#ifdef HAVE_GETPPID
static PyObject *
amiga_getppid(PyObject *self, PyObject *args)
{
	if (!PyArg_Parse(args,""))
		return NULL;
	return PyInt_FromLong((long)getppid());
}
#endif

#ifdef HAVE_GETUID
static PyObject *
amiga_getuid(PyObject *self, PyObject *args)
{
	if (!checkusergrouplib()) return NULL;
	if (!PyArg_Parse(args,""))
		return NULL;
	return PyInt_FromLong((long)getuid());
}
#endif

#ifdef HAVE_POPEN
static PyObject *
amiga_popen(PyObject *self, PyObject *args)
{
	char *name;
	char *mode = "r";
	int bufsize = -1;
	FILE *fp;
	PyObject *f;
	if (!PyArg_ParseTuple(args, "s|si", &name, &mode, &bufsize))
		return NULL;
	Py_BEGIN_ALLOW_THREADS
	fp = popen(name, mode);
	Py_END_ALLOW_THREADS
	if (fp == NULL)
		return amiga_error();
	f = PyFile_FromFile(fp, name, mode, pclose);
	if (f != NULL)
		PyFile_SetBufSize(f, bufsize);
	return f;
}
#endif

#ifdef HAVE_SETUID
static PyObject *
amiga_setuid(PyObject *self, PyObject *args)
{
	int uid;
	if (!checkusergrouplib()) return NULL;
	if (!PyArg_Parse(args, "i", &uid))
		return NULL;
	if (setuid(uid) < 0)
		return amiga_error();
	Py_INCREF(Py_None);
	return Py_None;
}
#endif /* HAVE_SETUID */

#ifdef HAVE_SETGID
static PyObject *
amiga_setgid(PyObject *self, PyObject *args)
{
	int gid;
	if (!checkusergrouplib()) return NULL;
	if (!PyArg_Parse(args, "i", &gid))
		return NULL;
	if (setgid(gid) < 0)
		return amiga_error();
	Py_INCREF(Py_None);
	return Py_None;
}
#endif /* HAVE_SETGID */

static PyObject *
amiga_lstat(PyObject *self, PyObject *args)
{
#ifdef HAVE_LSTAT
	return amiga_do_stat(self, args, lstat);
#else /* !HAVE_LSTAT */
	return amiga_do_stat(self, args, stat);
#endif /* !HAVE_LSTAT */
}

#ifdef HAVE_READLINK
static PyObject *
amiga_readlink(PyObject *self, PyObject *args)
{
	char buf[MAXPATHLEN];
	char *path;
	int n;
	if (!PyArg_Parse(args, "s", &path))
	        return NULL;
	Py_BEGIN_ALLOW_THREADS
	n = readlink(path, buf, (int) sizeof buf);
	Py_END_ALLOW_THREADS
	if (n < 0)
	        return amiga_error();
	return PyString_FromStringAndSize(buf, n);
}
#endif

#ifdef HAVE_SYMLINK
static PyObject *
amiga_symlink(PyObject *self, PyObject *args)
{
	return amiga_2str(args, symlink);
}
#endif

#ifdef HAVE_SETSID
static PyObject *
amiga_setsid(PyObject *self, PyObject *args)
{
	if (!checkusergrouplib()) return NULL;
	if (!PyArg_Parse(args,""))
		return NULL;
	if ((int)setsid() < 0)
		return amiga_error();
	Py_INCREF(Py_None);
	return Py_None;
}
#endif /* HAVE_SETSID */

#ifdef HAVE_SETPGID
static PyObject *
amiga_setpgid(PyObject *self, PyObject *args)
{
	int pid, pgrp;
	if (!PyArg_Parse(args, "(ii)", &pid, &pgrp))
		return NULL;
	if (setpgid(pid, pgrp) < 0)
		return amiga_error();
	Py_INCREF(Py_None);
	return Py_None;
}
#endif /* HAVE_SETPGID */

/* Functions acting on file descriptors */

static PyObject *
amiga_open(PyObject *self, PyObject *args)
{
	char *file;
	int flag;
	int mode = 0777;
	int fd;
	if (!PyArg_Parse(args, "(si)", &file, &flag)) {
		PyErr_Clear();
		if (!PyArg_Parse(args, "(sii)", &file, &flag, &mode))
			return NULL;
	}
	Py_BEGIN_ALLOW_THREADS
	fd = open(file, flag, mode);
	Py_END_ALLOW_THREADS
	if (fd < 0)
		return amiga_error();
	return PyInt_FromLong((long)fd);
}

static PyObject *
amiga_close(PyObject *self, PyObject *args)
{
	int fd, res;
	if (!PyArg_Parse(args, "i", &fd))
		return NULL;
	Py_BEGIN_ALLOW_THREADS
	res = close(fd);
	Py_END_ALLOW_THREADS
	if (res < 0)
		return amiga_error();
	Py_INCREF(Py_None);
	return Py_None;
}

#ifdef AMITCP
static PyObject *
amiga_dup(PyObject *self, PyObject *args)
{
	int fd;
	if (!checkbsdsocketlib()) { PyErr_Clear(); errno=EIO; return amiga_error(); }
	if (!PyArg_Parse(args, "i", &fd))
		return NULL;
	Py_BEGIN_ALLOW_THREADS
	fd = dup(fd);
	Py_END_ALLOW_THREADS
	if (fd < 0)
		return amiga_error();
	return PyInt_FromLong((long)fd);
}

static PyObject *
amiga_dup2(PyObject *self, PyObject *args)
{
	int fd, fd2, res;
	if (!checkbsdsocketlib()) { PyErr_Clear(); errno=EIO; return amiga_error(); }
	if (!PyArg_Parse(args, "(ii)", &fd, &fd2))
		return NULL;
	Py_BEGIN_ALLOW_THREADS
	res = dup2(fd, fd2);
	Py_END_ALLOW_THREADS
	if (res < 0)
		return amiga_error();
	Py_INCREF(Py_None);
	return Py_None;
}
#endif

static PyObject *
amiga_lseek(PyObject *self, PyObject *args)
{
	int fd, how;
	long pos, res;
	if (!PyArg_Parse(args, "(ili)", &fd, &pos, &how))
		return NULL;
#ifdef SEEK_SET
	/* Turn 0, 1, 2 into SEEK_{SET,CUR,END} */
	switch (how) {
	case 0: how = SEEK_SET; break;
	case 1: how = SEEK_CUR; break;
	case 2: how = SEEK_END; break;
	}
#endif /* SEEK_END */
	Py_BEGIN_ALLOW_THREADS
	res = lseek(fd, pos, how);
	Py_END_ALLOW_THREADS
	if (res < 0)
		return amiga_error();
	return PyInt_FromLong(res);
}

static PyObject *
amiga_read(PyObject *self, PyObject *args)
{
	int fd, size;
	PyObject *buffer;
	if (!PyArg_Parse(args, "(ii)", &fd, &size))
		return NULL;
	buffer = PyString_FromStringAndSize((char *)NULL, size);
	if (buffer == NULL)
		return NULL;
	Py_BEGIN_ALLOW_THREADS
	size = read(fd, PyString_AsString(buffer), size);
	Py_END_ALLOW_THREADS
	if (size < 0) {
		Py_DECREF(buffer);
		return amiga_error();
	}
	_PyString_Resize(&buffer, size);
	return buffer;
}

static PyObject *
amiga_write(PyObject *self, PyObject *args)
{
	int fd, size;
	char *buffer;
	if (!PyArg_Parse(args, "(is#)", &fd, &buffer, &size))
		return NULL;
	Py_BEGIN_ALLOW_THREADS
	size = write(fd, buffer, size);
	Py_END_ALLOW_THREADS
	if (size < 0)
		return amiga_error();
	return PyInt_FromLong((long)size);
}

static PyObject *
amiga_fstat(PyObject *self, PyObject *args)
{
	int fd;
	struct stat st;
	int res;
	if (!PyArg_Parse(args, "i", &fd))
		return NULL;
	Py_BEGIN_ALLOW_THREADS
	res = fstat(fd, &st);
	Py_END_ALLOW_THREADS
	if (res != 0)
		return amiga_error();
	return Py_BuildValue("(llllllllll)",
			(long)st.st_mode,
			(long)st.st_ino,
			(long)st.st_dev,
			(long)st.st_nlink,
			(long)st.st_uid,
			(long)st.st_gid,
			(long)st.st_size,
			(long)st.st_atime,
			(long)st.st_mtime,
			(long)st.st_ctime);
}

static PyObject *
amiga_fdopen(PyObject *self, PyObject *args)
{
	int fd;
	char *mode = "r";
	int bufsize = -1;
	FILE *fp;
	PyObject *f;
	if (!PyArg_ParseTuple(args, "i|si", &fd, &mode, &bufsize))
		return NULL;
	Py_BEGIN_ALLOW_THREADS
	fp = fdopen(fd, mode);
	Py_END_ALLOW_THREADS
	if (fp == NULL)
		return amiga_error();
	f = PyFile_FromFile(fp, "(fdopen)", mode, fclose);
	if (f != NULL)
		PyFile_SetBufSize(f, bufsize);
	return f;
}

#if 0
/*** XXX pipe() is useless without fork() or threads ***/
/***     TODO: guess what.. implement threads! ***/
static int pipe(int *fildes)
{
	/* 0=ok, -1=err, errno=EMFILE,ENFILE,EFAULT */
	char buf[50];
	static int num = 1;
	
	sprintf(buf,"PIPE:Py%ld_%ld",FindTask(0),num++);
	fildes[0]=open(buf,O_RDONLY,0);
	if(fildes[0]>0)
	{
		fildes[1]=open(buf,O_WRONLY|O_CREAT,FIBF_OTR_READ|FIBF_OTR_WRITE);
		if(fildes[1]>0)
		{
			return 0;
		}
		close(fildes[0]);
	}
	return -1;
}

static PyObject *
amiga_pipe(PyObject *self, PyObject *args)
{
	int fds[2];
	int res;
	if (!PyArg_Parse(args, ""))
		return NULL;
	Py_BEGIN_ALLOW_THREADS
	res = pipe(fds);
	Py_END_ALLOW_THREADS
	if (res != 0)
		return amiga_error();
	return Py_BuildValue("(ii)", fds[0], fds[1]);
}
#endif


static PyObject *
amiga_fullpath(PyObject *self, PyObject *args)
{
	BOOL ok=FALSE;
	BPTR lk;
	char *path;
	char buf[MAXPATHLEN];

	if (!PyArg_ParseTuple(args, "s", &path)) return NULL;

	Py_BEGIN_ALLOW_THREADS
	if(lk=Lock(path,SHARED_LOCK))
	{
		ok=NameFromLock(lk,buf,sizeof(buf));
		UnLock(lk);
	}
	Py_END_ALLOW_THREADS

	if(!ok)
	{
		errno=__io2errno(_OSERR=IoErr());
		return amiga_error();
	}
	else return PyString_FromString(buf);
}

static PyObject *amiga_putenv(PyObject *self, PyObject *args)
{
	char *s1, *s2;

	if (!PyArg_ParseTuple(args, "ss", &s1, &s2)) return NULL;
	if(setenv(s1,s2,1))
	{
		amiga_error(); return NULL;
	}
	
	Py_INCREF(Py_None); return Py_None;
}

#ifdef HAVE_STRERROR
static char posix_strerror__doc__[] =
"strerror(code) -> string\n\
Translate an error code to a message string.";

PyObject *
amiga_strerror(PyObject *self, PyObject *args)
{
	int code;
	char *message;
	if (!PyArg_ParseTuple(args, "i", &code))
		return NULL;
	message = strerror(code);
	if (message == NULL) {
		PyErr_SetString(PyExc_ValueError,
				"strerror code out of range");
		return NULL;
	}
	return PyString_FromString(message);
}
#endif /* strerror */


static struct PyMethodDef amiga_methods[] = {
	{"chdir",   amiga_chdir},
	{"chmod",   amiga_chmod},
#ifdef HAVE_CHOWN
	{"chown",   amiga_chown},
#endif
#ifdef HAVE_GETCWD
	{"getcwd",  amiga_getcwd},
#endif
	{"fullpath", amiga_fullpath,1},
#ifdef HAVE_LINK
	{"link",    amiga_link},
#endif
	{"listdir", amiga_listdir},
	{"lstat",   amiga_lstat},
	{"mkdir",   amiga_mkdir , 1},
#ifdef HAVE_READLINK
	{"readlink",    amiga_readlink},
#endif
	{"rename",  amiga_rename},
	{"rmdir",   amiga_rmdir},
	{"stat",    amiga_stat},
#ifdef HAVE_SYMLINK
	{"symlink", amiga_symlink},
#endif
#ifdef HAVE_SYSTEM
	{"system",  amiga_system},
#endif
#ifdef AMITCP
	{"umask",   amiga_umask},
#endif
#ifdef HAVE_UNAME
	{"uname",   amiga_uname},
#endif
	{"unlink",  amiga_unlink},
	{"remove",  amiga_unlink},
#ifdef AMITCP
	{"utime",   amiga_utime},
#endif
#ifdef HAVE_TIMES
	{"times",   amiga_times},
#endif
#ifdef HAVE_EXECV
	{"execv",	amiga_execv},
	{"execve",	amiga_execve},
#endif /* HAVE_EXECV */
#ifdef HAVE_GETEGID
	{"getegid", amiga_getegid},
#endif
#ifdef HAVE_GETEUID
	{"geteuid", amiga_geteuid},
#endif
#ifdef HAVE_GETGID
	{"getgid",  amiga_getgid},
#endif
	{"getpid",  amiga_getpid},
#ifdef HAVE_GETPGRP
	{"getpgrp", amiga_getpgrp},
#endif
#ifdef HAVE_GETPPID
	{"getppid", amiga_getppid},
#endif
#ifdef HAVE_GETUID
	{"getuid",  amiga_getuid},
#endif
#ifdef HAVE_POPEN
	{"popen",   amiga_popen,    1},
#endif
#ifdef HAVE_SETUID
	{"setuid",  amiga_setuid},
#endif
#ifdef HAVE_SETGID
	{"setgid",  amiga_setgid},
#endif
#ifdef HAVE_SETPGRP
	{"setpgrp", amiga_setpgrp},
#endif
#ifdef HAVE_SETSID
	{"setsid",  amiga_setsid},
#endif
#ifdef HAVE_SETPGID
	{"setpgid", amiga_setpgid},
#endif
#ifdef HAVE_TCGETPGRP
	{"tcgetpgrp",   amiga_tcgetpgrp},
#endif
#ifdef HAVE_TCSETPGRP
	{"tcsetpgrp",   amiga_tcsetpgrp},
#endif
	{"open",    amiga_open},
	{"close",   amiga_close},
#ifdef AMITCP
	{"dup",     amiga_dup},
	{"dup2",    amiga_dup2},
#endif
	{"lseek",   amiga_lseek},
	{"read",    amiga_read},
	{"write",   amiga_write},
	{"fstat",   amiga_fstat},
	{"fdopen",  amiga_fdopen,   1},
#ifdef HAVE_MKFIFO
	{"mkfifo",	amiga_mkfifo, 1},
#endif
#ifdef HAVE_FTRUNCATE
	{"ftruncate",	amiga_ftruncate, 1},
#endif
#ifdef HAVE_PUTENV
	{"putenv", amiga_putenv, 1},
#endif
#ifdef HAVE_STRERROR
	{"strerror",	amiga_strerror, 1},
#endif
#if 0
	/* XXX TODO: implement threads. Otherwise pipe() is useless. */
	{"pipe",    amiga_pipe},
#endif
	{NULL,      NULL}        /* Sentinel */
};


static int
ins(PyObject *d, char *symbol, long value)
{
        PyObject* v = PyInt_FromLong(value);
        if (!v || PyDict_SetItemString(d, symbol, v) < 0)
                return -1;                   /* triggers fatal error */

        Py_DECREF(v);
        return 0;
}

static int all_ins(PyObject *d)
{
#ifdef WNOHANG
        if (ins(d, "WNOHANG", (long)WNOHANG)) return -1;
#endif        
#ifdef O_RDONLY
        if (ins(d, "O_RDONLY", (long)O_RDONLY)) return -1;
#endif
#ifdef O_WRONLY
        if (ins(d, "O_WRONLY", (long)O_WRONLY)) return -1;
#endif
#ifdef O_RDWR
        if (ins(d, "O_RDWR", (long)O_RDWR)) return -1;
#endif
#ifdef O_NDELAY
        if (ins(d, "O_NDELAY", (long)O_NDELAY)) return -1;
#endif
#ifdef O_NONBLOCK
        if (ins(d, "O_NONBLOCK", (long)O_NONBLOCK)) return -1;
#endif
#ifdef O_APPEND
        if (ins(d, "O_APPEND", (long)O_APPEND)) return -1;
#endif
#ifdef O_DSYNC
        if (ins(d, "O_DSYNC", (long)O_DSYNC)) return -1;
#endif
#ifdef O_RSYNC
        if (ins(d, "O_RSYNC", (long)O_RSYNC)) return -1;
#endif
#ifdef O_SYNC
        if (ins(d, "O_SYNC", (long)O_SYNC)) return -1;
#endif
#ifdef O_NOCTTY
        if (ins(d, "O_NOCTTY", (long)O_NOCTTY)) return -1;
#endif
#ifdef O_CREAT
        if (ins(d, "O_CREAT", (long)O_CREAT)) return -1;
#endif
#ifdef O_EXCL
        if (ins(d, "O_EXCL", (long)O_EXCL)) return -1;
#endif
#ifdef O_TRUNC
        if (ins(d, "O_TRUNC", (long)O_TRUNC)) return -1;
#endif
#ifdef O_BINARY
        if (ins(d, "O_BINARY", (long)O_BINARY)) return -1;
#endif
#ifdef O_TEXT
        if (ins(d, "O_TEXT", (long)O_TEXT)) return -1;
#endif

#if defined(PYOS_OS2)
        if (insertvalues(d)) return -1;
#endif
        return 0;
}

void
initamiga(void)
{
	PyObject *m, *d, *globv, *locv, *bothv, *aliases;

	m = Py_InitModule("amiga", amiga_methods);
	d = PyModule_GetDict(m);
	
	/* Initialize amiga.environ dictionary */
	if(!convertenviron(&globv, &locv, &bothv, &aliases))
		Py_FatalError("can't read environment");

	if (PyDict_SetItemString(d, "environ", bothv) != 0)
		Py_FatalError("can't define amiga.environ");
	Py_DECREF(bothv);
	if (PyDict_SetItemString(d, "globalvars", globv) != 0)
		Py_FatalError("can't define amiga.globalvars");
	Py_DECREF(globv);
	if (PyDict_SetItemString(d, "shellvars", locv) != 0)
		Py_FatalError("can't define amiga.shellvars");
	Py_DECREF(locv);
	if (PyDict_SetItemString(d, "shellaliases", aliases ) != 0)
		Py_FatalError("can't define amiga.shellaliases");
	Py_DECREF(aliases);

	if(all_ins(d)) return;

	/* Initialize exception */
	AmigaError = PyErr_NewException("os.error", NULL, NULL);
	if (AmigaError != NULL)
		PyDict_SetItemString(d, "error", AmigaError);
}
