Add function to expand ~ in .stowrc files (#14)

Add a new expand_tilde() function that performs tilde expansion of
strings, and corresponding unit tests:

    * A ~ at the beginning of a path is expanded to the user's home
      directory.
    * Literal '~' can be provided with '\~'

Combine this with expand_environment() in a new expand_filepath()
function which applies all (both) required expansion functions to a
string, and use that in get_config_file_options() to expand .stowrc
options.

Add more tests to check that tilde expanded in correct places, i.e.:

    * expanded for --target and --dir
    * not expanded for --ignore, --defer, or --override

Update documentation on stowrc files according to this functionality
change.

Fixes #14: https://github.com/aspiers/stow/issues/14
This commit is contained in:
Charles LeDoux 2016-07-14 14:48:40 -05:00 committed by Adam Spiers
parent 9674738792
commit dc42c34107
4 changed files with 134 additions and 7 deletions

10
NEWS
View file

@ -22,6 +22,16 @@ News file for Stow.
Thanks to Joris Vankerschaver for this feature! Thanks to Joris Vankerschaver for this feature!
*** Shell-like expansion in .stowrc files
For options within .stowrc files which describe file paths, "~" can
be included to expand to the current value of $HOME, and
environment variables can be referenced e.g. via "$FOO" or
"${FOO}". To prevent expansion, escape with a backslash.
Thanks a lot to Charles LeDoux for his diligent work on this
feature!
*** chkstow now honours the $STOW_DIR environment variable *** chkstow now honours the $STOW_DIR environment variable
The stow script already honoured the $STOW_DIR environment The stow script already honoured the $STOW_DIR environment

View file

@ -343,6 +343,28 @@ Stow will re-fold the tree by removing the symlinks to the surviving
package, removing the directory, then linking the directory back to package, removing the directory, then linking the directory back to
the surviving package. the surviving package.
=head1 RESOURCE FILES
F<Stow> searches for default command line options at F<.stowrc> (current
directory) and F<~/.stowrc> (home directory) in that order. If both
locations are present, the files are effectively appended together.
The effect of options in the resource file is similar to simply prepending
the options to the command line. For options that provide a single value,
such as F<--target> or F<--dir>, the command line option will overwrite any
options in the resource file. For options that can be given more than once,
F<--ignore> for example, command line options and resource options are
appended together.
Environment variables and the tilde character (F<~>) will be expanded for
options that take a file path.
The options F<-D>, F<-R>, F<-S>, and any packages listed in the resource
file are ignored.
See the info manual for more information on how stow handles resource
file.
=head1 SEE ALSO =head1 SEE ALSO
The full documentation for F<stow> is maintained as a Texinfo manual. The full documentation for F<stow> is maintained as a Texinfo manual.
@ -633,7 +655,8 @@ sub check_packages {
# Throws : a fatal error if a bad option is given # Throws : a fatal error if a bad option is given
# Comments : Parses the contents of '~/.stowrc' and '.stowrc' with the same # Comments : Parses the contents of '~/.stowrc' and '.stowrc' with the same
# parser as the command line options. Additionally expands any # parser as the command line options. Additionally expands any
# environment variables in --target or --dir options. # environment variables or ~ character in --target or --dir
# options.
#============================================================================= #=============================================================================
sub get_config_file_options { sub get_config_file_options {
my @defaults = (); my @defaults = ();
@ -655,16 +678,36 @@ sub get_config_file_options {
# Expand environment variables and glob characters. # Expand environment variables and glob characters.
if (exists $rc_options->{target}) { if (exists $rc_options->{target}) {
$rc_options->{target} = $rc_options->{target} =
expand_environment($rc_options->{target}, '--target option'); expand_filepath($rc_options->{target}, '--target option');
} }
if (exists $rc_options->{dir}) { if (exists $rc_options->{dir}) {
$rc_options->{dir} = $rc_options->{dir} =
expand_environment($rc_options->{dir}, '--dir option'); expand_filepath($rc_options->{dir}, '--dir option');
} }
return ($rc_options, $rc_pkgs_to_unstow, $rc_pkgs_to_stow); return ($rc_options, $rc_pkgs_to_unstow, $rc_pkgs_to_stow);
} }
#===== SUBROUTINE ============================================================
# Name : expand_filepath()
# Purpose : Handles expansions that need to be applied to
# : file paths. Currently expands environment
# : variables and the tilde.
# Parameters: $path => string to perform expansion on.
# : $source => where the string came from
# Returns : String with replacements performed.
# Throws : n/a
# Comments : n/a
#=============================================================================
sub expand_filepath {
my ($path, $source) = @_;
$path = expand_environment($path, $source);
$path = expand_tilde($path);
return $path;
}
#===== SUBROUTINE ============================================================ #===== SUBROUTINE ============================================================
# Name : expand_environment() # Name : expand_environment()
# Purpose : Expands evironment variables. # Purpose : Expands evironment variables.
@ -700,6 +743,30 @@ sub _safe_expand_env_var {
return $ENV{$var}; return $ENV{$var};
} }
#===== SUBROUTINE ============================================================
# Name : expand_tilde()
# Purpose : Expands tilde to user's home directory path.
# Parameters: $path => string to perform expansion on.
# Returns : String with replacements performed.
# Throws : n/a
# Comments : http://docstore.mik.ua/orelly/perl4/cook/ch07_04.htm
#=============================================================================
sub expand_tilde {
my ($path) = @_;
# Replace tilde with home path.
$path =~ s{ ^ ~ ( [^/]* ) }
{ $1
? (getpwnam($1))[7]
: ( $ENV{HOME} || $ENV{LOGDIR}
|| (getpwuid($<))[7]
)
}ex;
# Replace espaced tilde with regular tilde.
$path =~ s/\\~/~/g;
return $path
}
#===== SUBROUTINE =========================================================== #===== SUBROUTINE ===========================================================
# Name : usage() # Name : usage()
# Purpose : print program usage message and exit # Purpose : print program usage message and exit

View file

@ -881,9 +881,10 @@ directory.
Default command line options may be set in @file{.stowrc} (current Default command line options may be set in @file{.stowrc} (current
directory) or @file{~/.stowrc} (home directory). These are parsed in directory) or @file{~/.stowrc} (home directory). These are parsed in
that order, and effectively prepended to the command line arguments that order, and are appended together if they both exist. The effect of
(with the notable difference that they won't be processed by the shell). the options in the resource file is similar to simply prepending the
This feature can be used for some interesting effects. options to the command line. This feature can be used for some
interesting effects.
For example, suppose your site uses more than one stow directory, perhaps in For example, suppose your site uses more than one stow directory, perhaps in
order to share around responsibilities with a number of systems order to share around responsibilities with a number of systems
@ -922,6 +923,22 @@ immediate parent directory @file{/usr/local/stow}), overriding any
pre-existing links to bin files or man pages, and ignoring some cruft pre-existing links to bin files or man pages, and ignoring some cruft
that gets installed by default. that gets installed by default.
If an option is provided both on the command line and in a resource file,
the command line option takes precedence. For options that provide a single
value, such as @command{--target} or @command{--dir}, the command line
option will overwrite any options in the resource file. For options that can
be given more than once, @command{--ignore} for example, command line
options and resource options are appended together.
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.
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
resource file.
@c =========================================================================== @c ===========================================================================
@node Compile-time vs Install-time, Bootstrapping, Resource Files, Top @node Compile-time vs Install-time, Bootstrapping, Resource Files, Top

View file

@ -7,7 +7,7 @@
use strict; use strict;
use warnings; use warnings;
use Test::More tests => 15; use Test::More tests => 23;
use testutil; use testutil;
@ -107,6 +107,16 @@ delete $ENV{'WITH_UNDERSCORE'};
# Expansion with escaped $ # Expansion with escaped $
is(expand_environment('\$HOME/stow'), '$HOME/stow', 'expand \$HOME'); is(expand_environment('\$HOME/stow'), '$HOME/stow', 'expand \$HOME');
#
# Test tilde (~) expansion
#
# Basic expansion
is(expand_tilde('~/path'), "$ENV{HOME}/path", 'tilde expansion to $HOME');
# Should not expand if middle of path
is(expand_tilde('/path/~/here'), '/path/~/here', 'middle ~ not expanded');
# Test escaped ~
is(expand_tilde('\~/path'), '~/path', 'escaped tilde');
# #
# Test that environment variable expansion is applied. # Test that environment variable expansion is applied.
# #
@ -130,6 +140,29 @@ is_deeply($options->{defer}, [qr(\A\$HOME)],
is_deeply($options->{override}, [qr(\A\$HOME)], is_deeply($options->{override}, [qr(\A\$HOME)],
"environment expansion not applied on --override"); "environment expansion not applied on --override");
#
# Test that tilde expansion is applied in correct places.
#
$rc_contents = <<'HERE';
--dir=~/stow
--target=~/stow
--ignore=~/stow
--defer=~/stow
--override=~/stow
HERE
make_file($RC_FILE, $rc_contents);
($options, $pkgs_to_delete, $pkgs_to_stow) = get_config_file_options();
is($options->{dir}, "$OUT_DIR/stow",
"apply environment expansion on stowrc --dir");
is($options->{target}, "$OUT_DIR/stow",
"apply environment expansion on stowrc --target");
is_deeply($options->{ignore}, [qr(~/stow\z)],
"environment expansion not applied on --ignore");
is_deeply($options->{defer}, [qr(\A~/stow)],
"environment expansion not applied on --defer");
is_deeply($options->{override}, [qr(\A~/stow)],
"environment expansion not applied on --override");
# Clean up files used for testing. # Clean up files used for testing.
# #
unlink $RC_FILE or die "Unable to clean up $RC_FILE.\n"; unlink $RC_FILE or die "Unable to clean up $RC_FILE.\n";