#!/usr/bin/perl
#
# writesigen2.pl - SIGEN ファイルを作る perl スクリプト
#                  kitamo さんの作った writesigen.pl の改造版
#
#   ※ SIGEN ファイルとは mksigen という perl で書かれたディレクトリ
#      データベースマネージャのための説明ファイルです.
#      詳しくは http://www.gfd-dennou.org/arch/cc-env/mksigen/desc.htm
#      を参照のこと.
#
    @MAINTAINERS = ('Kitamori Taichi   <kitamo@ep.sci.hokudia.ac.jp>',
		    'Morikawa Yasuhiro <morikawa@ep.sci.hokudai.ac.jp>',
		   );
    $UPDATE      = '2004/09/30';
    $VERSION     = '0.6';
    $URL         =
    'http://www.ep.sci.hokudai.ac.jp/~morikawa/perl/writesigen2/SIGEN_PUB.htm';

#
# ・ TODO
#    - 改行を含めた文字が書き込めない.
#
# ・ History
#
#    - 0.6  2004/09/30 (森川靖大)
#           + ユーザ名の自動取得にバグがあったので修正
#
#    - 0.5  2004/09/30 (森川靖大)
#           + 雛型ファイルを取り込めるように改良
#           + アップデート時に履歴の詳細を書き込めるように改良
#
#    - 0.4  2004/09/30 (森川靖大)
#           + ユーザ名を gate-toroku-system または /etc/passwd から
#             自動取得することを可能なように機能拡張
#           + 履歴の内容を書き込めるように改良
#
#    - 0.3  2004/09/29 (森川靖大)
#           + パスを含むファイル名を SIGEN として指定した場合に失敗する
#             バグを修正.
#
#    - 0.2  2004/09/28 (森川靖大)
#           + ディレクトリやシンボリックリンクに対して SIGEN を作成
#             出来ないバグを修正
#           + ディレクトリに対して作成する際に, 最後にスラッシュが付いても
#             それを取り除くように修正
#
#    - 0.1  2004/09/28 (森川靖大)
#           + 複数ファイルを処理できるように修正
#           + 既存の SIGEN ファイルをアップデートできるように修正
#           + Description を記述できるように修正
#           + Help を出力するように修正.
#           + 日付取得をサブルーチンに格納
#
#    - 0.0  2004/09/28 (森川靖大)
#           + kitamo さんのものをぱくってくる
#

##################################################
#####                                        #####
##           各ユーザの設定                    ###
#####                                        #####
##################################################

# 0: gate-toroku-system または passwd または
#    ログインネームを使用
#
# 1: 以下の $mname, $rname を使用

$FORCENAME = 1;

$mname = "森川 靖大"; # Maintainer
$rname = "森川 靖大"; # 更新者 (履歴欄に書く名前)

######### 以下は書き換えないで下さい. #############

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

# h, H, v, V, f, Fのみ引数をとる.
&Getopts('i:hHvVfF');

##################################################
#####                                        #####
##                 引数識別                    ###
#####                                        #####
##################################################
# まずは取得
@filenames = @ARGV;

# 引数オプションとして v や V が与えられた場合には
# バージョン情報だけ表示x
if ($opt_v || $opt_V) {
    &Caution if $FORCENAME;
    &PrintVersion;
    exit 1;
}

# 引数が無い場合やオプションとして h や H が与えられた
# 場合にはヘルプを表示
if ($#ARGV < 0 || $opt_h || $opt_H){
    &Caution if $FORCENAME;
    &Help;
    exit 1;
}

# $FORCENAME が真の場合は何は無くとも表示
&Caution if $FORCENAME;

# 引数オプションとして f や F が与えられた場合には強制的に
# アップデートするフラグを真に
$UpdateForce = 0;
if ($opt_f || $opt_F) {
    $UpdateForce = 1;
}

# 引数がオプション i の場合, 引数として与えられたものを
# 雛型として利用.
undef $EXAMPLE;
if ($opt_i) {
    $EXAMPLE = $opt_i;
}

##################################################
#####                                        #####
##           Help などのサブルーチン           ###
#####   (これ以外のサブルーチンは末尾に)     #####
##################################################
sub Help() {
    print STDOUT <<EOF;
  writesigen2.pl:
    USAGE:
      writesigen2.pl [-vVhHfF] [-i exam.SIGEN] file [file ...]

    OPTION:
      -v, -V        : Show Version Number
      -h, -H        : Show Help
      -f, -F        : Force Update
      -i exsm.SIGEN : Use existing SIGEN file for example

EOF
    &PrintVersion;
}
sub PrintVersion() {
    print STDOUT <<EOF;
  writesigen2 Version $VERSION, Last Update: $UPDATE.

EOF
    foreach $MAINTAINER (@MAINTAINERS) {
	print STDOUT "  $MAINTAINER \n";
    }
    print STDOUT "    All Right Reserved.\n\n";
}
sub Caution(){
    print STDOUT <<EOF
  ************   CAUTION !!! *****************
    IF YOU WANT GET USER-NAME AUTOMATICALLY,
    CHANGE \"\$FORCENAME\" to \"0\" !!!
  ********************************************

EOF
}

