/*
 * xfs.c -- extended file system for master v5.7
 * Author          : Edgar R”der
 * Created On      : Mon Apr 30 13:18:06 1990
 * Last Modified By: Edgar Roeder
 * Last Modified On: Mon Sep  3 19:25:16 1990
 * Update Count    : 134
 * Status          : should work now, needs some speedup
 *
 * HISTORY 
 * 25-May-1990		Edgar R”der	
 *    Last Modified: Fri May 25 22:31:12 1990 #127 (Edgar R”der)
 *    removed almost all debug output and canonicalize()-calls
 *    put dta_getname in module_header, so other programs (!= Master)
 *    can find it there
 *
 * 8-May-1990		Edgar R”der	
 *    Last Modified: Tue May  8 21:11:36 1990 #114 (Edgar R”der)
 *    disable dta_getname while dispatcher is active
 *
 * 8-May-1990		Edgar R”der	
 *    Last Modified: Tue May  8 15:29:08 1990 #107 (Edgar R”der)
 *    reset magic number in dta at the end of Fsnext
 *    add glob to do filename matching
 *    removed some debugging output and made cacheing configurable
 *
 * 7-May-1990		Edgar R”der	
 *    Last Modified: Sun May  6 23:04:48 1990 #91 (Edgar R”der)
 *    added Fforce and Fdup support
 *
 * 6-May-1990		Edgar R”der	
 *    Last Modified: Sun May  6 00:21:32 1990 #90 (Edgar R”der)
 *    added _unx2dos call in xfs_opendir to get an absolute path
 *    use XFSMODE environment variable to allow different unixmodes
 *    for xfs and application programs (they could use mode == "cu")
 *
 * 4-May-1990		Edgar R”der	
 *    Last Modified: Fri May  4 14:55:46 1990 #74 (Edgar R”der)
 *    added rewinddir if directory was already open
 *    added read/write/lseek for device manipulation
 *
 * 4-May-1990		Edgar R”der	
 *    Last Modified: Fri May  4 02:13:34 1990 #71 (Edgar R”der)
 *    if directory was already open, use that structure
 *
 * 4-May-1990		Edgar R”der	
 *    Last Modified: Fri May  4 01:57:04 1990 #69 (Edgar R”der)
 *    added special case for deleted files (pattern is 0xE5)
 *
 * 4-May-1990		Edgar R”der	
 *    changed data structure to record pattern and search attribute
 *
 */ 
#include <signal.h>
#include <stdlib.h>
#include <basepage.h>
#include <string.h>
#include <osbind.h>
#include <xbra.h>
#include <sysvars.h>
#include <errno.h>
#include <dirent.h>
#include <stdio.h>
#include <setjmp.h>
#include <stat.h>
#include <device.h>

/* to enable debug output define TRACING to 1 */
#define	TRACING	1	/* enable debug output */
/* if you use the GNU-malloc */
/*undef	GNU_MALLOC	/* enable malloc statistics */
/* directory cacheing might interfere with Ddelete and Fdelete */
/*undef	DIR_CACHE	/* enable directory cacheing */
/* if we don't use glob, we pretend that Fsfirst is either called with */
/* a full filename or with *.* */
#define	USE_GLOB	/* enable complex search patterns */

#ifdef bool
#undef bool
#endif

typedef short	bool;

BASEPAGE	*master = (BASEPAGE *) 0;
char	*master_var;

#define kByte	* 1024L

static char	__patch_stack[] = "{PatchVar}stack = %ld bytes";
extern long	_initial_stack = 100 kByte;
static char	__patch_var_format[] = "{PatchVar}default for unixmode = %15s";
static char	__default_unixmode[16] = "/.,LAHdb\0\0\0\0\0\0\0";
extern char	*_default_unixmode = __default_unixmode;
extern char	_tCASE;
extern char	_tDOTS;
extern char	_tSLASH;
extern char	_tUNLIMITED;
extern char	*_ltoa();
extern long	modul(const char *);
extern char	*dta_getname(struct _dta *);

typedef struct {
	char	*master_name;
	short	function_type;
	long	(*function_entry)(const char *cmdline);
} func;

func	module_entries[] = {
	{ "XFS", 0, modul }
};

