]> ruderich.org/simon Gitweb - fcscs/fcscs.git/blobdiff - bin/fcscs
fix crash when pressing enter after select mode with no selection
[fcscs/fcscs.git] / bin / fcscs
index f561af237ce9ec8dc77289268ed18288e0ae4e7c..017ae5bfa327ad4b109efaeb1b24a24cd1e7be17 100755 (executable)
--- a/bin/fcscs
+++ b/bin/fcscs
@@ -108,11 +108,13 @@ To change the selection mode (e.g. paths, files, etc.) use one of the mappings
 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>.
+
 I<NOTE>: When yanking (copying) a temporary file is used to pass the data to
-GNU screen/Tmux without exposing it to C<ps ux> or C<top>. However this may
+GNU screen/Tmux without exposing it to C<ps aux> or C<top>. However this may
 leak data if those temporary files are written to disk. To prevent this change
-your C<$TMP> accordingly to point to a memory-only location or encrypted
-storage.
+your C<$TMP> to point to a memory-only location or encrypted storage.
 
 If no window appears, try running B<fcscs> manually to catch the error message
 and please report the bug:
@@ -265,6 +267,8 @@ package Screen {
     sub draw_prompt {
         my ($self, $config) = @_;
 
+        $self->debug('draw_prompt', 'started');
+
         my $x = 0;
         my $y = $self->height - 1;
 
@@ -274,17 +278,20 @@ package Screen {
         # Draw prompt flags.
         if (defined (my $s = $self->{prompt}{flags})) {
             $s = "[$s]";
+            $self->debug('draw_prompt', $s);
             $self->draw_clipped($y, $x, $config->{attribute}{prompt_flags}, $s);
             $x += length($s) + 1; # space between next element
         }
         # Draw prompt name.
         if (defined (my $s = $self->{prompt}{name})) {
             $s = "[$s]";
+            $self->debug('draw_prompt', $s);
             $self->draw_clipped($y, $x, $config->{attribute}{prompt_name}, $s);
             $x += length($s) + 1;
         }
         # Draw prompt value, e.g. a search field.
         if (defined (my $s = $self->{prompt}{value})) {
+            $self->debug('draw_prompt', $s);
             $self->draw_clipped($y, $x, undef, $s);
             $x += length($s) + 1;
         }
@@ -342,6 +349,26 @@ 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;
+            }
+        }
+
+        foreach (@args) {
+            $_ = $self->encode($_);
+        }
+        say $fh "$module: @args";
+        return;
+    }
 
 
     sub prompt {
@@ -366,25 +393,6 @@ package Screen {
 
 # FUNCTIONS
 
-sub debug {
-    my ($config, $module, @args) = @_;
-
-    return if not $config->{setting}{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;
-        }
-    }
-
-    say $fh "$module: @args";
-    return;
-}
-
-
 sub prepare_input {
     my ($screen, $input_ref) = @_;
 
@@ -433,9 +441,9 @@ sub get_regex_matches {
 
 
 sub run_command {
-    my ($config, $cmd) = @_;
+    my ($screen, $config, $cmd) = @_;
 
-    debug $config, 'run_command', "running @{$cmd}";
+    $screen->debug('run_command', "running @{$cmd}");
 
     my $exit = do {
         # Perl's system() combined with a $SIG{__WARN__} which die()s has
@@ -459,7 +467,8 @@ sub run_command {
         # a working $$.
         no warnings;
 
-        system { $cmd->[0] } @{$cmd};
+        my @cmd = map { $screen->encode($_) } @{$cmd};
+        system { $cmd[0] } @cmd;
     };
     if ($exit != 0) {
         my $msg;
@@ -475,9 +484,9 @@ sub run_command {
     return;
 }
 sub run_in_background {
-    my ($config, $sub) = @_;
+    my ($screen, $sub) = @_;
 
-    debug $config, 'run_in_background', "running $sub";
+    $screen->debug('run_in_background', "running $sub");
 
     my $pid = fork;
     defined $pid or die $!;
@@ -509,12 +518,12 @@ sub run_in_background {
 sub select_match {
     my ($name, $screen, $config, $input, $matches) = @_;
 
-    debug $config, 'select_match', 'started';
+    $screen->debug('select_match', 'started');
 
     return if @{$matches} == 0;
     # Don't return on initial run to give the user a chance to select another
     # mode, e.g. to switch from URL selection to search selection.
-    if (@{$matches} == 1 and not $config->{state}->{initial}) {
+    if (@{$matches} == 1 and not $config->{state}{initial}) {
         return { match => $matches->[0] };
     }
     $config->{state}{initial} = 0;
@@ -573,7 +582,7 @@ sub select_match {
     foreach (@{$matches}) {
         return { match => $_ } if $_->{id} == $number;
     }
-    debug $config, 'select_match', 'no match selected';
+    $screen->debug('select_match', 'no match selected');
     return { match => undef };
 }
 
@@ -600,7 +609,9 @@ sub extend_match_regex_right {
 sub extend_match {
     my ($screen, $config, $input, $match) = @_;
 
-    debug $config, 'extend_match', 'started';
+    $screen->debug('extend_match', 'started');
+
+    return if not defined $match;
 
     $screen->prompt(name => 'extend', value => undef);
     $screen->draw_prompt($config);
@@ -652,7 +663,7 @@ sub extend_match {
         $screen->refresh;
     }
 
-    debug $config, 'extend_match', 'done';
+    $screen->debug('extend_match', 'done');
 
     return { match => $match };
 }
@@ -661,7 +672,7 @@ sub extend_match {
 sub mapping_paste {
     my ($key, $screen, $config, $input) = @_;
 
-    debug $config, 'mapping_paste', 'started';
+    $screen->debug('mapping_paste', 'started');
 
     $config->{state}{handler} = $config->{handler}{paste};
 
@@ -674,7 +685,7 @@ sub mapping_paste {
 sub mapping_yank {
     my ($key, $screen, $config, $input) = @_;
 
-    debug $config, 'mapping_yank', 'started';
+    $screen->debug('mapping_yank', 'started');
 
     $config->{state}{handler} = $config->{handler}{yank};
 
@@ -705,7 +716,7 @@ The following normal modes are available:
 sub mapping_mode_path {
     my ($key, $screen, $config, $input) = @_;
 
-    debug $config, 'mapping_mode_path', 'started';
+    $screen->debug('mapping_mode_path', 'started');
 
     my @matches = get_regex_matches($input, $config->{regex}{path});
     return {
@@ -717,7 +728,7 @@ sub mapping_mode_path {
 sub mapping_mode_url {
     my ($key, $screen, $config, $input) = @_;
 
-    debug $config, 'mapping_mode_url', 'started';
+    $screen->debug('mapping_mode_url', 'started');
 
     my @matches = get_regex_matches($input, $config->{regex}{url});
     return {
@@ -764,7 +775,7 @@ characters (C<\S+>), just like in Vim.
 sub mapping_mode_search {
     my ($key, $screen, $config, $input) = @_;
 
-    debug $config, 'mapping_mode_search', 'started';
+    $screen->debug('mapping_mode_search', 'started');
 
     $screen->cursor(1);
 
@@ -832,7 +843,7 @@ sub mapping_quit {
 sub handler_yank {
     my ($screen, $config, $match) = @_;
 
-    debug $config, 'handler_yank', 'started';
+    $screen->debug('handler_yank', 'started');
 
     require File::Temp;
 
@@ -843,18 +854,18 @@ sub handler_yank {
     close $fh or die $!;
 
     if ($config->{setting}{multiplexer} eq 'screen') {
-        debug $config, 'handler_yank', 'using screen';
+        $screen->debug('handler_yank', 'using screen');
 
         # GNU screen displays an annoying "Slurping X characters into buffer".
         # Use 'msgwait 0' as a hack to disable it.
         my $msgwait = $config->{setting}{screen_msgwait};
-        run_command($config, ['screen', '-X', 'msgwait', 0]);
-        run_command($config, ['screen', '-X', 'readbuf', $tmp]);
-        run_command($config, ['screen', '-X', 'msgwait', $msgwait]);
+        run_command($screen, $config, ['screen', '-X', 'msgwait', 0]);
+        run_command($screen, $config, ['screen', '-X', 'readbuf', $tmp]);
+        run_command($screen, $config, ['screen', '-X', 'msgwait', $msgwait]);
     } elsif ($config->{setting}{multiplexer} eq 'tmux') {
-        debug $config, 'handler_yank', 'using tmux';
+        $screen->debug('handler_yank', 'using tmux');
 
-        run_command($config, ['tmux', 'load-buffer', $tmp]);
+        run_command($screen, $config, ['tmux', 'load-buffer', $tmp]);
     } else {
         die 'unsupported multiplexer';
     }
@@ -865,22 +876,22 @@ sub handler_yank {
 sub handler_paste {
     my ($screen, $config, $match) = @_;
 
-    debug $config, 'handler_paste', 'started';
+    $screen->debug('handler_paste', 'started');
 
     require Time::HiRes;
 
     my @cmd;
     if ($config->{setting}{multiplexer} eq 'screen') {
-        debug $config, 'handler_paste', 'using screen';
+        $screen->debug('handler_paste', 'using screen');
         @cmd = qw( screen -X paste . );
     } elsif ($config->{setting}{multiplexer} eq 'tmux') {
-        debug $config, 'handler_paste', 'using tmux';
+        $screen->debug('handler_paste', 'using tmux');
         @cmd = qw( tmux paste-buffer );
     } else {
         die 'unsupported multiplexer';
     }
 
-    run_in_background($config, sub {
+    run_in_background($screen, sub {
         # We need to get the data in the paste buffer before we can paste
         # it.
         handler_yank($screen, $config, $match);
@@ -888,21 +899,18 @@ sub handler_paste {
         # Sleep until we switch back to the current window.
         Time::HiRes::usleep($config->{setting}{paste_sleep});
 
-        run_command($config, \@cmd);
+        run_command($screen, $config, \@cmd);
     });
     return;
 }
 sub handler_url {
     my ($screen, $config, $match) = @_;
 
-    debug $config, 'handler_url', "opening $match->{value}";
+    $screen->debug('handler_url', "opening $match->{value}");
 
-    run_in_background($config, sub {
-        my @cmd = map { $screen->encode($_) } (
-            @{$config->{setting}{browser}},
-            $match->{value},
-        );
-        run_command($config, \@cmd);
+    run_in_background($screen, sub {
+        my @cmd = ( @{$config->{setting}{browser}}, $match->{value} );
+        run_command($screen, $config, \@cmd);
     });
     return;
 }
@@ -1111,7 +1119,7 @@ my %setting = (
 
 =over
 
-=item B<url> used by C<\&mapping_mode_url()>
+=item B<url>  used by C<\&mapping_mode_url()>
 
 =item B<path> used by C<\&mapping_mode_path()>
 
@@ -1153,8 +1161,9 @@ Example:
         my ($screen, $config, $match) = @_;
 
         if ($match->{value} =~ m{^https://www.youtube.com/}) {
-            return run_in_background($config, sub {
-                run_command($config, ['youtube-dl-wrapper', $match->{value}]);
+            return run_in_background($screen, sub {
+                run_command($screen, $config,
+                            ['youtube-dl-wrapper', $match->{value}]);
             });
         }
         handler_url(@_);
@@ -1341,7 +1350,7 @@ eval {
     while (1) {
         if (not defined $mapping) {
             $key = $screen->getch unless defined $key;
-            debug \%config, 'input', "got key '$key'";
+            $screen->debug('input', "got key '$key'");
 
             $mapping = $config{mapping}{mode}{$key};
             $mapping = $config{mapping}{simple}{$key} unless defined $mapping;
@@ -1351,22 +1360,22 @@ eval {
             }
         }
 
-        debug \%config, 'input', 'running mapping';
+        $screen->debug('input', 'running mapping');
         my $result = $mapping->($key, $screen, \%config, $input);
         $mapping = undef;
 
 RESULT:
         if (defined $result->{quit}) {
-            debug \%config, 'input', 'quitting';
+            $screen->debug('input', 'quitting');
             last;
         }
         if (defined $result->{key}) {
             $key = $result->{key}; # lookup another mapping
-            debug \%config, 'input', "processing new key: '$key'";
+            $screen->debug('input', "processing new key: '$key'");
             next;
         }
         if (defined $result->{select}) {
-            debug \%config, 'input', 'selecting match';
+            $screen->debug('input', 'selecting match');
             my $tmp = $result;
             $result = select_match($result->{select},
                                 $screen, \%config, $input,
@@ -1376,22 +1385,22 @@ RESULT:
             goto RESULT; # reprocess special entries in result
         }
         if (defined $result->{extend}) {
-            debug \%config, 'input', 'extending match';
+            $screen->debug('input', 'extending match');
             $result = extend_match($screen, \%config, $input,
                                    $result->{match});
             goto RESULT; # reprocess special entries in result
         }
         if (defined $result->{match}) {
-            if (not defined $result->{match}->{value}) {
-                $result->{match}->{value} = $result->{match}->{string};
+            if (not defined $result->{match}{value}) {
+                $result->{match}{value} = $result->{match}{string};
             }
 
-            debug \%config, 'input', 'running handler';
+            $screen->debug('input', 'running handler');
 
             # Choose handler with falling priority.
             my @handlers = (
                 $config{state}{handler},     # set by user
-                $result->{match}->{handler}, # set by match
+                $result->{match}{handler},   # set by match
                 $result->{handler},          # set by mapping
                 $config{handler}{yank},      # fallback
             );