1
# $Id: Slack.pm,v 1.9 2006/09/27 07:46:42 alan Exp $
4
# Copyright (C) 2004-2006 Alan Sundell <alan@sundell.net>
5
# All Rights Reserved. This program comes with ABSOLUTELY NO WARRANTY.
6
# See the file COPYING for details.
12
use Carp qw(cluck confess croak);
15
use base qw(Exporter);
16
use vars qw($VERSION @EXPORT @EXPORT_OK $DEFAULT_CONFIG_FILE);
21
$DEFAULT_CONFIG_FILE = '/etc/slack.conf';
25
my @default_options = (
40
sub default_usage ($) {
47
Print this help message and exit.
53
Don't be verbose (Overrides previous uses of --verbose)
56
Use this config file instead of '$DEFAULT_CONFIG_FILE'.
59
Source for slack files
62
Local cache directory for slack files
65
Local staging directory for slack files
68
Root destination for slack files
71
Don't write any files to disk -- just report what would have been done.
74
Make backups of existing files in ROOT that are overwritten.
77
Put backups into this directory.
80
Pretend to be running on HOST, instead of the name given by
84
# Read options from a config file. Arguments:
85
# file => config file to read
86
# opthash => hashref in which to store the options
87
# verbose => whether to be verbose
93
confess "Slack::read_config: no config file given"
94
if not defined $arg{file};
96
if not defined $arg{opthash};
98
open($config_fh, '<', $arg{file})
99
or confess "Could not open config file '$arg{file}': $!";
101
# Make this into a hash so we can quickly see if we're looking
102
# for a particular option
104
if (ref $arg{options} eq 'ARRAY') {
105
%looking_for = map { $_ => 1 } @{$arg{options}};
108
while(<$config_fh>) {
110
s/#.*//; # delete comments
111
s/\s+$//; # delete trailing spaces
112
next if m/^$/; # skip empty lines
114
if (m/^[A-Z_]+=\S+/) {
115
my ($key, $value) = split(/=/, $_, 2);
116
$key =~ tr/A-Z_/a-z-/;
117
# Only set options we're looking for
118
next if (%looking_for and not $looking_for{$key});
119
# Don't set options that are already set
120
next if defined $arg{opthash}->{$key};
122
$arg{verbose} and print STDERR "Slack::read_config: Setting '$key' to '$value'\n";
123
$arg{opthash}->{$key} = $value;
125
cluck "Slack::read_config: Garbage line '$_' in '$arg{file}' line $. ignored";
130
or confess "Slack::read_config: Could not close config file: $!";
132
# The verbose option is treated specially in so many places that
133
# we need to make sure it's defined.
134
$arg{opthash}->{verbose} ||= 0;
136
return $arg{opthash};
139
# Just get the exit code from a command that failed.
140
# croaks if anything weird happened.
141
sub get_system_exit (@) {
144
croak "'@command' dumped core";
146
if (my $sig = $? & 127) {
147
croak "'@command' caught sig $sig";
150
return $exit if $exit;
152
croak "Syserr on system '@command': $!";
154
croak "Unknown error on '@command'";
157
sub check_system_exit (@) {
159
my $exit = get_system_exit(@command);
160
# Exit is non-zero if get_system_exit() didn't croak.
161
croak "'@command' exited $exit";
164
# get options from the command line and the config file
166
# opthash => hashref in which to store options
167
# usage => usage statement
168
# required_options => arrayref of options to require -- an exception
169
# will be thrown if these options are not defined
170
# command_line_hash => store options specified on the command line here
174
Getopt::Long::Configure('bundling');
176
if (not defined $arg{opthash}) {
180
if (not defined $arg{usage}) {
181
$arg{usage} = default_usage($0);
184
my @extra_options = (); # extra arguments to getoptions
185
if (defined $arg{command_line_options}) {
186
@extra_options = @{$arg{command_line_options}};
189
# Make a --quiet function that turns off verbosity
190
$arg{opthash}->{quiet} = sub { $arg{opthash}->{verbose} = 0; };
192
unless (GetOptions($arg{opthash},
196
print STDERR $arg{usage};
199
if ($arg{opthash}->{help}) {
204
# Get rid of the quiet handler
205
delete $arg{opthash}->{quiet};
207
# If we've been given a hashref, save our options there at this
208
# stage, so the caller can see what was passed on the command line.
209
# Unfortunately, perl has no .replace function, so we iterate.
210
if (ref $arg{command_line_hash} eq 'HASH') {
211
while (my ($k, $v) = each %{$arg{opthash}}) {
212
$arg{command_line_hash}->{$k} = $v;
216
# Use the default config file
217
if (not defined $arg{opthash}->{config}) {
218
$arg{opthash}->{config} = $DEFAULT_CONFIG_FILE;
221
# We need to decide whether to be verbose about reading the config file
222
# Currently we just do it if global verbosity > 2
223
my $verbose_config = 0;
224
if (defined $arg{opthash}->{verbose}
225
and $arg{opthash}->{verbose} > 2) {
229
# Read options from the config file, passing along the options we've
232
file => $arg{opthash}->{config},
233
opthash => $arg{opthash},
234
verbose => $verbose_config,
237
# The "verbose" option gets compared a lot and needs to be defined
238
$arg{opthash}->{verbose} ||= 0;
240
# The "hostname" option is set specially if it's not defined
241
if (not defined $arg{opthash}->{hostname}) {
243
$arg{opthash}->{hostname} = hostname;
246
# We can require some options to be set
247
if (ref $arg{required_options} eq 'ARRAY') {
248
for my $option (@{$arg{required_options}}) {
249
if (not defined $arg{opthash}->{$option}) {
250
croak "Required option '$option' not given on command line or specified in config file!\n";
255
return $arg{opthash};
260
if (not defined $term) {
261
require Term::ReadLine;
262
$term = new Term::ReadLine 'slack'
265
$term->readline($prompt);
269
# Calls the callback on absolute pathnames of files in the source directory,
270
# and also on names of directories that don't exist in the destination
271
# directory (i.e. where $source/foo exists but $destination/foo does not).
272
sub find_files_to_install ($$$) {
273
my ($source, $destination, $callback) = @_;
276
if (-l or not -d _) {
277
# Copy all files, links, etc
278
my $file = $File::Find::name;
281
# For directories, we only want to copy it if it doesn't
282
# exist in the destination yet.
283
my $dir = $File::Find::name;
284
# We know the root directory will exist (we make it above),
285
# so skip the base of the source
286
(my $short_source = $source) =~ s#/$##;
287
return if $dir eq $short_source;
289
# Strip the $source from the path,
290
# so we can build the destination dir from it.
292
($subdir =~ s#^$source##)
293
or croak "sub failed: $source|$subdir";
295
if (not -d "$destination/$subdir") {