#define _MODUL_MAGIC	0x58465332L	/* 'XFS1' */
#define _MMX_MAGIC	0x4D4D5845L	/* 'MMXE' */
#define _XFS_VERSION	2

typedef char	*(dta_func)(struct _dta *);

struct head {
	char		*module_description;
	short		module_version;
	short		number_of_functions;
	func		*jump_table;
	long		module_magic;
	long		xbra_magic;	/* 'XBRA' */
	long		mmx_magic;	/* 'MMXE' */
	xptr		next;
	short		jump;
	dta_func	*this;
} module_header = {
	"	Master-Module-eXtension eXtended File System V1.1 ("
	__DATE__ " " __TIME__ ")\r\n"
	"\n"
	"	    Usage		|	Explanation\r\n"
	"	------------------------+--------------------------\r\n"
	"	xfs help		| print this help message\r\n"
#ifdef GNU_MALLOC
	"	xfs stat		| show memory statistics\r\n"
#endif
#if TRACING
	"	xfs debug		| enable/disable debug\r\n"
#endif
	"	xfs start		| start new file system\r\n"
	"				| and read $XFSMODE\r\n"
	"	xfs stop		| stop new file system\r\n"
	"	xfs ln <new> <old>	| install a symbolic link\r\n"
	"\n"
	"	You should enable the file system with 'virgin 64'!\r\n"
	"",
	_XFS_VERSION,
	1,
	module_entries,
	_MODUL_MAGIC,
	_XBRA_MAGIC,
	_MMX_MAGIC,
	(xptr) 0,
	_JMP_OPCODE,
	dta_getname
};

static char	dospath[FILENAME_MAX];
static char	dospath2[FILENAME_MAX];

#define WRITE(f, text)	write(f, text, strlen(text))

#ifdef GNU_MALLOC
/* Statistics available to the user.  */
struct mstats
  {
    size_t bytes_total;		/* Total size of the heap. */
    size_t chunks_used;		/* Chunks allocated by the user. */
    size_t bytes_used;		/* Byte total of user-allocated chunks. */
    size_t chunks_free;		/* Chunks in the free list. */
    size_t bytes_free;		/* Byte total of chunks in the free list. */
  };

/* Pick up the current statistics. */
extern struct mstats mstats();

void
memory_statistics()
{
	struct mstats	ms;
	char	output[30];

	ms = mstats();
	WRITE(1, "Memory-Statistics:\r\n\theapsize\t= ");
	_ltoa(ms.bytes_total, output, 10);
	WRITE(1, output);
	WRITE(1, "\r\n\tchunks used\t= ");
	_ltoa(ms.chunks_used, output, 10);
	WRITE(1, output);
	WRITE(1, "\r\n\tbytes used\t= ");
	_ltoa(ms.bytes_used, output, 10);
	WRITE(1, output);
	WRITE(1, "\r\n\tchunks free\t= ");
	_ltoa(ms.chunks_free, output, 10);
	WRITE(1, output);
	WRITE(1, "\r\n\tbytes free\t= ");
	_ltoa(ms.bytes_free, output, 10);
	WRITE(1, output);
	write(1, "\r\n", 2);
}
#endif

#if TRACING
/* use BIOS i/o for debugging output since we might get called from GEM */
static short debugging = 0;
static char output[80];

void
Bconws(const char *str)
{
	if(!debugging) return;
	while(*str) {
		if(*str == '\n') Bconout(CON, '\r');
		Bconout(CON, *str++);
	}
}
#endif

#define DIR_MAGIC	0x4653	/* 'FS' */
#define DIR_STRUCT	0
#define SINGLE_FILE	1

struct Directory {
	short	magic;
	char	type;
	unsigned char	search_attribute;
	char	*path;
	char	*pattern;
	union {
		DIR	*dir;
		struct stat	*statbuf;
	}	contents;
};

static bool	dispatcher_active = 1;

/*
 * The reserved part of the dta buffer is used to pass information
 * from Fsfirst to Fsnext and dta_getname.
 * The layout is as follows:
 *
 *   0 	+--------+
 *	| 'XFS1' | ---> validate information
 *   4	+--------+
 *	| DIRECT | ---> pointer to directory structure
 *   8 	+--------+
 *	|	 |    
 *  12 	+--------+
 *	| OFFSET | ---> current position in directory
 *  16	+--------+
 *	    :
 *  43	+--------+
 *	|   'x'  | ---> validate information
 *  44	+--------+
 *
 * All other fields are unchanged.
 */

