fish-shell/kill.cpp

229 lines
4.4 KiB
C++
Raw Normal View History

/** \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 "config.h"
#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>
2012-03-04 11:27:41 +00:00
#include <algorithm>
#include "fallback.h"
#include "util.h"
#include "wutil.h"
#include "kill.h"
#include "proc.h"
#include "sanity.h"
#include "common.h"
#include "env.h"
#include "exec.h"
#include "path.h"
/**
Maximum entries in killring
*/
#define KILL_MAX 8192
2012-03-04 03:37:55 +00:00
/** Last kill string */
2012-03-04 05:46:06 +00:00
//static ll_node_t *kill_last=0;
2012-03-04 03:37:55 +00:00
/** Current kill string */
2012-03-04 05:46:06 +00:00
//static ll_node_t *kill_current=0;
2012-03-04 03:37:55 +00:00
/** Kill ring */
2012-03-04 05:46:06 +00:00
typedef std::list<wcstring> kill_list_t;
static kill_list_t kill_list;
/**
Contents of the X clipboard, at last time we checked it
*/
static wchar_t *cut_buffer=0;
/**
Test if the xsel command is installed. Since this is called often,
cache the result.
*/
static int has_xsel()
{
static int called=0;
static int res = 0;
2012-01-30 07:22:42 +00:00
if (!called) {
2012-01-30 07:22:42 +00:00
wchar_t *path = path_get_path( L"xsel" );
res = !!path;
2012-01-30 07:22:42 +00:00
free(path);
called = 1;
2012-01-30 07:22:42 +00:00
}
return res;
}
2012-03-04 03:37:55 +00:00
void kill_add( const wcstring &str )
{
2012-03-04 05:46:06 +00:00
ASSERT_IS_MAIN_THREAD();
if (str.empty())
return;
wchar_t *cmd = NULL;
wchar_t *escaped_str;
2012-03-04 05:46:06 +00:00
kill_list.push_front(str);
/*
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.
*/
const env_var_t clipboard_wstr = env_get_string(L"FISH_CLIPBOARD_CMD");
if( !clipboard_wstr.missing() )
{
2012-03-04 03:37:55 +00:00
escaped_str = escape( str.c_str(), 1 );
cmd = wcsdupcat(L"echo -n ", escaped_str, clipboard_wstr.c_str());
}
else
{
/* This is for sending the kill to the X copy-and-paste buffer */
if( !has_xsel() ) {
return;
}
const env_var_t disp_wstr = env_get_string( L"DISPLAY" );
if( !disp_wstr.missing() )
{
2012-03-04 03:37:55 +00:00
escaped_str = escape( str.c_str(), 1 );
cmd = wcsdupcat(L"echo ", escaped_str, L"|xsel -b" );
}
}
if (cmd != NULL)
{
if( exec_subshell( cmd) == -1 )
{
/*
Do nothing on failiure
*/
}
free( cut_buffer );
free( cmd );
cut_buffer = escaped_str;
}
}
/**
Remove first match for specified string from circular list
*/
2012-03-04 03:37:55 +00:00
static void kill_remove( const wcstring &s )
{
2012-03-04 05:46:06 +00:00
ASSERT_IS_MAIN_THREAD();
kill_list_t::iterator iter = std::find(kill_list.begin(), kill_list.end(), s);
if (iter != kill_list.end())
kill_list.erase(iter);
}
2012-03-04 03:37:55 +00:00
void kill_replace( const wcstring &old, const wcstring &newv )
{
kill_remove( old );
kill_add( newv );
}
const wchar_t *kill_yank_rotate()
{
2012-03-04 05:46:06 +00:00
ASSERT_IS_MAIN_THREAD();
// Move the first element to the end
if (kill_list.empty()) {
return NULL;
} else {
kill_list.splice(kill_list.end(), kill_list, kill_list.begin());
return kill_list.front().c_str();
}
}
/**
Check the X clipboard. If it has been changed, add the new
clipboard contents to the fish killring.
*/
static void kill_check_x_buffer()
{
if( !has_xsel() )
return;
const env_var_t disp = env_get_string(L"DISPLAY");
if( ! disp.missing())
{
size_t i;
wcstring cmd = L"xsel -t 500 -b";
wcstring new_cut_buffer=L"";
wcstring_list_t list;
if( exec_subshell( cmd, list ) != -1 )
{
for( i=0; i<list.size(); i++ )
{
wcstring next_line = escape_string( list.at(i), 0 );
if (i > 0) new_cut_buffer += L"\\n";
new_cut_buffer += next_line;
}
if( new_cut_buffer.size() > 0 )
{
/*
The buffer is inserted with backslash escapes,
since we don't really like tabs, newlines,
etc. anyway.
*/
if (cut_buffer == NULL || cut_buffer != new_cut_buffer)
{
free(cut_buffer);
cut_buffer = wcsdup(new_cut_buffer.c_str());
2012-03-04 05:46:06 +00:00
kill_list.push_front( new_cut_buffer );
}
}
}
}
}
const wchar_t *kill_yank()
{
kill_check_x_buffer();
2012-03-04 05:46:06 +00:00
if (kill_list.empty()) {
return L"";
} else {
return kill_list.front().c_str();
}
}
void kill_sanity_check()
{
}
void kill_init()
{
}
void kill_destroy()
{
if( cut_buffer )
free( cut_buffer );
}