home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
GEMini Atari
/
GEMini_Atari_CD-ROM_Walnut_Creek_December_1993.iso
/
files
/
diskutil
/
xfsys
/
xfs.c
next >
Wrap
C/C++ Source or Header
|
1993-08-06
|
23KB
|
935 lines
/*
* 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();
}