char *
dta_getname(struct _dta *dta)
{
	struct Directory	*dir;
	off_t	dirpos;
	struct dirent	*file;

	if(dispatcher_active & 1) return(dta->dta_name);
#if TRACING
	if(*((long *) &(dta->dta_buf[0])) != _MODUL_MAGIC) {
		Bconws("dta_getname: magic num XFS1 not found\n");
		goto go_out;
	}
	if(dta->dta_name[13] != 'x') {
		Bconws("dta_getname: name[13] != 'x'\n");
		goto go_out;
	}
	if(!(dir = *((struct Directory **) &(dta->dta_buf[4])))) {
		Bconws("dta_getname: no Directory struct\n");
		goto go_out;
	}
	if(dir->magic != DIR_MAGIC) {
		Bconws("dta_getname: magic num FS not found\n");
	      go_out:
		return(dta->dta_name);
	}
#else
	if(*((long *) &(dta->dta_buf[0])) != _MODUL_MAGIC ||
	   (dta->dta_name[13] != 'x') ||
	   !(dir = *((struct Directory **) &(dta->dta_buf[4]))) ||
	   (dir->magic != DIR_MAGIC))
		return(dta->dta_name);
#endif
	if(dir->type == SINGLE_FILE) return(dir->pattern);
	dirpos = *((short *) &(dta->dta_buf[12]));
	seekdir(dir->contents.dir, dirpos);
	file = readdir(dir->contents.dir);
	if(!file) return(dta->dta_name);
	return(file->d_name);
}

extern xptr	m_set_dtaname(void (*)());
extern char	*m_getenv(const char *);

void
start_xfs()
{
	char	*unixmode = m_getenv("XFSMODE");

	if(unixmode) _set_unixmode(unixmode);
	else if(errno == EINVAL) _set_unixmode(_default_unixmode);
	errno = ENOERR;
	if(!dispatcher_active) return;
	module_header.next = m_set_dtaname(_XBRA_VEC(module_header));
	dispatcher_active = 0;
}

void
stop_xfs()
{
	if(dispatcher_active) return;
	dispatcher_active = 1;
	m_set_dtaname((void (*)()) module_header.next);
}

long
modul(const char *cmdlin)
{
	int	retval;

	if(!*cmdlin) {
		return(0);
	} else if(!strcmp(cmdlin, "help")) {
		WRITE(1, module_header.module_description);
#ifdef GNU_MALLOC
	} else if(!strcmp(cmdlin, "stat")) {
		memory_statistics();
#endif
#if TRACING
	} else if(!strcmp(cmdlin, "debug")) {
		debugging = !debugging;
#endif
	} else if(!strcmp(cmdlin, "start")) {
		start_xfs();
	} else if(!strcmp(cmdlin, "stop")) {
		stop_xfs();
	} else if(!strcmp(cmdlin, "ln")) {
		char	*old, *new;

		strcpy(dospath, cmdlin);
		new = strtok(dospath, " \t");
		old = strtok(NULL, " \t");
		if(!old || !new) return(1);
		if(symlink(old, new)) {
			retval = -errno;
			errno = ENOERR;
			return(retval);
		}
	}
	return(0);
}

struct head	*current;

static long	my_cookies[16] = {
	_MODUL_MAGIC, (long) &module_header,
	0L, 8L
};

void
remove_cookies()
{
	set_sysvar_to_long(_p_cookies, 0L);
}

void
remove_cookie()
{
	long	*cp;

	if(cp = (long *) get_sysvar(_p_cookies)) {
		while(*cp && *cp != _MODUL_MAGIC) cp += 2;
		while(*cp) {
			cp[0] = cp[2];
			cp[1] = cp[3];
			cp += 2;
		}
	}
}

#define SUPERBIT	(1L << 13)
#define DCREATE		57
#define DDELETE		58
#define DSETPATH	59
#define FCREATE		60
#define FOPEN		61
#define FCLOSE		62
#define FREAD		63
#define FWRITE		64
#define FDELETE		65
#define FSEEK		66
#define FATTRIB		67
#define FDUP		69
#define FFORCE		70
#define DGETPATH	71
#define PEXEC		75
#define FSFIRST		78
#define FSNEXT		79
#define FRENAME		86
#define FDATIME		87

