diff options
Diffstat (limited to 'kill.c')
-rw-r--r-- | kill.c | 256 |
1 files changed, 256 insertions, 0 deletions
@@ -0,0 +1,256 @@ +/** \file kill.c + The killring. + + Works like the killring in emacs and readline. The killring is cut + and paste with a memory of previous cuts. It supports integration + with the X clipboard. +*/ + +#include <stdlib.h> +#include <stdio.h> +#include <wchar.h> +#include <termios.h> +#include <unistd.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> + + +#include "config.h" +#include "util.h" +#include "wutil.h" +#include "kill.h" +#include "proc.h" +#include "sanity.h" +#include "common.h" +#include "env.h" +#include "expand.h" +#include "exec.h" +#include "parser.h" + +/** + Maximum entries in killring +*/ +#define KILL_MAX 8192 + + +static ll_node_t /** Last kill string */*kill_last=0, /** Current kill string */*kill_current =0; +/** + Contents of the X clipboard, at last time we checked it +*/ +static wchar_t *cut_buffer=0; + +/** + Test if the xsel command is installed +*/ +static int has_xsel() +{ + wchar_t *path = get_filename( L"xsel" ); + if( path) + { + free(path); + return 1; + } + else + return 0; +} + + + +/** + Add the string to the internal killring +*/ +static void kill_add_internal( wchar_t *str ) +{ + if( wcslen( str ) == 0 ) + return; + + if( kill_last == 0 ) + { + kill_current = kill_last=malloc( sizeof( ll_node_t ) ); + kill_current->data = wcsdup(str); + kill_current->prev = kill_current; + } + else + { + kill_current = malloc( sizeof( ll_node_t ) ); + kill_current->data = kill_last->data; + kill_last->data = wcsdup(str); + kill_current->prev = kill_last->prev; + kill_last->prev = kill_current; + kill_current = kill_last; + } +} + + +void kill_add( wchar_t *str ) +{ + kill_add_internal(str); + + if( !has_xsel() ) + return; + + /* This is for sending the kill to the X copy-and-paste buffer */ + wchar_t *disp; + if( (disp = env_get( L"DISPLAY" )) ) + { + wchar_t *escaped_str = expand_escape( wcsdup(str), 1 ); + wchar_t *cmd = wcsdupcat2(L"echo ", escaped_str, L"|xsel -b",0); + exec_subshell( cmd, 0 ); + free( cut_buffer ); + free( cmd ); + + cut_buffer = escaped_str; + } +} + + +wchar_t *kill_yank_rotate() +{ + if( kill_current == 0 ) + return L""; + kill_current = kill_current->prev; + return (wchar_t *)kill_current->data; +} + +/** + Check the X clipboard. If it has been changed, add the new + clipboard contents to the fish killring. +*/ +static void kill_check_x_buffer() +{ + wchar_t *disp; + + if( !has_xsel() ) + return; + + + if( (disp = env_get( L"DISPLAY" )) ) + { + int i; + wchar_t *cmd = L"xsel -t 500 -b"; + wchar_t *new_cut_buffer=0; + array_list_t list; + al_init( &list ); + exec_subshell( cmd, &list ); + + for( i=0; i<al_get_count( &list ); i++ ) + { + wchar_t *next_line = expand_escape(wcsdup(al_get( &list, i )), 0); + if( i==0 ) + { + new_cut_buffer = next_line; + } + else + { + wchar_t *old = new_cut_buffer; + new_cut_buffer= wcsdupcat2( new_cut_buffer, L"\\n", next_line, 0 ); + free( old ); + free( next_line ); + } + } + + if( new_cut_buffer ) + { + /* + The buffer is inserted with backslash escapes, + since we don't really like tabs, newlines, + etc. anyway. + */ + + if( cut_buffer != 0 ) + { + if( wcscmp( new_cut_buffer, cut_buffer ) == 0 ) + { + free( new_cut_buffer ); + new_cut_buffer = 0; + } + else + { + free( cut_buffer ); + cut_buffer = 0; + } + } + if( cut_buffer == 0 ) + { + cut_buffer = new_cut_buffer; + kill_add_internal( cut_buffer ); + } + } + + al_foreach( &list, (void (*)(const void *))&free ); + al_destroy( &list ); + } +} + + +wchar_t *kill_yank() +{ + kill_check_x_buffer(); + if( kill_current == 0 ) + return L""; + kill_current=kill_last; + return (wchar_t *)kill_current->data; +} + +void kill_sanity_check() +{ + int i; + if( is_interactive ) + { + /* Test that the kill-ring is consistent */ + if( kill_current != 0 ) + { + int kill_ok = 0; + ll_node_t *tmp = kill_current->prev; + for( i=0; i<KILL_MAX; i++ ) + { + if( tmp == 0 ) + break; + if( tmp->data == 0 ) + break; + + if( tmp == kill_current ) + { + kill_ok = 1; + break; + } + tmp = tmp->prev; + } + if( !kill_ok ) + { + debug( 0, + L"Killring inconsistent" ); + sanity_lose(); + } + } + + } +} + +void kill_init() +{ +} + +void kill_destroy() +{ + if( cut_buffer ) + free( cut_buffer ); + + if( kill_current != 0 ) + { + kill_current = kill_last->prev; + kill_last->prev = 0; + + while( kill_current ) + { + ll_node_t *tmp = kill_current; + kill_current = kill_current->prev; + free( tmp->data ); + free( tmp ); + } + } + +} + |