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