static long	trap1_dispatcher(char *);
static long	new_trap1(long);

static xbra_struct	Trap1 = _TRAP_INIT(new_trap1);

static jmp_buf	oldTrap;

static long
new_trap1(long arg)
{
	char	*sp;
	register long	ret;

	if(dispatcher_active & 1) goto old_trap1;
	/* ATTENTION: setjmp uses trap #1, so we have to disable this now */
	dispatcher_active = 1;
	if(setjmp(oldTrap)) {
		dispatcher_active = 0;
	      old_trap1:
		asm("movel %0, sp" :: "g" (&arg));
		asm("jmp %0@" :: "a" (Trap1.next));
	}
	if(*((short *) &arg) & SUPERBIT) {
		sp = ((char *) &arg) + 6;
	} else {
		asm("movel usp, %0" : "=a" (sp));
	}
	/* now sp points to our argument list */
	ret = trap1_dispatcher(sp);
	dispatcher_active = 0;
	asm("movel %0, sp" :: "g" (&arg));
	/* for the next asm to work,*/
	/* ret should better not be */
	/* addressed relative to sp */
	asm("movel %0, d0" :: "d" (ret));
	asm("rte");
}

extern int	_unx2dos(const char * const, char * const); 
extern int	_dos2unx(const char * const, char * const); 

#ifdef strcpy
#undef strcpy
#endif

extern DIR	*__opendir_chain;

static void
xfs_closedir(struct Directory *dir)
{
	if(!dir || dir->magic != DIR_MAGIC) return;
	free(dir->path);
	free(dir->pattern);
	if(dir->type == SINGLE_FILE) free(dir->contents.statbuf);
	else closedir(dir->contents.dir);
}

static inline struct Directory *
xfs_opendir(const char *path, struct Directory *old_dir)
{
	char	*pattern;
	struct Directory	*dd;
	struct stat	*st;

	if((!(dd = old_dir) || (old_dir->magic != DIR_MAGIC)) &&
	   !(dd = malloc((size_t)sizeof(struct Directory)))) {
		errno = ENOMEM;
		return NULL;
	}
	strcpy(dospath, path);
	do {
		if(!(pattern = strrchr(dospath, '\\')) &&
		   !(pattern = strrchr(dospath, '/'))) {
			pattern = dospath;
		} else {
			*pattern++ = '\0';
		}
	} while(pattern != dospath && !*pattern);
#ifdef CACHE_DIR
	/* this cacheing of open directories is problematic, since */
	/* deleted files will still be listed */
	if((dd == old_dir) && !strcmp(dospath, dd->path)
	   && !strcmp(pattern, dd->pattern)) {
		if(dd->type == DIR_STRUCT) rewinddir(dd->contents.dir);
		return(dd);
	} else
#endif
		xfs_closedir(old_dir);
	dd->magic = DIR_MAGIC;
	dd->path = strdup(dospath);
	dd->pattern = strdup(pattern);
#ifdef USE_GLOB
	/* look for glob-metacharacters in pattern */
	if(glob(pattern, "*[*?[\\]{}]*"))
#else
	if(!strcmp(pattern, "*.*"))
#endif
	{
		if(pattern == dospath) strcpy(dospath, ".");
		if(!*dospath) strcpy(dospath, "\\");
		dd->type = DIR_STRUCT;
		if(!(dd->contents.dir = opendir(dospath))) {
			xfs_closedir(dd);
			free(dd);
			return(NULL);
		}
	} else {
#ifndef USE_GLOB
		/* FIXME: for now assume that pattern is either *.* or */
		/* a normal filename (exclude things like a*.b), could */
		/* be done with glob()				       */
#endif
		dd->type = SINGLE_FILE;
		if(!(st = malloc((size_t)sizeof(struct stat)))) {
			errno = ENOMEM;
			free(dd->path);
			free(dd->pattern);
			free(dd);
			return NULL;
		}
		dd->contents.statbuf = st;
		if(pattern != dospath) pattern[-1] = '\\';
		if(stat(dospath, st) < 0) {
			errno = ENOENT;
			xfs_closedir(dd);
			return NULL;
		}
	}
	return(dd);
}

