| /* |
| * 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 ); |
| } |