##################################################
#####                                        #####
##               ユーザ名取得                  ###
#####                                        #####
##################################################
$newname = &GetUserName;

if (!$FORCENAME && $newname) {
    $mname = $newname; # Maintainer
    $rname = $newname; # 更新者 (履歴欄に書く名前)
}

##################################################
#####                                        #####
##          手本 SIGEN ファイル解析            ###
#####                                        #####
##################################################
if ($EXAMPLE) {
    # $EXAMPLE は SIGEN ファイルか?
    # もしも SIGEN が末尾に付いていない場合は .SIGEN を付けて探査
    if ($EXAMPLE !~ /^.*SIGEN$/) {
	# ディレクトリの場合を考慮して末尾のスラッシュを排除
	$EXAMPLE =~ s|/+$||;
	# 末尾に ".SIGEN" を追加
	$EXAMPLE = "$EXAMPLE".".SIGEN";
    }

    # $EXAMPLE はファイルとして存在するか?
    unless (-f $EXAMPLE) {
	die "$EXAMPLE is not found.\n" ;
    }

    print STDOUT "Input $EXAMPLE for example file ...  ";

    %exam = &ReadHeaders($EXAMPLE);

    # ヘッダが正しい情報を持っているかチェック
    $exam_subject = $exam{'subject:'};
    $exam_desc    = $exam{'description:'};
    $exam_note    = $exam{'note:'};

    unless ($exam_subject) {
	die "$EXAMPLE is not correct SIGEN file.\n";
    }

    print STDOUT "done.\n";
}


##################################################
#####                                        #####
##             メインルーチン                  ###
#####                                        #####
##################################################
#
# 1つ1つのファイルに関して処理
#
foreach $file (@filenames){
    #
    # 引数が存在しない場合はスキップ
    #
    unless (-e $file) {
	print STDOUT "$file is not found. Skipping ...\n" ;
	next;
    }

    #
    # 引数が SIGEN ファイルである場合はスキップ
    #
    if ($file =~ /^.*SIGEN$/) {
	print STDOUT "$file is SIGEN file. Skipping ...\n" ;
	next;
    }

    #
    # ディレクトリを表す末尾のスラッシュ「/」を取り除く
    #
    $file =~ s|/+$||;

    #
    # SIGEN ファイル名生成
    #
    $sigenfile = "${file}.SIGEN";

    #
    # SIGEN ファイルが存在する場合は Update と履歴を更新する
    #
    if (-f $sigenfile){
	unless ($UpdateForce) {
	    print STDOUT "Update ${file}.SIGEN ? [Y/n]: " ;
	    $ans = <STDIN>;
	    if ($ans =~ /^[nN].*$/) {
		print STDOUT "Skipping $sigenfile ... \n";
		next;
	    }
	}
	print STDOUT "  History: ";
	$hist = <STDIN>;
	chomp($hist);
	print STDOUT "Updating $sigenfile ...";
	&UpdateSigen($sigenfile, $hist);
	print STDOUT " done.\n";
	
	next;
    }

    #
    # SIGEN ファイルが存在しない場合, 問い合わせて新規作成する
    #
    
    #  日付情報の生成
    $today = &GetToday;

    #####
    #  ファイル情報入力
    print STDOUT "Please Input Data for $sigenfile ...\n";
    # Subject
    if ($exam_subject) {
	print STDOUT "  Subject: [", "$exam_subject", "] ";
	$subject = <STDIN>;
	chomp($subject);
	if ($subject eq '') {
	    $subject = $exam_subject;
	}
    } else {
	print STDOUT "  Subject: ";
	$subject = <STDIN>;
    }
    chomp($subject);
    # Description
    if ($exam_desc) {
	print STDOUT "  Description: [", "$exam_desc", "] ";
	$desc = <STDIN>;
	chomp($desc);
	if ($desc eq '') {
	    $desc = $exam_desc;
	}
    } else {
	print STDOUT "  Description: ";
	$desc = <STDIN>;
    }
    chomp($desc);
    # Note
    if ($exam_note) {
	print STDOUT "  Note: [", "$exam_note", "] ";
	$note = <STDIN>;
	chomp($note);
	if ($note eq '') {
	    $note = $exam_note;
	}
    } else {
	print STDOUT "  Note: ";
	$note = <STDIN>;
    }
    chomp($note);
    # 履歴
    print STDOUT "  History: ";
    $hist = <STDIN>;
    chomp($hist);
    
    #  SIGEN ファイルに 出力
    print STDOUT "Generating $sigenfile ...";
    open(SIGEN, ">$sigenfile");
    print SIGEN <<EOF;
Subject:	$subject
Maintainer:	$mname
Description:	$desc
Note:		$note
Update:		$today

履歴
	$today  $rname  $hist
EOF
    print STDOUT " done.\n";
}

