2005-09-20 23:26:39 +10:00
/** \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 .
*/
2006-08-11 11:18:35 +10:00
# include "config.h"
2005-09-20 23:26:39 +10:00
# 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>
2006-02-28 23:17:16 +10:00
# include "fallback.h"
2005-09-20 23:26:39 +10:00
# include "util.h"
2006-02-28 23:17:16 +10:00
2005-09-20 23:26:39 +10:00
# include "wutil.h"
# include "kill.h"
# include "proc.h"
# include "sanity.h"
# include "common.h"
# include "env.h"
# include "exec.h"
2006-06-13 00:12:33 +10:00
# include "halloc.h"
2006-10-19 21:50:23 +10:00
# include "path.h"
2005-09-20 23:26:39 +10:00
/**
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 ;
/**
2010-11-05 09:22:28 -06:00
Test if the xsel command is installed . Since this is called often ,
cache the result .
2005-09-20 23:26:39 +10:00
*/
static int has_xsel ( )
{
2010-11-05 09:22:28 -06:00
static int called = 0 ;
static int res = 0 ;
2005-09-20 23:26:39 +10:00
2010-11-05 09:22:28 -06:00
if ( ! called ) {
void * context = halloc ( 0 , 0 ) ;
wchar_t * path = path_get_path ( context , L " xsel " ) ;
res = ! ! path ;
halloc_free ( context ) ;
called = 1 ;
}
2005-09-20 23:26:39 +10:00
2010-11-05 09:22:28 -06:00
return res ;
}
2005-09-20 23:26:39 +10:00
/**
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 )
{
2010-11-05 09:32:05 -06:00
wchar_t * cmd = NULL ;
wchar_t * escaped_str ;
2005-09-20 23:26:39 +10:00
kill_add_internal ( str ) ;
2010-11-05 09:32:05 -06:00
/*
Check to see if user has set the FISH_CLIPBOARD_CMD variable ,
and , if so , use it instead of checking the display , etc .
I couldn ' t think of a safe way to allow overide of the echo
command too , so , the command used must accept the input via stdin .
*/
wchar_t * clipboard ;
if ( ( clipboard = env_get ( L " FISH_CLIPBOARD_CMD " ) ) )
{
escaped_str = escape ( str , 1 ) ;
cmd = wcsdupcat ( L " echo -n " , escaped_str , clipboard ) ;
}
else
{
/* This is for sending the kill to the X copy-and-paste buffer */
if ( ! has_xsel ( ) ) {
return ;
}
wchar_t * disp ;
if ( ( disp = env_get ( L " DISPLAY " ) ) )
{
escaped_str = escape ( str , 1 ) ;
cmd = wcsdupcat ( L " echo " , escaped_str , L " |xsel -b " ) ;
}
}
if ( cmd ! = NULL )
{
if ( exec_subshell ( cmd , 0 ) = = - 1 )
{
/*
Do nothing on failiure
*/
}
free ( cut_buffer ) ;
free ( cmd ) ;
2005-09-20 23:26:39 +10:00
2010-11-05 09:32:05 -06:00
cut_buffer = escaped_str ;
2005-09-20 23:26:39 +10:00
}
}
2006-10-12 23:27:32 +10:00
/**
Remove the specified node from the circular list
*/
static void kill_remove_node ( ll_node_t * n )
{
if ( n - > prev = = n )
{
kill_last = kill_current = 0 ;
}
else
{
ll_node_t * nxt = n - > prev ;
while ( nxt - > prev ! = n )
{
nxt = nxt - > prev ;
}
nxt - > prev = n - > prev ;
if ( kill_last = = n )
{
kill_last = n - > prev ;
}
kill_current = kill_last ;
free ( n - > data ) ;
free ( n ) ;
}
}
/**
Remove first match for specified string from circular list
*/
static void kill_remove ( wchar_t * s )
{
2006-10-13 08:32:00 +10:00
ll_node_t * n , * next = 0 ;
2006-10-12 23:27:32 +10:00
if ( ! kill_last )
{
return ;
}
for ( n = kill_last ;
n ! = kill_last | | next = = 0 ;
n = n - > prev )
{
if ( wcscmp ( ( wchar_t * ) n - > data , s ) = = 0 )
{
kill_remove_node ( n ) ;
2006-10-17 01:00:17 +10:00
break ;
2006-10-12 23:27:32 +10:00
}
next = n ;
}
}
void kill_replace ( wchar_t * old , wchar_t * new )
{
kill_remove ( old ) ;
kill_add ( new ) ;
}
2005-09-20 23:26:39 +10:00
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 ) ;
2006-10-20 01:36:03 +10:00
if ( exec_subshell ( cmd , & list ) ! = - 1 )
2005-09-20 23:26:39 +10:00
{
2006-10-20 01:36:03 +10:00
for ( i = 0 ; i < al_get_count ( & list ) ; i + + )
2005-09-20 23:26:39 +10:00
{
2006-10-20 01:36:03 +10:00
wchar_t * next_line = escape ( ( wchar_t * ) al_get ( & list , i ) , 0 ) ;
if ( i = = 0 )
2005-09-20 23:26:39 +10:00
{
2006-10-20 01:36:03 +10:00
new_cut_buffer = next_line ;
2005-09-20 23:26:39 +10:00
}
else
{
2006-10-20 01:36:03 +10:00
wchar_t * old = new_cut_buffer ;
2007-09-29 07:32:27 +10:00
new_cut_buffer = wcsdupcat ( new_cut_buffer , L " \\ n " , next_line ) ;
2006-10-20 01:36:03 +10:00
free ( old ) ;
free ( next_line ) ;
2005-09-20 23:26:39 +10:00
}
}
2006-10-20 01:36:03 +10:00
if ( new_cut_buffer )
2005-09-20 23:26:39 +10:00
{
2006-10-20 01:36:03 +10:00
/*
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 ) ;
}
2005-09-20 23:26:39 +10:00
}
}
2006-06-13 07:47:42 +10:00
al_foreach ( & list , & free ) ;
2005-09-20 23:26:39 +10:00
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 ) ;
}
}
}