/*m---------------------------------------------------------------------------+
|   fpted.c								      |
+-----------------------------------------------------------------------------+
|									      |
|   Description: FPTED -> General purpose Text editor			      |
|									      |
|   Contact: Fernando Joaquim Ganhao Pereria (fjp@minerva.inesc.pt).	      |
|									      |
|   Dependences: If your curses lib doesn't have some of the functions called |
|		 near line 155 (idlok(), scrollok(), meta(), etc) you should  |
|		 comment that lines because this functions are optional.      |
|									      |
+----------------------------------------------------------------------------*/

#include <stdio.h>
#include <ncurses.h>
#include <ctype.h>
#include <keys.h>
#include <fpted.h>


#ifdef UNIX
#include <setjmp.h>
#include <sys/signal.h>
#include <fcntl.h>
#ifndef O_NDELAY
#include <sys/file.h>
#endif /* O_NDELAY */
#endif /* UNIX */

#ifdef POSIX_IO
#include <locale.h>
#endif /* POSIX_IO */


WINDOW* menu_wind;

int screen_lines;
int screen_cols;
int lat_shift;
int pref_key = PREF_KEY;
int first_col = 0;
int act_col = 0;
int indent = FALSE;
int insert = TRUE;
int sensit = TRUE;
int mouse = FALSE;
int completion = FALSE;
int direction = TRUE;
int wrap_margin = LSIZE + 1;
int shift = DEF_SHIFT;
int edit_position = 0;
int interactive = TRUE;
int *command_ptr;
int repeating_key = 0;

#ifdef STATUS_LINE
int status_error = FALSE;
#endif /* STATUS_LINE */

w_area working_area = {
    { NULL, & head, & head },
    & head,
    & head,
    0,
    0,
    FALSE,
    0,
    0,
    0,
    "",
    1,
    0
};

w_area wareas[MAX_WAREAS];
int current_warea = 0;

uchar undo_buffer[LSIZE];
int def_macro[2*LSIZE];
uchar clip_line[LSIZE] = "";
int single_sel = TRUE;
line* last;
int n_alias = 0;
int n_kdefs = 0;
int macro_changed = 0;
int def_macro_ndx = 0;
alias *alias_list = NULL;
kdef *key_defs = NULL;
line *last_page = NULL;


uchar *get_string();
char *getenv();



main( argc, argv )
int argc;
char *argv[];
{
    if( argc > MAX_WAREAS ) {
	fprintf( stderr, "Cannot open that many files !" );
	exit( 0 );
    }

#ifdef POSIX_IO
    setlocale(LC_ALL,"");
#endif /* POSIX_IO */

    init_scr();
    init( argc, argv );
    read_defs( getenv( DEFVAR ) );
    main_loop();
}



#ifdef MOUSE_SUPPORT

enable_mouse()
{
    printf( "\033[?9h" );	/* Enable mouse events when using xterms */
    fflush( stdout );
    mouse = TRUE;
}



disable_mouse()
{
    printf( "\033[?9r" );	/* Disable mouse events when using xterms */
    fflush( stdout );
    mouse = FALSE;
}

#endif /* MOUSE_SUPPORT */


init_scr()
{

#ifdef MOUSE_SUPPORT
    if( getenv("TERM") && !strcmp( getenv("TERM"), "xterm" ) ) enable_mouse();
#endif /* MOUSE_SUPPORT */

    do {
	initscr();
	screen_lines = SCREEN_LINES;
	screen_cols  = SCREEN_COLS;
	lat_shift = LAT_SHIFT;
	if( screen_lines < 3 || screen_cols <= 2*TABS ) {
	    beep();
	    endwin();
	    sleep( 1 );
	}
    } while( screen_lines < 3 || screen_cols <= 2*TABS );

    raw();
    noecho();
    nonl();
    idlok(stdscr, TRUE);	/* If any of these functions isn't available */
    scrollok(stdscr, TRUE);	/* in your curses lib, just erase that line! */
#ifndef VT_TERM
    keypad(stdscr, 2);
    meta(stdscr, TRUE);
#endif /* VT_TERM */
}



end_win()
{
#ifdef MOUSE_SUPPORT
    if( mouse ) disable_mouse();
#endif /* MOUSE_SUPPORT */

    noraw();
    echo();
    nl();
    idlok(stdscr, FALSE);	/* If any of these functions isn't available */
    scrollok(stdscr, FALSE);	/* in your curses lib, just erase that line! */
#ifndef VT_TERM
    keypad(stdscr, 0);
    meta(stdscr, FALSE);
#endif /* VT_TERM */
    fflush( stdout );
    move( screen_lines, 0 );
    endwin();
    putchar( '\n' );
}



line* insert_newline( pos, str )
line* pos;
uchar* str;
{
    line *new_line;
    new_line = (line *)malloc( sizeof( line ) );
    new_line->l = ALLOC( strlen( str ) + 1 );
    strcpy( new_line->l, str );
    pos->next->prev = new_line;
    new_line->next = pos->next;
    new_line->prev = pos;
    pos->next = new_line;
    ++buffer_size;
    return new_line;
}



delete_line( pos )
line* pos;
{
    if( screen_top == pos ) screen_top = pos->next;
    pos->next->prev = pos->prev;
    pos->prev->next = pos->next;
    --buffer_size;
    free( pos->l );
    free( (char *)pos );
}



#ifdef UNIX

void panic_save( int sig )
{
    uchar name[LSIZE];
    int i;

    wareas[current_warea] = working_area;

    for( i = 1; i < MAX_WAREAS; ++i ) {
	working_area = wareas[i];
	if( fname[0] ) sprintf( name, "%s-save", fname );
	else sprintf( name, "*[B%02d]-save", i );
	if( changed ) save_file( name, 1, buffer_size, FALSE );
    }
    finish();
}

#endif /* UNIX */



init( argc, argv )
int argc;
char *argv[];
{
    int i;

    clear();
    put_message( "FPTED 4.0 --- 1990 (C) Fernando Joaquim Ganhao Pereira ." );

    for( i = 0; i < MAX_WAREAS; ++i ) wareas[i] = working_area;

    current_warea = 0;
    insert_newline( &head, "\n" );
    strcpy( fname, "ClipBoard" );
    curr = screen_top = head.next;
    l_number = 1;
    if( getenv(CLIPVAR) && read_file( getenv( CLIPVAR ), FALSE ))
	single_sel = FALSE;    
    wareas[0] = working_area;

    for( i = 1; i < MAX_WAREAS; ++i ) {
	working_area = wareas[i];
	if( argc > i ) strcpy( fname, argv[i] );
	if( i>=argc || read_file(fname, FALSE)==0) insert_newline(&head, "\n");
	curr = screen_top = head.next;
	l_number = 1;
	wareas[i] = working_area;
    }

    working_area = wareas[0];
    select_warea( 1 );

#ifdef UNIX
    signal( SIGTERM, panic_save );
#ifdef SIGDANGER
    signal( SIGDANGER, panic_save );
#endif /* SIGDANGER */
    signal( SIGHUP, panic_save );
#ifdef SIGPWR
    signal( SIGPWR, panic_save );
#endif /* SIGPWR */
#endif /* UNIX */
}



read_file( f_name, warn )
uchar *f_name;
int warn;
{
    uchar b[LSIZE];
    FILE *fptr;
    line *ptr;

    if( strncmp( f_name, "~/", 2 ) == 0 && getenv( "HOME" ) )
	 sprintf( b, "%s/%s", getenv( "HOME" ), f_name+2 );
    else strcpy( b, f_name );

    fptr = fopen( b, "r" );
    if( fptr == NULL ) {
	sprintf( b, "Cannot read \"%s\" !", f_name );
	if( warn ) put_warning( b, TRUE );
	return 0;
    }

    ptr = curr;
    while( 1 ) {
	if( fgets( b, LSIZE-1, fptr ) == NULL ) break;
	if( b[strlen(b)-1] != '\n' ) strcat( b, "\n" );
	ptr = insert_newline( ptr, b );
    }
    fclose( fptr );
    if( fname[0] == '\0' ) strcpy( fname, f_name );
    sprintf( b, "Read file \"%s\" !", f_name );
    if( warn ) {
	put_warning( b, FALSE );
	changed = TRUE;
    }
    return (buffer_size != 0);
}



save_file( f_name, start, end, warn )
uchar *f_name;
int start, end, warn;
{
    FILE *fptr;
    line *ptr;
    uchar string[LSIZE];
    int i;

    if( start > end ) {
	i = start;
	start = end;
	end = i;
    }

    if( strncmp( f_name, "~/", 2 ) == 0 && getenv( "HOME" ) )
	 sprintf( string, "%s/%s", getenv( "HOME" ), f_name+2 );
    else strcpy( string, f_name );

    fptr = fopen( string, "w" );
    if( fptr == NULL ) {
	sprintf( string, "Error writing to file \"%s\" !", fname );
	put_warning( string, TRUE );
	return 0;
    }

    ptr = &head;
    i = 0;
    do {
	ptr = ptr->next;
	++i;
	if( i < start || i > end ) continue;
	fputs( ptr->l, fptr );
    } while( ptr != &head );

    sprintf( string, "Saved \"%s\" : %d lines !", f_name, end-start+1 );
    if( warn ) put_warning( string, FALSE );

    fclose( fptr );
    return 1;
}



put_message( mess )
uchar *mess;
{
    move( screen_lines, 0 );
    if( mess )
    {
#ifdef A_REVERSE
	attron( A_REVERSE );
	printw( "%s ", mess );
	edit_position = 1 + strlen( mess );
	attroff( A_REVERSE );
#else /* A_REVERSE */
	printw( "-> %s ", mess );
	edit_position = 4 + strlen( mess );
#endif /* A_REVERSE */
    }
    clrtoeol();
    refresh();
}



put_warning( mess, is_error )
uchar *mess;
int is_error;
{
    put_message( mess );
    if( is_error ) beep();
    refresh();
#ifdef STATUS_LINE
    status_error = TRUE;
#endif /* STATUS_LINE */
}



go_to( lnumber )
int lnumber;
{
    int i = 0;

    if( lnumber < 1 ) lnumber = 1;
    if( lnumber > buffer_size ) lnumber = buffer_size;

    curr = &head;
    while( i < lnumber ) {
	curr = curr->next;
	if( curr == &head ) break;
	++i;
    }
    l_number = (lnumber > 0) ? lnumber : buffer_size;
    adjust_screen( FALSE );
}



adjust_screen( mode )
int mode;
{
    line *ptr = screen_top;
    int i;

    ADJUST_X();

    for( i = 0; i < screen_lines; ++i ) {
	if( ptr == &head ) break;
	if( ptr == curr ) {
	    y = i;
	    if( mode ) redraw_screen();
	    return;
	}
	ptr = ptr->next;
    }

    screen_top = curr;
    for( i = 0; i < screen_lines / 2; ++i ) {
	if( screen_top->prev == &head ) break;
	screen_top = screen_top->prev;
    }
    y = i;
    redraw_screen();
}



