diff --git a/bin/stow.in b/bin/stow.in index 024b152..5e4da93 100755 --- a/bin/stow.in +++ b/bin/stow.in @@ -460,6 +460,7 @@ require 5.006_001; use POSIX qw(getcwd); use Getopt::Long qw(GetOptionsFromArray); use Scalar::Util qw(reftype); +use Text::ParseWords qw(shellwords); @USE_LIB_PMDIR@ use Stow; @@ -581,19 +582,19 @@ sub parse_options { 'ignore=s' => sub { my $regex = $_[1]; - push @{$options{ignore}}, qr($regex\z); + push @{$options{ignore}}, qr{($regex)\z}; }, 'override=s' => sub { my $regex = $_[1]; - push @{$options{override}}, qr(\A$regex); + push @{$options{override}}, qr{\A($regex)}; }, 'defer=s' => sub { my $regex = $_[1]; - push @{$options{defer}}, qr(\A$regex); + push @{$options{defer}}, qr{\A($regex)}; }, # a little craziness so we can do different actions on the same line: @@ -683,7 +684,7 @@ sub get_config_file_options { or die "Could not open $file for reading\n"; while (my $line = <$FILE>){ chomp $line; - push @defaults, split " ", $line; + push @defaults, shellwords($line); } close $FILE or die "Could not close open file: $file\n"; } diff --git a/doc/stow.texi b/doc/stow.texi index a875308..6eba53e 100644 --- a/doc/stow.texi +++ b/doc/stow.texi @@ -1016,7 +1016,11 @@ For options that take a file path, environment variables and the tilde character (@command{~}) are expanded. An environment variable can be given in either the @command{$VAR} or @command{$@{VAR@}} form. To prevent expansion, escape the @command{$} or @command{~} with a -backslash. +backslash. Since these values are first subject to standard shell +quoting rules, if you want special characters such as @command{\b} or +@command{$} to be treated as regular expression assertions then they +will need extra escaping, i.e. @command{\\b} and @command{\\\$} +respectively. The options @command{-D}, @command{-S}, and @command{-R} are ignored in resource files. This is also true of any package names given in the diff --git a/t/cli_options.t b/t/cli_options.t index 8b73bf5..7c8a253 100755 --- a/t/cli_options.t +++ b/t/cli_options.t @@ -73,7 +73,7 @@ local @ARGV = ( 'dummy' ); ($options, $pkgs_to_delete, $pkgs_to_stow) = process_options(); -is_deeply($options->{defer}, [ qr(\Aman), qr(\Ainfo) ] => 'defer man and info'); +is_deeply($options->{defer}, [ qr{\A(man)}, qr{\A(info)} ] => 'defer man and info'); # # Check setting override paths @@ -84,7 +84,7 @@ local @ARGV = ( 'dummy' ); ($options, $pkgs_to_delete, $pkgs_to_stow) = process_options(); -is_deeply($options->{override}, [qr(\Aman), qr(\Ainfo)] => 'override man and info'); +is_deeply($options->{override}, [qr{\A(man)}, qr{\A(info)}] => 'override man and info'); # # Check setting ignored paths @@ -95,7 +95,7 @@ local @ARGV = ( 'dummy' ); ($options, $pkgs_to_delete, $pkgs_to_stow) = process_options(); -is_deeply($options->{ignore}, [ qr(~\z), qr(\.#.*\z) ] => 'ignore temp files'); +is_deeply($options->{ignore}, [ qr{(~)\z}, qr{(\.#.*)\z} ] => 'ignore temp files'); # # Check that expansion not applied. diff --git a/t/rc_options.t b/t/rc_options.t index cbbbee2..ef00ead 100755 --- a/t/rc_options.t +++ b/t/rc_options.t @@ -22,7 +22,7 @@ use strict; use warnings; -use Test::More tests => 34; +use Test::More tests => 35; use testutil; @@ -102,7 +102,7 @@ HERE ($options, $pkgs_to_delete, $pkgs_to_stow) = process_options(); is($options->{target}, "../target", "--target from \$HOME/.stowrc"); -is($options->{dir}, "../stow", "-d from \$HOME/.stowrc"); +is($options->{dir}, "../stow", "-d ../stow from \$HOME/.stowrc"); # # Test ~/.stowrc file with one absolute option per line. @@ -117,7 +117,20 @@ HERE is($options->{target}, "$ABS_TEST_DIR/target" => "--target from \$HOME/.stowrc"); is($options->{dir}, "$ABS_TEST_DIR/stow" - => "-d from \$HOME/.stowrc"); + => "-d $ABS_TEST_DIR/stow from \$HOME/.stowrc"); + +# +# Test ~/.stowrc file with with options with paths containing spaces. +# +local @ARGV = ('dummy'); +make_file($HOME_RC_FILE, <{dir}, "$ABS_TEST_DIR/stow directory", + => "-d from \$HOME/.stowrc with spaces"); # # Test that some but not all options ~/.stowrc file are overridden by @@ -140,7 +153,7 @@ is($options->{target}, "$ABS_TEST_DIR/target" => "--target overridden by \$PWD/.stowrc"); is($options->{dir}, "$ABS_TEST_DIR/stow" => "-d overridden \$PWD/.stowrc"); -is_deeply($options->{defer}, [qr(\Ainfo), qr(\Aman)], +is_deeply($options->{defer}, [qr{\A(info)}, qr{\A(man)}], 'defer man and info'); unlink($CWD_RC_FILE) or die "Failed to unlink $CWD_RC_FILE"; @@ -166,7 +179,7 @@ make_file($HOME_RC_FILE, <{defer}, [qr(\Ainfo), qr(\Aman)], +is_deeply($options->{defer}, [qr{\A(info)}, qr{\A(man)}], 'defer man and info'); # ======== Filepath Expansion Tests ======== @@ -215,26 +228,32 @@ is(expand_tilde('/path/~/here'), '/path/~/here', 'middle ~ not expanded'); is(expand_tilde('\~/path'), '~/path', 'escaped tilde'); # -# Test that environment variable expansion is applied. +# Test that environment variable expansion is applied unless quoted. +# Include examples from the manual # make_file($HOME_RC_FILE, <<'HERE'); --dir=$HOME/stow ---target=$HOME/stow ---ignore=\$HOME ---defer=\$HOME ---override=\$HOME +--target="$HOME/dir with space in/file with space in" +--ignore=\\$FOO\\$ +--defer="foo\\b.*bar" +--defer="\\.jpg\$" +--override=\\.png\$ +--override=bin|man +--ignore='perllocal\.pod' +--ignore='\.packlist' +--ignore='\.bs' HERE ($options, $pkgs_to_delete, $pkgs_to_stow) = get_config_file_options(); is($options->{dir}, "$ABS_TEST_DIR/stow", - "apply environment expansion on \$HOME/.stowrc --dir"); -is($options->{target}, "$ABS_TEST_DIR/stow", - "apply environment expansion on \$HOME/.stowrc --target"); -is_deeply($options->{ignore}, [qr(\$HOME\z)], - "environment expansion not applied on --ignore"); -is_deeply($options->{defer}, [qr(\A\$HOME)], - "environment expansion not applied on --defer"); -is_deeply($options->{override}, [qr(\A\$HOME)], - "environment expansion not applied on --override"); + "apply environment expansion on --dir"); +is($options->{target}, "$ABS_TEST_DIR/dir with space in/file with space in", + "apply environment expansion on --target"); +is_deeply($options->{ignore}, [qr{(\$FOO\$)\z}, qr{(perllocal\.pod)\z}, qr{(\.packlist)\z}, qr{(\.bs)\z}], + 'environment expansion not applied on --ignore but backslash removed'); +is_deeply($options->{defer}, [qr{\A(foo\b.*bar)}, qr{\A(\.jpg$)}], + 'environment expansion not applied on --defer but backslash removed'); +is_deeply($options->{override}, [qr{\A(\.png$)}, qr{\A(bin|man)}], + 'environment expansion not applied on --override but backslash removed'); # # Test that tilde expansion is applied in correct places. @@ -251,11 +270,11 @@ is($options->{dir}, "$ABS_TEST_DIR/stow", "apply tilde expansion on \$HOME/.stowrc --dir"); is($options->{target}, "$ABS_TEST_DIR/stow", "apply tilde expansion on \$HOME/.stowrc --target"); -is_deeply($options->{ignore}, [qr(~/stow\z)], +is_deeply($options->{ignore}, [qr{(~/stow)\z}], "tilde expansion not applied on --ignore"); -is_deeply($options->{defer}, [qr(\A~/stow)], +is_deeply($options->{defer}, [qr{\A(~/stow)}], "tilde expansion not applied on --defer"); -is_deeply($options->{override}, [qr(\A~/stow)], +is_deeply($options->{override}, [qr{\A(~/stow)}], "tilde expansion not applied on --override"); # @@ -263,4 +282,3 @@ is_deeply($options->{override}, [qr(\A~/stow)], # unlink $HOME_RC_FILE or die "Unable to clean up $HOME_RC_FILE.\n"; remove_dir($ABS_TEST_DIR); - diff --git a/t/testutil.pm b/t/testutil.pm index 2b4e097..9fb9862 100755 --- a/t/testutil.pm +++ b/t/testutil.pm @@ -56,7 +56,7 @@ sub init_test_dirs { # Create a run_from/ subdirectory for tests which want to run # from a separate directory outside the Stow directory or # target directory. - for my $dir ("target", "stow", "run_from") { + for my $dir ("target", "stow", "run_from", "stow directory") { my $path = "$test_dir/$dir"; -d $path and remove_tree($path); make_path($path);