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