redraw_screen()
{
    static uchar m[LSIZE];
    line *ptr = screen_top;
    int i;

    if( screen_top != last_page ) clear();
    last_page = screen_top;

    for( i = 0; i < screen_lines; ++i ) {
	if( ptr == &head ) break;
	redraw_line( i, ptr->l );
	ptr = ptr->next;
    }
    while( i < screen_lines ) redraw_line( i++, "" );
    refresh();

#ifdef STATUS_LINE
    put_status_line();
#endif /* STATUS_LINE */
}



put_status_line()
{
    static uchar m[LSIZE];

#ifdef STATUS_LINE
    status_error = FALSE;
#endif /* STATUS_LINE */
    
    if( fname[0] )
	sprintf( m, "#### [%02d] File \"%s\" %s    PRESS <ESC> to MENU.",
		 current_warea, fname, changed ? "[changed]" : "" );
    else sprintf( m, "#### [%02d] No name !    PRESS <ESC> to MENU.",
	 current_warea);
    move( screen_lines, 0 );
    m[SCREEN_COLS-10] = '\0';
    printw( "%s", m );
    clrtoeol();
    refresh();
}



redraw_line( yl, ptr )
int yl;
uchar *ptr;
{
    register int i;
    static int col;

    col = 0;
    move( yl, 0 );

    if( *ptr ) {
	while( col < first_col && *ptr != '\n' ) {
	    col += CSZ( *ptr, col );
	    ++ptr;
	}
	col -= first_col;
	if( col > 0 ) {
	    clrtoeol();
	    move( yl, col );
	}

	while( TRUE ) {
	    if( col >= screen_cols ) {
		if( ptr[1] == '\0' ) break;
		move( yl, screen_cols-1 );
#ifdef A_REVERSE
		attron( A_REVERSE );
		addch( (*(ptr-1))>=' ' ? *(ptr-1): '>' );
		attroff( A_REVERSE );
#else /* A_REVERSE */
		addch( '>' );
#endif /* A_REVERSE */
		return;
	    }
	    if( *ptr < ' ' || *ptr == K_DELETE ) {
		if( *ptr == '\t' ) {
		    i = TABS-col%TABS;
		    col += i;
		    while( i-- ) addch( ' ' );
		}
		else {
		    if( ptr[1] == '\0' ) break;
		    addch( '^' );
		    if( *ptr < ' ' ) addch( 'A'+*ptr-1 );
		    else addch( '?' );
		    col += 2;
		}
	    }
	    else {
		addch( *ptr );
		++col;
	    }
	    ++ptr;
	}
    }
    else addch( '~' );
    if( col < screen_cols ) clrtoeol();
}



main_loop()
{
    while( TRUE ) {
	place_cursor();
	manage_key( get_pref_ch() );
    }
}



place_cursor()
{
    static int col;

    col = real_col(x, curr->l);
    if( lateral_adjust( col ) ) redraw_screen();
    move( y, col - first_col);
    refresh();
}



real_col( col, str )
int col;
uchar *str;
{
    register int i, j=0;

    for( i=0; i<col; ++i ) {
	j += CSZ( *str, j );
	++str;
    }
    return j;
}



lateral_adjust( col )
int col;
{
    if( col < first_col || col - first_col >= screen_cols ) {
	while( col - first_col >= screen_cols ) first_col += lat_shift;
	while( col < first_col ) first_col -= lat_shift;
	return TRUE;
    }
    return FALSE;
}



primitive_key( c )
int c;
{
    static int last_key = 0;

    repeating_key = (last_key == c);
    last_key = c;

    if( curr != last )
    {
	strcpy( undo_buffer, curr->l );
	last = curr;
    }

    if( is_print( c ) ) {
	if( insert ) insert_char( c );
		  else overwrite_char( c );
	return;
    };

    switch( c )
    {
	case K_DEFKEY: define_key(); break;
	case K_INSOVR: insert = !insert;
		   put_warning( insert ? "Insert" : "Overwrite", FALSE ); break;
	case K_INDENT: indent = !indent;
		   put_warning( indent ? "Indent ON":"Indent OFF",FALSE); break;
	case K_SENSIT: sensit = !sensit;
		   put_warning( sensit ? "Case sensitivity ON" :
					 "Case sensitivity OFF", FALSE ); break;
	case K_COMPLET: completion = !completion;
		   put_warning( completion ? "Completion ON" :
					     "Completion OFF", FALSE ); break;
	case K_CHCASE:  change_case(); break;
	case K_MATCH:	match(); break;
	case K_DELCHAR: delete_char(); break;
	case K_DELSEL:	delete_selected(); break;
	case K_COPYCHAR:copy_char(); break;
	case K_COPYSEL: copy_and_select(); break;
	case K_PASTE:	paste_buffer(); break;
	case K_MARK:	mark(); break;
	case K_EXMARK:	exchange_mark(); break;
	case K_SEARCH:	search(); break;
	case K_FPREV:	direction = !direction;
	case K_FNEXT:	find_next(); break;
	case K_REPEAT:	repeat(); break;
	case K_SUBST:	substitute(); break;
	case K_REFRESH: clear(); redraw_screen(); break;
	case K_PREVSCR:	prev_screen(); break;
	case K_BOF:	act_col = x = act_x = 0; go_to( 1 ); break;
	case K_NEXTSCR: next_screen(); break;
	case K_EOF:	act_x = LSIZE; ADJUST_X(); go_to( buffer_size ); break;
	case K_UNCTRL:  un_ctrl(); break;
	case K_KILL:	kill_line(); break;
	case K_DELTO:	delete_to(); break;
	case K_WORD:	advance_word(); break;
	case K_DELWORD:	del_next_word(); break;
	case K_BWORD:	back_word(); break;
	case K_DELBWORD:del_word(); break;
	case K_UNDO:	undo(); break;
	case K_HOME:	home_col(); break;
	case K_DELBOL:	delBOL(); break;
	case K_EOL:	act_x = LSIZE; x = EOL( curr ); break;
	case K_DELEOL:	delEOL(); break;
	case K_AGAIN:   repeat_again(); break;

	case K_DOWN:
#ifdef KEY_DOWN
	case KEY_DOWN:
#endif /* KEY_DOWN */
		        move_down(); break;
	case K_LEFT:
#ifdef KEY_LEFT
	case KEY_LEFT:
#endif /* KEY_LEFT */
		        move_left(); break;
	case K_RIGHT:
#ifdef KEY_RIGHT
	case KEY_RIGHT:
#endif /* KEY_RIGHT */
		        move_right(); break;
	case K_UP:
#ifdef KEY_UP
	case KEY_UP:
#endif /* KEY_UP */
		        move_up(); break;

	case K_RETURN:	new_line(); break;
	case K_BSPACE:
	case K_DELETE:	delete_backchar(); break;
	case K_MENU:	return menu(); break;
	case K_COMMAND:	return parse_command(); break;
	case K_HELP:	help(); break;
	case K_PREVB:	select_warea( current_warea-1 ); break;
	case K_NEXTB:	select_warea( current_warea+1 ); break;
	case K_SAVE:	if( changed )
			    if( save_file( fname, 1, buffer_size, TRUE ) )
			        changed = FALSE;
			break;
	case K_EXIT:	exit_and_save( TRUE ); break;
	case K_CENTER:  screen_top = &head;
			return go_to( l_number );

	case K_PIPE:	pipe_through(); break;

#ifdef MOUSE_SUPPORT
	case K_MOUSE:	if( mouse ) disable_mouse();
			else enable_mouse();
			redraw_screen();
		        put_warning( mouse ? "Mouse On" : "Mouse Off", FALSE );
			break;
#endif /* MOUSE_SUPPORT */

	case K_RSHIFT:  set_indent_level( shift ); break;
	case K_LSHIFT:  set_indent_level( -shift ); break;
	case K_NULL:	beep(); break;
	case K_EXPAND:  expand(); break;
	case K_ALIAS: if( subst_alias() ) break;
		      if( completion && expand() ) break;
	default : if( c > 256 || c < K_TAB ) beep();
		  else if( insert ) insert_char( c );
		  else overwrite_char( c );
    }
    return 0;
}



find_next()
{
    if( find( "", direction ) ) adjust_screen( FALSE );
    else put_warning( "Pattern Not Found !", TRUE );
}



search()
{
    uchar *str;

    put_message( "Search ? " );
    str = get_string();
    if( *str == '>' || *str == '<' ) direction = (*str++=='>');
    if( find( str, direction ) ) adjust_screen( FALSE );
    else put_warning( "Pattern Not Found !", TRUE );
}



find( str, dir )
uchar *str;
int dir;
{
    static uchar pattern[LSIZE] = " ";
    line *first;

    if( str[0] ) strcpy( pattern, str );
    first = curr;

    if( dir ) {
	if( x < EOL( curr ) && f_instr( pattern, curr->l+x+1 ) != -1 ) {
	    act_x = x += f_instr( pattern, curr->l+x+1 )+1;
	    return 1;
	}
	++l_number;
	curr = curr->next;
	while( f_instr( pattern, curr->l ) == -1 ) {
	    if( curr == first ) return 0;
	    if( curr == &head ) l_number = 0;
	    curr = curr->next;
	    ++l_number;
	}
	act_x = x = f_instr( pattern, curr->l );
	return 1;
    }
    if( x > 0 && r_instr( pattern, curr->l, x-1 ) != -1 ) {
	act_x = x = r_instr( pattern, curr->l, x-1 );
	return 1;
    }
    curr = curr->prev;
    --l_number;
    while( f_instr( pattern, curr->l ) == -1 ) {
	if( curr == first ) return 0;
	if( curr == &head ) l_number = buffer_size+1;
	curr = curr->prev;
	--l_number;
    }
    act_x = x = r_instr( pattern, curr->l, EOL( curr ) );
    return 1;
}



