/* * Creation Date: <2003/12/28 14:16:31 samuel> * Time-stamp: <2004/01/07 10:37:40 samuel> * * * * 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; incol = 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 ); }