static dev_t	forced_standard_handle[3];

static long
trap1_dispatcher(char *sp)
{
	long	ret;
	int	save_errno;
	char	*path;
	char	save_tSLASH;
	struct _device	*device;
	struct _dta	*dtabuf;

	save_errno = errno;
	errno = ENOERR;
	path = *((char **) &sp[2]);
	switch(*((short *) sp)) {
	      case DCREATE :
		      ret = mkdir(path);
		      break;
	      case DDELETE :
		      ret = rmdir(path);
		      break;
	      case DSETPATH :
		      ret = chdir(path);
		      break;
	      case FCREATE :
		      ret = creat(path, 0644);
		      break;	/* does not support file modes */
	      case FOPEN :
		      ret = open(path, *((short *) &sp[6]));
		      break;
	      case FCLOSE : {
		      short	fd;

		      fd = *((short *) &sp[2]);
		      if(fd >= 0 && fd <= 2 && forced_standard_handle[fd])
			      fd = forced_standard_handle[fd];
		      ret = close(fd);
		      break;
	      }
	      case FREAD : {
		      short	fd;

		      fd = *((short *) &sp[2]);
		      if(fd >= 0 && fd <= 2 && forced_standard_handle[fd])
			      fd = forced_standard_handle[fd];
		      ret = read(fd, *((void **) &sp[8]), *((long *) &sp[4]));
		      break;
	      }
	      case FWRITE : {
		      short	fd;

		      fd = *((short *) &sp[2]);
		      if(fd >= 0 && fd <= 2 && forced_standard_handle[fd])
			      fd = forced_standard_handle[fd];
		      ret = write(fd, *((void **) &sp[8]), *((long *) &sp[4]));
		      break;
	      }
	      case FDELETE :	/* may get trouble if file is open */
		      ret = unlink(path);
		      break;
	      case FSEEK : {
		      short	fd;

		      fd = *((short *) &sp[2]);
		      if(fd >= 0 && fd <= 2 && forced_standard_handle[fd])
			      fd = forced_standard_handle[fd];
		      ret = lseek(*((short *) &sp[6]), fd,
				  *((short *) &sp[8]));
		      break;
	      }
	      case FATTRIB :
		      _unx2dos(path, dospath);
		      *((char **) &sp[2]) = dospath;
		      goto do_old_trap;
	      case FDUP : {
		      short	fd;

		      fd = *((short *) &sp[2]);
		      if(fd >= 0 && fd <= 2 &&
			 (ret = forced_standard_handle[fd])) break;
		      goto do_old_trap;
	      }
	      case FFORCE : {
		      short	std_fd = *((short *) &sp[2]);
		      short	nstd_fd = *((short *) &sp[4]);

		      if(std_fd == 0) {
			      if((device = _dev_devnum(nstd_fd)) &&
				 device->d_read) {
				      forced_standard_handle[0] = nstd_fd;
				      ret = 0;
				      break;
			      } else {
				      forced_standard_handle[0] = 0;
				      goto do_old_trap;
			      }
		      } else if(std_fd == 1 || std_fd == 2) {
			      if((device = _dev_devnum(nstd_fd)) &&
				 device->d_write) {
				      forced_standard_handle[std_fd] = nstd_fd;
				      ret = 0;
				      break;
			      } else {
				      forced_standard_handle[std_fd] = 0;
				      goto do_old_trap;
			      }
		      }
		      goto do_old_trap;
	      }
	      case DGETPATH : {
		      short	drive;
		      short	save_drive;

		      drive = *((short *) &sp[6]);
		      if (!drive) dospath[0] = Dgetdrv() + 'A';
		      else dospath[0] = drive - 1 + 'A';
		      dospath[1] = ':';
		      ret = Dgetpath(dospath+2, drive);
		      save_tSLASH = _tSLASH;
		      _tSLASH = 0;
		      _dos2unx(dospath, dospath2);
		      _tSLASH = save_tSLASH;
		      if (dospath2[0] && dospath2[1] == ':')
			      strcpy(path, dospath2+2);
		      else
			      strcpy(path, dospath2);
		      break;
	      }
	      case PEXEC : {
		      char	*path;

		      path = *((char **) &sp[4]);
		      _unx2dos(path, dospath);
		      *((char **) &sp[4]) = dospath;
		      goto do_old_trap;
	      }
	      case FSFIRST : {
		      struct Directory	*dir;

		      if((*path == '\345') || (*((short *) &sp[6]) & 0x08))
			      goto do_old_trap;
		      dtabuf = (struct _dta *) Fgetdta();
		      if(*((long *) &(dtabuf->dta_buf[0])) == _MODUL_MAGIC)
			      dir = *((struct Directory **)
				      &(dtabuf->dta_buf[4]));
		      else dir = NULL;
		      if(!(dir = xfs_opendir(path, dir))) break;
		      dir->search_attribute = (*((short *) &sp[6]) | 0x21)
			      & 0xFF;
		      dtabuf = (struct _dta *) Fgetdta();
		      *((long *) &(dtabuf->dta_buf[0])) = _MODUL_MAGIC;
		      *((struct Directory **) &(dtabuf->dta_buf[4])) = dir;
		      if(dir->type == DIR_STRUCT)
			      *((long *) &(dtabuf->dta_buf[12])) =
				      telldir(dir->contents.dir);
	      }
	      case FSNEXT : {
		      struct Directory	*dir;
		      long	dirpos;

		      dtabuf = (struct _dta *) Fgetdta();
		      if(*((long *) &(dtabuf->dta_buf[0])) != _MODUL_MAGIC) {
#if TRACING
			      Bconws("Fsnext: magic num XFS1 not found\n");
#endif
			      errno = EINVAL;
			      break;
		      }
		      dir = *((struct Directory **) &(dtabuf->dta_buf[4]));
		      if(!dir || (dir->magic != DIR_MAGIC)) {
#if TRACING
			      Bconws("Fsnext: directory struct invalid\n");
#endif
			      errno = EINVAL;
			      break;
		      }
		      if(dir->type == SINGLE_FILE) {
			      union {
				      DOSTIME	dt;
				      time_t	tt;
			      } ft;
			      
			      path = dir->pattern;
			      ft.tt = dostime(dir->contents.statbuf->st_mtime);
			      dtabuf->dta_attribute =
				      dir->contents.statbuf->st_attr;
			      dtabuf->dta_time = ft.dt.time;
			      dtabuf->dta_date = ft.dt.date;
			      dtabuf->dta_size =
				      dir->contents.statbuf->st_size;
		      } else {
			      struct dirent	*file;
		      
			      if(!dir->contents.dir) {
#if TRACING
				      Bconws("Fsnext: DIR not open\n");
#endif
				      errno = EINVAL;
				      break;
			      }
			      dirpos = *((short *) &(dtabuf->dta_buf[14]));
			      if(dirpos != telldir(dir->contents.dir))
				      seekdir(dir->contents.dir, dirpos);
#ifdef USE_GLOB
			      do {
#endif
				      file = readdir(dir->contents.dir);
#ifdef USE_GLOB
			      /* we try as long as we have a valid */
			      /* file, but no matching file 	   */
			      } while(
				      file
				      && !(
/* a matching file would be any file */
/* with attribute 0 or an attribute  */	   (!file->d_attribute
/* matching the search attribute and */     || (file->d_attribute &
						dir->search_attribute))
/* a name matching the searchpattern */    && (!strcmp(dir->pattern, "*.*") ||
					       glob(file->d_name, dir->pattern)
					       )
					   )
				      );
#endif
			      if(!file) {
				      errno = ENMFILES;
				      break;
			      }
			      path = file->d_name;
			      dtabuf->dta_attribute = file->d_attribute;
			      dtabuf->dta_time = file->d_time;
			      dtabuf->dta_date = file->d_date;
			      dtabuf->dta_size = file->d_size;
		      }
		      if(!strcmp(path, ".") || !strcmp(path, "..")) {
			      strcpy(dtabuf->dta_name, path);
		      } else {
			      _unx2dos(path, dospath);
			      strcpy(dtabuf->dta_name,
				     strrchr(dospath, '\\') + 1);
		      }
		      *((long *) &(dtabuf->dta_buf[0])) = _MODUL_MAGIC;
		      *((struct Directory **) &(dtabuf->dta_buf[4])) = dir;
		      if(dir->type == DIR_STRUCT) {
			      dirpos = (dirpos << 16) |
				      telldir(dir->contents.dir) & 0x0000FFFFL;
			      /* offset 12 used to make st_inode() happy */
			      *((long *) &(dtabuf->dta_buf[12])) = dirpos;
		      }
		      dtabuf->dta_name[13] = 'x';
		      ret = 0;
		      errno = ENOERR;
		      break;
	      }
	      case FRENAME :
		      ret = rename(path, *((char **) &sp[6]));
		      break;
	      default :
do_old_trap:	      errno = save_errno;
		      longjmp(oldTrap, 1);
	}
	if(errno) ret = - errno;
	errno = save_errno;
	return(ret);
}