substitute()
{
    static uchar pat[LSIZE] = "............";
    static uchar subs[LSIZE] = "............";
    int conf, i, j, c, n_subst;
    uchar *str;
    uchar string[LSIZE];

    put_message( "Find > " );
    str = get_string();
    if( str[0] ) strcpy( pat, str );
    put_message( "Substitute by > " );
    str = get_string();
    if( str[0] ) strcpy( subs, str );
    put_message( "From Line > " );
    i = get_val( get_string() );
    if( i < 1 || i > buffer_size ) return;
    put_message( "To Line > " );
    j = get_val( get_string() );
    if( j < 1 || j > buffer_size ) return;
    if( i > j ) { conf = i; i = j; j = conf; }
    put_message( "Individual Confirmations [y/n] ? " );
    conf = ( tolower(get_string()[0]) != 'n' );

    go_to( i );
    n_subst = 0;

    while( find( pat, TRUE ) && l_number >= i && l_number <= j ) {
	i = l_number;
	if( conf ) {
	    adjust_screen( FALSE );
	    put_message( "Confirm?" );
	    place_cursor();
	    c = tolower(get_pref_ch());
	    if( c == 'y' ) {
		subst_string( pat, subs );
		redraw_line( y, curr->l );
		++n_subst;
	    }
	    else if( c != 'n' ) break;
	}
	else {
	    subst_string( pat, subs );
	    ++n_subst;
	}
    }
    adjust_screen( TRUE );
    sprintf( string, "%03d Substitutions !", n_subst );
    put_warning( string, FALSE );
}



subst_string( old, new )
uchar *old, *new;
{
    uchar aux[LSIZE];

    if( x + strlen(old) > EOL( curr ) ) old[strlen(old)-1] = '\0';
    if( EOL( curr ) + strlen( new ) - strlen( old ) >= LSIZE ) return beep();

    changed = TRUE;
    strcpy( aux, curr->l+x+strlen(old) );
    curr->l = REALLOC(curr->l,strlen(curr->l)+strlen(new)-strlen(old)+1);
    curr->l[x] = '\0';
    x+=strlen(new);
    strcat( curr->l, new );
    strcat( curr->l, aux );
}



prev_screen()
{
    int i;

    for( i = 0; i < screen_lines; ++i ) {
	if( screen_top->prev == &head ) {
	    go_to( 1 );
	    redraw_screen();
	    return;
	}
	screen_top = screen_top->prev;
	curr = curr->prev;
	--l_number;
    }
    ADJUST_X();
    redraw_screen();
}



next_screen()
{
    int i;
    line *ptr;

    ptr = screen_top;

    for( i = 0; i < screen_lines; ++i ) {
	ptr = ptr->next;
	curr = curr->next;
	if( curr == &head ) return go_to( buffer_size );
	++l_number;
    }
    screen_top = ptr;
    ADJUST_X();
    redraw_screen();
}



home_col()
{
    int i = 0;

    if( indent ) while(curr->l[i]==' ' || curr->l[i]=='\t')++i;
    act_x = x = ( x > i ? i : 0 );
}



un_ctrl()
{
    uchar c;

    addch( '^' );
    place_cursor();
    refresh();
#ifdef VT_TERM
    c = get_char();
#else /* VT_TERM */
    keypad(stdscr,FALSE);
    c = get_char();
    keypad(stdscr,TRUE);
#endif /* VT_TERM */
    if( c ) {
	if( insert )insert_char( c );
	else overwrite_char( c );
    }
    else {
	beep();
	redraw_line( y, curr->l );
    }
}



kill_line()
{
    if( current_warea == 0 ) { beep(); return; }

    single_sel = FALSE;
    erase_working_area( 0 );
    write_to_buffer( 0, l_number, l_number );
    delete_range( l_number, l_number );
}




delEOL()
{
    strcpy( clip_line, curr->l+x );
    clip_line[strlen(clip_line)-1] = '\0';
    single_sel = TRUE;

    strcpy( curr->l+x, "\n" );
    curr->l = REALLOC( curr->l, x + 2 );
    changed = TRUE;
    redraw_line( y, curr->l );
}



delBOL()
{
    int s = 0;
    if( indent ) while( curr->l[s] == ' ' || curr->l[s] == '\t' ) ++s;
    if( s >= x ) s = 0;

    strncpy( clip_line, curr->l+s, x-s+1 );
    clip_line[x-s] = '\0';
    single_sel = TRUE;

    strcpy( curr->l+s, curr->l+x );
    curr->l = REALLOC( curr->l, strlen( curr->l ) + 1 );
    x = act_x = s;
    changed = TRUE;
    redraw_line( y, curr->l );
}



advance_word()
{
    if( x >= EOL(curr) ) return move_right();

    if( isalnum(curr->l[x]) || curr->l[x] == '_' )
	while( x<EOL(curr) && (isalnum(curr->l[x])||curr->l[x] == '_')) ++x;
    else if( ispunct(curr->l[x]) )
	while( x<EOL(curr) && ispunct(curr->l[x]) ) ++x;
    else if( isspace(curr->l[x]) )
	while( x<EOL(curr) && isspace(curr->l[x]) ) ++x;
    else if( iscntrl(curr->l[x]) )
	while( x<EOL(curr) && iscntrl(curr->l[x]) ) ++x;
    else return;
    act_x = x;
}



back_word()
{
    if( !x ) return move_left();
    --x;

    if( isalnum(curr->l[x]) || curr->l[x] == '_' )
	while( x>=0 && (isalnum(curr->l[x])||curr->l[x] == '_')) --x;
    else if( ispunct(curr->l[x]) )
	while( x>=0 && ispunct(curr->l[x]) ) --x;
    else if( isspace(curr->l[x]) )
	while( x>=0 && isspace(curr->l[x]) ) --x;
    else if( iscntrl(curr->l[x]) )
	while( x>=0 && iscntrl(curr->l[x]) ) --x;
    else return;
    ++x;
    act_x = x;
}



del_word()
{
    int i;

    if( !x ) return beep();
    i = x-1;

    if( isalnum(curr->l[i]) || curr->l[i] == '_' )
	while( i>=0 && (isalnum(curr->l[i])||curr->l[i] == '_')) --i;
    else if( ispunct(curr->l[i]) )
	while( i>=0 && ispunct(curr->l[i]) ) --i;
    else if( isspace(curr->l[i]) )
	while( i>=0 && isspace(curr->l[i]) ) --i;
    else if( iscntrl(curr->l[i]) )
	while( i>=0 && iscntrl(curr->l[i]) ) --i;
    else return;

    ++i;

    strncpy( clip_line, curr->l+i, x-i );
    clip_line[x-i] = '\0';
    single_sel = TRUE;

    strcpy( curr->l+i, curr->l+x );
    curr->l = REALLOC( curr->l, strlen( curr->l ) + 1 );
    x = act_x = i;
    changed = TRUE;
    redraw_line( y, curr->l );
}




del_next_word()
{
    int i;

    if( x >= EOL(curr) ) return beep();
    i = x;

    if( isalnum(curr->l[i]) || curr->l[i] == '_' )
	while( i<EOL(curr) && (isalnum(curr->l[i])||curr->l[i] == '_')) ++i;
    else if( ispunct(curr->l[i]) )
	while( i<EOL(curr) && ispunct(curr->l[i]) ) ++i;
    else if( isspace(curr->l[i]) )
	while( i<EOL(curr) && isspace(curr->l[i]) ) ++i;
    else if( iscntrl(curr->l[i]) )
	while( i<EOL(curr) && iscntrl(curr->l[i]) ) ++i;
    else return;

    strncpy( clip_line, curr->l+x, i-x );
    clip_line[i-x] = '\0';
    single_sel = TRUE;

    strcpy( curr->l+x, curr->l+i );
    curr->l = REALLOC( curr->l, strlen( curr->l ) + 1 );
    changed = TRUE;
    redraw_line( y, curr->l );
}



undo()
{
    curr->l = REALLOC( curr->l, strlen( undo_buffer ) + 1 );
    strcpy( curr->l, undo_buffer );
    redraw_line(y, curr->l);
    ADJUST_X();
}



move_down()
{
    int retv = 1;

    if( x == act_x ) act_col = real_col( act_x, curr->l );

    if( curr->next == &head ) {
	beep();
	retv = 0;
    }
    else {
	curr = curr->next;
	++l_number;
	if( y < screen_lines-1 ) ++y;
	else scroll_up( curr->l );
    }

    act_x = char_at_col( act_col, curr->l );
    ADJUST_X();

    return retv;
}


move_left()
{
    if( x ) act_x = --x;
    else
    {
	act_x = x = LSIZE;
	if(! move_up())
	{
	    act_x = x = 0;
	    return 0;
	};
    }
    return 1;
}


move_right()
{
    if( x < EOL( curr ) ) act_x = ++x;
    else
    {
	act_x = x = 0;
	if(! move_down())
	{
	    x = act_x = EOL( curr );
	    return 0;
	}
    }
    return 1;
}



move_up()
{
    int retv = 1;

    if( x == act_x ) act_col = real_col( act_x, curr->l );

    if( curr->prev == &head ) {
	beep();
	retv = 0;
    }
    else {
	curr = curr->prev;
	--l_number;
	if( y ) --y;
	else scroll_down( curr->l );
    }

    act_x = char_at_col( act_col, curr->l );
    ADJUST_X();

    return retv;
}



delete_char()
{
    if( x < EOL( curr ) ) move_right();
    delete_backchar();
}


delete_backchar()
{
    line *ptr;
    int i, max;

    if( x != 0 ) {
	max = EOL( curr );
	for( i = --x; i <= max; ++i )curr->l[i] = curr->l[i+1];
	redraw_line( y, curr->l );
    }
    else if( curr->prev == &head ) {
	beep();
	return;
    }
    else {
	if( EOL( curr ) + EOL( curr->prev ) >= LSIZE ) return beep();

	curr = curr->prev;
	--l_number;
	act_x = x = EOL( curr );
	curr->l[ strlen( curr->l ) - 1] = '\0';
	curr->l = REALLOC( curr->l, x + strlen( curr->next->l ) + 2 );
	strcat( curr->l, curr->next->l );
	delete_line( curr->next );
	if( y ) {
	    deleteln();
	    redraw_line( --y, curr->l );
 	    if( y == 0 ) refresh();	/* avoid curses bug - Ted Powell */
	    ptr = curr;
	    for( i = y+1; i<screen_lines && ptr!=&head; ++i ) ptr = ptr->next;
	    if( ptr != &head ) redraw_line( screen_lines-1, ptr->l );
	    else redraw_line( screen_lines-1, "" );
#ifdef STATUS_LINE
	    put_status_line();
#endif /* STATUS_LINE */
	}
	else {
	    screen_top = curr;
	    redraw_line( 0, curr->l );
	}
    }
    changed = TRUE;
    act_x = x;
}



word_wrap()
{
    int i = x;

    do --x; while( x > 0 && curr->l[x] != ' ' );
    if( x > 0 ) {
	i = i - ++x;
	place_cursor();
	delete_backchar();
	place_cursor();
	new_line();
	x += i;
	place_cursor();
    }
    else x = i;
}



overwrite_char( c )
uchar c;
{
    if( x >= wrap_margin ) word_wrap();

    if( x >= EOL( curr ) ) return insert_char( c );
    changed = TRUE;
    curr->l[x] = c;
    redraw_line( y, curr->l );
    act_x = ++x;
}



