# fcscs - fast curses screen content select
-# Copyright (C) 2013-2016 Simon Ruderich
+# Copyright (C) 2013-2017 Simon Ruderich
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
B<fcscs> is a small tool which allows quick selection of terminal screen
contents (like URLs, paths, regex matches, etc.) and passes the selection to
-GNU Screen's or Tmux's buffer or any other program. The selection can then
-quickly be pasted, e.g. in the shell. Requires GNU Screen or Tmux. It's
-licensed under the GPL 3 or later.
+GNU Screen's or Tmux's paste buffer or any other program. The selection can
+then quickly be pasted, e.g. in the shell. Requires GNU Screen or Tmux to
+capture the terminal content. It's licensed under the GPL 3 or later.
=head1 OPTIONS
change this.
I<NOTE>: Opening URLs in the browser passes the URL via the command line which
-leaks URLs to other users on the current system via C<ps aux> or C<top>.
+might leak URLs to other users on the current system via C<ps aux> or C<top>
+(mount C</proc> with C<hidepid=2> on Linux to prevent this information
+leakage).
I<NOTE>: When yanking (copying) a temporary file is used to pass the data to
GNU screen/Tmux without exposing it to C<ps aux> or C<top>. However this may
return;
}
+
+ sub prompt {
+ my ($self, %settings) = @_;
+
+ foreach (keys %settings) {
+ die if not exists $self->{prompt}{$_};
+ $self->{prompt}{$_} = $settings{$_};
+ }
+ return;
+ }
+
+
+ sub debug {
+ my ($self, $module, @args) = @_;
+
+ return if not $self->{debug};
+
+ state $fh; # only open the file once per run
+ if (not defined $fh) {
+ # Ignore errors if the directory doesn't exist.
+ if (not open $fh, '>', "$ENV{HOME}/.config/fcscs/log") {
+ $fh = undef; # a failed open still writes a value to $fh
+ return;
+ }
+ $fh->autoflush(1);
+ }
+
+ foreach (@args) {
+ $_ = $self->encode($_);
+ }
+ say $fh "$module: @args" or die $!;
+ return;
+ }
sub die {
my ($self, @args) = @_;
$self->deinit;
exit 1;
}
- sub debug {
- my ($self, $module, @args) = @_;
-
- return if not $self->{debug};
-
- state $fh; # only open the file once per run
- if (not defined $fh) {
- # Ignore errors if the directory doesn't exist.
- if (not open $fh, '>', "$ENV{HOME}/.config/fcscs/log") {
- $fh = undef; # a failed open still writes a value to $fh
- return;
- }
- $fh->autoflush(1);
- }
-
- foreach (@args) {
- $_ = $self->encode($_);
- }
- say $fh "$module: @args" or CORE::die $!;
- return;
- }
-
-
- sub prompt {
- my ($self, %settings) = @_;
-
- foreach (keys %settings) {
- CORE::die if not exists $self->{prompt}{$_};
- $self->{prompt}{$_} = $settings{$_};
- }
- return;
- }
# Wrapper for Curses.
sub width { return $Curses::COLS; }
# Necessary for GNU screen or it'll keep the window open until an
# external command has run.
- require File::Spec;
+ require File::Spec; # load here to speedup startup
my $devnull = File::Spec->devnull();
open STDIN, '<', $devnull or die $!;
open STDOUT, '>', $devnull or die $!;
$number = 1;
}
- $screen->draw_matches($config, $matches, []); # remove matches
+ $screen->draw_matches($config, $matches, []); # clear matches
foreach (@{$matches}) {
return { match => $_ } if $_->{id} == $number;
} elsif ($char eq '$') { # select to end of line
extend_match_regex_right($line, $match, qr/.+/);
- # Allow mode changes if not overwritten by local mappings.
+ # Allow mode changes if not overwritten by above mappings.
} elsif (defined $config->{mapping}{mode}{$char}) {
$screen->draw_matches($config, [$match_old], []); # clear match
return { key => $char };
}
-sub mapping_paste {
- my ($key, $screen, $config, $input) = @_;
+sub mapping_state_helper {
+ my ($name, $flags, $key, $screen, $config, $input) = @_;
- $screen->debug('mapping_paste', 'started');
+ $screen->debug("mapping_$name", 'started');
- $config->{state}{handler} = $config->{handler}{paste};
+ $config->{state}{handler} = $config->{handler}{$name};
- $screen->prompt(flags => 'P'); # paste
+ $screen->prompt(flags => $flags);
$screen->draw_prompt($config);
$screen->refresh;
return {};
}
-sub mapping_paste_now {
- my ($key, $screen, $config, $input) = @_;
+sub mapping_state_now_helper {
+ my ($name, $key, $screen, $config, $input) = @_;
- $screen->debug('mapping_paste_now', 'started');
+ $screen->debug("mapping_${name}_now", 'started');
- $config->{state}{handler} = $config->{handler}{paste};
+ $config->{state}{handler} = $config->{handler}{$name};
return {
select_match => 1,
};
}
-sub mapping_yank {
- my ($key, $screen, $config, $input) = @_;
-
- $screen->debug('mapping_yank', 'started');
-
- $config->{state}{handler} = $config->{handler}{yank};
-
- $screen->prompt(flags => 'Y'); # yank
- $screen->draw_prompt($config);
- $screen->refresh;
+sub mapping_paste {
+ return mapping_state_helper('paste', 'P', @_);
+}
+sub mapping_paste_now {
+ return mapping_state_now_helper('paste', @_);
+}
- return {};
+sub mapping_yank {
+ return mapping_state_helper('yank', 'Y', @_);
}
sub mapping_yank_now {
- my ($key, $screen, $config, $input) = @_;
-
- $screen->debug('mapping_yank_now', 'started');
-
- $config->{state}{handler} = $config->{handler}{yank};
-
- return {
- select_match => 1,
- };
+ return mapping_state_now_helper('yank', @_);
}
=back
=cut
-sub mapping_mode_path {
- my ($key, $screen, $config, $input) = @_;
+sub mapping_mode_helper {
+ my ($name, $select, $key, $screen, $config, $input) = @_;
- $screen->debug('mapping_mode_path', 'started');
+ $screen->debug("mapping_mode_$name", 'started');
- my @matches = get_regex_matches($input, $config->{regex}{path});
+ my @matches = get_regex_matches($input, $config->{regex}{$name});
return {
- select => 'path select',
+ select => $select,
matches => \@matches,
- handler => $config->{handler}{path},
+ handler => $config->{handler}{$name},
};
}
+sub mapping_mode_path {
+ return mapping_mode_helper('path', 'path select', @_);
+}
sub mapping_mode_url {
- my ($key, $screen, $config, $input) = @_;
-
- $screen->debug('mapping_mode_url', 'started');
-
- my @matches = get_regex_matches($input, $config->{regex}{url});
- return {
- select => 'url select',
- matches => \@matches,
- handler => $config->{handler}{url},
- };
+ return mapping_mode_helper('url', 'url select', @_);
}
sub mapping_mode_ip {
my ($key, $screen, $config, $input) = @_;
};
}
sub mapping_mode_checksum {
- my ($key, $screen, $config, $input) = @_;
-
- $screen->debug('mapping_mode_checksum', 'started');
-
- my @matches = get_regex_matches($input, $config->{regex}{checksum});
- return {
- select => 'checksum select',
- matches => \@matches,
- handler => $config->{handler}{checksum},
- };
+ return mapping_mode_helper('checksum', 'checksum select', @_);
}
=head2 SEARCH MODE (AND EXTEND MODE)
$screen->debug('handler_yank', 'started');
- require File::Temp;
+ require File::Temp; # load here to speedup startup
# Use a temporary file to prevent leaking the yanked data to other users
# with the command line, e.g. ps aux or top.
$screen->debug('handler_paste', 'started');
- require Time::HiRes;
+ require Time::HiRes; # load here to speedup startup
my @cmd;
if ($config->{setting}{multiplexer} eq 'screen') {
=head1 CONFIGURATION
-fcscs is configured through F<~/.fcscsrc> or F<~/.config/fcscs/fcscsrc> which
-is a normal Perl script with all of Perl's usual features.
+fcscs is configured through F<~/.fcscsrc> (preferred) or
+F<~/.config/fcscs/fcscsrc> which is a normal Perl script with all of Perl's
+usual features (only loaded if not writable by others).
All configuration values are stored in the hash C<%config>. All manually
defined keys overwrite the default settings.
# Draw numbers in bold yellow.
$config{attribute}{match_id} = color_pair(COLOR_YELLOW, -1)
| A_BOLD;
- # Disable Vim-like 'smartcase', ignore case until an upper character is
- # searched.
+ # Disable Vim-like 'smartcase' (ignore case until an upper character is
+ # searched) which is enabled by default.
$config{setting}{smartcase} = 0;
# Use chromium to open URLs if running under X, elinks otherwise.
=item B<paste_sleep> sleep x us before running paste command (C<100_000>)
-=item B<screen_msgwait> GNU Screen's msgwait variable, used when yanking (C<5>)
+=item B<screen_msgwait> GNU Screen's msgwait variable, overwritten with this value when yanking (C<5>)
=item B<alternative_return> additional accept key like return, set to C<\n> to disable (C<s>)
=head1 LICENSE AND COPYRIGHT
-Copyright (C) 2013-2016 by Simon Ruderich
+Copyright (C) 2013-2017 by Simon Ruderich
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by