2001-12-24 09:57:46 -05:00
|
|
|
#!@PERL@
|
|
|
|
|
|
|
|
# GNU Stow - manage the installation of multiple software packages
|
2001-12-30 12:56:45 -05:00
|
|
|
# Copyright (C) 1993, 1994, 1995, 1996 by Bob Glickstein
|
2011-11-16 09:04:03 -05:00
|
|
|
# Copyright (C) 2000, 2001 Guillaume Morin
|
|
|
|
# Copyright (C) 2007 Kahlil Hodgson
|
|
|
|
#
|
2001-12-24 09:57:46 -05:00
|
|
|
# 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
|
|
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
|
|
# (at your option) any later version.
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful, but
|
|
|
|
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
# General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
|
# along with this program; if not, write to the Free Software
|
|
|
|
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
2011-11-16 09:04:03 -05:00
|
|
|
|
|
|
|
use strict;
|
|
|
|
use warnings;
|
2001-12-24 09:57:46 -05:00
|
|
|
|
2011-11-16 10:22:12 -05:00
|
|
|
require 5.6.1;
|
|
|
|
|
2011-11-16 09:04:03 -05:00
|
|
|
use POSIX qw(getcwd);
|
|
|
|
use Getopt::Long;
|
|
|
|
|
2011-11-24 11:28:09 -05:00
|
|
|
use Stow;
|
|
|
|
use Stow::Util qw(parent);
|
|
|
|
|
2011-11-16 09:04:03 -05:00
|
|
|
my $ProgramName = $0;
|
|
|
|
$ProgramName =~ s{.*/}{};
|
|
|
|
|
2011-11-24 11:28:09 -05:00
|
|
|
main() unless caller();
|
2011-11-16 09:04:03 -05:00
|
|
|
|
2011-11-24 11:28:09 -05:00
|
|
|
sub main {
|
|
|
|
my ($options, $pkgs_to_unstow, $pkgs_to_stow) = process_options();
|
2011-11-16 09:04:03 -05:00
|
|
|
|
2011-11-24 11:28:09 -05:00
|
|
|
my $stow = new Stow(%$options);
|
2011-11-16 09:04:03 -05:00
|
|
|
# current dir is now the target directory
|
|
|
|
|
2011-11-24 11:28:09 -05:00
|
|
|
$stow->plan_unstow(@$pkgs_to_unstow);
|
|
|
|
$stow->plan_stow (@$pkgs_to_stow);
|
|
|
|
|
|
|
|
my @conflicts = $stow->get_conflicts;
|
2011-11-16 09:04:03 -05:00
|
|
|
|
|
|
|
# --verbose: tell me what you are planning to do
|
|
|
|
# --simulate: don't execute planned operations
|
|
|
|
# --conflicts: just list any detected conflicts
|
|
|
|
|
2011-11-24 11:28:09 -05:00
|
|
|
if (scalar @conflicts) {
|
2011-11-16 09:04:03 -05:00
|
|
|
warn "WARNING: conflicts detected.\n";
|
2011-11-24 11:28:09 -05:00
|
|
|
if ($options->{'conflicts'}) {
|
|
|
|
map { warn $_ } @conflicts;
|
2011-11-16 09:04:03 -05:00
|
|
|
}
|
|
|
|
warn "WARNING: all operations aborted.\n";
|
|
|
|
}
|
|
|
|
else {
|
2011-11-24 11:28:09 -05:00
|
|
|
$stow->process_tasks();
|
2011-11-16 09:04:03 -05:00
|
|
|
}
|
2001-12-24 09:57:46 -05:00
|
|
|
}
|
|
|
|
|
2011-11-16 09:04:03 -05:00
|
|
|
|
|
|
|
#===== SUBROUTINE ===========================================================
|
|
|
|
# Name : process_options()
|
2011-11-24 11:28:09 -05:00
|
|
|
# Purpose : parse command line options
|
2011-11-16 09:04:03 -05:00
|
|
|
# Parameters: none
|
2011-11-24 11:28:09 -05:00
|
|
|
# Returns : (\%options, \@pkgs_to_unstow, \@pkgs_to_stow)
|
2011-11-16 09:04:03 -05:00
|
|
|
# Throws : a fatal error if a bad command line option is given
|
|
|
|
# Comments : checks @ARGV for valid package names
|
|
|
|
#============================================================================
|
|
|
|
sub process_options {
|
2011-11-24 11:28:09 -05:00
|
|
|
my %options = ();
|
|
|
|
my @pkgs_to_unstow = ();
|
|
|
|
my @pkgs_to_stow = ();
|
|
|
|
my $action = 'stow';
|
|
|
|
|
|
|
|
unshift @ARGV, get_config_file_options();
|
2011-11-16 09:04:03 -05:00
|
|
|
#$,="\n"; print @ARGV,"\n"; # for debugging rc file
|
|
|
|
|
|
|
|
Getopt::Long::config('no_ignore_case', 'bundling', 'permute');
|
|
|
|
GetOptions(
|
2011-11-24 11:28:09 -05:00
|
|
|
\%options,
|
2011-11-17 09:12:12 -05:00
|
|
|
'verbose|v:+', 'help|h', 'simulate|n|no', 'conflicts|c',
|
|
|
|
'version|V', 'compat|p', 'dir|d=s', 'target|t=s',
|
2011-11-16 09:04:03 -05:00
|
|
|
|
|
|
|
# clean and pre-compile any regex's at parse time
|
2011-11-21 08:59:36 -05:00
|
|
|
'ignore=s' =>
|
2011-11-24 11:28:09 -05:00
|
|
|
sub {
|
|
|
|
# FIXME: do we really need strip_quotes here?
|
2011-11-16 09:04:03 -05:00
|
|
|
my $regex = strip_quotes($_[1]);
|
2011-11-24 11:28:09 -05:00
|
|
|
push @{$options{'ignore'}}, qr($regex\z);
|
2011-11-16 09:04:03 -05:00
|
|
|
},
|
|
|
|
|
2011-11-21 08:59:36 -05:00
|
|
|
'override=s' =>
|
|
|
|
sub {
|
2011-11-16 09:04:03 -05:00
|
|
|
my $regex = strip_quotes($_[1]);
|
2011-11-24 11:28:09 -05:00
|
|
|
push @{$options{'override'}}, qr(\A$regex);
|
2011-11-16 09:04:03 -05:00
|
|
|
},
|
|
|
|
|
2011-11-21 08:59:36 -05:00
|
|
|
'defer=s' =>
|
|
|
|
sub {
|
2011-11-16 09:04:03 -05:00
|
|
|
my $regex = strip_quotes($_[1]);
|
2011-11-24 11:28:09 -05:00
|
|
|
push @{$options{'defer'}}, qr(\A$regex);
|
2011-11-16 09:04:03 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
# a little craziness so we can do different actions on the same line:
|
2011-11-21 08:59:36 -05:00
|
|
|
# a -D, -S, or -R changes the action that will be performed on the
|
2011-11-16 09:04:03 -05:00
|
|
|
# package arguments that follow it.
|
2011-11-24 11:28:09 -05:00
|
|
|
'D|delete' => sub { $action = 'unstow' },
|
|
|
|
'S|stow' => sub { $action = 'stow' },
|
|
|
|
'R|restow' => sub { $action = 'restow' },
|
|
|
|
|
|
|
|
# Handler for non-option arguments
|
2011-11-21 08:59:36 -05:00
|
|
|
'<>' =>
|
2011-11-16 09:04:03 -05:00
|
|
|
sub {
|
2011-11-24 11:28:09 -05:00
|
|
|
if ($action eq 'restow') {
|
|
|
|
push @pkgs_to_unstow, $_[0];
|
|
|
|
push @pkgs_to_stow, $_[0];
|
2011-11-16 09:04:03 -05:00
|
|
|
}
|
2011-11-24 11:28:09 -05:00
|
|
|
elsif ($action eq 'unstow') {
|
|
|
|
push @pkgs_to_unstow, $_[0];
|
2011-11-16 09:04:03 -05:00
|
|
|
}
|
|
|
|
else {
|
2011-11-24 11:28:09 -05:00
|
|
|
push @pkgs_to_stow, $_[0];
|
2011-11-16 09:04:03 -05:00
|
|
|
}
|
|
|
|
},
|
|
|
|
) or usage();
|
|
|
|
|
2011-11-24 11:28:09 -05:00
|
|
|
usage() if $options{'help'};
|
|
|
|
version() if $options{'version'};
|
|
|
|
|
|
|
|
sanitize_path_options(\%options);
|
|
|
|
check_packages(\@pkgs_to_unstow, \@pkgs_to_stow);
|
|
|
|
|
|
|
|
return (\%options, \@pkgs_to_unstow, \@pkgs_to_stow);
|
|
|
|
}
|
|
|
|
|
|
|
|
sub sanitize_path_options {
|
|
|
|
my ($options) = @_;
|
2011-11-16 09:04:03 -05:00
|
|
|
|
2011-11-24 11:28:09 -05:00
|
|
|
if (exists $options->{'dir'}) {
|
|
|
|
$options->{'dir'} =~ s/\A +//;
|
|
|
|
$options->{'dir'} =~ s/ +\z//;
|
2011-11-16 09:04:03 -05:00
|
|
|
}
|
2011-11-24 11:28:09 -05:00
|
|
|
else {
|
2011-11-21 08:59:36 -05:00
|
|
|
$options->{'dir'} = exists $ENV{STOW_DIR} ? $ENV{STOW_DIR} : getcwd();
|
2011-11-16 09:04:03 -05:00
|
|
|
}
|
|
|
|
|
2011-11-24 11:28:09 -05:00
|
|
|
if (exists $options->{'target'}) {
|
|
|
|
$options->{'target'} =~ s/\A +//;
|
|
|
|
$options->{'target'} =~ s/ +\z//;
|
2011-11-16 09:04:03 -05:00
|
|
|
}
|
2011-11-24 11:28:09 -05:00
|
|
|
else {
|
|
|
|
$options->{'target'} = parent($options->{'dir'});
|
2011-11-16 09:04:03 -05:00
|
|
|
}
|
2011-11-24 11:28:09 -05:00
|
|
|
}
|
2011-11-16 09:04:03 -05:00
|
|
|
|
2011-11-24 11:28:09 -05:00
|
|
|
sub check_packages {
|
|
|
|
my ($pkgs_to_stow, $pkgs_to_unstow) = @_;
|
|
|
|
|
|
|
|
if (not @$pkgs_to_stow and not @$pkgs_to_unstow) {
|
|
|
|
usage("No packages to stow or unstow");
|
2011-11-16 09:04:03 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
# check package arguments
|
2011-11-24 11:28:09 -05:00
|
|
|
for my $package (@$pkgs_to_stow, @$pkgs_to_unstow) {
|
2011-11-16 09:04:03 -05:00
|
|
|
$package =~ s{/+$}{}; # delete trailing slashes
|
2011-11-17 09:17:24 -05:00
|
|
|
if ($package =~ m{/}) {
|
2011-11-16 09:04:03 -05:00
|
|
|
error("Slashes are not permitted in package names");
|
|
|
|
}
|
|
|
|
}
|
2001-12-24 09:57:46 -05:00
|
|
|
}
|
|
|
|
|
2011-11-17 08:26:04 -05:00
|
|
|
|
2011-11-16 09:04:03 -05:00
|
|
|
#===== SUBROUTINE ============================================================
|
2011-11-21 08:59:36 -05:00
|
|
|
# Name : get_config_file_options()
|
2011-11-24 11:28:09 -05:00
|
|
|
# Purpose : search for default settings in any .stowrc files
|
2011-11-16 09:04:03 -05:00
|
|
|
# Parameters: none
|
2011-11-24 11:28:09 -05:00
|
|
|
# Returns : a list of default options
|
2011-11-16 09:04:03 -05:00
|
|
|
# Throws : no exceptions
|
2011-11-24 11:28:09 -05:00
|
|
|
# Comments : prepends the contents of '~/.stowrc' and '.stowrc' to the command
|
2011-11-16 09:59:58 -05:00
|
|
|
# : line so they get parsed just like normal arguments. (This was
|
2011-11-16 09:04:03 -05:00
|
|
|
# : hacked in so that Emil and I could set different preferences).
|
|
|
|
#=============================================================================
|
2011-11-24 11:28:09 -05:00
|
|
|
sub get_config_file_options {
|
2011-11-16 09:04:03 -05:00
|
|
|
my @defaults = ();
|
2011-11-24 11:28:09 -05:00
|
|
|
for my $file ("$ENV{'HOME'}/.stowrc", '.stowrc') {
|
2011-11-17 09:17:24 -05:00
|
|
|
if (-r $file) {
|
2011-11-16 09:04:03 -05:00
|
|
|
warn "Loading defaults from $file\n";
|
2011-11-21 08:59:36 -05:00
|
|
|
open my $FILE, '<', $file
|
2011-11-16 09:04:03 -05:00
|
|
|
or die "Could not open $file for reading\n";
|
2011-11-17 09:17:24 -05:00
|
|
|
while (my $line = <$FILE>){
|
2011-11-16 09:04:03 -05:00
|
|
|
chomp $line;
|
|
|
|
push @defaults, split " ", $line;
|
|
|
|
}
|
|
|
|
close $FILE or die "Could not close open file: $file\n";
|
|
|
|
}
|
|
|
|
}
|
2011-11-24 11:28:09 -05:00
|
|
|
return @defaults;
|
2011-11-21 08:59:36 -05:00
|
|
|
}
|
2011-11-16 09:04:03 -05:00
|
|
|
|
|
|
|
#===== SUBROUTINE ===========================================================
|
|
|
|
# Name : usage()
|
|
|
|
# Purpose : print program usage message and exit
|
2011-11-17 11:35:57 -05:00
|
|
|
# Parameters: $msg => string to prepend to the usage message
|
2011-11-21 08:59:36 -05:00
|
|
|
# Returns : n/a
|
2011-11-16 09:04:03 -05:00
|
|
|
# Throws : n/a
|
|
|
|
# Comments : if 'msg' is given, then exit with non-zero status
|
|
|
|
#============================================================================
|
2001-12-24 09:57:46 -05:00
|
|
|
sub usage {
|
2011-11-16 09:04:03 -05:00
|
|
|
my ($msg) = @_;
|
|
|
|
|
|
|
|
if ($msg) {
|
|
|
|
print "$ProgramName: $msg\n\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
print <<"EOT";
|
2011-11-24 11:28:09 -05:00
|
|
|
$ProgramName (GNU Stow) version $Stow::VERSION
|
2011-11-16 09:04:03 -05:00
|
|
|
|
|
|
|
SYNOPSIS:
|
|
|
|
|
|
|
|
$ProgramName [OPTION ...] [-D|-S|-R] PACKAGE ... [-D|-S|-R] PACKAGE ...
|
|
|
|
|
|
|
|
OPTIONS:
|
|
|
|
|
|
|
|
-n, --no Do not actually make any filesystem changes
|
|
|
|
-c, --conflicts Scan for and print any conflicts, implies -n
|
|
|
|
-d DIR, --dir=DIR Set stow dir to DIR (default is current dir)
|
|
|
|
-t DIR, --target=DIR Set target to DIR (default is parent of stow dir)
|
|
|
|
-v, --verbose[=N] Increase verbosity (levels are 0,1,2,3;
|
|
|
|
-v or --verbose adds 1; --verbose=N sets level)
|
|
|
|
|
|
|
|
-S, --stow Stow the package names that follow this option
|
|
|
|
-D, --delete Unstow the package names that follow this option
|
|
|
|
-R, --restow Restow (like stow -D followed by stow -S)
|
|
|
|
-p, --compat use legacy algorithm for unstowing
|
|
|
|
|
|
|
|
--ignore=REGEX ignore files ending in this perl regex
|
|
|
|
--defer=REGEX defer stowing files begining with this perl regex
|
|
|
|
if the file is already stowed to another package
|
|
|
|
--override=REGEX force stowing files begining with this perl regex
|
|
|
|
if the file is already stowed to another package
|
|
|
|
-V, --version Show stow version number
|
|
|
|
-h, --help Show this help
|
2001-12-24 09:57:46 -05:00
|
|
|
EOT
|
2011-11-24 11:28:09 -05:00
|
|
|
exit defined $msg ? 1 : 0;
|
2011-11-16 09:04:03 -05:00
|
|
|
}
|
|
|
|
|
2011-11-24 11:28:09 -05:00
|
|
|
sub version {
|
|
|
|
print "$ProgramName (GNU Stow) version $Stow::VERSION\n";
|
|
|
|
exit 0;
|
2011-11-16 09:04:03 -05:00
|
|
|
}
|
|
|
|
|
2011-11-24 11:28:09 -05:00
|
|
|
#===== METHOD ================================================================
|
2011-11-21 08:59:36 -05:00
|
|
|
# Name : strip_quotes
|
2011-11-16 09:04:03 -05:00
|
|
|
# Purpose : remove matching outer quotes from the given string
|
|
|
|
# Parameters: none
|
|
|
|
# Returns : n/a
|
|
|
|
# Throws : no exceptions
|
|
|
|
# Comments : none
|
|
|
|
#=============================================================================
|
|
|
|
sub strip_quotes {
|
|
|
|
my ($string) = @_;
|
|
|
|
|
|
|
|
if ($string =~ m{\A\s*'(.*)'\s*\z}) {
|
|
|
|
return $1;
|
|
|
|
}
|
|
|
|
elsif ($string =~ m{\A\s*"(.*)"\s*\z}) {
|
|
|
|
return $1
|
|
|
|
}
|
|
|
|
return $string;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-12-24 09:57:46 -05:00
|
|
|
# Local variables:
|
|
|
|
# mode: perl
|
2011-11-17 09:12:14 -05:00
|
|
|
# cperl-indent-level: 4
|
|
|
|
# end:
|
2011-11-16 09:04:03 -05:00
|
|
|
# vim: ft=perl
|