insert_char( c )
uchar c;
{
    register int i;

    if( x >= wrap_margin ) word_wrap();

    if( ( i = EOL( curr )+1 ) >= LSIZE ) return beep();
    changed = TRUE;
    curr->l = REALLOC( curr->l, strlen( curr->l ) + 2 );
    for( ; i >= x; --i ) curr->l[i+1] = curr->l[i];
    curr->l[x] = c;
    redraw_line( y, curr->l );
    act_x = ++x;
}



new_line()
{
    uchar new[LSIZE];
    int old_x;

    old_x = x;
    act_x = x = 0;

    strcpy( new, "" );
    if( indent ) {
	while( x < old_x && curr->l[x] == ' ' || curr->l[x] == '\t' ) {
	   new[x] = curr->l[x];
	   ++x;
	}
	new[act_x = x] = '\0';
    }
    strcat( new, curr->l+old_x );

#ifdef SMART_INDENT
    strcpy( (x == old_x) ? curr->l : (curr->l+old_x), "\n" );
#else /* SMART_INDENT */
    strcpy( curr->l+old_x, "\n" );
#endif /* SMART_INDENT */

    curr->l = REALLOC( curr->l, old_x + 2 );

    clrtoeol();
    curr = insert_newline( curr, new );			       /* Ted Powell */
    if( y < screen_lines-1 ) {
	move( ++y, 0 );
	if( y == 1 ) refresh();				 /* avoid curses bug */
	insertln();
	redraw_line( y, curr->l );
#ifdef STATUS_LINE
	put_status_line();
#endif /* STATUS_LINE */
    }
    else scroll_up( curr->l );
    ++l_number;
    changed = TRUE;
}



scroll_up( l )
uchar *l;
{
#ifdef QUICK_SCROLL
    int i;
    for( i = 0; i < screen_lines / JUMP_SCROLL; ++i ) {
        if( screen_top->next == &head ) break;
        screen_top = screen_top->next;
    }
    y = screen_lines - ( i > 0 ? i : 0) ;
    redraw_screen();
#else /* QUICK_SCROLL */
    last_page = screen_top = screen_top->next;
    scroll( stdscr );
    redraw_line(screen_lines-1, l );
#endif /* QUICK_SCROLL */

#ifdef STATUS_LINE
    put_status_line();
#endif /* STATUS_LINE */
}



scroll_down( l )
uchar *l;
{
#ifdef QUICK_SCROLL
    int i;
    for( i = 0; i < screen_lines / JUMP_SCROLL; ++i ) {
        if( screen_top->prev == &head ) break;
        screen_top = screen_top->prev;
    }
    y = i > 0 ? i-1 : 0;
    redraw_screen();
#else /* QUICK_SCROLL */
    last_page = screen_top = screen_top->prev;
    move( 0, 0 );
    refresh ();			/* avoid curses bug - Ted Powell */
    insertln();
    redraw_line(0, l);
#endif /* QUICK_SCROLL */

#ifdef STATUS_LINE
    put_status_line();
#endif /* STATUS_LINE */
}


char* cmd_char_to_string( c )
int c;
{
    static char aux[NSIZE];
    static char pref_aux[NSIZE];

    switch( c ) {
	case '\t' :	return "<TAB>";
	case '\r' :	return "<RET>";
	case '\b' :	return "<BS>";
	case '\033' :	return "<ESC>";
	case K_DELETE : return "<DEL>";
	case KEY_UP :
	case K_UP :	return "<UP>";
	case KEY_DOWN :
	case K_DOWN :	return "<DOWN>";
	case KEY_LEFT :
	case K_LEFT :	return "<LEFT>";
	case KEY_RIGHT :
	case K_RIGHT :	return  "<RIGHT>";
	default: if( c < ' ' ) sprintf( aux, "^%c", 'A'+c-1 );
		 else if( isascii( c ) ) sprintf( aux, "%c", c );
		 else if( c > PREF ) {
		    strcpy( pref_aux, cmd_char_to_string( c - PREF ) );
		    if( pref_aux[0] == '<' || pref_aux[0] == '>' )
			if( pref_aux[1] == '\0' )
			     sprintf( aux, "<PREF-\"%c\">", pref_aux[0] );
			else sprintf( aux, "<PREF-%s", pref_aux+1 );
		    else sprintf( aux, "<PREF-%s>", pref_aux );
		 }
		 else sprintf( aux, "<\\0%o>", c );
		 return aux;
    }
}


char* cmd_str_to_ascii( str, size )
int *str, size;
{
    static char ascii_str[2*LSIZE];
    int i;

    ascii_str[0] = '\0';
    for( i = 0; i < size; ++i ) {
	if( str[i] == 0 ) break;
	if( strlen( ascii_str ) < 2*LSIZE-12 )
	    strcat( ascii_str, cmd_char_to_string( str[i] ) );
	else {
	    beep();
	    break;
	}
    }
    return ascii_str;
}



int *get_cmd_str()
{
    static int b[LSIZE];
    char *ascii_b;
    int c;

    c = 0;
    while(TRUE) {
	ascii_b = cmd_str_to_ascii( b, c );
	if( edit_position < screen_cols - 10 &&
	    edit_position + strlen( ascii_b ) > screen_cols - 10 )
		ascii_b += strlen( ascii_b ) + edit_position - screen_cols + 10;
	move( screen_lines, edit_position );
	printw( "%s", ascii_b );
	clrtoeol();
	refresh();

	b[c] = get_pref_ch();

	if( b[c] == K_ENTER || b[c] == K_RETURN ) break;
	if( b[c] == K_DELETE || b[c]==K_BSPACE ) {
	    if( c ) --c;
	    else beep();
	    continue;
	}

	if( b[c] == K_UNCTRL ) {
	    addch( '^' );
	    refresh();
#ifdef VT_TERM
	    b[c] = get_char();
#else /* VT_TERM */
	    keypad(stdscr,FALSE);
	    b[c] = get_char();
	    keypad(stdscr,TRUE);
#endif /* VT_TERM */
	}
	if( c < LSIZE-1 ) ++c;
	else beep();
    }
    b[c] = 0;
    return b;
}



uchar *get_string()
{
    static uchar b[LSIZE];
    int *cmds;
    int i = -1;

    cmds = get_cmd_str();

    while(cmds[++i]) b[i] = (uchar)cmds[i];
    b[i] = '\0';
    return b;
}



get_val( str )
uchar *str;
{
    int i = 0;

    switch (*str) {
	case '.' : i = l_number; ++str; break;
	case '$' : i = buffer_size; ++str; break;
	case 'x' : i = x; ++str; break;
	case 'y' : i = y; ++str; break;
	case 'e' : i = EOL( curr ); ++str; break;
	case 'c' : i = real_col(x, curr->l ); ++str; break;
	case 'm' : i = l_mark; ++str; break;
	case 'M' : i = x_mark; ++str; break;
	case 'b' : i = current_warea; ++str; break;
	case '/' : case '*' : return 0;
	case '+' : case '-' : break;
	default  : while( *str == ' ' || *str == '\t' ) ++str;
		   i = atoi( str );
		   while( *str >= '0' && *str <= '9' ) ++str;
    }
    while( *str == ' ' || *str == '\t' ) ++str;
    switch( *str ) {
	case '+' : i += get_val( ++str ); break;
        case '-' : i -= get_val( ++str ); break;
        case '*' : i *= get_val( ++str ); break;
        case '/' : i /= get_val( ++str );
    }
    return i;
}



repeat()
{
    static int last_cmd[LSIZE];
    static int n = -1;
    int *cmd;
    int i;

    put_message( "How Many Times > " );
    i = get_val( get_string() );
    if( i > 0 ) n = i;
    if( n > 0 ) {
	put_message( "Command:" );
	cmd = get_cmd_str();
	if( cmd[0] ) memcpy( last_cmd, cmd, LSIZE * sizeof( int ) );
	rep_command( n, last_cmd );
    }
    put_message( NULL );
}



rep_command( n, cmd )
int n;
int *cmd;
{
    uchar number[20];
    int  num[20];
    int i,j;

    if( interactive==FALSE ) return;
    interactive = FALSE;

    for( i = 1; i <= n; ++i ) {
	command_ptr = cmd;
	while( *command_ptr ) {
	    place_cursor();
	    if( *command_ptr == K_REFRESH|| *command_ptr == K_REPEAT ||
		*command_ptr == K_DEFKEY || *command_ptr == K_UNCTRL ) {
		sprintf( number, "%d",(*command_ptr==K_REFRESH)? l_number :
				      (*command_ptr==K_DEFKEY)? i :
				      (*command_ptr==K_UNCTRL)? x+1 :
				      real_col( x, curr->l)+1 );
		for( j = 0; number[j]; ++j ) num[j] = number[j];
		num[j] = 0;
		rep_command( 1, num );
	    }
	    else if( *command_ptr == PREF_KEY ) {
		interactive = TRUE;
		primitive_key( get_pref_ch() );
		interactive = FALSE;
	    }
	    else primitive_key( *command_ptr );
	    ++command_ptr;
	}
    }

    interactive = TRUE;
}



f_instr( st1, source )
uchar *st1, *source;
{
    register int n, p=0;

    if( !source ) return -1;

    n = strlen( st1 );
    if( sensit ) while( *source ) {
	if( *st1 == *source && strncmp( source, st1, n ) == 0 ) return p;
	++source;
	++p;
    }
    else while( *source ) {
	if( (islower(*st1) ? toupper(*st1) : *st1) ==
	    (islower(*source) ? toupper(*source) : *source) &&
	    case_ins_strncmp( source, st1, n ) == 0 ) return p;
	++source;
	++p;
    }
    return -1;
}



r_instr( st1, source, start )
uchar *st1, *source;
int start;
{
    register int n, p=0;

    if( !source ) return -1;

    n = strlen( st1 );
    if( sensit ) {
	for( p = start; p >= 0; --p )
	if( *st1 == source[p] && strncmp( source+p, st1, n ) == 0 ) return p;
    }
    else {
	for( p = start; p >= 0; --p )
	if( (islower(*st1) ? toupper(*st1) : *st1) == 
	    (islower(source[p]) ? toupper(source[p]) : source[p]) &&
	    case_ins_strncmp( source+p, st1, n ) == 0 ) return p;
    }
    return -1;
}



case_ins_strncmp( s1, s2, n )
uchar *s1, *s2;
int n;
{
    while( ( islower(*s1) ? toupper(*s1) : *s1 ) ==
    	   ( islower(*s2) ? toupper(*s2) : *s2 ) && n>1 ) { --n; ++s1; ++s2; }
    return (islower(*s1)?toupper(*s1):*s1) - (islower(*s2)?toupper(*s2):*s2);
}