#define TRAP1	33

void
init_module(void)
{
	signal(SIGINT, SIG_IGN);
	signal(SIGQUIT, SIG_IGN);
	signal(SIGTSTP, SIG_IGN);
	Trap1.gnuc_magic = _MODUL_MAGIC;
	Trap1.next = (xptr) Setexc(TRAP1, _TRAP_VEC(TRAP1, Trap1));
}

void
finish_module(void)
{
	stop_xfs();
	unlink_handler(&Trap1, TRAP1);
}

void
install_cookie(int resident)
{
	long	*cp;
	long	cookies_used = 0;
	long	tmp;

	if(!(cp = (long *) get_sysvar(_p_cookies))) {
		if(!resident) {
			write(2, "Unable to install cookie!\r\n", 27);
			exit(1);
		}
		signal(SIGRESET, remove_cookies);
		set_sysvar_to_long(_p_cookies, (long)(cp = my_cookies));
	}
	while(*cp && *cp != _MODUL_MAGIC) {
		cp += 2;
		cookies_used++;
	}
	if(*cp) {
		current = (struct head *) cp[1];
		if(current == &module_header) init_module();
		return;
	}
	if(!(cp[1] - cookies_used - 1)) {
		write(2, "No room for cookie!\r\n", 21);
		exit(1);
	}
	*cp++ = _MODUL_MAGIC;
	tmp = *cp;
	*cp++ = (long) (current = &module_header);
	*cp++ = 0L;
	*cp = tmp;
	init_module();
	return;
}

