#!/usr/bin/perl -w
#
# "Cmmi" is a "configure ; make ; make install" tool.
#               You can do it more easy.
#   Copyright (C) 1999-2004 Kiyoka Nishyama
#     $Date: 2007/01/18 14:33:40 $
#
# This file is part of Cmmi
#
# Cmmi 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, or (at your option)
# any later version.
# 
# Cmmi 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.  
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with Cmmi; see the file COPYING.
#
#
require 5.003;
use English;

use strict 'vars';
use vars qw($VERSION  $debug $verbose
	    $SCRIPT_NAME
	    $ARCHIVE_DIR
	    $PACKAGE_PREFIX
	    $PACKAGE_TYPE
	    $PACKAGE_DIR
	    $TARGET_PREFIX
	    $opt_v $opt_d $opt_n
	    $GET_URLs_COMMAND
	    @ARC_SUFFIX_LIST
	    );
use Getopt::Std;
use File::Basename;
# ---------- CONSTANT VALUE ----------
$VERSION = qw( 0.5.2 );

$debug   = 0;
$verbose = 0;

$SCRIPT_NAME = "cmmi.script";
$GET_URLs_COMMAND = "cat ./.url | xargs wget -nc -nd";
@ARC_SUFFIX_LIST = qw( .tar.gz .tgz );
# ---------- PROGRAM CODE --------
main( );
exit( 0 );

sub main {
    my( $comm ) = "";
    my( $done ) = 0;
    my( $home ) = $ENV{'HOME'};
    load_rcfiles( "$home/.cmmirc" );
    getopts( 'vdn' );
    if( $opt_v ) { $verbose = 1; }         # verbose switch
    if( $opt_d ) { $debug   = 1; }         # debug   switch
    if( $opt_n ) { $PACKAGE_TYPE = 0; }    # set package type to NON
    if( scalar(@ARGV)) { $comm = $ARGV[0]; }
    if( "clean" eq $comm ) {
	my( $state ) = get_curdir_state( $home );
	if( 1 == $state ) {
	    my( $name ) = sprintf( "/bin/rm -rf */%s */build */tmp", $SCRIPT_NAME );
	    system( $name );
	    $done = 1;
	}
	if( 2 == $state ) {
	    my( $name ) = sprintf( "/bin/rm -rf ./%s ./build ./tmp", $SCRIPT_NAME );
	    system( $name );
	    if( -e '.url' ) {
		my( $suffix );
		foreach $suffix ( @ARC_SUFFIX_LIST ) {
		    $name = "/bin/rm -f ./*" . $suffix;
		    system( $name );
		}
	    }
	    $done = 1;
	}
    }
    if( "install" eq $comm ) {
	system( sprintf( "./%s install", $SCRIPT_NAME ));
	$done = 1;
    }
    if( ( $comm =~ m/.*[.]tar[.].*/ ) || ( $comm =~ m/.*[.]tgz$/ ) ) {
	get_the_archive( $home, @ARGV );
	$done = 1;
    }
    if( !$done || ( "make" eq $comm )) {
	my( $go ) = 1;
	my( $configure, $dir ) = get_dirname( ( <*.tar.*>, <*.tgz> ) );
	my( $state, $pwd     ) = get_curdir_state( $home );
	if( 2 != $state      ) { $go = 0; }
	
	if( $go ) {
	    # make cmmi.script
	    make_cmmi_script( );
	    $done = 1;
	}
    }
    if( !$done ) { usage( $home ); exit( 1 ); }
}