parse_command()
{
    uchar string[LSIZE];
    uchar key[LSIZE];
    uchar *comm;
    int number;

    put_message( "FPTED Command >" );
    comm = get_string();

    switch( toupper( comm[0] ) ) {
	case 'Q' : return exit_and_save( FALSE );
	case 'R' : read_to_warea( comm );
		   break;
	case 'W' : write_warea( comm );
		   break;
	case 'G' : number = get_val( comm+1 );
		   go_to( number );
		   break;
	case '!' : exec_shell_cmd( comm+1 );
		   break;
	case 'I' : info();
		   break;
	case 'A' : if(sscanf(comm,"%*s %10s %[^\n]", key, string)!=2) break;
		   create_alias( key, string );
		   break;
	case 'S' : if( strlen(comm)>2 ) save_defs( comm+2 );
		   else print_defs();
		   break;
	case 'L' : read_defs( strlen(comm)>2 ? comm+2 :(uchar*)getenv(DEFVAR) );
		   break;
	case 'X' : return exit_and_save( TRUE );
	case 'B' : number = get_val( comm+1 );
		   select_warea( number );
		   break;
	case 'H' : help();
		   break;
	case 'P' : put_message( "Hit the new PREFIX key !" );
		   number = get_pref_ch();
		   if( number == K_RETURN ) break;
		   pref_key = number;
		   sprintf( string, "New PREFIX key = [%03x HEX] !", pref_key );
		   put_warning( string, FALSE );
		   break;
	case 'D' : put_message( "Delete everything ?" );
	 	   if( toupper( get_string()[0] ) == 'Y' )
			delete_range( 1, buffer_size );
		   break;
	case 'M' : if( comm[1] ) {
		       wrap_margin = atoi( comm+1 );
		       if( wrap_margin <= 0 || wrap_margin > LSIZE )
			   wrap_margin = LSIZE + 1;
		   }
		   sprintf( string, "Wrap margin = %d .", wrap_margin );
		   put_warning( string, FALSE );
		   break;
	case 'O' : if( comm[1] ) {
		       shift = atoi( comm+1 );
		       if( shift <= 0 || shift > LSIZE ) shift = DEF_SHIFT;
		   }
		   sprintf( string, "Shift Width = %d .", shift );
		   put_warning( string, FALSE );
		   break;
	default  : put_warning( "Invalid command ! (^K<H> for help)", TRUE );
    }

#ifdef STATUS_LINE
    put_status_line();
#endif /* STATUS_LINE */

    return 0;
}



exit_and_save( realy_save )
int realy_save;
{
    int c = 'Y';
    int number;

    for( number = 1; number < MAX_WAREAS; ++number )
	if( number != current_warea && wareas[number]._changed ) {
	    put_message( "There are other changed buffers! Quit anyway ?" );
	    if( toupper( get_string()[0] ) != 'Y' ) return;
	    break;
	}

    if( changed ) {
	if( !realy_save ) {
	    put_message( "Save Changes ?" );
	    c = toupper(get_string()[0]);
	}
	if( c == 'Y' && !save_file(fname,1,buffer_size, TRUE)) return;
    }
    finish();
}



finish()
{
    working_area = wareas[0];
    if( changed && getenv(CLIPVAR) )
	save_file( getenv( CLIPVAR ), 2, buffer_size, FALSE );
    move( screen_lines, 0 );
    refresh();
    end_win();
    exit( 0 );
}



create_alias( k, s )
uchar *k, *s;
{
    int i;

    for( i = 0; i< n_alias; ++i )
	if( !strcmp( k, alias_list[i].key )) break;
    if( i == n_alias ) {
	alias_list = (alias*) (n_alias ?
	    realloc( alias_list, sizeof(alias)*++n_alias) :
	    malloc( sizeof(alias) * ++n_alias ) );
	i = n_alias-1;
	strcpy( alias_list[i].key, k );
    }
    strncpy( alias_list[i].subs, s, 4*MAX_KEYS-1 );
    alias_list[i].subs[4*MAX_KEYS-1] = '\0';
}



uchar *find_alias( key )
uchar *key;
{
    int i;

    for( i = 0; i < n_alias; ++i )
	if( strcmp( key, alias_list[i].key )==0 ) return alias_list[i].subs;
    return NULL;
}


subst_alias()
{
    int i;
    uchar key[LSIZE];
    uchar *s;

    if( !x ) return 0;
    i = ( x == 1 || isspace(curr->l[x-1]) ) ? x-1 : x-2;

    if( isalnum(curr->l[i]) || curr->l[i] == '_' )
	while( i>=0 && (isalnum(curr->l[i])||curr->l[i] == '_')) --i;
    else if( ispunct(curr->l[i]) )
	while( i>=0 && ispunct(curr->l[i]) ) --i;
    else if( isspace(curr->l[i]) )
	while( i>=0 && isspace(curr->l[i]) ) --i;
    else if( iscntrl(curr->l[i]) )
	while( i>=0 && iscntrl(curr->l[i]) ) --i;
    else return 0;

    if( ++i >= x ) return 0;
    strncpy( key, curr->l+i, x-i );
    key[x-i] = '\0';
    if( (s = find_alias( key ) ) == NULL ) return 0;
    x = i;
    subst_string( key, s );
    x = i + strlen( s );
    redraw_line( y, curr->l );
    return 1;
}



define_key()
{
    int key, *def;
    char mult[NSIZE];

    put_message( "Press the KEY or the mouse BUTTON you want to redefine !" );
    key = get_pref_ch();
    if( key == '\033' ) {
	put_warning( "Too dangerous to define that !", TRUE );
	return;
    }
    if( mouse && key == MOUSE_EVENT ) {
        key = get_char() - ' '; get_char(); get_char();
	put_message( "Command before moving>" );
	def = get_cmd_str();
	key_definition( BEFORE_BUTTON_DEF( key ), "BMOUSE", def );
	put_message( "Command after moving>" );
	def = get_cmd_str();
	key_definition( AFTER_BUTTON_DEF( key ), "AMOUSE", def );
    }
    else {
	put_message( "How many times should the command repeat ?" );
	strncpy( mult, get_string(), NSIZE-1 );
	mult[NSIZE-1] = '\0';
	if( get_val( mult ) < 1 ) {
	    put_warning( "Number should be bigger than 0", TRUE );
	    return;
	}
	put_message( "Enter command >" );
	def = get_cmd_str();
	key_definition( key, mult, def );
    }
}



key_definition( key, multi, def )
int key;
char *multi;
int *def;
{
    int i, j;
    for( i = 0; i< n_kdefs; ++i ) if( key == key_defs[i].key ) break;

    if( i == n_kdefs ) {
	key_defs = (kdef*) (n_kdefs ?
	    realloc( key_defs, sizeof(kdef)*++n_kdefs) :
	    malloc( sizeof(kdef) * ++n_kdefs ) );
	i = n_kdefs-1;
	key_defs[i].key = key;
    }
    strncpy( key_defs[i].multi, multi, NSIZE-1 );
    key_defs[i].multi[NSIZE-1] = '\0';
    for( j = strlen( key_defs[i].multi ); j >= 0 ; --j ) 
	if( key_defs[i].multi[j] == '\t' ) key_defs[i].multi[j] = ' ';
    memcpy( key_defs[i].meaning, def, LSIZE*sizeof(int) );
}



manage_key( c )
int c;
{
    register int i;

#ifdef STATUS_LINE
    if( status_error ) put_status_line();
#endif /* STATUS_LINE */

#ifdef MOUSE_SUPPORT
    if( c == MOUSE_EVENT ) return handle_mouse_events();
#endif /* MOUSE_SUPPORT */

    for( i = 0; i < n_kdefs; ++i ) if( c == key_defs[i].key ) break;
    if( i == n_kdefs )
    {
	if( is_print( c ) || c == K_DELETE || c == K_BSPACE ||
	    c == K_DELWORD || c == K_DELBWORD ||
	    c == K_DELEOL || c == K_DELBOL || c == K_CHCASE )
	{
	    if( macro_changed || def_macro_ndx >= 2*(LSIZE-1) )
	    {
		def_macro_ndx = 0;
		macro_changed = 0;
	    }
	    def_macro[def_macro_ndx++] = c;
	}
	else macro_changed = 1;
	return primitive_key( c );
    }
    else rep_command( get_val(key_defs[i].multi), key_defs[i].meaning );
}



save_defs( f_name )
uchar *f_name;
{
    int i, j;
    FILE *fptr;
    uchar string[LSIZE];

    if( !f_name ) return;

    fptr = fopen( f_name, "w" );
    if( fptr == NULL ) {
	put_warning( "Cannot write to file !", TRUE );
	return;
    }

    fprintf(fptr,"# FPTED 4.0 - 1990 (C) Fernando Joaquim Ganhao Pereira !\n" );
    fprintf( fptr, "# Lisboa - Portugal .\n#\n#\n" );
    fprintf( fptr, "# Alias definitions:\n" );
    for( i = 0; i < n_alias; ++i )
       fprintf(fptr,"\"%s\"\t== \"%s\"\n",alias_list[i].key,alias_list[i].subs);
    fprintf( fptr, "\n" );
    fprintf( fptr, "# Key defenitions: (consult /usr/include/curses.h)\n" );
    fprintf( fptr, "# Key\tMult\tMeaning\n" );
    for( i = 0; i < n_kdefs; ++i ) {
	fprintf(fptr,"0%o\t%s\t :",key_defs[i].key,key_defs[i].multi);
	for( j = 0; j < LSIZE && key_defs[i].meaning[j] != 0; ++j )
	    fprintf( fptr, " 0x%02x", key_defs[i].meaning[j] );
	fprintf( fptr, "\n" );
    }
    fprintf( fptr, "\nPrefix = %03x HEX\n", pref_key );
    fprintf( fptr, "Wrap margin = %03d\n", wrap_margin );
    fprintf( fptr, "Shift width = %03d\n", shift );
    fprintf( fptr, "%s\n", indent ? "Indent" : "NoIndent" );
    fprintf( fptr, "%s\n", insert ? "Insert" : "Overwrite" );
    fprintf( fptr, "%s\n", completion ? "Completion" : "NoCompletion" );
    fprintf( fptr, "%s\n", sensit ? "Sensitive" : "Insensitive" );
    fclose( fptr );
    
    sprintf( string, "Definitions saved to file \"%s\".", f_name );
    put_warning( string, FALSE );
}



