[comp.sources.atari.st] v02i036: nutshell -- Use a shell from within a GEM prog

koreth@ssyx.ucsc.edu (Steven Grimm) (04/14/89)

Submitted-by: t19@nikhefh.hep.nl (Geert J v Oldenborgh)
Posting-number: Volume 2, Issue 36
Archive-name: nutshell

[Documentation posted with the binaries. -sg]

#!/bin/sh
# shar:	Shell Archiver  (v1.22)
#
#	Run the following text with /bin/sh to create:
#	  MAKEFILE
#	  NUTSHELL.C
#
sed 's/^X//' << 'SHAR_EOF' > MAKEFILE &&
Xnutshell.prg: nutshell.o makefile
X	cc -LS=1024 -o nutshell.prg nutshell.o -lstd -ltos -lgem
X
Xnutshell.acc: nutshell.prg
X	cp -of nutshell.prg nutshell.acc
X
Xinstall: nutshell.acc
X	cp -of nutshell.acc d:/
X	cp -of nutshell.prg d:/intercom/uniterm
SHAR_EOF
chmod 0600 MAKEFILE || echo "restore of MAKEFILE fails"
sed 's/^X//' << 'SHAR_EOF' > NUTSHELL.C &&
X/************************************************
X*												*
X*	NutShell, an accessory for mouse-haters.	*
X*												*
X*	written by									*
X*		Geert Jan van Oldenborgh,				*
X*		Ank van der Moerstraat 24,				*
X*		NL-2331 HS Leiden,						*
X*		tel 071-317512.							*
X*		t19@nikhefh.hep.nl						*
X*												*
X*	and free for anyone.						*
X*												*
X************************************************/
X/*	#[ header: 									*/
X#include <stdio.h>
X#include <stdlib.h>
X#include <aes.h>
X#include <tos.h>
X#include <string.h>
X#include <ext.h>
X
X/* REDRAW: no separate screen, save only menu bar, 
Xno REDRAW: save whole screen. */
X#define  REDRAW
X
X#define MOUSE_ON	graf_mouse( M_ON, (void *)0 )
X#define MOUSE_OFF	graf_mouse( M_OFF, (void *)0 )
X
X#define BYTE unsigned char
X
X#define VT52_LEFT		'D'
X#define VT52_RIGHT		'C'
X#define VT52_CLRLINE 	'K'
X#define VT52_CUR_ON 	'e'
X#define VT52_CUR_OFF	'f'
X#define VT52_HOME		'H'
X#define VT52_CLEAR		"E<-H"[0]
X#define CRETURN '\x0D'
X
X#define PROMPT		"$ "
X
X/* prototypes */
X
Xint getscreen(void);
Xvoid redrawscreen(void);
Xvoid eventloop(void);
Xvoid putstring(char *string);
Xvoid getstring(BYTE *string, int length);
Xvoid putletter(char letter);
Xvoid putescape(char letter);
Xvoid clearcursor(void);
Xvoid newline(void);
Xvoid backspace(int n);
Xvoid putenvparent(char *entry, char *env);
Xvoid setshell(void);
Xint mysystem(char *string);
Xvoid zero_shell_p(void);
X
X/*	#] header: 									*/
X/*	#[ globals: 								*/
X
X/* AES garbage */
Xint appl_id;
Xint menu_id;
Xint buffer[16];
X
Xextern int _app;
X
X/* Basepage */
Xextern BASPAG *_BasPag;
X
X/* screen info */
X#ifndef REDRAW
Xlong	screensize = 5 + 32256L;
X#else
Xint		screensize;
X#endif
Xint		res,screenh,screenw;
Xvoid	*oldLogbase, *oldPhysbase, *newBase;
Xchar	*screen_mem, *extra_mem,
X		motd[] = "NutShell version 0.1, "\
X				__DATE__\
X				", use 'lo' to exit",
X		shell[] = "d:\\shell.prg\0<- your shell",
X		home[]  = "HOME=e:\\\0<- your home",
X		error[] = "cannot execute or find shell",
X		magic[] = "GJvO",
X		st[] = "unset tosonly";
Xlong	extra_size = 0L;
Xint 	extra_allocated = 0,
X		screen_allocated = 0,
X		busy = 0,
X		no_shell;
XBYTE	string[79],
X		oudstring[78] = "lo";
X
X/*	#] globals: 								*/
X/*	#[ main:									*/
X
Xint
Xmain(void)
X/********************************************************
X*														*
X*	NutShell: passes commends to any running shell		*
X*	which accepts system() calls over _shell_p			*
X*														*
X********************************************************/
X{
X	/* go through the all the formalities */
X
X	if ( !_app ) 
X	{
X		appl_id = appl_init();
X		if ( appl_id == -1 ) return(0);
X		menu_id = menu_register( appl_id, "  NutShell");
X		if ( menu_id == -1 ) return(-1);
X	
X#ifdef REDRAW
X		/*	find ot the resolution, set some globals accordingly (will not 
X			work on a 19" screen) */
X		res = Getrez();
X		switch ( res )
X		{
X		case 2:
X			screenh = 400;
X			screenw = 640;
X			screensize = 19*80 + 5;
X			break;
X		case 1:
X			screenh = 400;
X			screenw = 320;
X			screensize = 19*40 + 5;
X			break;
X		case 0:
X			screenh = 200;
X			screenw = 320;
X			screensize = 10*40 + 5;
X			break;
X		}
X#endif
X
X		/*	if an accessory, hit _shell_p to 0 (there cannot be a shell) */
X		Supexec(zero_shell_p);
X	}
X	
X	/* and GO */
X	eventloop();
X	
X	return(0);
X}
X
X/*	#] main:									*/
X/*	#[ eventloop:								*/
Xvoid
Xeventloop(void)
X{
X/*	#[ 		loop:								*/
X	/*	the main (unending) loop: if an accesory, wait for an AC_OPEN or
X		AC_CLOSE */
X
X	while ( 1 ) {
X
X		if ( !_app ) evnt_mesag(buffer);
X		if ( busy ) continue;
X
X/*	#] 		loop:								*/
X/*	#[ 		open:								*/
X		if ( _app || buffer[0] == AC_OPEN && buffer[4] == menu_id )
X		{
X			/* set a flag to indicate that we are being called
X			recursively */
X			busy = 1;
X
X			/*	The first thing to do is to free the memory we stole
X				during startop of the .prg program (or desktop) */
X			if ( extra_allocated )
X			{
X				extra_allocated = 0;
X				if ( ! strcmp(extra_mem,magic) )
X				{
X					#ifdef DEBUG
X					puts("freed extra_mem ");
X					#endif
X					Mfree( extra_mem );
X				}
X				else
X					#ifdef DEBUG
X					puts("lost extra_mem ");
X					#endif
X					;
X			}
X			/* check whether we still own the screen */
X			if ( screen_allocated )
X			{
X				if ( strcmp(screen_mem,magic) )
X				{
X					#ifdef DEBUG
X					puts("lost screen ");
X					#endif
X					screen_allocated = 0;
X				}
X				else
X				{
X					#ifdef DEBUG
X					puts("screen still OK ");
X					#endif
X				}
X			}
X			if ( getscreen() ) continue;
X
X			if ( system("free") )
X			{
X				no_shell = 1;
X				if ( mysystem("free") )
X				{
X					putstring(error);
X					newline();
X				}
X			}
X            else
X            	no_shell = 0;
X			/* make doubly sure we only execute TOS progs (not much use)*/
X			if ( !_app ) system(st+2);
X
X			while ( 1 )
X			{
X				putstring(PROMPT);
X				clearcursor();
X				getstring(string,78);
X				if ( ! strcmp(string,"lo") ) break;
X				else if ( ! strncmp(string,"malloc",6) )
X				{
X					if ( (int)strlen(string) <= 7 )
X						putstring("Usage: malloc extra_size[kB]");
X					else
X					{
X						extra_size = 1024L * atol( string + 6 );
X						if ( extra_size )
X							putstring("trying");
X						else
X							putstring("given");
X					}
X					newline();
X				}
X				else 
X				{
X					if ( no_shell ) 
X						mysystem(string);
X					else
X						system(string);
X				}
X			}
X
X			/* clean up */
X			if ( _app ) 
X			{
X				MOUSE_ON;
X				putescape(VT52_CUR_OFF);
X				break;
X			}
X
X			system(st);
X			redrawscreen();
X
X			if ( extra_size )
X			{
X				if ( ( extra_mem = Malloc( extra_size ) ) > NULL )
X				{
X					#ifdef DEBUG
X					puts("allocated extra_mem ");
X					#endif
X					strcpy(extra_mem,magic);
X					extra_allocated = 1;
X				}
X			}
X			else
X			{
X				/* give up everything */
X				#ifdef DEBUG
X				puts("freed screen ");
X				#endif
X				Mfree( screen_mem );
X				screen_allocated = 0;
X			}
X
X			busy = 0;
X		}
X/*	#] 		open:								*/
X/*	#[ 		close:								*/
X		if ( buffer[0] == AC_CLOSE )
X		{
X			/*	Just for safety if we happen to receive an AC_CLOSE within one
X				application.  Note that we rely heavily on the zeroing of
X				memory when an application starts */
X
X				if ( screen_allocated && ! strcmp(screen_mem,magic) )
X				{
X					#ifdef DEBUG
X					puts("freed screen ");
X					#endif
X					Mfree(screen_mem);
X				}
X				if ( extra_allocated && ! strcmp(extra_mem,magic) )
X				{
X					#ifdef DEBUG
X					puts("freed extra_mem ");
X					#endif
X					Mfree(extra_mem);
X				}
X				extra_allocated = 0;
X				screen_allocated = 0;
X
X			/*	This may look strange, but remember that when running a shell
X				the AC_CLOSE does not arrive until we start a GEM application
X				which does an appl_init() or evnt_time(0,0).  The result is
X				that we steal (extra_size + screensize) bytes from every .prg
X				application, but NOT from .tos or .ttp jobs like TeX and tcc.
X			*/
X			/*	I should check that I am not in the desktop, does anyone know 
X				a trick on how to do this??? */
X
X			if ( extra_size )
X			{
X				if ( ( screen_mem = Malloc( screensize )) > NULL )
X				{
X					#ifdef DEBUG
X					puts("allocated screen ");
X					#endif
X					screen_allocated = 2;
X					strcpy(screen_mem,magic);
X					if ( ( extra_mem = Malloc( extra_size )) > NULL )
X					{
X						#ifdef DEBUG
X						puts("allocated extra ");
X						#endif
X						extra_allocated = 1;
X						strcpy(extra_mem,magic);
X					}
X				}
X			}
X		}
X	}
X/*	#] 		close:								*/
X}
X/*	#] eventloop: 								*/
X/*	#[ getscreen: 								*/
X
Xint
Xgetscreen(void)
X{
X	if ( !_app )
X	{
X		if ( !screen_allocated )
X		{
X			screen_mem = Malloc( screensize + 17000L );
X			if ( screen_mem <= NULL )
X			{
X				form_alert(1,"[3][| Not enough | memory left ][ Back to GEM ]");
X				busy = 0;
X				return(1);
X			}
X			Mshrink( 0, screen_mem, screensize );
X			#ifdef DEBUG
X			puts("Allocated screen");
X			#endif
X		}
X		oldPhysbase = Physbase();
X		oldLogbase  = Logbase();
X#ifdef REDRAW
X		/*	copy the menubar to own memory */
X		memcpy(screen_mem + 5, oldPhysbase, screensize - 5);
X#else
X		/*  switch to new screen */
X		newBase = (void *)( (long)(screen_mem + 260) & 0xFFFFFF00L);
X		Setscreen( newBase, newBase, -1 );
X		Vsync();
X#endif
X	}
X	/* cursor on and clear screen */
X	MOUSE_OFF;
X	putescape(VT52_CUR_ON);
X	putescape(VT52_HOME);
X	if ( screen_allocated != 1 )
X	{
X#ifndef REDRAW
X		putescape(VT52_CLEAR);
X#endif
X		putstring(motd);
X		newline();
X		if ( !_app )
X		{
X			screen_allocated = 1;
X			strcpy(screen_mem,magic);
X		}
X	}
X	return(0);
X}
X/*	#] getscreen: 								*/
X/*	#[ redrawscreen: 							*/
X
Xvoid
Xredrawscreen(void)
X{
X	/* cursor off */
X	putescape(VT52_CUR_OFF);
X#ifdef REDRAW
X	/*	copy the menubar back */
X	memcpy(oldPhysbase, screen_mem + 5, screensize - 5);
X	/*  send a redraw to all windows */
X	form_dial(FMD_FINISH, 0, 0, 0, 0, 0, 0, screenw, screenh);
X#else
X	/* set back the old screen */
X	Setscreen( oldLogbase, oldPhysbase, -1 );
X	Vsync();
X#endif
X	MOUSE_ON;
X}
X
X/*	#] redrawscreen: 							*/
X/*	#[ getstring:	 							*/
X
Xvoid getstring(BYTE *string, int length)
X{
X	BYTE *current, *end;
X	long key;
X	BYTE ascii, scan;
X	int i,echo;
X	char let10[] = "1234567890";
X
X	current = end = string;
X	*current = '\0';
X
X	while ( 1 )
X	{
X		/*	first use the GemDos buffer (we use the Bios, but a repeat or
X		typeahead may have filled this).  Hope nobody presses ^c in this time. 
X		*/
X		if ( Cconis() )
X		{
X			key = Cconin();
X			if ( key == 0L ) key = 0x00610000L; /*undo*/
X			echo = 0;
X		}
X		else
X		{
X			key = Bconin(2);
X			echo = 1;
X		}
X		ascii = (char)(key & 0x000000FFl);
X		scan = (char)( ( key >> 16 ) & 0x000000FFl);
X		if ( ascii == '\x00' )
X		{
X			switch ( scan )
X			{
X			case 0x61:
X			case 0x5d:
X				strcpy(string,"lo");
X				return;
X			case 0x48:
X				strcpy(string,oudstring);
X				backspace((int)(current-string));
X				if ( string != end ) putescape(VT52_CLRLINE);
X				putstring(string);
X				current = end = string + (int)strlen(string);
X				break;
X			case 0x50:
X				if ( string != end )
X				{
X					backspace((int)(current-string));
X					putescape(VT52_CLRLINE);
X					current = end = string;
X					*current = '\0';
X				}
X				break;
X			case 0x4b:
X				if ( current > string )
X				{
X					--current;
X					putletter('\b');
X				}
X				break;
X			case 0x4d:
X				if ( current < end )
X				{
X					current++;
X					putescape(VT52_RIGHT);
X				}
X				break;
X			case 0x47:
X				if ( current == string )
X				{
X					strcpy(string,"clear");
X					return;
X				}
X			case 0x62:
X				if ( current == string )
X				{
X					putstring(motd);
X					newline();
X					putstring(PROMPT);
X				}
X				break;
X			}
X			if ( current == string && scan >= 0x3b && scan < 0x44 )
X			{
X				strcpy(string,"if($?PF1 )echo $PF1 ;$PF1 ");
X				string[7] = string[18] = string[24] = let10[scan - 0x3b];
X				return;
X			}
X			if ( current == string && scan >= 0x54 && scan < 0x5d )
X			{
X				strcpy(string,"if($?PF1 )echo $PF1 ;$PF1 ");
X				string[8] = string[19] = string[25] = let10[scan - 0x3b];
X				return;
X			}
X		}
X		else
X		{
X			switch( scan )
X			{
X			case 0x4b:
X				if ( !echo ) putletter('\b');
X				backspace((int)(current-string));
X				current = string;
X				continue;
X			case 0x4d:
X				if ( !echo ) putletter('\b');
X				for ( i=(int)(end-current); i; --i) putescape(VT52_RIGHT);
X				current = end;
X				continue;
X			}
X			switch ( ascii )
X			{
X			case '\b':
X				if ( current == string ) break;
X				if ( echo ) putescape(VT52_LEFT);
X				--current;
X			case '\x7f':
X				if ( current == end ) break;
X				memcpy(current, current+1, (int)(end-current));
X				putstring(current);
X				putletter(' ');
X				backspace((int)(--end - current + 1));
X				break;
X			case '\x0d':
X				if ( scan == 0x72 ) current = end;
X				*current = '\0';
X				if ( current != end ) putescape(VT52_CLRLINE);
X				strcpy(oudstring,string);
X				newline();
X				return;
X			case '\x15':
X				backspace((int)(current-string));
X				putescape(VT52_CLRLINE);
X				current = end = string;
X				*current = '\0';
X				break;
X			case '\x14':
X				if ( current - string > 1 )
X				{
X					scan = *(current-2);
X					*(current - 2) = *(current - 1);
X					*(current - 1) = scan;
X					backspace(2);
X					putstring(current - 2);
X					backspace(end-current); 
X				}
X				break;
X			default:
X				if ( (int)(end-string) < length-2 )
X				{
X					memcpy(current + 1, current, (int)(end-current)+1);
X					*current = ascii;
X					end++;
X					if ( echo ) putstring(current);
X					backspace((int)(end - ++current));
X				}
X				else
X					putletter('\a');
X			}
X		}
X		if ( current == end ) clearcursor();
X	}
X}
X/*	#] getstring:	 							*/
X/*	#[ putthings:	 							*/
X
Xvoid
Xputstring(char *string)
X{
X	char *p;
X	p = string;
X	while ( *p ) Bconout(5,(int)(*p++));
X}
Xvoid
Xputletter(char letter)
X{
X	Bconout(2,(int)letter);
X}
Xvoid
Xputescape(char letter)
X{
X	Bconout(2,0x1b);
X	Bconout(2,(int)letter);
X}
Xvoid
Xclearcursor(void)
X{
X	putletter(' ');
X	putescape(VT52_LEFT);
X}
Xvoid
Xnewline(void)
X{
X	putletter('\x0d');
X	putletter('\n');
X}
Xvoid
Xbackspace(int n)
X{
X	int i;
X	for( i=n; i; --i) putescape(VT52_LEFT);
X}
X/*	#] putthings:	 							*/
X/*	#[ mysystem:								*/
Xint mysystem(char *string)
X{
X	char arg[82],env[200],*p,*q;
X	long l;
X	int i,j;
X
X	strcpy(arg,"x-c ");
X	strcat(arg,string);
X	arg[0] = strlen(arg+1);
X#ifdef DEBUG
X	printf("mysytem calls Pexec with argument %s\n",arg+1);
X#endif
X	p = env;
X	q = "unixpath=";
X	while ( *p++ = *q++ );
X	q = home;
X	while ( *p++ = *q++ );
X	q = "_PBP=";
X	while ( *p++ = *q++ );
X	l = (long)_BasPag;
X	p += 7;
X	for( i=0; i<8; i++)
X	{
X		j = l & 0x0000000FL;
X		*--p = "0123456789ABCDEF"[j];
X		l >>= 4;
X	}
X	p += 8;
X	*p++ = 0;
X	q = "ARGV=CCC?????????????????????????";
X	while ( *p++ = *q++ );
X	q = shell;
X	while ( *p++ = *q++ );
X	q = "-c";
X	while ( *p++ = *q++ );
X	q = string;
X	while ( *p++ = *q++ );
X	*p = 0;
X#ifdef DEBUG
X	p = env;
X	putstring("environment:");
X	newline();
X	while (*p)
X	{
X		while(*p++) putletter(*p);
X		newline();
X	}
X#endif
X	l = Pexec(0, shell, arg, env);
X	putescape(VT52_CUR_ON);
X	return( (int)l);
X}
X/*	#] mysystem:								*/
X/*	#[ zero_shell_p:							*/
X	void zero_shell_p(void)
X	{
X		long *p;
X		p = 0x4f6;
X		*p = 0L;
X	}
X/*	#] zero_shell_p:							*/
X
SHAR_EOF
chmod 0600 NUTSHELL.C || echo "restore of NUTSHELL.C fails"
exit 0