#
# Get the archive file
# 
sub get_the_archive ( @ ) {
    my( $home, @arc ) = @_;
    my( $url_flag ) = 0;

    if( $debug ) {
	printf( "Info:get_the_archive home=%s arc=", $home );
	print join( " ", @arc ), "\n";
    }

    # argument is an only URL?
    foreach (@arc) {
	if( m/^http:/ || m/^ftp:/ ) {
	    $url_flag++;
	}
    }

    # arguments are mixed of URL and filename -> Error
    if ( ($url_flag > 0) && ($url_flag != scalar ( @arc ) ) ) {
	printf( STDERR "Cmmi Error : You must not specify URL and filename together for argument -- url_flag:%d  scalar( \@arc ):%d\n",
		$url_flag, scalar ( @arc ) );
	exit( 1 );
    }

    if ( $url_flag ) {

	# Get the package name from URL
	if( $debug ) {
	    printf( "Info:original URL = %s\n", $arc[0] );
	}
	$arc[0] =~ /\/([^\/]*)[.](tar.gz|tgz|tar.bz2)/;
	my( $dir ) = $1;
	if( $debug ) {
	    printf( "Info:package name = %s\n", $dir );
	}

	# Storing URLs to file ".url"
	system( sprintf( "/bin/mkdir %s/%s/%s", $home, $ARCHIVE_DIR, $dir ));
	open( FP, sprintf( ">%s/%s/%s/.url", $home, $ARCHIVE_DIR, $dir ));
	foreach (@arc) {
	    print FP $_, "\n";
	    printf( "%13s : %s -> %s/%s/%s/.url\n", "register URL", $_, $home, $ARCHIVE_DIR, $dir );
	}
	close( FP );
	printf( "%13s : %s\n", "dir",          $dir );
	printf( "# (for Emacs shell-mode, Please return)\n" );
	printf( "cd %s/%s/%s\n", $home, $ARCHIVE_DIR, $dir );
    }
    else {
	# Storing archives to $ARCHIVE_DIR
	my( $configure, $dir ) = get_dirname( @arc );
	printf( "%13s : %s\n", "arcives",      join( " ", @arc ));
	printf( "%13s : %s\n", "dir",          $dir );
	system( sprintf( "/bin/mkdir %s/%s/%s", $home, $ARCHIVE_DIR, $dir ))
	    unless(-d sprintf( "%s/%s/%s", $home, $ARCHIVE_DIR, $dir ));
	foreach ( @arc ) {
	    system( sprintf( "/bin/mv %s %s/%s/%s", $_, $home, $ARCHIVE_DIR, $dir ));
	    printf( "%13s  : %s -> %s/%s/%s\n", "move", $_, $home, $ARCHIVE_DIR, $dir );
	    printf( "# (for Emacs shell-mode, Please return)\n" );
	    printf( "cd %s/%s/%s\n", $home, $ARCHIVE_DIR, $dir );
	}
    }
}



#
# Loading the rcfile
# 
sub load_rcfiles () {
    my ($rcfile) = @_;
    
    if( $verbose ) {
	printf( "Info: rcfile = %s\n", $rcfile );
    }
    if (-f $rcfile) { do $rcfile; }
    
    if( !defined( $ARCHIVE_DIR )) {
	$ARCHIVE_DIR  = "cmmi";
    }
    if( !defined( $PACKAGE_TYPE )) {
	$PACKAGE_TYPE  = 0;
    }
    if( !defined( $PACKAGE_PREFIX )) {
	$PACKAGE_PREFIX  = "cmmi-";
    }
    if( !defined( $TARGET_PREFIX )) {
	$TARGET_PREFIX  = "/usr/local";
    }
    else {
	if( !defined( $PACKAGE_DIR )) {
	    printf( STDERR "Cmmi Error : You must define pair variable \$PACKAGE_TYPE,\$PACKAGE_DIR\n" );
	}
    }
}

# Getting the current state
#  RETURN VALUE=( $state, $name )
#  $state ... 0: out of ~/$ARCHIVE_DIR
#             1: in     ~/$ARCHIVE_DIR 
#             2: just   ~/$ARCHIVE_DIR/xxxx, and $name is full path-name of current directory
sub get_curdir_state ($) {
    my( $home ) = @_;
    my( $state ) = 0;
    my( $pwd  ) = $ENV{'PWD'};
    my( $name ) = "";
    if( $debug ) {
	printf( "Info:home = %s, cwd = %s\n", $home,  $pwd );
    }
    
    if( $debug ) {
	printf( "Info: ARCHIVE_DIR = %s\n", $ARCHIVE_DIR );
	printf( "Info: base1(%s)  base0(%s)\n", basename( dirname( $pwd )), basename( $pwd ));
    }
    
    if( $ARCHIVE_DIR eq basename( $pwd )) { $state = 1; }   
    if( $ARCHIVE_DIR eq basename( dirname( $pwd ))) {
	$state = 2;
	$name  = $pwd;
    }
    if( $debug ) {
	printf( "Info: $state = %d\n", $state );
    }
    return( $state, $name );
}

sub usage ($) {
    my( $home ) = @_;
    printf( "cmmi $VERSION\n" );
    printf( "usage : cmmi [switch]  [install|clean|make|ARCFILE] \n" );
    printf( "  cmmi ARCFILE .... checkin ARCFILE to %s/%s/xxxxx/\n", $home, $ARCHIVE_DIR );
    printf( "  cmmi         .... configure and make\n" );
    printf( "  cmmi install .... install \n" );
    printf( "  cmmi clean   .... cleaning work \n" );
    printf( "switch : \n" );
    printf( "  -v   verbose mode\n" );
    printf( "  -d   debug   mode\n" );
    printf( "  -n   force unset '\$PACKAGE_TYPE'\n" );
}