read_defs( f_name )
uchar *f_name;
{
    FILE *fptr;
    uchar multi[LSIZE], b[LSIZE];
    uchar key[MAX_KEYS+1];
    uchar def[4*MAX_KEYS+1];
    int k, meaning[LSIZE];
    int i, j;

    if( !f_name ) f_name = (uchar*)DEFAULT_DEFS;

    fptr = fopen( f_name, "r" );
    if( fptr == NULL ) {
	put_warning( "Cannot read defs file!", TRUE );
	return;
    }

    while( TRUE ) {
	fgets( b, LSIZE, fptr );
	if( feof( fptr ) ) return;
	if( strcmp( b, "\n" ) == 0 ) break;
	if( b[0] == '#' ) continue;
	if( sscanf( b, "%*c%s%*c %*2c %*c%[^\n]", key, def ) != 2 ) continue;
	key[strlen(key)-1] = '\0';
	def[strlen(def)-1] = '\0';
	create_alias( key, def );
    }

    while( TRUE ) {
	fgets( b, LSIZE, fptr );
	if( feof( fptr ) ) return;
	if( strcmp( b, "\n" ) == 0 ) break;
	if( b[0] == '#' ) continue;
	if( sscanf( b, "%o %[^\t]", &k, multi ) != 2 ) continue;
	for( i = 0; b[i] != ':' && i < strlen(b); ++i );
	j = 0;
	while( i < strlen( b ) ) {
	    while( b[i] && b[++i] != ' ' );
	    if( b[i] ) sscanf( b+i, " %*2c%x", &meaning[j++] );
	}
	meaning[j] = 0;
	if( j > 0 ) key_definition( k, multi, meaning );
    }
    fgets( b, LSIZE, fptr );
    if( sscanf( b, "%*s %*c %x", &pref_key ) != 1 ) pref_key = PREF_KEY;
    fgets( b, LSIZE, fptr );
    if( sscanf( b, "%*s %*s %*c %d", &wrap_margin )!= 1) wrap_margin = LSIZE+1;
    fgets( b, LSIZE, fptr );
    if( sscanf( b, "%*s %*s %*c %d", &shift )!= 1) shift = DEF_SHIFT;
    fgets( b, LSIZE, fptr );
    indent = !strncmp( b, "Indent", 6 );
    fgets( b, LSIZE, fptr );
    insert = !strncmp( b, "Insert", 6 );
    fgets( b, LSIZE, fptr );
    completion = !strncmp( b, "Completion", 6 );
    fgets( b, LSIZE, fptr );
    sensit = !strncmp( b, "Sensitive", 9 );
    fclose( fptr );
}



print_defs()
{
    int i, j, ln;

    clear();
    printw( "\nAlias definitions:\n" );
    ln = 3;
    for( i = 0; i < n_alias; ++i ) {
        printw( "\"%s\"\t== \"%s\"\n",alias_list[i].key,alias_list[i].subs);
	if( ++ln >= screen_lines ) { wait_any_key(); ln = 0; }
    }
    
    printw( "\n\n" );
    printw( "Key defenitions:\n" );
    printw( "Key\tMult\tMeaning\n" );
    ln += 4;

    for( i = 0; i < n_kdefs; ++i ) {
	printw( "%s", cmd_char_to_string( key_defs[i].key ) );
	printw( "\t%s\t", key_defs[i].multi );
	printw( "%s\n", cmd_str_to_ascii( key_defs[i].meaning, LSIZE ) );
	if( ++ln >= screen_lines ) { wait_any_key(); ln = 0; }
    }
    if( ln + 7 >= screen_lines ) wait_any_key();
    
    printw( "\nPrefix = %s\n" , cmd_char_to_string( pref_key ) );
    printw( "Wrap margin = %03d\n", wrap_margin );
    printw( "Shift width = %03d\n", shift );
    printw( "\n%s\t", indent ? "Indent" : "NoIndent" );
    printw( "%s\n", insert ? "Insert" : "Overwrite" );
    printw( "%s\n", completion ? "Completion" : "NoCompletion" );
    printw( "Case Sensitivity %s\n", sensit ? "On" : "Off" );

#ifdef MOUSE_SUPPORT
    printw( "Mouse events %s\n", mouse ? "On" : "Off" );
#endif /* MOUSE_SUPPORT */

    wait_any_key();
    redraw_screen();
}



select_warea( n )
int n;
{
    if( n < 0 || n >= MAX_WAREAS ) {
	put_warning( "Invalid Buffer Number !", TRUE );
	return;
    }

    wareas[current_warea] = working_area;
    current_warea = n;
    working_area = wareas[current_warea];
    redraw_screen();

#ifndef STATUS_LINE
    put_status_line();
#endif /* STATUS_LINE */
}



read_to_warea( string )
uchar *string;
{
    int bnumber;

    if( toupper(string[1]) == 'B' ) {
	bnumber = get_val( string+2 );
	read_buffer( bnumber, 1, wareas[bnumber]._buffer_size );
	redraw_screen();
    }
    else if( strlen( string ) > 2 ) {
	if( read_file( string+2, TRUE ) ) redraw_screen();
    }
    else put_warning( "Invalid READ command !", TRUE );
}



read_buffer( n, li, lf )
int n, li, lf;
{
    line *ptr;
    int number;
    uchar string[LSIZE];

    if( n < 0 || n >= MAX_WAREAS || n == current_warea ) {
	put_warning( "Invalid Buffer Number !", TRUE );
	return;
    }

    if( li > lf ) { number = li; li = lf; lf = number; }

    ptr = &wareas[n]._head;
    curr = curr->prev;
    number = 0;
    while( number < lf ) {
	ptr = ptr->next;
	if( ptr == &head ) break;
	++number;
	if( number >= li ) curr = insert_newline( curr, ptr->l );
    }
    go_to( l_number );
    sprintf(string, "Read %d lines from buffer %d !", lf-li+1, n);
    put_warning( string, FALSE );
    changed = TRUE;
}


write_warea( string )
uchar *string;
{
    int buff;
    int lim_inf = 1, lim_sup = buffer_size;
    uchar name[LSIZE];

    strcpy( name, fname );

    if( toupper( string[1] ) == 'B' ) buff = get_val( string+2 );
    else buff = -1;

    while( *string && !isspace( *string ) ) ++string;

    if( *string ) {
	if( *++string == '[' ) {
	    lim_inf = get_val( ++string );
	    while( *string && *string != ',' &&
		   *string != ';' && *string != ':' ) ++string;
	    lim_sup = get_val( ++string );
	    while( *string && *string != ']' ) ++string;
	    if( *string++ != ']' ) {
		put_warning( "Invalid range !", TRUE );
		return;
	    }
	}
	while( *string && isspace( *string ) ) ++string;
	if( strlen( string ) ) strcpy( name, string );
    }
    if( buff >= 0 ) write_to_buffer( buff, lim_inf, lim_sup );
    else if( save_file( name, lim_inf, lim_sup, TRUE ) &&
	     strcmp( name, fname ) == 0 &&
	     lim_inf == 1 && lim_sup == buffer_size ) changed = FALSE;
}



write_to_buffer( n, li, lf )
int n, li, lf;
{
    int i;
    line tmp, *ptr, *store_ptr, *new_line;

    if( n < 0 || n >= MAX_WAREAS || n == current_warea ) {
	put_warning( "Invalid Buffer Number !", TRUE );
	return;
    }
    if( li > lf ) { i = li; li = lf; lf = i; }

    tmp = head;
    store_ptr = wareas[n]._curr;
    ptr = &head;
    i = 0;
    do {
	ptr = ptr->next;
	++i;
	if( i < li || i > lf ) continue;
	new_line = (line *)malloc( sizeof( line ) );
	new_line->l = ALLOC( strlen( ptr->l ) + 1 );
	strcpy( new_line->l, ptr->l );
	store_ptr->next->prev = new_line;
	new_line->next = store_ptr->next;
	new_line->prev = store_ptr;
	store_ptr = store_ptr->next = new_line;
	++wareas[n]._buffer_size;
    } while( ptr != &head );

    wareas[n]._changed = TRUE;
    head = tmp;
}


delete_range( li, lf )
int li, lf;
{
    int i;
    line *ptr;
    uchar string[LSIZE];

    if( li > lf ) { i = li; li = lf; lf = i; }

    ptr = &head;
    i = 0;
    while( i <= lf ) {
	ptr = ptr->next;
	++i;
	if( i > li ) delete_line( ptr->prev );
	if( ptr == &head ) break;
    }
    if( head.next == &head ) insert_newline( &head, "\n" );

    changed = TRUE;
    if( ptr == &head ) {
	ptr = head.prev;
	l_number = buffer_size;
    }
    else l_number = li;

    curr = ptr;
    adjust_screen( TRUE );
    if( lf != li ) {
	sprintf( string, "%d lines deleted.", lf - li + 1 );
	put_warning( string, FALSE );
    }
}



erase_working_area( n )
int n;
{
    line *ptr, *next_ptr;

    if( n < 0 || n >= MAX_WAREAS ) {
	put_warning( "Invalid Buffer Number !", TRUE );
	return;
    }

    next_ptr = wareas[n]._head.next;
    while( TRUE ) {
	ptr = next_ptr;
	next_ptr = ptr->next;
        if( next_ptr == &head ) break;
	free( ptr->l );
	free( ptr );
    }
    ptr->next = ptr->prev = &head;
    wareas[n]._head.prev = wareas[n]._screen_top =
    wareas[n]._curr = wareas[n]._head.next = ptr;
    strcpy( ptr->l, "\n" );
    wareas[n]._x = wareas[n]._y = wareas[n]._act_x = 0;
    wareas[n]._l_number = wareas[n]._buffer_size = 1;
    wareas[n]._changed = TRUE;
}



mark()
{
    uchar string[LSIZE];

    x_mark = x;
    l_mark = l_number;
    sprintf( string, "Mark at line %d, column %d !", l_number, x );
    put_warning( string, FALSE );
}



exchange_mark()
{
    int first_x = x_mark;
    int first_line = l_mark;
    mark();
    x = act_x = first_x;
    go_to( first_line );
}



delete_selected()
{
    int i;

    if( l_mark == l_number ) {
	single_sel = TRUE;
	act_x = (x>x_mark) ? x_mark : x;
	strncpy( clip_line, curr->l+act_x, abs(x-x_mark) );
	clip_line[abs(x-x_mark)] = '\0';
	x = act_x;
	subst_string( clip_line, "" );
	x = act_x;
	redraw_line( y, curr->l );
	return;
    }

    if( l_mark > buffer_size ) {
	put_warning( "Mark no longer valid !", TRUE );
	return;
    }
    if( current_warea == 0 ) { beep(); return; }
    
    single_sel = FALSE;
    if( l_mark > l_number ) { i = l_mark; l_mark = l_number; l_number = i; }

    erase_working_area( 0 );
    write_to_buffer( 0, l_mark, l_number );
    delete_range( l_mark, l_number );
}



