#!/usr/bin/perl
#
# easybk.pl
#       -- コピーの際に年月日, 時刻と秒数をファイル名に付加することで
#	   一時的なバックアップを簡単にとることが出来る Perl スクリプト
#
# Usage: easybk.pl [オプション] 対象ファイル [バックアップ先のディレクトリ]

    $MAINTAINER='Morikawa Yasuhiro <morikawa@ep.sci.hokudai.ac.jp>';
    $UPDATE='2005/11/28';
    $VER='0.7';
    $URL='http://www.ep.sci.hokudai.ac.jp/~morikawa/perl/easybk/SIGEN_PUB.htm';
#
# 履歴:
#       2005/11/28  ver 0.7
#        - 現時点の時刻ではなく, ファイルの修正時刻を用いてバックアップを
#          とるためのオプション -s を追加.
#
#       2005/05/18  ver 0.6
#        - ドットが 2 つ以上あるファイルの先頭の文字列を消してしまうバグを修正
#        - .tar.gz ファイルに関しては拡張子を ".gz" ではなく ".tar.gz" と
#          認識するように修正
#        - コマンドを表示するだけで実際には動作しないモード
#          -n オプションを追加
#
#       2005/03/08  ver 0.5
#        - 同じファイル名が存在したら、savelog で元々のものを
#          消さないようにする。
#
#       2005/03/08  ver 0.4
#        - コピーせずに移動するオプションを追加。
#
#       2004/11/14  ver 0.3
#        - 行頭がドットのファイルのバックアップに対応
#
#       2004/11/12  ver 0.2
#        - 時間を日本時間にする
#
#       2004/11/07  ver 0.1
#        - easybk.sh を Perl スクリプトへ
#        - オプション -d をつけると分と秒をつけないようにする。
#        - オプション -D をつけるとデバックモードにする。
#
# TODO:
#

##################################################
##                 初期設定                    ###
##################################################

# オプション処理のため, getopts を組み込む.
require 'getopts.pl'
    || die "Error: getopts.pl is not found.\n";

    # デバッグ用変数
$DEBUG="0";

##################################################
##                 引数識別                    ###
##################################################

&Getopts('dhHDvVGmfns');

# D オプションの場合は $DEBUG を真に
if ($opt_D) {
    $DEBUG = 1;
}

# d オプションの場合は $dayonly を真に
if ($opt_d) {
    $dayonly = 1;
}

# G オプションの場合は $gmt を真に
if ($opt_G) {
    $gmt = 1;
}

# m オプションの場合は $move_not_copy を真に
if ($opt_m) {
    $move_not_copy = 1;
}

# f オプションの場合は $overwrite を真に
if ($opt_f) {
    $overwrite = 1;
}

# n オプションの場合は $only_show を真に
if ($opt_n) {
    $only_show = 1;
}

# s オプションの場合は $time_of_modification を真に
if ($opt_s) {
    $time_of_modification = 1;
}

if ($#ARGV < 0){
    &Help;
    exit 1;
}

sub Help() {
    print STDOUT <<EOF;
  easybk.pl: temporary backup program
    USAGE:
      easybk.sh [-dGmfns] [-D] target [dir]

    ARGUMENT:
      target : バックアップしたいファイルまたはディレクトリ。
      dir    : バックアップ先のディレクトリ。
               デフォルトはカレントディレクトリ。

    OPTION:
      -d     : 年月日のみバックアップファイルにつける。
      -G     : GMT 時間を用いる。
      -m     : コピーせずに改名する。
      -f     : バックアップ先のファイルが存在しても上書き。
      -n     : コマンドを表示するだけで実際に動作しない。
      -s     : ファイルの修正時刻を使用する。
      -D     : デバッグモード (開発者用)。

    VERSION:
      easybk.sh Version ${VER}, Last Update: ${UPDATE}.
      ${MAINTAINER} All Right Reserved.

EOF
}

# バックアップをとるファイル名
$target=$ARGV[0];

# 移動先のディレクトリの取得
if ($#ARGV < 1){
    $dest='.';
} else {
    $dest=$ARGV[1];
}

# $file が存在するか？ ファイルかディレクトリかを取得。
if (-f "$target") {
    $target_is_file  = 1;
    $target_is_dir   = 0;
} elsif (-d "$target") {
    $target_is_file  = 0;
    $target_is_dir   = 1;
} else {
    $target_is_file  = 0;
    $target_is_dir   = 0;
}

# ファイルが存在しない場合はそこで終了
if (!$target_is_file && !$target_is_dir) {
    die "\"$target\" is not found.\n";
}

# $dest が存在するか？
unless (-d "$dest" ) {
    die "\"$dest\" directory is not found.\n";
}

# 日付取得

if (!$time_of_modification) {
    $t = time unless defined $t;

} else {
    ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, 
        $atime, $mtime, $ctime, $blksize, $blocks)
           = stat("$target");
    $t = $mtime;
}

if ($gmt) {
    # GMT 時間
    ($sec, $min, $hour, $day, $mon, $year) = gmtime($t);
} else {
    # 日本時間
    ($sec, $min, $hour, $day, $mon, $year) = readableDate($t);
}

if ($dayonly) {
    $time = sprintf("%04d-%02d-%02d", $year+1900, $mon+1, $day);
} else {
    $time =
	sprintf("%04d-%02d-%02d-%02d-%02d-%02d", $year+1900, $mon+1, $day, $hour, $min, $sec);
}
print STDOUT "Time : $time\n" if ($DEBUG);