exit 0;

##################################################
#####                                        #####
##             サブルーチン群                  ###
#####                                        #####
##################################################

#
# 元々存在する $sigenfile をアップデートするサブルーチン
#
sub UpdateSigen(){
    local($sigenfile, $hist) = @_;
    # PATH が書かれている場合は, ファイル名のみを取り出す.
    @dir_names = split(/\//, $sigenfile);
    $sigenfilename = pop(@dir_names);
    local($sigentmp)  = "/tmp/${sigenfilename}.$$";

    local($today)  = &GetToday;
    open(ORG, "$sigenfile");
    open(UPDATE, "> $sigentmp");

    while (<ORG>){
	chomp($_);
	if ($_ =~ /^Update.*$/){
	    $_ = "Update:\t\t$today";
	}
	print UPDATE "$_\n";
    }
    # 履歴情報追加
    print UPDATE "\t$today  $rname  $hist\n";

    close(ORG);
    close(UPDATE);
    system "mv -f $sigentmp $sigenfile";
    system "chmod 664 $sigenfile";
}

#
# 本日の日時を取得し, 2004/09/28 のような形に整形するサブルーチン
#
sub GetToday() {
    local(@date, $year, $month, $day, $today);
    @date = localtime();
    $year = 1900 + @date[5];
    $month = 1 + @date[4];
    $day = @date[3];
    
    if ($month < 10) {
	$month = "0${month}";
    }
    if ($day < 10) {
	$day = "0${day}";
    }
    $today = "$year/$month/$day";
    return $today;
}

#
# ユーザ名取得用サブルーチン (dcreal-sigen から移植したものを改造)
#   http://www.gfd-dennou.org/arch/cc-env/dcreal/SIGEN.htm
#
sub GetUserName(){
    local(@passwd)    = getpwuid($<);
    local($loginname) = $passwd[0];
    local(@userinfo)  = split(/,/, $passwd[6]);
    local($name)      = $userinfo[0];
    local($tmpfile) = "/tmp/nametmpfile.$$";
    local(@knames, $kname);
    
    # まずは gate のデータベースから
    if (-x "/usr/local/bin/gate-user-show"){
	system ("gate-user-show $loginname > $tmpfile");
	open (GATE, "$tmpfile");
	while (<GATE>) {
	    chomp($_);
	    if ($_ =~ /^kname/){
		@knames = split(/: /, $_);
		$kname  = $knames[1];
	    }
	}
	close(GATE);
	system ("rm $tmpfile");
	return $kname;

    # gate が無い場合は /etc/passwd の情報より
    } elsif ($name) {
	return $name;

    # それもないならユーザー名
    } elsif ($loginname) {
	return $loginname;

    # 最後は偽を返す
    } else {
	return nil;
    }
}

#
# SIGEN ファイルのヘッダ解析用サブルーチン (mksigen.pl から移植して改造)
#   http://www.gfd-dennou.org/arch/cc-env/mksigen/desc.htm
#
sub ReadHeaders() {
    local($emlfile) = @_;
    local($name, $val, %headers);
    $name = ""; undef %headers;
    #
    open(READ, "$emlfile");
    while (<READ>) {
	# 行末の改行を取り除く
	chomp;
	s/\r$//;
	# 何も書き込まれていなければ終了
	last if /^$/;
	# 行の始めにスペースが入らないものを要素として取り込む
	if (!/^\s/) {
	    # 「Subject: Test」のような書式を想定
	    if (!/^([-A-Za-z0-9]*:)\s*(.*)/) {
		warn "Error: broken header \"$_\" in $emlfile\n";
		next;
	    }
	    ($name = $1) =~ tr/A-Z/a-z/;
	    ($val = $2) =~ s/[\r]/ /g;
	    if (defined $headers{$name}) {
		$headers{$name} .= "\n$val";
	    } else {
		$headers{$name} = $val;
	    }
	} else {
	    s/[\r]/ /g;
	    s/^ */ /;
	    $headers{$name} .= "\n$_";
	}
    }
    return %headers;
}