copy_and_select()
{
    uchar string[LSIZE];

    if( l_mark == l_number ) {
	single_sel = TRUE;
	strncpy( clip_line, curr->l + (x>x_mark ? x_mark:x), abs(x-x_mark) );
	clip_line[abs(x-x_mark)] = '\0';
	return;
    }

    if( l_mark > buffer_size ) {
	put_warning( "Mark no longer valid !", TRUE );
	return;
    }

    single_sel = FALSE;
    if( current_warea == 0 ) { beep(); return; }
    erase_working_area( 0 );
    write_to_buffer( 0, l_mark, l_number );
    sprintf( string, "%d lines copied to the clip buffer [b0] !",
	     abs( l_mark - l_number ) + 1 );
    put_warning( string, FALSE );
}


copy_char()				 /* copy_char - By Ted Powell */
{
    if( curr->prev == &head || x >= EOL(curr->prev) ) { beep(); return; }
    if( insert ) insert_char( curr->prev->l[x] );
	 else overwrite_char( curr->prev->l[x] );
}



paste_buffer()
{
    if( single_sel ) {
	act_x = x;
	subst_string( "", clip_line );
	x = act_x += strlen( clip_line );
	redraw_line( y, curr->l );
	return;
    }

    if( current_warea == 0 ) { beep(); return; }
    read_buffer( 0, 2, wareas[0]._buffer_size );
    redraw_screen();
    changed = TRUE;
}



match()
{
    int c = 1;
    uchar fnd = curr->l[x];
    line* ptr = curr;
    int number = l_number;
    uchar obj;
    int i = x;

    switch( curr->l[x] ) {
	case '{' : obj = '}'; break;
	case '[' : obj = ']'; break;
	case '(' : obj = ')'; break;
	case '}' : obj = '{'; break;
	case ']' : obj = '['; break;
	case ')' : obj = '('; break;
	default  : return;
    }

    if( fnd == '}' || fnd == ']' || fnd == ')' ) {
	while( c > 0 ) {
	    if( --i < 0 ) {
		ptr = ptr->prev;
		if( --number <= 0 ) {
		    beep();
		    return;
		}
		i = EOL( ptr );
	    }
	    if( ptr->l[i] == fnd ) ++c;
	    else if( ptr->l[i] == obj ) --c;
	}
    }
    else {
	while( c > 0 ) {
	    if( ++i > EOL(ptr) ) {
		ptr = ptr->next;
		if( ++number > buffer_size ) {
		    beep();
		    return;
		}
		i = 0;
	    }
	    if( ptr->l[i] == fnd ) ++c;
	    else if( ptr->l[i] == obj ) --c;
	}
	
    }
    x = act_x = i;
    go_to( number );
}


help()
{
    int i =0;
    uchar buff[LSIZE];
    FILE* fptr;
    char* env_help = getenv( HELPVAR );

    if( !env_help ) env_help = HELP_FILE;

    if( getenv( "PAGER" ) ) {
	sprintf( buff, "%s %s", getenv( "PAGER" ), env_help );
	exec_shell_cmd( buff );
	return;
    }

    fptr = fopen( env_help, "r" );
    if( fptr == NULL ) {
	if( !getenv( HELPVAR ) ) {
	    sprintf( buff, "Environment %s not defined !", HELPVAR );
	    put_warning( buff, TRUE );
	}
	else put_warning( "Cannot read help file!", TRUE );
	return;
    }

    clear();
    while( !feof( fptr ) ) {
	fgets( buff, LSIZE, fptr );
	printw( "%s", buff );
	if( i++ == screen_lines-2 ) {
	    i = 0;
	    put_message( "Press q to STOP or any other key to continue !" );
	    if( toupper( get_pref_ch() ) == 'Q' ) return redraw_screen();
	    clear();
	}
    }
    wait_any_key();
    redraw_screen();
}


info()
{
    int i, c;

    if( screen_lines < 22 || screen_cols < 60 ) {
	put_warning( "Window too small!", TRUE );
	return;
    }

    wareas[current_warea] = working_area;
    clear();
    printw( "\t\t\t\tBuffer Info !" );
    for( i = 0; i < MAX_WAREAS; ++i ) buffer_info( i, FALSE );
    put_message( "Use <TAB>, <SPACE> or the cursor keys to select a buffer!" );
    i = current_warea;

    while( TRUE ) {
	buffer_info( i, TRUE );
	refresh();
	c = get_pref_ch();
	buffer_info( i, FALSE );

	switch( c ) {
	    case K_ESCAPE: redraw_screen(); return;
	    case K_TAB:
	    case ' ' :
	    case K_DOWN:
	    case K_RIGHT:
#ifdef KEY_DOWN
	    case KEY_DOWN:
	    case KEY_RIGHT:
#endif /* KEY_DOWN */
		 if( ++i >= MAX_WAREAS ) i = 0;
		 break;
#ifdef KEY_UP
	    case KEY_UP:
	    case KEY_LEFT:
#endif /* KEY_UP */
	    case K_UP:
	    case K_LEFT:
	    case K_DELETE:
	    case K_BSPACE: if( --i < 0 ) i = MAX_WAREAS-1;
		 break;
	    case K_RETURN: select_warea( i );
		       return;
	    case MOUSE_EVENT: get_char(); get_char();
			      i = get_char() - '!' - 1;
			      if( i >= MAX_WAREAS ) i = MAX_WAREAS-1;
			      else if( i < 0 ) i = 0;
			      break;
	    default: beep();
	}
    }
}



buffer_info( i, sel )
int i, sel;
{
    move( i+1, 0 );
#ifdef A_REVERSE
    if( sel ) attron( A_REVERSE );
#endif /* A_REVERSE */

    printw( "%2s B%02d --- line %4d of %4d %s--- %s",
	     sel ? "->" : "  ", i,
	     wareas[i]._l_number, wareas[i]._buffer_size,
	     wareas[i]._changed  ? "[changed] " : "----------",
	     wareas[i]._fname[0] ? wareas[i]._fname : (uchar*)"No Name" );

#ifdef A_REVERSE
	attroff( A_REVERSE );
#endif /* A_REVERSE */
    clrtoeol();
}



wait_any_key()
{
    int key, old_x, old_y;
    getyx( stdscr, old_y, old_x );
    put_message( "Press any key to continue !" );
    refresh();
    key = get_pref_ch();
    put_message( NULL );
    move( old_y, old_x );
    return key;
}



#ifdef UNIX
#ifdef VT_TERM

jmp_buf ret_buff;

void catch_sig_alrm( int sig )
{
    longjmp( ret_buff, 1 );
}

#endif /* VT_TERM */
#endif /* UNIX */



get_esc_ch()
{
    static int c, esc_c ;

#ifdef VT_TERM
    if( esc_c ) {
	c = esc_c;
	esc_c = 0;
    }
    else
#endif /* VT_TERM */
    c = get_char();

#ifdef VT_TERM
    if( c == K_ESCAPE || c == PREF+K_ESCAPE ) {
#ifdef UNIX
	signal( SIGALRM, catch_sig_alrm );
	alarm( ESC_TIMEOUT );
	if( setjmp( ret_buff ) ) esc_c = 0;
	else  esc_c = get_char();
	alarm( 0 );
#else /* UNIX */
	esc_c = get_char();
#endif /* UNIX */
	if( esc_c == '[' || esc_c == 'O') {
	    c = ((esc_c == '[')+1)<< 8;
	    do c += (esc_c = get_char());
	    while( !(esc_c == '~' || isalpha(esc_c) && esc_c != 'O' ));
	    esc_c = 0;
	}
    }
#endif /* VT_TERM */

    return c;
}



get_pref_ch()
{
    static int c, old_y, old_x;

    c = get_esc_ch();

    if( c == pref_key ) {
	getyx( stdscr, old_y, old_x );
	move( screen_lines, screen_cols - TABS );
#ifdef A_REVERSE
	attron( A_REVERSE );
	printw( "PREFIX" );
	attroff( A_REVERSE );
#else  /* A_REVERSE */
	printw( "PREFIX" );
#endif /* A_REVERSE */
	move( old_y, old_x );
	refresh();
	c = PREF + get_esc_ch();
	move( screen_lines, screen_cols - TABS );
	clrtoeol();
	move( old_y, old_x );
    }

    return c;
}



menu()
{
    static int option = 1;
    int lim_inf, lim_sup;
    int i, c = 0;
    
    if( screen_lines < 20 || screen_cols < 60 ) return parse_command();

    menu_wind = subwin( stdscr, 15, 40, 3, 20 );
    wprintw( menu_wind, "\n                  FPTED - OPTIONS\n" );
    wprintw( menu_wind, "       1 - HELP.\n" );
    wprintw( menu_wind, "       2 - Save\n" );
    wprintw( menu_wind, "       3 - Read File\n" );
    wprintw( menu_wind, "       4 - Show Definitions\n" );
    wprintw( menu_wind, "       5 - Info/Select Buffer\n" );
    wprintw( menu_wind, "       6 - Read Buffer\n" );
    wprintw( menu_wind, "       7 - Shell Escape\n" );
    wprintw( menu_wind, "       8 - Write to file\n" );
    wprintw( menu_wind, "       9 - Goto Line\n" );
    wprintw( menu_wind, "       A - Command Line\n" );
    wprintw( menu_wind, "       B - Save and Exit\n" );
    wprintw( menu_wind, "       C - Quit\n" );
#ifdef A_REVERSE
    wattron( menu_wind, A_REVERSE );
#endif /* A_REVERSE */
    wmove( menu_wind, 0, 0 );
    for( i = 0; i < 40; ++i ) waddch( menu_wind, '*' );
    for( i = 1; i < 14; ++i ) {
	wmove( menu_wind, i, 0 );
	waddch( menu_wind, '*' );
	wmove( menu_wind, i, 39 );
	waddch( menu_wind, '*' );
    }
    wmove( menu_wind, 14, 0 );
    for( i = 0; i < 40; ++i ) waddch( menu_wind, '*' );

#ifdef A_REVERSE
    wattroff( menu_wind, A_REVERSE );
#endif /* A_REVERSE */
    wrefresh( menu_wind );

    put_message( "Use <TAB>, <SPACE> or the cursor keys to select one option!");

    while( c != K_RETURN && c != K_ESCAPE ) {
	wmove( menu_wind, option+1, 2 );
#ifdef A_REVERSE
	wattron( menu_wind, A_REVERSE );
	wprintw( menu_wind, " -> " );
	wattroff( menu_wind, A_REVERSE );
#else /* A_REVERSE */
	wprintw( menu_wind, " -> " );
#endif /* A_REVERSE */
	wrefresh( menu_wind );
	c = get_pref_ch();
	wmove( menu_wind, option+1, 2 );
	wprintw( menu_wind, "    " );

	if( isdigit( c ) ) {
	    option = c - '0';
	    break;
	}

	if( isalpha( c ) && toupper( c ) <= 'C' ) {
	    option = toupper(c) + 10 - 'A';
	    break;
	}

	switch( c ) {
	    case K_ESCAPE: break;
	    case K_TAB: case ' ' : case K_DOWN: case K_RIGHT:
#ifdef KEY_DOWN
	    case KEY_DOWN: case KEY_RIGHT:
#endif /* KEY_DOWN */
		 ++option;
		 if( option > 12 ) option = 1;
		 break;
	    case K_UP: case K_LEFT:
	    case K_DELETE: case K_BSPACE:
#ifdef KEY_UP
	    case KEY_UP: case KEY_LEFT:
#endif /* KEY_UP */
		--option;
		if( option <= 0 ) option = 12;
	    case K_RETURN: break;
	    case MOUSE_EVENT: get_char(); get_char();
			      option = get_char() - '!' - 4;
			      if( option < 1 ) option = 1;
			      if( option > 12 ) option = 12;
			      break;
	    default: beep();
	}
    }
    delwin( menu_wind );
    redraw_screen();

    if( c != K_ESCAPE ) switch( option ) {
	case  1: return help();
	case  2: return write_warea( "w" );
        case  3: put_message( "File Name >" );
		 if( read_file( get_string(), TRUE ) ) redraw_screen();
		 break;
	case  4: return print_defs();
	case  5: return info();
	case  6: put_message( "Buffer number >" );
		 option = get_val( get_string() );
		 read_buffer( option, 1, wareas[option]._buffer_size );
		 redraw_screen();
		 return 1;
	case  7: put_message( "SHELL $" );
		 return exec_shell_cmd( get_string() );
	case  8: put_message( "From Line >" );
		 lim_inf = get_val( get_string() );
		 put_message( "To Line >" );
		 lim_sup = get_val( get_string() );
		 put_message( "File Name >" );
		 return save_file( get_string(), lim_inf, lim_sup, TRUE );
	case  9: put_message( "Line number >" );
		 return go_to( get_val( get_string() ) );
	case 10: return parse_command();
	case 11: return exit_and_save( TRUE );
	case 12: return exit_and_save( FALSE );
	default: return 1;
    }
}



