]> ruderich.org/simon Gitweb - fcscs/fcscs.git/blobdiff - bin/fcscs
Update documentation
[fcscs/fcscs.git] / bin / fcscs
index 6e3d9cb0ca5800986ed8289fab9162c0f6400029..42b63db010c192edd6baa14187f14fc94a485742 100755 (executable)
--- a/bin/fcscs
+++ b/bin/fcscs
@@ -2,7 +2,7 @@
 
 # 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
@@ -44,9 +44,9 @@ B<fcscs> [I<options>] I<path/to/screen/capture/file>
 
 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
 
@@ -64,6 +64,7 @@ Short overview of the general usage, details below:
         - f: file paths
         - i: IPs
         - u: URLs
+        - c: checksums (e.g. MD5, SHA1, ..)
         - ...
         - /: search mode
     - for `normal' modes:
@@ -110,7 +111,9 @@ explained below. Per default URLs are selected, see options for a way to
 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
@@ -322,6 +325,39 @@ package Screen {
         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) = @_;
 
@@ -354,38 +390,6 @@ package Screen {
         $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; }
@@ -504,7 +508,7 @@ sub run_in_background {
 
         # 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 $!;
@@ -558,9 +562,6 @@ sub select_match {
             $number = int($number / 10);
         } elsif ($char eq "\n"
                 or $char eq $config->{setting}{alternative_return}) {
-            if ($number == 0) { # number without selection matches last entry
-                $number = 1;
-            }
             last;
 
         # Selecting a new mode requires falling through into the main input
@@ -570,7 +571,8 @@ sub select_match {
             return { key => $char };
         # All other mappings stay in the current mode.
         } elsif (defined (my $m = $config->{mapping}{simple}{$char})) {
-            $m->($char, $screen, $config, $input);
+            my $result = $m->($char, $screen, $config, $input);
+            last if defined $result->{select_match};
             next;
 
         } else {
@@ -585,8 +587,12 @@ sub select_match {
         $screen->draw_matches($config, $matches, \@remaining);
         $screen->refresh;
     }
+    # Number without selection matches last entry.
+    if ($number == 0) {
+        $number = 1;
+    }
 
-    $screen->draw_matches($config, $matches, []); # remove matches
+    $screen->draw_matches($config, $matches, []); # clear matches
 
     foreach (@{$matches}) {
         return { match => $_ } if $_->{id} == $number;
@@ -660,7 +666,7 @@ sub extend_match {
         } 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 };
@@ -679,31 +685,43 @@ sub extend_match {
 }
 
 
-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_yank {
-    my ($key, $screen, $config, $input) = @_;
+sub mapping_state_now_helper {
+    my ($name, $key, $screen, $config, $input) = @_;
 
-    $screen->debug('mapping_yank', 'started');
+    $screen->debug("mapping_${name}_now", 'started');
 
-    $config->{state}{handler} = $config->{handler}{yank};
+    $config->{state}{handler} = $config->{handler}{$name};
 
-    $screen->prompt(flags => 'Y'); # yank
-    $screen->draw_prompt($config);
-    $screen->refresh;
+    return {
+        select_match => 1,
+    };
+}
 
-    return {};
+sub mapping_paste {
+    return mapping_state_helper('paste', 'P', @_);
+}
+sub mapping_paste_now {
+    return mapping_state_now_helper('paste', @_);
+}
+
+sub mapping_yank {
+    return mapping_state_helper('yank', 'Y', @_);
+}
+sub mapping_yank_now {
+    return mapping_state_now_helper('yank', @_);
 }
 
 
@@ -716,38 +734,34 @@ The following normal modes are available:
 
 =over 4
 
-=item B<path mode> select relative/absolute paths
+=item B<path mode>     select relative/absolute paths
 
-=item B<url mode>  select URLs
+=item B<url mode>      select URLs
 
-=item B<ip mode>   select IPv4 and IPv6 addresses
+=item B<ip mode>       select IPv4 and IPv6 addresses
+
+=item B<checksum mode> select checksums (MD5, SHA1, SHA256, SHA512)
 
 =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}{yank},
+        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) = @_;
@@ -762,6 +776,9 @@ sub mapping_mode_ip {
         handler => $config->{handler}{ip},
     };
 }
+sub mapping_mode_checksum {
+    return mapping_mode_helper('checksum', 'checksum select', @_);
+}
 
 =head2 SEARCH MODE (AND EXTEND MODE)
 
@@ -875,7 +892,7 @@ sub handler_yank {
 
     $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.
@@ -930,7 +947,7 @@ sub handler_paste {
 
     $screen->debug('handler_paste', 'started');
 
-    require Time::HiRes;
+    require Time::HiRes; # load here to speedup startup
 
     my @cmd;
     if ($config->{setting}{multiplexer} eq 'screen') {
@@ -973,8 +990,9 @@ sub handler_url {
 
 =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.
@@ -994,8 +1012,8 @@ settings see below):
     # 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.
@@ -1053,6 +1071,8 @@ them in parentheses):
 
 =item B<i> select IPv4 and IPv6 addresses (C<\&mapping_mode_ip>)
 
+=item B<c> select checksums (e.g. MD5, SHA) (C<\&mapping_mode_checksum>)
+
 =item B</> search for regex to get selection (C<\&mapping_mode_search>)
 
 =item B<q> quit fcscs (C<\&mapping_quit>)
@@ -1065,8 +1085,12 @@ The following simple mappings are available by default:
 
 =item B<p> enable pasting (C<\&mapping_paste>)
 
+=item B<P> paste current selection (like C<\n> but paste) (C<\&mapping_paste_now>)
+
 =item B<y> enable yanking (copying) (C<\&mapping_yank>)
 
+=item B<Y> yank current selection (like C<\n> but yank) (C<\&mapping_yank_now>)
+
 =back
 
 Note that yanking only uses the GNU screen or Tmux paste buffer by default. To
@@ -1102,12 +1126,15 @@ my %mapping_mode = (
     f   => \&mapping_mode_path,
     u   => \&mapping_mode_url,
     i   => \&mapping_mode_ip,
+    c   => \&mapping_mode_checksum,
     '/' => \&mapping_mode_search,
     q   => \&mapping_quit,
 );
 my %mapping_simple = (
     p => \&mapping_paste,
+    P => \&mapping_paste_now,
     y => \&mapping_yank,
+    Y => \&mapping_yank_now,
 );
 
 =head2 ATTRIBUTES
@@ -1157,7 +1184,7 @@ Defaults in parentheses.
 
 =item B<initial_mode>       start in this mode, must be a valid mode mapping (C<\&mapping_mode_url>)
 
-=item B<multiplexer>        set multiplexer ("screen" or "tmux") if not autodetected (C<undef>)
+=item B<multiplexer>        set multiplexer ("screen" or "tmux"), defaults to autodetection (C<undef>)
 
 =item B<ignorecase>         ignore case when searching (C<0>)
 
@@ -1167,7 +1194,7 @@ Defaults in parentheses.
 
 =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>)
 
@@ -1225,6 +1252,8 @@ my %regex = (
     # enough.
     ipv4 => qr!\b(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(?:/\d{1,2})?)\b!,
     ipv6 => qr!\b((?:[0-9a-fA-F]{1,4})?(?::+[0-9a-fA-F]{1,4})+(?:/\d{1,3})?)\b!,
+    # MD5, SHA1, SHA256, SHA512
+    checksum => qr!\b([0-9a-fA-F]{32}|[0-9a-fA-F]{40}|[0-9a-fA-F]{64}|[0-9a-fA-F]{128})\b!,
 );
 
 =head2 HANDLERS
@@ -1235,13 +1264,17 @@ The following handlers are available, defaults in parentheses.
 
 =over
 
-=item B<yank>  used to yank (copy) selection to paste buffer (C<\&handler_yank>)
+=item B<yank>     used to yank (copy) selection to paste buffer (C<\&handler_yank>)
+
+=item B<paste>    used to paste selection into window (C<\&handler_paste>)
 
-=item B<paste> used to paste selection into window (C<\&handler_paste>)
+=item B<path>     used to handle paths (C<\&handler_yank>)
 
-=item B<url>   used to open URLs (e.g. in a browser) (C<\&handler_url>)
+=item B<url>      used to open URLs (e.g. in a browser) (C<\&handler_url>)
 
-=item B<ip>    used to handle IPs (C<\&handler_yank>)
+=item B<ip>       used to handle IPs (C<\&handler_yank>)
+
+=item B<checksum> used to handle checksums (C<\&handler_yank>)
 
 =back
 
@@ -1262,10 +1295,12 @@ Example:
 
 =cut
 my %handler = (
-    yank  => \&handler_yank,
-    paste => \&handler_paste,
-    url   => \&handler_url,
-    ip    => \&handler_yank,
+    yank     => \&handler_yank,
+    paste    => \&handler_paste,
+    path     => \&handler_yank,
+    url      => \&handler_url,
+    ip       => \&handler_yank,
+    checksum => \&handler_yank,
 );
 
 my %state = (
@@ -1288,10 +1323,13 @@ Create a new Curses attribute with the given fore- and background color.
     mapping_mode_path()
     mapping_mode_url()
     mapping_mode_ip()
+    mapping_mode_checksum()
     mapping_mode_search()
 
     mapping_paste()
+    mapping_paste_now()
     mapping_yank()
+    mapping_yank_now()
     mapping_quit()
 
 Used as mappings, see L</MAPPINGS> above.
@@ -1307,7 +1345,8 @@ Used as handler to yank, paste selection or open URL in browser.
     run_command()
     run_in_background()
 
-Helper functions when writing custom mappings, see the source for details.
+Helper functions when writing custom mappings, see the source and example for
+details.
 
 Example:
 
@@ -1337,7 +1376,7 @@ Example:
         }
 
         return $result;
-    }
+    };
     # Also update initial mode to use our new "URL mode".
     $config{setting}{initial_mode} = $config{mapping}{mode}{u};
 
@@ -1351,8 +1390,6 @@ Example:
         });
     };
 
-
-
 =cut
 
 # All variables and functions which are usable by ~/.fcscsrc.
@@ -1365,18 +1402,19 @@ package Fcscs {
     sub mapping_mode_path { return main::mapping_mode_path(@_); }
     sub mapping_mode_url { return main::mapping_mode_url(@_); }
     sub mapping_mode_ip { return main::mapping_mode_ip(@_); }
+    sub mapping_mode_checksum { return main::mapping_mode_checksum(@_); }
     sub mapping_mode_search { return main::mapping_mode_search(@_); }
 
     sub mapping_paste { return main::mapping_paste(@_); }
+    sub mapping_paste_now { return main::mapping_paste_now(@_); }
     sub mapping_yank { return main::mapping_yank(@_); }
+    sub mapping_yank_now { return main::mapping_yank_now(@_); }
     sub mapping_quit { return main::mapping_quit(@_); }
 
     sub handler_yank { return main::handler_yank(@_); }
     sub handler_paste { return main::handler_paste(@_); }
     sub handler_url { return main::handler_url(@_); }
 
-    sub debug { return main::debug(@_); }
-
     sub get_regex_matches { return main::get_regex_matches(@_); }
     sub select_match { return main::select_match(@_); }
 
@@ -1416,10 +1454,10 @@ package Fcscs {
         }
         # Make sure the file is not writable by other users. Doesn't handle
         # ACLs and see comment above about race conditions.
-        my @stat = stat $path or die $!;
+        my @stat = stat $path or $screen->die("Config '$decoded': $!");
         my $mode = $stat[2];
         if (($mode & Fcntl::S_IWGRP) or ($mode & Fcntl::S_IWOTH)) {
-            die "Config '$decoded' must not be writable by other users.";
+            $screen->die("Config '$decoded' must not be writable by other users.");
         }
 
         my $result = do $path;
@@ -1581,7 +1619,7 @@ Simon Ruderich E<lt>simon@ruderich.orgE<gt>
 
 =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