415 lines
6.9 KiB
C
415 lines
6.9 KiB
C
/*
|
|
* Creation Date: <2003/12/28 14:16:31 samuel>
|
|
* Time-stamp: <2004/01/07 10:37:40 samuel>
|
|
*
|
|
* <cmdline.c>
|
|
*
|
|
* OpenFirmwware User Interface
|
|
*
|
|
* Copyright (C) 2003, 2004 Samuel Rydh (samuel@ibrium.se)
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* version 2
|
|
*
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include "libopenbios/bindings.h"
|
|
#include "packages.h"
|
|
#include "libc/vsprintf.h"
|
|
|
|
typedef struct {
|
|
char *buf; /* size: ncol+1 */
|
|
char *killbuf; /* size: ncol+1 */
|
|
char *history;
|
|
int hsize; /* size of history buffer */
|
|
int ncol; /* #columns */
|
|
} cmdline_info_t;
|
|
|
|
DECLARE_NODE( cmdline, INSTALL_OPEN, sizeof(cmdline_info_t),
|
|
"+/packages/cmdline" );
|
|
|
|
static void
|
|
emit( int ch )
|
|
{
|
|
PUSH( ch );
|
|
fword("emit");
|
|
}
|
|
|
|
static int
|
|
emit_str( const char *str )
|
|
{
|
|
int n = 0;
|
|
while( *str ) {
|
|
n++;
|
|
emit( *str++ );
|
|
}
|
|
return n;
|
|
}
|
|
|
|
static void
|
|
move_cursor( int n )
|
|
{
|
|
if( n >= 0 ) {
|
|
while( n-- )
|
|
emit( '\f' );
|
|
} else {
|
|
while( n++ )
|
|
emit( 8 );
|
|
}
|
|
}
|
|
|
|
static void
|
|
clear( int n )
|
|
{
|
|
int i;
|
|
for( i=0; i<n; i++ )
|
|
emit(' ');
|
|
move_cursor( -n );
|
|
}
|
|
|
|
static void
|
|
clearline( int pos, int n )
|
|
{
|
|
move_cursor( -pos );
|
|
clear( n );
|
|
}
|
|
|
|
static int
|
|
key( void )
|
|
{
|
|
fword("key");
|
|
return POP();
|
|
}
|
|
|
|
/* ( -- flag ) */
|
|
static void
|
|
cmdline_open( cmdline_info_t *ci )
|
|
{
|
|
ci->ncol = 80;
|
|
ci->buf = malloc( ci->ncol + 1 );
|
|
ci->killbuf = malloc( ci->ncol + 1 );
|
|
|
|
ci->hsize = 40;
|
|
ci->history = malloc( ci->hsize );
|
|
ci->history[0] = 0;
|
|
|
|
RET( -1 );
|
|
}
|
|
|
|
/* ( -- ) */
|
|
static void
|
|
cmdline_close( cmdline_info_t *ci )
|
|
{
|
|
free( ci->buf );
|
|
free( ci->killbuf );
|
|
free( ci->history );
|
|
}
|
|
|
|
|
|
static char *
|
|
history_get( cmdline_info_t *ci, int n )
|
|
{
|
|
char *p = ci->history;
|
|
int len;
|
|
|
|
while( n-- && p )
|
|
if( (p=strchr(p,'\n')) )
|
|
p++;
|
|
|
|
ci->buf[0] = 0;
|
|
if( !p )
|
|
return NULL;
|
|
|
|
for( len=0; len <= ci->ncol && p[len] != '\n' && p[len] ; len++ )
|
|
;
|
|
memcpy( ci->buf, p, len );
|
|
ci->buf[len] = 0;
|
|
return p;
|
|
}
|
|
|
|
static int
|
|
history_remove( cmdline_info_t *ci, int line )
|
|
{
|
|
char *s, *p = history_get( ci, line );
|
|
|
|
if( !p || !(s=strchr(p, '\n')) )
|
|
return 1;
|
|
s++;
|
|
memmove( p, s, strlen(s)+1 );
|
|
return 0;
|
|
}
|
|
|
|
static int /* ( -- ) */
|
|
add_to_history( cmdline_info_t *ci, char *str )
|
|
{
|
|
int n, len;
|
|
|
|
if( !ci->history )
|
|
return 0;
|
|
len = strlen(str);
|
|
if( !len )
|
|
return 0;
|
|
|
|
/* make room for line in history */
|
|
for( ;; ) {
|
|
char *p;
|
|
n = strlen(ci->history) + 1;
|
|
|
|
if( n + len + 1 <= ci->hsize )
|
|
break;
|
|
|
|
if( !(p=strrchr(ci->history,'\n')) )
|
|
return 0;
|
|
*p = 0;
|
|
if( !(p=strrchr(ci->history, '\n')) )
|
|
p = ci->history-1;
|
|
p[1] = 0;
|
|
}
|
|
|
|
memmove( ci->history + len + 1, ci->history, n );
|
|
memcpy( ci->history, str, len );
|
|
ci->history[ len ] = '\n';
|
|
return 1;
|
|
}
|
|
|
|
static void /* ( -- ) */
|
|
cmdline_prompt( cmdline_info_t *ci )
|
|
{
|
|
int cur_added=0, histind=0, ch, i, pos=0, n=0, prompt=1;
|
|
char *buf;
|
|
int terminate = 0;
|
|
|
|
buf = ci->buf;
|
|
selfword("prepare");
|
|
|
|
emit('\n');
|
|
#ifdef NOLEAVE
|
|
for (;;)
|
|
#else
|
|
while (rstackcnt && !terminate)
|
|
#endif
|
|
{
|
|
int drop = 0;
|
|
terminate = 0;
|
|
|
|
if( prompt ) {
|
|
fword("print-prompt");
|
|
buf[0] = 0;
|
|
cur_added = prompt = histind = pos = n = 0;
|
|
}
|
|
|
|
ch = key();
|
|
switch( ch ) {
|
|
case 27:
|
|
switch( key() ) {
|
|
case 'f':
|
|
while( buf[pos] == ' ' )
|
|
emit( buf[pos++] );
|
|
while( buf[pos] && buf[pos] != ' ' )
|
|
emit( buf[pos++] );
|
|
break;
|
|
|
|
case 'b':
|
|
while( pos && buf[pos-1] == ' ' ) {
|
|
move_cursor( -1 );
|
|
pos--;
|
|
}
|
|
while( pos && buf[pos-1] != ' ' ) {
|
|
move_cursor( -1 );
|
|
pos--;
|
|
}
|
|
break;
|
|
case '[':
|
|
switch( key() ) {
|
|
case 'A':
|
|
goto go_up;
|
|
case 'B':
|
|
goto go_down;
|
|
case 'C':
|
|
goto go_right;
|
|
case 'D':
|
|
goto go_left;
|
|
case '3':
|
|
key();
|
|
goto delete;
|
|
}
|
|
break;
|
|
case 'O':
|
|
switch(key()) {
|
|
case 'F':
|
|
goto go_end;
|
|
case 'H':
|
|
goto go_home;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case '\n':
|
|
case '\r':
|
|
if( cur_added )
|
|
history_remove( ci, 0 );
|
|
add_to_history( ci, ci->buf );
|
|
|
|
emit_str( &buf[pos] );
|
|
emit(' ');
|
|
PUSH( feval(buf) );
|
|
fword("print-status");
|
|
|
|
/* Leave the interpreter if terminate? value set */
|
|
fword("terminate?");
|
|
if (POP())
|
|
terminate = 1;
|
|
|
|
prompt = 1;
|
|
break;
|
|
|
|
case 3: /* ^c */
|
|
emit_str("\n");
|
|
prompt = 1;
|
|
if( cur_added )
|
|
history_remove( ci, 0 );
|
|
break;
|
|
|
|
case 4: /* ^d */
|
|
delete:
|
|
if( pos == n )
|
|
break;
|
|
emit( buf[pos++] );
|
|
/* fall through */
|
|
|
|
case 8: /* ^h */
|
|
case 127: /* backspace */
|
|
drop = 1;
|
|
if( !pos )
|
|
break;
|
|
move_cursor( -1 );
|
|
emit_str( &buf[pos] );
|
|
emit(' ');
|
|
memmove( &buf[pos-1], &buf[pos], n+1-pos );
|
|
move_cursor( pos-n-1 );
|
|
pos--;
|
|
n--;
|
|
break;
|
|
|
|
case 1: /* ^a */
|
|
go_home:
|
|
move_cursor( -pos );
|
|
pos = 0;
|
|
break;
|
|
|
|
case 5: /* ^e */
|
|
go_end:
|
|
pos += emit_str( &buf[pos] );
|
|
break;
|
|
|
|
//case 68: /* left */
|
|
// drop = 1;
|
|
case 2: /* ^b */
|
|
go_left:
|
|
if( pos ) {
|
|
move_cursor( -1 );
|
|
pos--;
|
|
}
|
|
break;
|
|
|
|
//case 67: /* right */
|
|
// drop = 1;
|
|
case 6: /* ^f */
|
|
go_right:
|
|
if( pos < n )
|
|
emit( buf[pos++] );
|
|
break;
|
|
|
|
case 11: /* ^k */
|
|
strcpy( ci->killbuf, &buf[pos] );
|
|
clear( n-pos );
|
|
n = pos;
|
|
buf[pos] = 0;
|
|
break;
|
|
|
|
case 25: /* ^y */
|
|
for( i=0; n < ci->ncol && ci->killbuf[i] ; i++, n++ ) {
|
|
memmove( &buf[pos+1], &buf[pos], n+1-pos );
|
|
buf[pos] = ci->killbuf[i];
|
|
move_cursor( 1-emit_str(&buf[pos++]) );
|
|
}
|
|
break;
|
|
|
|
case 9: /* TAB */
|
|
for( i=0; n < ci->ncol && (!i || (pos%4)) ; i++, n++ ) {
|
|
memmove( &buf[pos+1], &buf[pos], n+1-pos );
|
|
buf[pos] = ' ';
|
|
move_cursor( 1-emit_str(&buf[pos++]) );
|
|
}
|
|
break;
|
|
|
|
case 12: /* ^l */
|
|
move_cursor( -ci->ncol -pos );
|
|
fword("print-prompt");
|
|
move_cursor( pos-emit_str(buf) );
|
|
break;
|
|
|
|
//case 66: /* down */
|
|
// drop = 1;
|
|
case 14: /* ^n */
|
|
go_down:
|
|
if( !histind )
|
|
break;
|
|
history_get( ci, --histind - 1);
|
|
clearline( pos, n );
|
|
emit_str( buf );
|
|
pos = n = strlen( buf );
|
|
if( !histind && cur_added ) {
|
|
cur_added = 0;
|
|
history_remove( ci, 0 );
|
|
}
|
|
break;
|
|
|
|
//case 65: /* up */
|
|
// drop = 1;
|
|
case 16: /* ^p */
|
|
go_up:
|
|
if( !histind && add_to_history(ci, ci->buf) ) {
|
|
cur_added = 1;
|
|
histind++;
|
|
}
|
|
if( history_get(ci, histind) )
|
|
histind++;
|
|
clearline( pos, n );
|
|
emit_str( buf );
|
|
pos = n = strlen( buf );
|
|
break;
|
|
}
|
|
if( (unsigned int)ch < 32 )
|
|
drop = 1;
|
|
|
|
if( !drop && n < ci->ncol ) {
|
|
memmove( &buf[pos+1], &buf[pos], n+1-pos );
|
|
n++;
|
|
buf[pos] = ch;
|
|
move_cursor( 1-emit_str(&buf[pos++]) );
|
|
}
|
|
}
|
|
|
|
/* we only get here if terminate? is non-zero; this should
|
|
* only ever be done for a subordinate forth interpreter
|
|
* e.g. for debugging */
|
|
|
|
/* Reset stack and terminate? */
|
|
rstackcnt = dbgrstackcnt;
|
|
feval("0 to terminate?");
|
|
}
|
|
|
|
NODE_METHODS( cmdline ) = {
|
|
{ "open", cmdline_open },
|
|
{ "close", cmdline_close },
|
|
{ "cmdline", cmdline_prompt },
|
|
};
|
|
|
|
void
|
|
cmdline_init( void )
|
|
{
|
|
REGISTER_NODE( cmdline );
|
|
}
|