blob: ea6bca3db962661d2f45e3a08a8d846d62e0d6d5 [file] [log] [blame]
/*
* 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 );
}