# Making the cmmi.script
sub make_cmmi_script {
    my( $name );
    my( @arc, $arcstr );
    my( @out );
    my( $found ) = 0;
    
    # Recognizing archive files.
    if( -e "./.url" ) {
	$name = $GET_URLs_COMMAND;
	system( $name );
    }
    @arc = ( <*.tar.*>, <*.tgz> );
    if( ! @arc ) { printf( STDERR "Error : No archives.\n" ); exit( 0 ); }
    $arcstr = join( " ", @arc );
    if( $verbose ) {
	printf( "%13s : %s\n", "archives", $arcstr );
    }
    
    # Searching a configure file.
    my( $configure, $dir ) = get_dirname( @arc );
    if( !$configure  ) { printf( STDERR "%13s : error configure script was not found.\n", "Warning" ); }
    if( $verbose ) { printf( "%13s : %s\n", "dir", $dir ); }
    
    # Searching a .cmmi file and read the contents.
    my( $configure_sw, $init_config, $make_str, $inst_str, $excl_str ) = read_dot_cmmi( );
    
    # Searching package relations between other cmmi packages (symbolic link file)
    my( @depends );
    if( -d "./.depends" ) {
	opendir( DIR, "./.depends" );
	my( @filelist ) = readdir( DIR );
	closedir( DIR );
	foreach ( @filelist ) {
	    if ( /^[^.]/ ) {
		my( $package_name, $package_version ) = get_package_info( $_ );
		push( @depends, $package_name );
	    }
	}
    }
    foreach ( @depends ) {
	printf( "%13s : %s\n", "depends", $_ );
    }
    my( $depends_str ) = join( "!!!", @depends );

    # Generating the cmmi.script
    @out = output_cmmi_script( $arcstr, $dir, $configure_sw, $init_config, $make_str, $inst_str, $depends_str, $excl_str );
    if( $verbose ) {
	printf( "%13s : %s ( %s, %s )\n", "generate", $SCRIPT_NAME, $arcstr, $dir );
	my( $t ) = "NON (\"make install\" immediately)";
	if( $PACKAGE_TYPE eq "D" ) { $t = "Debian    package"; }
	if( $PACKAGE_TYPE eq "R" ) { $t = "RPM       package"; }
	if( $PACKAGE_TYPE eq "S" ) { $t = "Slackware package"; }
	if( $PACKAGE_TYPE eq "C" ) { $t = "Cygwin    package"; }
	printf( "%13s : %s\n", "package type", $t );
    }
    open( FP, ">$SCRIPT_NAME" );
    foreach (@out) { print FP $_, "\n"; }
    close( FP );
    system( "/bin/chmod +x $SCRIPT_NAME" );
    
    # Executing of configure and make
    $name = sprintf( "./%s config ; ./%s make", $SCRIPT_NAME, $SCRIPT_NAME );
    system( $name );
}

# Reading a .cmmi
sub read_dot_cmmi {
    my( @conf, @init, @make, @inst, @excl );
    my( $state ) = "";
    unless( open( FP, ".cmmi" )) {
	if( $verbose ) {
	    printf( "%13s : .cmmi not found\n", "Warning" );
	}
	return( ("", "", "", "") );
    }
    foreach ( <FP> ) {
	chop;
	if( $debug ) { print( ".cmmi : $_ \n" ); }
	if( /^conf/ ) { $state = "conf"; push( @conf, "#" ); next; }
	if( /^init/ ) { $state = "init"; push( @init, "#" ); next; }
	if( /^make/ ) { $state = "make"; push( @make, "#" ); next; }
	if( /^inst/ ) { $state = "inst"; push( @inst, "#" ); next; }
	if( /^excl/ ) { $state = "excl"; push( @excl, "#" ); next; }
	
	if( "conf" eq $state ) { push( @conf,   $_ ); }
	if( "init" eq $state ) { push( @init,   $_ ); }
	if( "make" eq $state ) { push( @make,   $_ ); }
	if( "inst" eq $state ) { push( @inst,   $_ ); }
	if( "excl" eq $state ) { push( @excl,   $_ ); }
    }
    close( FP );
    my( $conf_str )   = join( "!!!", @conf );
    my( $init_str )   = join( "!!!", @init );
    my( $make_str   ) = join( "!!!", @make );
    my( $inst_str   ) = join( "!!!", @inst );
    my( $excl_str   ) = join( "!!!", @excl );
    return( ( $conf_str, $init_str, $make_str, $inst_str, $excl_str ) );
}