void
install_module(int resident, char *firstcmd)
{
	long	keep;

	install_cookie(resident);
	if(master) {
		strcpy(master_var, "Master-Module ");
		_ltoa(current, master_var+14, 10);
	}
	if(current != &module_header) {
		(*(current->jump_table[0].function_entry))(firstcmd);
		exit(0);
	}
	if(!*firstcmd) modul("help");
	else modul(firstcmd);
	if(!resident) return;
	keep = _base->p_tlen + _base->p_dlen + _base->p_blen
		+ sizeof(BASEPAGE) + _initial_stack;
	_base -= 2;
	Ptermres(keep, 0);
}

void
remove_module()
{
	finish_module();
	remove_cookie();
}

void
usage(char *prog)
{
	write(2, "Usage: [ module ] ", 18);
	WRITE(2, prog);
	write(2, " [ <cmd> ]\r\n", 12);
	exit(2);
}

main(int argc, char *argv[])
{
	char	*mmx;
	BASEPAGE	*caller;
	char	*prog;
	char	*firstcmd;

	if(**argv) prog = *argv;
	else prog = "xfs";
	argc--;
	argv++;
	if(argc) {
		firstcmd = *argv;
		argc--;
		argv++;
	} else firstcmd = "";
	if(argc) usage(prog);
	if(mmx = getenv("MMX")) {
		if(strncmp(mmx, "Master-Call", 11)) exit(1);
		mmx += 11;
		if(!(master = (BASEPAGE *) strtol(mmx, &mmx, 10)) ||
		   !(master_var = (char *) strtol(mmx, &mmx, 10)) ||
		   !(caller = (BASEPAGE *) strtol(mmx, &mmx, 10)) ||
		   (caller != _base->p_parent))
			exit(1);
		install_module(1, firstcmd);
	} else if(!system(NULL)) install_module(1, firstcmd);
	install_module(0, firstcmd);
	system("-i");
	remove_module();
}