# パスを取り除く

@dir_names = split(/\//, $target);
$file = pop(@dir_names);

if ($DEBUG) {
    print STDOUT "first argument :          $target\n";
    print STDOUT "filename excluding path : $file\n";
}

# $target がファイルの場合、ファイル名を拡張子とその前の部分に分離
if ($target_is_file) {
    # ドットで始まるファイル
    if ($file =~ /^\./) {
	$target_have_extension = 0;
	print STDOUT "$file is starting with \".\"\n" if ($DEBUG);
    # ファイル名中にドットがある (拡張子がある) 場合はファイル名を分離
    } elsif ($file =~ /\./) {
	$target_have_extension = 1;
	
	@file_parts = split(/\./, $file);
	$base      = join(".", @file_parts[0..($#file_parts - 1)]);
	$extension = $file_parts[$#file_parts];
	# 拡張子 tar.gz の場合は例外
	if ($file =~ /\.tar\.gz$/) {
	    $base      = join(".", @file_parts[0..($#file_parts - 2)]);
	    $extension = join(".",
			      @file_parts[($#file_parts - 1)..$#file_parts]);
	}

	if ($DEBUG) {
	    print STDOUT "base :      $base\n";
	    print STDOUT "extension : $extension\n";
	}
    # ドットが無いファイル
    } else {
	$target_have_extension = 0;
	print STDOUT "$file don\'t have extension.\n" if ($DEBUG);
    }

}


# 実際にコピー

$cmd    = "cp -p" unless $move_not_copy;
$cmd    = "mv"    if     $move_not_copy;

if ($target_is_file) {
    if ($target_have_extension) {
	$destfile = "${dest}/${base}_${time}.${extension}";
	&savelog("$destfile") unless $overwrite;
	$cmdstr   = "$cmd" . " $target". " $destfile";
	print STDOUT "$cmdstr\n";
	system("$cmdstr") unless $only_show;
    } elsif (!$target_have_extension) {
	$destfile = "${dest}/${file}_${time}";
	&savelog("$destfile") unless $overwrite;
	$cmdstr   = "$cmd" . " $target" . " $destfile";
	print STDOUT "$cmdstr\n";
	system("$cmdstr") unless $only_show;
    }
} elsif ($target_is_dir) {
	$destfile = "${dest}/${file}_${time}";
	&savelog("$destfile") unless $overwrite;
	$cmdstr = "$cmd" . " -r" unless $move_not_copy;
	$cmdstr = "$cmd"         if     $move_not_copy;
	$cmdstr = "$cmdstr" . " $target" . " $destfile";
	print STDOUT "$cmdstr\n";
	system("$cmdstr") unless $only_show;
}

exit 0;


	# gmt_date の出力をより読みやすい日付の形式に変換する
	#   - gate-toroku-system/include/gate-common.pl から移植して改造
	#
	# 引数
	#	$time	gmt_date によって作られた時刻
	#
	# 返却値
	#	日時をあらわす文字列
	# バグ
	#	入力形式にまったく融通が利かない.
	#	日本時間だと思い込んでいる.
sub readableDate (;$) {
	local($time) = @_;
	local($s, $mi, $h, $d, $mo, $y) = gmtime($t);
	# ここは日本決めうち.
	$h += 9;
	($h -= 24, $d++) if $h >= 24;
	if ($mo == 2) {
		$monthlen = ($y % 4) ? 28 : 29;
		$monthlen = 28 if ($y % 100 == 0);	# 2100年対応
		$monthlen = 29 if ($y % 400 == 0);	# 2000年問題対応
	} else {
		if ($mo == 4 || $mo == 6 || $mo == 9 || $mo == 11) {
			$monthlen = 30;
		} else {
			$monthlen = 31;
		}
	}
	($d = 1, $mo++) if $d >= $monthlen;
	($y++, $mo = 1) if $mo >= 13;

	return ($s, $mi, $h, $d, $mo, $y);
}


	# savelog を用いて、引数に与えられたファイルやディレクトリを
	# バックアップする。引数に与えられたファイルやディレクトリが
	# 存在しない場合は何もしない。ディレクトリの場合、savelog が直接
        # 効かないので、tar.gz 圧縮した後 savelog をかける。
	#
	# 引数
	#	文字列 (ファイル名またはディレクトリ名を想定)
	#
	# 返却値
	#	ファイルまたはディレクトリが存在しない場合は偽、
	#	存在した場合はそのファイル名を返す。
	#
sub savelog (;$) {
	local($file) = @_;
	local($cmd)    = "savelog -p";
	local($tarcmd) = "tar cvfz";
	local($tarext) = ".tar.gz";
	if (-f "$file") {
		print STDOUT "$cmd $file\n";
		system("$cmd $file") unless $only_show;
		return "$file";
	} elsif (-d "$file"){
		chomp($file);
		local($tarball) ="$file" . "$tarext";

		if (-f "$tarball") {
			print STDOUT "$cmd $tarball\n";
			system("$cmd $tarball") unless $only_show;
		}

		local($tarcmdstr) = "$tarcmd" . " $tarball" . " $file";
		print("$tarcmdstr\n");
		system("$tarcmdstr") unless $only_show;

		local($rmcmdstr) = "rm -fr $file";
		print("$rmcmdstr\n");
		system("$rmcmdstr") unless $only_show;

	} else {
		return 0;
	}
}