# Analyzing the directory path-name
sub get_dirname {
    my( @arc ) = @_;
    my( $found ) = 0;
    my( $dir, @list );
    foreach ( @arc ) {
	my( $name ) = "";
	if ( /.gz$/ ) {
	    $name = sprintf( "tar ztf %s|", $_ );
	}
	elsif( /.bz2$/ ) {
	    $name = sprintf( "tar jtf %s|", $_ );
	}
	open( FP, $name );
	@list = <FP>;
	foreach ( @list ) {  
	    chop;
	    if( /\/configure/ ) { $found = 1; }
	}
	$list[0] =~ s/^[.]+\///;
	$list[0] =~ m/([^\/]+)/;
	$dir = $1;
	close( FP );
    }
    return( ($found, $dir) );
}


# Analyzing 'package name' and 'version' string from dirname
sub get_package_info {
    my( $package_name )    = @_;
    my( $package_version ) = @_;

    $package_name        =~ s/\-[0-9\.]+$//;
    $package_version     =~ s/^.+\-//;
    if ( $package_name =~ /^[@]/ ) {
	$package_name =~ s/^[@]//;
    }
    else {
	$package_name = $PACKAGE_PREFIX . $package_name;
    }
    return( $package_name, $package_version );
}


sub output_cmmi_script {
    my( $arcfiles, $srcdir, $configure_str, $init_config_str, $make_str, $inst_str, $depends_str, $excl_str ) = @_;
    my( @arc )              = split( / /, $arcfiles );
    my( @configure_sw  )    = split( /!!!/, $configure_str );
    my( @init_config )      = split( /!!!/, $init_config_str );
    my( @make )             = split( /!!!/, $make_str );
    my( @inst )             = split( /!!!/, $inst_str );
    my( @depends )          = split( /!!!/, $depends_str );
    my( @out );
    if( $debug ) {
	printf( "%13s : %s\n", "conf_str", $configure_str );
	printf( "%13s : %s\n", "init_str", $init_config_str );
	printf( "%13s : %s\n", "make_str", $make_str );
	printf( "%13s : %s\n", "inst_str", $inst_str );
    }
    
    push( @out, "#!/bin/sh" );
    if( "C" eq $PACKAGE_TYPE ) {
        push( @out, "TARGET_ROOT=" );
        push( @out, "if [ \"\$2\" != \"\" ] ; then" );
        push( @out, "  TARGET_ROOT=`realpath \$2`" );
        push( @out, "fi" );
    }
    push( @out, "" );
    push( @out, "case \$1 in" );
    push( @out, "  \"config\")" );
    push( @out, "    echo \"--------------------------------------------------------------------------------\";" );
    push( @out, "    if [[ -e .url ]] ; then" );
    push( @out, sprintf( "      %s", $GET_URLs_COMMAND ));
    push( @out, "    fi" );
    push( @out, "    /bin/rm    -rf build" );
    push( @out, "    /bin/mkdir     build" );
    push( @out, "    cd             build" );
    foreach ( @arc ) {
	if ( /.gz$/ ) {
	    push( @out, "    tar zxf ../$_" );
	}
	elsif( /.bz2$/ ) {
	    push( @out, "    tar jxf ../$_" );
	}
	else {
	    push( @out, " echo 'unsupported archive format' ; exit 1 " ); 
	}
    }
    push( @out, "    cd $srcdir" );
    
    foreach ( @init_config  ) { push( @out, $_ ); }
    if( length( $configure_str )) {
	foreach ( @configure_sw ) {   push( @out, "    $_" ); }
    }
    else {
	push( @out, "    if [[ -f ./configure ]] ; then" );
	if( defined( $TARGET_PREFIX ) ) {
	    push( @out, sprintf( "      ./configure --prefix=%s", $TARGET_PREFIX ));
	}
	else {
	    push( @out, "      ./configure" );
	}
	push( @out, "    fi" )
    }
    push( @out, "" );
    push( @out, "    if [ $? -ne 0 ]; then exit 1; fi;" );
    push( @out, "    ;;" );
    push( @out, "  \"make\")" );
    push( @out, "  " );
    push( @out, "    echo \"--------------------------------------------------------------------------------\";" );
    push( @out, "    cd build/$srcdir" );
    if( "C" eq $PACKAGE_TYPE ) {
	push( @out, "    if [ ! -f Makefile ]; then echo 'Error:This source archive does not have Makefile.'; exit 1; fi;" );
	push( @out, "    grep 'prefix = ' Makefile" );
	push( @out, "    if [ $? -ne 0 ]; then echo 'Error:This source archive does not support prefix switch...' exit 1; fi;" );	
    }
    if( $make_str ) {	foreach ( @make ) { push( @out, $_ ); }}
    else {              push( @out, "make" );    }
    push( @out, "    cd .." );
    push( @out, "    if [ $? -ne 0 ]; then exit 1; fi;" );
    push( @out, "    ;;" );
    push( @out, "  \"install\")" );
    push( @out, "    echo \"--------------------------------------------------------------------------------\";" );
    push( @out, "    cd build/$srcdir" );
    if( $PACKAGE_TYPE =~ /[DSR]/ ) {
	my( $package_name, $package_version ) = get_package_info( $srcdir );
	push( @out, "mkdir -p $PACKAGE_DIR" );
	push( @out, "mkdir doc-pak" );
	push( @out, sprintf( "echo \'%s\' > description-pak", $package_name ));
	my( $exclude_switch ) = "";
	if( $excl_str ) {
	    $excl_str =~ s/!!!/,/g;
	    $excl_str =~ s/ //g;
	    $exclude_switch = sprintf( "-exclude=%s", $excl_str );
	}
	push( @out, sprintf( "checkinstall -d 1 --require='%s' --install=no --pkgname='%s' --pkgversion='%s' %s -%s ../../%s install-core /", join( ',', @depends ), $package_name, $package_version, $exclude_switch, $PACKAGE_TYPE, $SCRIPT_NAME));
    }
    if( "C" eq $PACKAGE_TYPE ) {
	# Making hint file for Cygwin Package
	my( $package_name ) = $srcdir . "-1";
	my( $_dirname ) = $srcdir;
	$_dirname =~ s/\-[0-9\.]+$//;
	my( $package_dir  ) = $PACKAGE_DIR . "/" . $_dirname;
	my( $temp_dir )     = "../../tmp";
	push( @out, sprintf( "    /bin/rm -rf %s", $temp_dir ));
	push( @out, sprintf( "    mkdir -p %s", $package_dir ));
	push( @out, sprintf( "    mkdir -p %s%s", $temp_dir, $TARGET_PREFIX ));
	push( @out, sprintf( "../../%s install-core %s", $SCRIPT_NAME,  $temp_dir ));
	# making hint file
	push( @out, sprintf( "    echo \'sdesc: %s\' >  %s/setup.hint", $package_name, $temp_dir ));
	push( @out, sprintf( "    echo \'ldesc: %s\' >> %s/setup.hint", $package_name, $temp_dir ));
	push( @out, sprintf( "    cd %s", $temp_dir ));
	push( @out, sprintf( "    tar cf %s.tar * ; bzip2 %s.tar", $package_name, $package_name ));
	push( @out, sprintf( "    /bin/cp %s.tar.bz2 %s", $package_name, $package_dir ));
	push( @out, sprintf( "    /bin/cp setup.hint %s",                $package_dir ));
    }
    if( "D" eq $PACKAGE_TYPE ) {
	push( @out, " if [ $? -ne 0 ]; then exit 1; fi;" );
	push( @out, " pushd .");
	push( @out, " /bin/cp -f *.deb $PACKAGE_DIR" );
	push( @out, " cd $PACKAGE_DIR" );
	push( @out, " dpkg-scanpackages . /dev/null | gzip -c9 > Packages.gz" );
	push( @out, " popd ");
    }
    if( !$PACKAGE_TYPE ) {
	push( @out, sprintf( "../../%s install-core /", $SCRIPT_NAME));
    }
    push( @out, "    cd .." );
    push( @out, "    if [ $? -ne 0 ]; then exit 1; fi;" );
    push( @out, "    ;;" );
    push( @out, "  \"install-core\")" );
    if( $inst_str ) {	foreach ( @inst ) { push( @out, $_ ); }}
    else {
	if( "C" eq $PACKAGE_TYPE ) {
	    push( @out, sprintf( "    make install prefix=\$TARGET_ROOT%s", $TARGET_PREFIX ));
	}
	else {
	    push( @out, "    make install" );
	}
    }
    push( @out, "    ;;" );
    push( @out, "  *)" );
    push( @out, "    echo \"usage : \`basename \$0\` (config|make|install)\";" );
    push( @out, "    exit 2;" );
    push( @out, "    ;;" );
    push( @out, "esac" );
    push( @out, "exit 0;" );
    return( @out );
}