exec_shell_cmd( com )
uchar *com;
{
#ifdef DISABLE_SHELL_ESCAPES
    put_warning( "Shell escapes disabled !", TRUE );
#else /* DISABLE_SHELL_ESCAPES */
    clear();
    end_win();
    if( com[0] )system( com );
    printf( "\nHit RETURN to continue !" );
    fflush( stdout );
    while( getchar() != '\n' );
    init_scr();
    redraw_screen();
#endif /* DISABLE_SHELL_ESCAPES */
}



change_case()
{
    if( isupper( curr->l[x] ) ) curr->l[x] = tolower( curr->l[x] );
    else if( islower( curr->l[x] ) ) curr->l[x] = toupper( curr->l[x] );
    redraw_line( y, curr->l );
    changed = TRUE;
    move_right();
}



repeat_again()
{
    int i;
    for( i = 0; i < def_macro_ndx; ++i ) primitive_key( def_macro[i] );
}



char_at_col( col, str )
int col;
uchar *str;
{
    register int i=0, j=0;

    while( j < col ) {
	j += CSZ( *str, j );
	++str;
	++i;
    }
    return i;
}



#ifdef MOUSE_SUPPORT


handle_mouse_events()
{
    int i, button, mouse_x, mouse_y, button_def;

    button = get_char() - ' ';
    mouse_x = get_char() - '!' + first_col;
    mouse_y = get_char() - '!';

    button_def = BEFORE_BUTTON_DEF( button );
    for( i = 0; i < n_kdefs; ++i ) if( button_def == key_defs[i].key ) break;
    if( i != n_kdefs ) rep_command( 1, key_defs[i].meaning );

    go_to( l_number + mouse_y - y );
    act_x = char_at_col( mouse_x, curr->l );
    ADJUST_X();

    button_def = AFTER_BUTTON_DEF( button );
    for( i = 0; i < n_kdefs; ++i ) if( button_def == key_defs[i].key ) break;
    if( i != n_kdefs ) rep_command( 1, key_defs[i].meaning );

    return 0;
}


#endif /* MOUSE_SUPPORT */



delete_to()
{
    uchar c, *ptr;

    if( x >= EOL( curr ) ) return;

    put_message( "Delete to ?" );
    c = get_char();
    ptr = (curr->l)+(x+1);
    while( *ptr != c && *ptr ) ++ptr;

    if( *ptr == c ) {
	strcpy( clip_line, (curr->l)+x );
	clip_line[(ptr-curr->l)-x+1] = '\0';
	subst_string( clip_line, "" );
	redraw_line( y, curr->l );
	single_sel = TRUE;
    }
    act_x = x;

#ifdef STATUS_LINE
    put_status_line();
#endif /* STATUS_LINE */
}



uchar* find_backwards( pattern )
uchar *pattern;
{
    static line *ptr = &head;
    static uchar patt[NSIZE];
    static int pos = 0, size = 0;

    if( !repeating_key || ptr == &head ) {
	pos = x - size - 1;
	ptr = curr;
	if( !repeating_key ) {
	    strncpy( patt, pattern, NSIZE-1 );
	    patt[NSIZE-1] = '\0';
	    size = strlen( patt );
	}
    }
    if( --pos < 0 ) {
	ptr = ptr->prev;
        if( ptr == &head ) return repeating_key ? patt : NULL;
	pos = EOL( ptr );
    }

    if( size == 0 ) return NULL;

    while( 1 ) {
	while( pos >= 0 )
	    if( ptr->l[pos]==*patt &&
		(pos == 0 || !(isalnum(ptr->l[pos-1]) || ptr->l[pos-1]=='_')) &&
		!strncmp(ptr->l+pos, patt, size) &&
		(isalnum(ptr->l[pos+size]) || ptr->l[pos+size] == '_')
	    ) return ptr->l + pos;
	    else  --pos;

	ptr = ptr->prev;
        if( ptr == &head ) return repeating_key ? patt : NULL;
	pos = EOL( ptr );
    }
}



expand()
{
    int i, j;
    uchar key[LSIZE];
    uchar subst[LSIZE];
    uchar *s;

    if( x == 0 || isspace(curr->l[x-1]) ) return 0;
    i = x - 1;

    if( isalnum(curr->l[i]) || curr->l[i] == '_' )
	while( i>=0 && (isalnum(curr->l[i])||curr->l[i] == '_')) --i;
    else return 0;

    if( ++i >= x ) return 0;
    strncpy( key, curr->l+i, x-i );
    key[x-i] = '\0';

    do {
	if( ( s = find_backwards( key ) ) == NULL ) return 0;

	j = 1;
	if( isalnum(s[0]) || s[0] == '_' )
	    while( s[j] != '\n' && (isalnum(s[j]) || s[j] == '_')) ++j;
	else return 0;
	strncpy( subst, s, j );
	subst[j] = '\0';
    } while( strcmp( key, subst ) == 0 );

    x = i;
    subst_string( key, subst );
    act_x = x = i + strlen( subst );
    redraw_line( y, curr->l );
    return 1;
}



set_indent_level( delta )
int delta;
{
    int col = 0, i = 0;
    uchar init[LSIZE], subst[LSIZE];

    while( curr->l[i] == ' ' || curr->l[i] == '\t' ) {
	init[i] = curr->l[i];
	col += CSZ( init[i], col );
	++i;
    }
    init[i] = '\0';

    col += delta;
    if( col < 0 ) col = 0;

    i = 0;
    while( col >= TABS ) {
	subst[i++] = '\t';
	col -= TABS;
    };
    while( col-- > 0 ) subst[i++] = ' ';
    subst[i] = '\0';

    x = 0;
    subst_string( init, subst );
    act_x = x = i;
    redraw_line( y, curr->l );
}



pipe_through()
{
#ifdef DISABLE_SHELL_ESCAPES
    put_warning( "Shell escapes disabled !", TRUE );
#else /* DISABLE_SHELL_ESCAPES */

#ifdef UNIX

    int fd_in[2], fd_out[2];
    uchar buff[LSIZE];
    uchar command[LSIZE];
    FILE *fptr;
    line *read_ptr, *write_ptr;
    int pid;
    int flags;

    put_message( "Pipe through command>" );
    sprintf( command, "exec %s", get_string() );

    end_win();

    pipe( fd_in );
    pipe( fd_out );

    pid = fork();

    if( pid == -1 ) return;

    if( pid != 0 ) {
	signal( SIGINT, SIG_IGN );
	signal( SIGQUIT, SIG_IGN );
	signal( SIGPIPE, SIG_IGN );
	close( fd_in[0] );
	close( fd_out[1] );
	flags = fcntl( fd_out[0], F_GETFL, 0 );
	fcntl( fd_out[0], F_SETFL, flags | O_NDELAY );
	fptr = fdopen( fd_out[0], "r" );

	read_ptr = curr->prev;

	if( single_sel ) {
	    write( fd_in[1], clip_line, strlen( clip_line ) );
	    write( fd_in[1], "\n", 1 );
	}
	else {
	    write_ptr = wareas[0]._head.next->next;
	    while( write_ptr != &head ) {

		while( TRUE ) {
		    if( fgets( buff, LSIZE-1, fptr ) == NULL ) break;
		    if( buff[strlen(buff)-1] != '\n' ) strcat( buff, "\n" );
		    read_ptr = insert_newline( read_ptr, buff );
		}

		write( fd_in[1], write_ptr->l, strlen( write_ptr->l ) );
		write_ptr = write_ptr->next;
	    }
	}
	close( fd_in[1] );

	fcntl( fd_out[0], F_SETFL, flags );

	while( TRUE ) {
	    if( fgets( buff, LSIZE-1, fptr ) == NULL ) break;
	    if( buff[strlen(buff)-1] != '\n' ) strcat( buff, "\n" );
	    read_ptr = insert_newline( read_ptr, buff );
	}

	fclose( fptr );
	wait( NULL );
    }
    else {
	close( 0 );
	dup( fd_in[0] );
	close( fd_in[0] );
	close( 1 );
	dup( fd_out[1] );
	close( 2 );
	dup( fd_out[1] );
	close( fd_out[1] );
	close( fd_in[1] );
	close( fd_out[0] );
	execlp( "sh", "sh", "-c", command, NULL );
	exit( 0 );
    }

    changed = TRUE;
    init_scr();
    signal( SIGINT, SIG_DFL );
    signal( SIGQUIT, SIG_DFL );
    signal( SIGPIPE, SIG_DFL );

#endif /* UNIX */

    go_to( l_number );
    redraw_screen();

#endif /* DISABLE_SHELL_ESCAPES */
}



get_char()
{
    if( interactive || !command_ptr[1] ) return getch();
    ++command_ptr;
    return *command_ptr;
}


/* End of File */

