summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorfukachan <fukachan>2003-01-28 04:16:16 +0000
committerfukachan <fukachan>2003-01-28 04:16:16 +0000
commitad9332ceadc2fb94cb1515efd0ca0332a188eb95 (patch)
treef063b8ba0fc7070b2857016688387672a9c8a711
parent32df1cf02b3796d632dd5db19800b7629e1c8d20 (diff)
downloadfml8-ad9332ceadc2fb94cb1515efd0ca0332a188eb95.tar.gz
fml8-ad9332ceadc2fb94cb1515efd0ca0332a188eb95.tar.bz2
fml8-ad9332ceadc2fb94cb1515efd0ca0332a188eb95.zip
installer perl version
-rw-r--r--fml/etc/install.cf.in104
-rw-r--r--fml/lib/FML/Install.pm974
-rwxr-xr-xinstall.pl.in85
3 files changed, 1163 insertions, 0 deletions
diff --git a/fml/etc/install.cf.in b/fml/etc/install.cf.in
new file mode 100644
index 00000000..f43990a0
--- /dev/null
+++ b/fml/etc/install.cf.in
@@ -0,0 +1,104 @@
+#
+# Copyright (C) 2003 Ken'ichi Fukamachi
+# All rights reserved. This program is free software; you can
+# redistribute it and/or modify it under the same terms as Perl itself.
+#
+# $FML$
+#
+
+
+LIBS = @LIBS@
+
+CC = @CC@
+
+CFLAGS = @CFLAGS@
+
+LDFLAGS = @LDFLAGS@
+
+INSTALLCMD = @INSTALL@
+
+
+prefix = @prefix@
+
+exec_prefix = @exec_prefix@
+
+bindir = @bindir@
+
+mandir = @mandir@
+
+config_dir = @fmlconfdir@
+
+libexec_dir = @libexecdir@/fml
+
+lib_dir = @libdir@/fml
+
+data_dir = @datadir@/fml
+
+
+# ml spool
+ml_spool_dir = @mlspooldir@
+
+
+# owner of /var/spool/ml
+owner = @fml_owner@
+
+group = @fml_group@
+
+
+
+vendors = fml
+ cpan
+ img
+
+mandatory_dirs = prefix
+ exec_prefix
+ config_dir
+ default_config_dir
+ bindir
+ mandir
+ libexec_dir
+ lib_dir
+ data_dir
+
+# install /etc/fml/defauls/$version/
+nl_template_files = default_config.cf
+ config.cf
+
+
+# install /etc/fml/defauls/$version/
+template_files = include
+ include-ctl
+ include-error
+ aliases
+ postfix_virtual
+ dot_htaccess
+ dot-qmail
+ dot-qmail-admin
+ dot-qmail-ctl
+ dot-qmail-default
+ dot-qmail-request
+ procmailrc
+
+
+bin_programs = fml
+ fmladdr
+ fmlalias
+ fmldoc
+ fmlthread
+ fmlconf
+ fmlerror
+ makefml
+ fmlsch
+ fmlhtmlify
+ fmlspool
+ fmlsummary
+ fmlsuper
+
+
+libexec_programs = fml.pl
+ distribute
+ digest
+ command
+ error
+ mead
+ fmlserv
diff --git a/fml/lib/FML/Install.pm b/fml/lib/FML/Install.pm
new file mode 100644
index 00000000..91b49958
--- /dev/null
+++ b/fml/lib/FML/Install.pm
@@ -0,0 +1,974 @@
+#-*- perl -*-
+#
+# Copyright (C) 2003 Ken'ichi Fukamachi
+# All rights reserved. This program is free software; you can
+# redistribute it and/or modify it under the same terms as Perl itself.
+#
+# $FML$
+#
+
+package FML::Install;
+
+use strict;
+use vars qw(@ISA @EXPORT @EXPORT_OK $AUTOLOAD $debug);
+use Carp;
+use FileHandle;
+use File::Spec;
+use File::Copy;
+use File::Basename;
+
+
+=head1 NAME
+
+FML::Install - utility functions used in installation
+
+=head1 SYNOPSIS
+
+ # use FML::Install;
+ my $installer = new FML::Install;
+ my $config = $installer->load_install_cf( $cf );
+
+ printf $format, "version", $installer->get_version() if $debug;
+
+ my $list = $config->get_as_array_ref('mandatory_dirs');
+ for my $dir (@$list) {
+ my $path = $installer->path($dir);
+ printf $format, $dir, $path if $debug;
+ unless (-d $path) {
+ $installer->mkdir($path);
+ }
+ else {
+ print STDERR "ok $path\n" if $debug;
+ }
+ }
+
+ # XXX-TODO: check uid, gid
+
+ $installer->install_main_cf();
+ $installer->install_sample_cf_files();
+ $installer->install_default_config_files();
+ $installer->install_mtree_dir();
+ $installer->install_lib_dir();
+ $installer->install_libexec_dir();
+ $installer->install_data_dir();
+
+ # install programs hereafter.
+ $installer->install_bin_programs();
+
+ # update loader.
+ if ( $installer->need_resymlink_loader() ) {
+ $installer->install_loader();
+ $installer->resymlink_loader();
+ }
+
+ # set up ml_spool_dir such as /var/spool/ml if needed.
+ $installer->setup_ml_spool_dir();
+
+=head1 DESCRIPTION
+
+Our installer C<install.pl> at the fml8 top directory uses this
+module.
+
+=head1 METHODS
+
+=head2 C<new()>
+
+constructor.
+
+=cut
+
+
+# Descriptions: constructor.
+# Arguments: OBJ($self)
+# Side Effects: initiailze $self->{ _show_message } flag.
+# Return Value: OBJ
+sub new
+{
+ my ($self) = @_;
+ my ($type) = ref($self) || $self;
+ my $me = {};
+
+ # show several messages by default
+ enable_message($me);
+
+ return bless $me, $type;
+}
+
+
+=head1 CONFIG
+
+=head2 load_install_cf( $cf )
+
+read the specified config file, initialize and return config object.
+
+=cut
+
+
+# Descriptions: read the specified config file and initialize config object
+# Arguments: OBJ($self) STR($cf)
+# Side Effects: initialize configuration object
+# Return Value: OBJ
+sub load_install_cf
+{
+ my ($self, $cf) = @_;
+
+ use FML::Config;
+ my $config = new FML::Config;
+ croak( $config->error() ) if $config->error();
+
+ if (-f $cf) {
+ $config->load_file( $cf );
+ croak( $config->error() ) if $config->error();
+
+ $config->update();
+ $self->{ _config } = $config;
+ return $config;
+ }
+ else {
+ croak("no such file: $cf");
+ }
+
+ return undef;
+}
+
+
+=head1 INSTALL METHODS
+
+=head2 convert($src, $dst, [$mode])
+
+create $dst with variable substitutions.
+In addition, chmod() if $mode specified.
+
+=cut
+
+
+# Descriptions: create $dst with variable substitutions.
+# Arguments: OBJ($self) STR($src) STR($dst)
+# Side Effects: create $dst file.
+# Return Value: none
+sub convert
+{
+ my ($self, $src, $dst, $mode) = @_;
+ my $tmp = $dst. ".new.$$";
+ my $in = new FileHandle $src;
+ my $out = new FileHandle "> $tmp";
+
+ # special flag to influence message
+ my $dst_already_exist = -f $out ? 1 : 0;
+ my $is_show_message = $self->_is_show_message();
+
+ if (defined $in && defined $out) {
+ my $version = $self->get_version();
+
+ my $buf = '';
+ while ($buf = <$in>) {
+ $buf =~ s/__fml_version__/$version/;
+ print $out $buf;
+ }
+
+ $out->close();
+ $in->close();
+
+ if (rename($tmp, $dst)) {
+ if (-f $dst) {
+ if (defined $mode) { chmod $mode, $dst;}
+
+ if ($is_show_message) {
+ if ($dst_already_exist) {
+ print STDERR "updating $dst\n";
+ }
+ else {
+ print STDERR "creating $dst\n";
+ }
+ }
+ }
+ else {
+ _errmsg("fail to create $dst");
+ }
+ }
+ else {
+ _errmsg("fail to rename $tmp $dst");
+ }
+ }
+ else {
+ _errmsg("cannot open $src") unless defined $in;
+ _errmsg("cannot open $dst") unless defined $out;
+ _errmsg("fail to create $dst");
+ }
+}
+
+
+=head2 install_main_cf()
+
+install main.cf e.g. /etc/fml/main.cf.
+
+=cut
+
+
+# Descriptions: install main.cf
+# Arguments: OBJ($self)
+# Side Effects: create main.cf.
+# Return Value: none
+sub install_main_cf
+{
+ my ($self) = @_;
+
+ # XXX src = relative path, dst = absolute path
+ my $src = File::Spec->catfile("fml", "etc", "main.cf");
+ my $config_dir = $self->path( 'config_dir' );
+ my $dst = File::Spec->catfile($config_dir, "main.cf");
+
+ if (-f $dst) {
+ print STDERR "skipping $dst (debug)\n" if $debug;
+ }
+ else {
+ $self->convert($src, $dst, 0644);
+ }
+}
+
+
+=head2 install_sample_cf_files()
+
+install sample .cf files:
+
+ site_default_config.cf
+ mime_component_filter
+
+=cut
+
+
+# Descriptions: install sample .cf files.
+# Arguments: OBJ($self)
+# Side Effects: create sample .cf files in /etc/fml/.
+# Return Value: none
+sub install_sample_cf_files
+{
+ my ($self) = @_;
+ my $config_dir = $self->path( 'config_dir' );
+
+ for my $file (qw(site_default_config.cf
+ mime_component_filter)) {
+ # XXX src = relative path, dst = absolute path
+ my $src = File::Spec->catfile("fml", "etc", $file);
+ my $dst = File::Spec->catfile($config_dir, $file);
+
+ if (-f $dst) {
+ print STDERR "skipping $dst (debug)\n" if $debug;
+ }
+ else {
+ $self->convert($src, $dst, 0644);
+ }
+ }
+}
+
+
+=head2 install_default_config_files()
+
+install default templates at /etc/fml/defautls/$version/.
+
+=head2 install_mtree_dir()
+
+install mtree info at /etc/fml/defautls/$version/mtree/.
+
+=cut
+
+
+# Descriptions: install default templates.
+# Arguments: OBJ($self)
+# Side Effects: create files in /etc/fml/defaults/$version/.
+# Return Value: none
+sub install_default_config_files
+{
+ my ($self) = @_;
+ my $config = $self->{ _config };
+ my $config_dir = $self->path( 'default_config_dir' );
+
+ print STDERR "updating $config_dir\n";
+
+ $self->disable_message();
+
+ my $nl_template_files = $config->get_as_array_ref('nl_template_files');
+ for my $file (@$nl_template_files) {
+ # XXX-TODO: how should we handle natural language .cf ?
+ # XXX src = relative path, dst = absolute path
+ my $src = File::Spec->catfile("fml", "etc", $file . ".ja");
+ my $dst = File::Spec->catfile($config_dir, $file);
+
+ # always override.
+ $self->convert($src, $dst, 0644);
+ }
+
+ my $template_files = $config->get_as_array_ref('template_files');
+ for my $file (@$template_files) {
+ # XXX src = relative path, dst = absolute path
+ my $src = File::Spec->catfile("fml", "etc", $file);
+ my $dst = File::Spec->catfile($config_dir, $file);
+
+ # always override.
+ $self->convert($src, $dst, 0644);
+ }
+
+ $self->enable_message();
+}
+
+
+# Descriptions: install mtree config.
+# Arguments: OBJ($self)
+# Side Effects: create files in /etc/fml/defaults/$version/mtree/.
+# Return Value: none
+sub install_mtree_dir
+{
+ my ($self) = @_;
+ my $config = $self->{ _config };
+
+ # XXX src = relative path, dst = absolute path
+ my $dst_dir = File::Spec->catfile($self->path( 'default_config_dir' ),
+ "mtree");
+ my $src_dir = File::Spec->catfile("fml", "etc", "mtree");
+
+ print STDERR "updating $dst_dir\n" if $debug;
+ $self->copy_dir( $src_dir, $dst_dir );
+}
+
+
+=head2 install_lib_dir()
+
+install library (perl modules).
+
+=head2 install_libexec_dir()
+
+install libexec executables.
+
+=head2 install_data_dir()
+
+install files under fml/share/.
+
+=cut
+
+
+# Descriptions: install perl modules.
+# Arguments: OBJ($self)
+# Side Effects: install lib/
+# Return Value: none
+sub install_lib_dir
+{
+ my ($self) = @_;
+ my $config = $self->{ _config };
+ my $dst_dir = $self->path( 'lib_dir' );
+ my $src_dir = '';
+
+ print STDERR "updating $dst_dir\n";
+
+ my $vendors = $config->get_as_array_ref('vendors');
+ for my $vendor (@$vendors) {
+ # XXX src = relative path, dst = absolute path
+ $src_dir = File::Spec->catfile($vendor, "lib");
+ print STDERR " copying from $src_dir\n";
+ $self->copy_dir( $src_dir, $dst_dir );
+ }
+}
+
+
+# Descriptions: install executables.
+# Arguments: OBJ($self)
+# Side Effects: update libexec/.
+# Return Value: none
+sub install_libexec_dir
+{
+ my ($self) = @_;
+
+ # XXX src = relative path, dst = absolute path
+ my $src_dir = File::Spec->catfile("fml", "libexec");
+ my $dst_dir = $self->path( 'libexec_dir' );
+
+ print STDERR "updating $dst_dir\n";
+ $self->copy_dir( $src_dir, $dst_dir );
+}
+
+
+# Descriptions: install message files et.al.
+# Arguments: OBJ($self)
+# Side Effects: update share/.
+# Return Value: none
+sub install_data_dir
+{
+ my ($self) = @_;
+
+ # XXX src = relative path, dst = absolute path
+ my $src_dir = File::Spec->catfile("fml", "share");
+ my $dst_dir = $self->path( 'data_dir' );
+
+ print STDERR "updating $dst_dir\n";
+ $self->copy_dir( $src_dir, $dst_dir );
+}
+
+
+=head2 install_bin_programs()
+
+install utitily programs typically located at /usr/local/bin.
+
+=cut
+
+
+# Descriptions: install utitily programs typically located at /usr/local/bin.
+# Arguments: OBJ($self)
+# Side Effects: update /usr/lcoal/bin/.
+# Return Value: none
+sub install_bin_programs
+{
+ my ($self) = @_;
+ my $config = $self->{ _config };
+ my $progs = $config->get_as_array_ref('bin_programs');
+ my $dst_dir = $self->path( 'bindir' );
+
+ for my $prog (@$progs) {
+ # XXX src = relative path, dst = absolute path
+ my $src = File::Spec->catfile("fml", "bin", $prog);
+ my $dst = File::Spec->catfile($dst_dir, $prog);
+
+ print STDERR "updating $dst\n" if $debug;
+ unless (-f $dst) {
+ $self->_need_resymlink_loader();
+ }
+
+ # override always
+ $self->convert($src, $dst, 0755);
+ }
+}
+
+
+=head1 LOADER
+
+=head2 need_resymlink_loader()
+
+check if we need to update loader symlink?
+
+=head2 install_loader()
+
+install loader.
+
+=head2 resymlink_loader()
+
+re-symlink loader.
+
+=cut
+
+
+# Descriptions: toggle on that we need to re-symlink loader.
+# Arguments: OBJ($self)
+# Side Effects: update $self->{ _need_resymlink_loader }.
+# Return Value: 1
+sub _need_resymlink_loader
+{
+ my ($self) = @_;
+ $self->{ _need_resymlink_loader } = 1;
+}
+
+
+# Descriptions: check if we need to update loader symlink?
+# Arguments: OBJ($self)
+# Side Effects: none
+# Return Value: NUM(1 or 0)
+sub _is_need_resymlink_loader
+{
+ my ($self) = @_;
+ return( $self->{ _need_resymlink_loader } ? 1 : 0);
+}
+
+
+# Descriptions: check if we need to update loader symlink?
+# Arguments: OBJ($self)
+# Side Effects: none
+# Return Value: NUM(1 or 0)
+sub need_resymlink_loader
+{
+ my ($self) = @_;
+ my $status = 0;
+ my $config = $self->{ _config };
+
+ # XXX src = relative path, dst = absolute path
+ my $loader = File::Spec->catfile("fml", "libexec", "loader");
+ my $libexec_dir = $config->{ libexec_dir };
+ my $cur_loader = File::Spec->catfile($libexec_dir, "loader");
+
+ if ($debug) {
+ print STDERR "cur $cur_loader\n";
+ print STDERR "new $loader\n";
+ }
+
+ # when new bin/$program found
+ return 1 if $self->_is_need_resymlink_loader();
+
+ my $cur_sum = $self->md5( $cur_loader );
+ my $new_sum = $self->md5( $loader );
+
+ # need to update loader.
+ if ($cur_sum ne $new_sum) {
+ use Term::ReadLine;
+ my $term = new Term::ReadLine 'Simple Perl calc';
+ my $prompt = "You must upgrade loader. Replace it ? [y/n]: ";
+ my $OUT = $term->OUT || \*STDOUT;
+ my $res = '';
+
+ READLINE:
+ while (defined ($res = $term->readline($prompt))) {
+ if ($res eq 'y' || $res eq 'Y') {
+ $status = 1;
+ last READLINE;
+ }
+ warn $@ if $@;
+ }
+ }
+
+ return $status;
+}
+
+
+# Descriptions: install loader (fml/libexec/loader).
+# Arguments: OBJ($self)
+# Side Effects: update loader.
+# Return Value: none
+sub install_loader
+{
+ my ($self) = @_;
+ my $config = $self->{ _config };
+
+ # XXX src = relative path, dst = absolute path
+ my $loader = File::Spec->catfile("fml", "libexec", "loader");
+ my $libexec_dir = $config->{ libexec_dir };
+ my $cur_loader = File::Spec->catfile($libexec_dir, "loader");
+ my $tmp = $cur_loader . ".$$";
+
+ use File::Copy;
+ copy($loader, $tmp);
+ chmod 0755, $tmp;
+
+ unless (rename($tmp, $cur_loader)) {
+ _errmsg("fail to rename $tmp $cur_loader");
+ }
+
+ unless (-f $cur_loader) {
+ _errmsg("fail to install $cur_loader");
+ croak("fail to install $cur_loader\n");
+ }
+}
+
+
+# Descriptions: re-symlink executable to loader.
+# Arguments: OBJ($self)
+# Side Effects: update symlink.
+# Return Value: none
+sub resymlink_loader
+{
+ my ($self) = @_;
+ my $config = $self->{ _config };
+ my $libexec_dir = $config->{ libexec_dir };
+ my $cur_loader = File::Spec->catfile($libexec_dir, "loader");
+ my $bin_programs = $config->get_as_array_ref('bin_programs');
+ my $exec_programs = $config->get_as_array_ref('libexec_programs');
+
+ chdir $libexec_dir || croak("fail to chdir $libexec_dir");
+
+ print STDERR "symlink: loader to";
+ my $p = length("symlink: loader to");
+ my $n = $p;
+ for my $prog (@$bin_programs, @$exec_programs) {
+ $n += length(" $prog");
+ print STDERR " $prog";
+
+ unlink($prog);
+ symlink("loader", $prog);
+
+ if ($n > 72) {
+ print STDERR "\n";
+ print STDERR " " x $p;
+ $n = $p;
+ }
+ }
+ print STDERR "\n";
+}
+
+
+=head1 SET UP ML SPOOL
+
+=head2 setup_ml_spool_dir()
+
+set up $ml_spool_dir e.g. /var/spool/ml.
+
+=cut
+
+
+# Descriptions: set up $ml_spool_dir.
+# Arguments: OBJ($self)
+# Side Effects: mkdir and chown /var/spool/ml.
+# Return Value: none
+sub setup_ml_spool_dir
+{
+ my ($self) = @_;
+ my $config = $self->{ _config };
+ my $dir = $self->path( 'ml_spool_dir' );
+ my $owner = $config->{ owner };
+ my $group = $config->{ group };
+
+ if (-d $dir and -w $dir) {
+ print STDERR " * info: $dir exists. not touch it.\n";
+ }
+ else {
+ print STDERR "creating $dir\n";
+ $self->mkdir( $dir );
+
+ print STDERR " chown $owner:$group $dir\n";
+ $self->chown( $owner, $group, $dir );
+ }
+}
+
+
+=head1 UTILITY FUNCTIONS
+
+=head2 get_version()
+
+return fml version.
+return current-YYYYMMDD if ".version" file is not found.
+
+=cut
+
+
+# Descriptions: return fml version.
+# Arguments: OBJ($self)
+# Side Effects: none
+# Return Value: STR
+sub get_version
+{
+ my ($self) = @_;
+ my $vers = '';
+
+ if (-f ".version") {
+ use FileHandle;
+ my $fh = new FileHandle ".version";
+ if (defined $fh) {
+ chomp($vers = <$fh>);
+ $fh->close;
+ }
+ }
+
+ unless ($vers) {
+ use POSIX;
+ $vers = strftime("current-%C%y%m%d", localtime());
+ }
+
+ return $vers;
+}
+
+
+=head2 is_valid_owner( $owner )
+
+check if $owner is a valid user ?
+
+=head2 is_valid_group( $group )
+
+check if $group is a valid group ?
+
+=cut
+
+
+# Descriptions: check if the $user is valid?
+# Arguments: OBJ($self) STR($user)
+# Side Effects: croak() if critical error found.
+# Return Value: NUM(1 or 0)
+sub is_valid_owner
+{
+ my ($self, $user) = @_;
+
+ use User::pwent;
+ my $pw = getpwnam($user) || croak("no such user: $user");
+ if ($pw->uid == 0) {
+ croak("user should be not ROOT!");
+ }
+
+ return 1;
+}
+
+
+# Descriptions: check if the $group is valid?
+# Arguments: OBJ($self) STR($group)
+# Side Effects: croak() if critical error found.
+# Return Value: NUM(1 or 0)
+sub is_valid_group
+{
+ my ($self, $group) = @_;
+
+ use User::grent;
+ my $gr = getgrnam($group) || croak("no such group: $group");
+
+ return 1;
+}
+
+
+=head2 path( $dir )
+
+return the absolute directory path for the specified type C<$dir>.
+
+=cut
+
+
+# Descriptions: return the absolute dir path for the type $dir.
+# Arguments: OBJ($self) STR($dir)
+# Side Effects: none
+# Return Value: STR
+sub path
+{
+ my ($self, $dir) = @_;
+ my $config = $self->{ _config };
+ my $version = $self->get_version();
+ my $config_dir = $config->{ config_dir };
+
+ if ($dir eq 'prefix' ||
+ $dir eq 'exec_prefix' ||
+ $dir eq 'config_dir' ||
+ $dir eq 'bindir' ||
+ $dir eq 'mandir' ||
+ $dir eq 'ml_spool_dir') {
+ return $config->{ $dir };
+ }
+ elsif ($dir eq 'default_config_dir') {
+ return File::Spec->catfile($config->{ config_dir },
+ "defaults",
+ $version);
+ }
+ else {
+ if (defined $config->{ $dir }) {
+ return File::Spec->catfile($config->{ $dir }, $version);
+ }
+ else {
+ return '';
+ }
+ }
+}
+
+
+=head1 UTILITY FUNCTIONS FOR FILE HANDLING
+
+=head2 mkdir( $dir, [$mode] )
+
+mkdir $dir with the mode $mode if $mode specified.
+Whereas, mkdir $dir with the mode 0755 if $mode unspecified.
+
+=head2 copy_dir( $src_dir, $dst_dir )
+
+copy files recursively.
+
+=head2 chown( $owner, $group, $dir )
+
+chown $owner:$group $dir.
+
+=cut
+
+
+# Descriptions: mkdir $dir with the mode $mode
+# Arguments: OBJ($self) STR($dir) NUM($mode)
+# Side Effects: mkdir $dir
+# Return Value: none
+sub mkdir
+{
+ my ($self, $dir, $mode) = @_;
+
+ unless (-d $dir) {
+ use File::Path;
+ mkpath( [ $dir ], 0, ($mode || 0755) );
+ }
+}
+
+
+my @_cache = ();
+
+
+# Descriptions: copy all files recursively.
+# Arguments: OBJ($self) STR($src_dir) STR(dst_dir)
+# Side Effects: update $dst_dir
+# Return Value: none
+sub copy_dir
+{
+ my ($self, $src_dir, $dst_dir) = @_;
+
+ @_cache = (); # XXX global in this package.
+
+ use File::Find;
+ find(\&_want_file, $src_dir);
+
+ my $n;
+ for my $file (@_cache) {
+ $n = $file;
+ $n =~ s@$src_dir@@;
+
+ my $src = $file;
+ my $dst = File::Spec->catfile( $dst_dir, $n );
+
+ my $dst_dir = dirname($dst);
+ unless (-d $dst_dir) {
+ print STDERR " ** ? ** $dst_dir\n" if -f $dst_dir;
+ $self->mkdir( $dst_dir );
+ }
+
+ if (-f $src && -f $dst) {
+ copy($src, $dst);
+ }
+ else {
+ print "warning $src -> $dst\n" if $debug;
+ }
+ }
+}
+
+
+# Descriptions: subroutine used by File::Find().
+# Arguments: none
+# Side Effects: update @_cache
+# Return Value: none
+sub _want_file
+{
+ my ($s) = $File::Find::name;
+
+ if ($s !~ /CVS/) {
+ push(@_cache, $s);
+ }
+}
+
+
+# Descriptions: chown
+# Arguments: OBJ($self) STR($owner) STR($group) STR($dir)
+# Side Effects: change owner and group of $dir
+# Return Value: none
+sub chown
+{
+ my ($self, $owner, $group, $dir ) = @_;
+
+ use User::pwent;
+ my $pw = getpwnam($owner) || croak("no such user: $owner");
+ my $uid = $pw->uid;
+
+ use User::grent;
+ my $gr = getgrnam($group) || croak("no such group: $group");
+ my $gid = $gr->gid;
+
+ @_cache = (); # XXX global in this package.
+
+ use File::Find;
+ find(\&_want_file, $dir);
+
+ for my $file (@_cache) {
+ print STDERR "chown $uid, $gid, $file\n" if $debug;
+ chown $uid, $gid, $file;
+ }
+}
+
+
+=head2 md5( $file )
+
+return MD5 checksum for the file.
+
+=cut
+
+
+# Descriptions: return MD5 checksum for the file.
+# Arguments: OBJ($self) STR($file)
+# Side Effects: none
+# Return Value: STR
+sub md5
+{
+ my ($self, $file) = @_;
+ my $buf = '';
+
+ my $fh = new FileHandle $file;
+ if (defined $fh) {
+ while (<$fh>) { $buf .= $_;}
+ $fh->close();
+ }
+
+ use Mail::Message::Checksum;
+ my $cksum = new Mail::Message::Checksum;
+ my $sum = $cksum->md5( \$buf );
+
+ return $sum;
+}
+
+
+=head1 MESSAGE MANIPULATION
+
+=head2 enable_message()
+
+enable verbose message output.
+
+=head2 disable_message()
+
+disable verbose message output.
+
+=cut
+
+
+# Descriptions: enable message output.
+# Arguments: OBJ($self)
+# Side Effects: update $self->{ _show_message }.
+# Return Value: NUM
+sub enable_message
+{
+ my ($self) = @_;
+ $self->{ _show_message } = 1;
+}
+
+
+# Descriptions: disable message output.
+# Arguments: OBJ($self)
+# Side Effects: update $self->{ _show_message }.
+# Return Value: NUM
+sub disable_message
+{
+ my ($self) = @_;
+ $self->{ _show_message } = 0;
+}
+
+
+# Descriptions: check if we show message
+# Arguments: OBJ($self)
+# Side Effects: none
+# Return Value: NUM
+sub _is_show_message
+{
+ my ($self) = @_;
+ return $self->{ _show_message };
+}
+
+
+# Descriptions: show error message by some predefined fomrat.
+# Arguments: STR($s)
+# Side Effects: none
+# Return Value: none
+sub _errmsg
+{
+ my ($s) = @_;
+ print STDERR " * error: $s\n";
+}
+
+
+=head1 CODING STYLE
+
+See C<http://www.fml.org/software/FNF/> on fml coding style guide.
+
+=head1 AUTHOR
+
+Ken'ichi Fukamachi
+
+=head1 COPYRIGHT
+
+Copyright (C) 2003 Ken'ichi Fukamachi
+
+All rights reserved. This program is free software; you can
+redistribute it and/or modify it under the same terms as Perl itself.
+
+=head1 HISTORY
+
+FML::Install appeared in fml8 mailing list driver package.
+See C<http://www.fml.org/> for more details.
+
+=cut
+
+
+1;
diff --git a/install.pl.in b/install.pl.in
new file mode 100755
index 00000000..9c3f413f
--- /dev/null
+++ b/install.pl.in
@@ -0,0 +1,85 @@
+#! @PERL@ -w
+#-*- perl -*-
+#
+# Copyright (C) 2003 Ken'ichi Fukamachi
+# All rights reserved. This program is free software; you can
+# redistribute it and/or modify it under the same terms as Perl itself.
+#
+# $FML$
+#
+
+use strict;
+use Carp;
+use lib qw(fml/lib cpan/lib img/lib);
+
+# Run this from the top-level fml source directory.
+$ENV{'PATH'} = '/usr/xpg4/bin:/bin:/usr/bin:/usr/sbin:/usr/etc:/sbin:/etc';
+
+my $debug = 0;
+
+if ($0 eq __FILE__) {
+ umask(022);
+
+ unless (@ARGV) {
+ croak("Usage: $0 install.cf\n");
+ }
+ else {
+ _install( $ARGV[0] );
+ }
+}
+else {
+ croak("Usage: $0 install.cf\n");
+}
+
+exit 0;
+
+
+# Descriptions: install fml8
+# Arguments: STR($cf)
+# Side Effects: install a lot of files: bin, lib, libexec/ and share/.
+# Return Value: none
+sub _install
+{
+ my ($cf) = @_;
+ my $format = "%20s = %s\n";
+
+ use FML::Install;
+ my $installer = new FML::Install;
+ my $config = $installer->load_install_cf( $cf );
+
+ printf $format, "version", $installer->get_version() if $debug;
+
+ my $list = $config->get_as_array_ref('mandatory_dirs');
+ for my $dir (@$list) {
+ my $path = $installer->path($dir);
+ printf $format, $dir, $path if $debug;
+ unless (-d $path) {
+ $installer->mkdir($path);
+ }
+ else {
+ print STDERR "ok $path\n" if $debug;
+ }
+ }
+
+ # XXX-TODO: check uid, gid
+
+ $installer->install_main_cf();
+ $installer->install_sample_cf_files();
+ $installer->install_default_config_files();
+ $installer->install_mtree_dir();
+ $installer->install_lib_dir();
+ $installer->install_libexec_dir();
+ $installer->install_data_dir();
+
+ # install programs hereafter.
+ $installer->install_bin_programs();
+
+ # update loader.
+ if ( $installer->need_resymlink_loader() ) {
+ $installer->install_loader();
+ $installer->resymlink_loader();
+ }
+
+ # set up ml_spool_dir such as /var/spool/ml if needed.
+ $installer->setup_ml_spool_dir();
+}