#!/usr/bin/perl
#
# dcnote-mkindex:
# 
## hogehoge.des って何? 
#  hogehoge.des, desc.txt を読んで index.html ファイルを作成する.
#
#  ●Usage : dcnote-mkindex [-o index.htm] [ -dc desc.txt ] 
# 	                    [ -topdir $TOPDIR] [-tdirname $TOPDIRNAME]
# 	                    [ -prjdir $PTOPDIR] [-pdirname $PTOPDIRNAME]
# 	                    [ -wrkdir $WORKDIR] 
#                           target_dir
#
# 	$TOPDIR			プロジェクトトップディレクトリ
#				/GFD_Dennou_Club/ftp/arch/riron
#	$TOPDIRNAME		プロジェクトの名前
#				地球流体理論マニュアル
# 	$PTOPDIR		個別資源格納用トップディレクトリ
#				/GFD_Dennou_Club/ftp/arch/riron/renzoku/gaisetu
#	$PTOPDIRNAME		個別資源の名前
#				連続体概説
# 	$WORKDIR		個別資源格コンパイル作業ディレクトリ
#				/GFD_Dennou_Club/ftp/arch/riron/renzoku/gaisetu/src
#       target_dir		公開資源インストール先
#       
#  ●作成されるファイル
#
#    hogehoge/          : 作業ディレクトリ      
#             index.htm : html ファイル
#
# 履歴  1998/08/31 豊田英司; botindex.pl - update bottom directory lising HTML
#       1999/06/27 小高正嗣; dcnote-mkindex へ
#       1999/07/21 小高正嗣
#       1999/12/20 杉山耕一朗
#

# スカラー変数の定義

        $OUTPUT= "index.htm";            # 生成ファイル名
        $GROUP = "地球流体電脳倶楽部" ;  # 
        $TOPDIRINDEX = "index.htm" ;     # リンクファイル名
        $DESCFILE = "desc.txt" ;         # 情報記述 desc ファイル名


        # 日本語文字コード変換
        &initkanzi;


## オプションの判定. 
## 引数 @ARGV のそれぞれ要素に対して while ループを回す. 
## 先頭に "-" が付いてなければ次のループへ移る. 
## 先頭に "-" が付いていれば $_ に @ARGV の最初の要素を入れる. 
## "-o" 等, それぞれのオプションが現れたら, その次の要素をスカラーにいれる. 
## next 関数によって次のループに移る. 

        # オプションの判定
        while (@ARGV) {
	    $_ = shift;
	    last unless (/^-/);
	    if (/^-o/) { $OUTPUT = shift; next; }
	    if (/^-dc/) { $DESCFILE = shift; next; }
	    if (/^-topdir/) {$TOPDIR = shift; next; }
	    if (/^-tdirname/) { $TOPDIRNAME = shift; next; }
	    if (/^-prjdir/) {$PTOPDIR = shift; next; }
	    if (/^-pdirname/) { $PTOPDIRNAME = shift; next; }
	    if (/^-wrkdir/) { $WORKDIR = shift; next; }
	    die "unknown option $_";
        }

## もしも引数として target_dir が指定されていないならば, 
## カレントディレクトリとする. 

        $TARGETDIR = $_ ? $_ : ".";

# 上位のリンク先ディレクトリ名を与える.
#
## $lvdiff_ct には TOPDIR と WORKDIR のパスに含まれるディレクトリ数の差. 
## $lvdiff_pt には TOPDIR と PTOPDIR のパスに含まれるディレクトリ数の差. 
## $titlename には PTOPDIR のパスに含まれるが TOPDIR のパスには含まれない
## ディレクトリ名. (例では $titlename[0]=gaisetu, $titlename[1]=renzoku. 
## ディレクトリ名の順番が逆になっていることに注意)

        ($lvdiff_ct, $lvdiff_pt, @titlename) = &ReadTitle;


# $DESCFILE から必要情報を読み込む.
#
## %setumei は desc.txt の各行の第 1 フィールドを key とし, 第 2 
## フィールドを値とするような連想配列. 

        %setumei = &ReadDesc($DESCFILE);


# 目次用 html ファイルの作成.
#
## target_dir をオープンする. もし target_dir がなければ死んでおしまい. 
## target_dir の下に存在するファイルの中で「数字 3 つ + 末尾がgif」という
## ファイル名を @file に入れる. 

        opendir(DIR, $TARGETDIR) || die "cannot open $TARGETDIR";
        @files = sort grep(/^\d\d\d.gif$/, readdir(DIR));
        closedir(DIR);

## サブルーチン open_w を呼び出す. $OUTPUT が存在しなければ死ぬ. 
## $OUTPUT には index.htm が入っている. 
## open_w で euc を sjis に変換する. 
## STDERR に出力. (画面に表示される)
## select でデフォルトのファイルハンドルを $OUTPUT にする. 

        &open_w($OUTPUT) || die "$OUTPUT: cannot open";
        print STDERR "output written to $OUTPUT\n";
        select($OUTPUT);

# ファイルのヘッダ, 中身, フッダをファイルに書き出す. 
#
## サブルーチン HtmlHeader を呼び出す. 
## @file のそれぞれの要素に対して, サブルーチン ListEntry を呼び出す. 
## @file には ***.gif という値が入っている. 
## サブルーチン HtmlTailer を呼び出す. 

        &HtmlHeader;
        foreach (@files) {
	    &ListEntry($_);
        }
        &HtmlTailer;
        close($OUTPUT);

## $OUTPUT を target_dir に移動させる. 
        system ("mv $OUTPUT $TARGETDIR");
        exit 0;




#
# --- subroutines
#


# 目次用 html ファイルヘッダー部の作成
#
## @titlename の要素は(ディレクトリ名)は順番が逆になっているので
## reverse 関数でひっくり返す
## (例では $titlename[0]=gaisetu, $titlename[1]=renzoku)
## .= で $title に "" を結合し, それを $title に入れる. → @title の生成
## ヘッダを標準出力(現在は index.htm ファイル)に書き出す. 
### この title は意味がない. PTOPDIR の名前を入れた方がいいのでは. 
### $GROUP は意味ない

sub HtmlHeader {
#    for $level (reverse(0 .. $#titlename)) {
#	$title .= "/$titlename[$level]" ;
#    }
    print "<HTML>\n";
#    print "<HEAD><TITLE>$title</TITLE></HEAD>\n";
    print "<HEAD><TITLE>$PTOPDIRNAME</TITLE></HEAD>\n";
    print "<BODY BGCOLOR=\"#ffffcc\">\n";
    print "<P>$GROUP\n" if ($GROUP);
    print "<P>\n";

## ('../' x $sigenlv) は ../ を $sigenlv 回書けということ. 

    for $level (reverse(0 .. $#titlename)) {
	$sigenlv = $level + 2 ;
	print "/<A HREF='", ('../' x $sigenlv), 
              "SIGEN.htm'>$titlename[$level]</A>\n";
    }
    print "<P>\n";
    print "<A HREF='", ('../' x $lvdiff_ct), 
              "$TOPDIRINDEX'>$TOPDIRNAME</A>\n";
    print "<H1>$setumei{'subject:'}</H1>\n";
    print "<P>$setumei{'from:'} $setumei{'date:'}\n" if $setumei{'date:'};
    print "<P>$setumei{'desc:'}\n" if $setumei{'desc:'};
    print "<P>$setumei{'note:'}\n" if $setumei{'note:'};
    print "<HR>\n";
    # Pdf: フィールドがある場合
    if (defined $setumei{'pdf:'}) {
	# Pdf: フィールドの値が空白でない場合
	unless ( $setumei{'pdf:'} =~ m/\s+/ ) {
	    print "<BR>\n";
	    print "<A HREF=\"./$setumei{'pdf:'}\">全体の pdf ファイル</A>\n";
	    print "<BR>\n";
	}
    }
    print "<BR>\n";
    print "各ページの gif ファイル\n";
    print "<BR>\n";
    print "<UL>\n";
}







# 目次用 html ファイル各ページ記述部の作成.
#
## @file には ***.gif というファイル名が入っている. 
## $link に ***.htm というファイル名を入れる. 
## $bangou に数字(***)を入れる. 
## $listitem を定義し, 標準出力に書き出す. 
## もしも $setumei{$bangou} が定義されていたら(ページの説明が存在するならば), 
## リターンを $listitem で置換する(?)
## $listname には改行記号(\n)が含まれているのでそれを除く
### e オプションは後ろにくっつけるという意味合いらしい(要確認)

sub ListEntry {
    local($link) = @_;
    local($bangou) = $link;
    $link =~ s/\.gif$/.htm/;
    $bangou =~ s/\.gif//;
    local($listitem) = "<LI><A HREF=\"$link\">$bangou</A>\n";
    print $listitem;
    local($line);
	if (defined $setumei{$bangou}) {
                 $line = $setumei{$bangou};
		 $line =~ s/\r/$listitem/ge;
                 print "$line\n";
	}
}








# 目次用 html ファイル最下端部の作成.
#
## サブルーチン FmtTime を呼び出す. 
## $ACKLINK と $ACKMSG があれば書く. 昔は「このページは hogehoge によって
## 作られました」と書くところ. 

sub HtmlTailer {
    print "</UL>\n";
    print "<HR>\n";
    print "last update: ". &FmtTime .";\n";
    print "<a href=\"$ACKLINK\">$ACKMSG</a>\n" if $ACKMSG;
    print "</BODY>\n";
    print "</HTML>\n";
}




# スクリプト起動時刻を与える.
#
## なぜか $mon は +1 しないとならない. 

sub FmtTime {
    local($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst)
	= localtime time;
    sprintf "%4d/%02d/%02d %02d:%02d:%02d",
    $year + 1900, $mon + 1, $mday, $hour, $min, $sec;
}






# 情報記述ファイル $DESCFILE から, 各フィールド名とその記述を読み出す.
#
## $elmfile =  $DESCFILE

sub ReadDesc {

## 変数の準備
    local($emlfile) = @_;
    local(%hdrs, %newhdrs);

# $emlfile がない場合
## 戻り値 0 でメインループに返る. 

    if (!-f $emlfile) {
# not suitable for gfdsemi project
#		warn "$0: $emlfile not exist\n";
	return ();
    }

## 漢字コードを EUC に変換
## 開けなければ返り値 0 でメインループに戻る. 

    &open_r($emlfile) || return ();

# 各フィールドを引数としその記述部を値に持つ連想配列を作る.
## サブルーチン ReadHeaders を呼ぶ(引数は desc ファイルを与える). 
## %hdrs は desc ファイルの第 1 フィールドを key にし, 
## 第 2 フィールドをその値とするような連想配列

    %hdrs = &ReadHeaders($emlfile);

# gif ページ番号の記述が適切でない場合の処理.
## 連想配列の key を取り出し, 数字で始まるものに関しては 10 進数の
## 3 桁の数字に直す. その key をもとにして連想配列を定義し直す. 

    for (keys %hdrs) {
	($newkey = $_) =~ s/\.gif//;
        if ($newkey =~ /^[0-9]/) {
	    $newkey = sprintf("%03d", $newkey);
	    $newhdrs{$newkey} = $hdrs{$_};
	} else {
	    $newhdrs{$_} = $hdrs{$_};
	}
    }
## 戻り値
    %newhdrs;
}







# 既に開かれたファイルハンドル $emlfile から
# RFC 822 形式のヘッダを読み取りフィールド名をキーとする連想配列
# として返す.
# 値に \r または \t が存在すれば SPACE に置換される.
# 同じ名前のヘッダが複数存在する場合は値は \r で区切られる. 

sub ReadHeaders {
    local($emlfile) = @_;
    local($name, $val, %headers);
    $name = ""; undef %headers;


## desc ファイル 1 行 1 行について while ループを回す. 

    while (<$emlfile>) {
	chop;                                   # 改行消去
	s/\r$//;                                # 行末が CRLF なら CR 除去(Win)
	last if /^$/;                           # 空行はヘッダの終り
	if (!/^\s/) {                           # 継続行でない場合
            # ヘッダの開始行として適当かチェック
	    ## カッコで括った部分が $1 に, それ以外が $2 に代入される. 
	    if (!/^([-A-Za-z0-9]*:)\s*(.*)/) {
		warn "broken header \"$_\" in $emlfile\n";
		next;
	    }
            # さっきのパターンマッチでフィールド名と値が取り出せる
	    ($name = $1) =~ tr/A-Z/a-z/;    # 大文字小文字の同一視
	    ($val = $2) =~ s/[\t\r]/ /g;    # 値に \t \r がないことを保証
	    if (defined $headers{$name}) {
                # 前に同じ名前のヘッダがあれば \r をはさんで連結
		$headers{$name} .= "\r$val";
	    } else {
                # 前に同じ名前のヘッダがなければただ代入
		$headers{$name} = $val;
	    }
	} else {                                # 継続行の場合
            # 値に \r \t がないことを保証
	    s/[\t\r]/ /g;
            # 行頭の空白はただ一つにまとめる (単に審美的理由)
	    s/^ */ /;
            # 直前の $name のヘッダに追加
	    $headers{$name} .= $_;
	}
    }
## 戻り値の指定
    %headers;
}






# 上位のリンク先ディレクトリ名を与える.
# 作業ディレクトリと $TOPDIR の段数を比較し, 
# 作業ディレクトリより 1 段上位のディレクトリから $TOPDIR までの
# ディレクトリ名を返す.
#
## TOPDIR と PTOPDIR と WORKDIR に共通項が存在することを前提にしている. 

## 変数の用意

sub ReadTitle {
    local($cwdir, $tpdir, $pjdir);
    local($cwd_num, $tpd_num, $pjdir_num);
    local(@cwd_item, @tpd_item, @pjdir_item, @updirname) ;
    local($diff, $and);


## TOPDIR と PTOPDIR と WORKDIR のそれぞれのパスから最初と最後の / を削る. 

#    $cwdir=`echo pwd | /bin/bash`;      # 現在の作業ディレクトリ名を出力
#    $cwdir=`pwd`;      # 現在の作業ディレクトリ名を出力
#    $cwdir=~ s/^\/// ; # 先頭の / を削除
#    $cwdir=~ s/\/$// ; # 末尾の / を削除
#    print $cwdir;

    $cwdir="$WORKDIR";  # 現在の作業ディレクトリ名を出力
    $cwdir=~ s/^\/// ; # 先頭の / を削除
    $cwdir=~ s/\/$// ; # 末尾の / を削除

    $tpdir="$TOPDIR"; # 参照する最上段のディレクトリ名を出力.
    $tpdir=~ s/^\/// ; # 先頭の / を削除.
    $tpdir=~ s/\/$// ; # 末尾の / を削除.

    $pjdir="$PTOPDIR"; # 参照する最下段のディレクトリ名を出力.
    $pjdir=~ s/^\/// ; # 先頭の / を削除.
    $pjdir=~ s/\/$// ; # 末尾の / を削除.


## TOPDIR と PTOPDIR と WORKDIR のそれぞれを / で分割し, 
## ディレクトリ名の入った配列を作る
## 例では
## @cwd_item = GFD_Dennou_Club, ftp, arch, riron, renzoku, gaisetu, src
## @tpr_item = GFD_Dennou_Club, ftp, arch, riron
## @prj_item = GFD_Dennou_Club, ftp, arch, riron, renzoku, gaisetu

    @cwd_item = split ("/", "$cwdir" ); # / で文字列を分割, 配列へ代入.
    @tpd_item = split ("/", "$tpdir" ) ; # / で文字列を分割, 配列へ代入.
    @prj_item = split ("/", "$pjdir" ) ; # / で文字列を分割, 配列へ代入.

    $diff_ct = @cwd_item - @tpd_item ; # @cwd_item @tpd_item の配列要素数の差.
    $diff_pt = @prj_item - @tpd_item ; # @prj_item @tpd_item の配列要素数の差.


    # $TOPDIR と PWD の共通部分がマッチするか調べる.
    ## "$#" は配列の最後の要素の番号を答える(例では $#tpd_item = 3) 
    for ($and = $#tpd_item; $and >= 0; $and--) {
	die "Warning; $TOPDIR is different"
            unless ($cwd_item[$and] eq $tpd_item[$and]);
    }

    # $PTOPDIR と PWD の共通部分がマッチするか調べる.
    ## 例では $#prj_item = 5
    for ($and = $#prj_item; $and >= 0; $and--) {
	die "Warning; $PTOPDIR is different from $WORKDIR "
            unless ($cwd_item[$and] eq $prj_item[$and]);
    }

    # $TOPDIR と $PTOPDIR の共通部分がマッチするか調べる.
    for ($and = $#tpd_item; $and >= 0; $and--) {
	die "Warning; $PTOPDIR is different from $TOPDIR"
            unless ($tpd_item[$and] eq $prj_item[$and]);
    }

    # @prj_item の最後から 2 番目の要素から $diff 個を選ぶ.
    ## 例では $diff = 5 - 3 = 2. 
    ## $updirname[1] = prj_item[3] = riron
    ## $updirname[0] = prj_item[4] = renzoku
    for ($diff = @prj_item - @tpd_item; $diff > 0; $diff--) {
	$updirname[$diff - 1] = $prj_item[@prj_item - $diff - 1];
    }

## 戻り値を指定
    ($diff_ct, $diff_pt, @updirname) ;
}





# basename 
#
### これ使っていない
sub Basename {
    local($x, $suffix) = @_;
    $x =~ s|/$||;
    $x =~ s|^.*/||;
    $x =~ s/$suffix$// if ($suffix);
    $x;
}




# kanzi code convertor(日本語文字コード変換)
## Windows を想定している

sub initkanzi {
    $INCONV = "nkf -e";
    $OUTCONV = "nkf -E -s";
}

sub open_r {
    local($file) = @_;
    open($file, "$INCONV \'$file\' |");
}



sub open_w {
    local($file) = @_;
#    return open($file, "| $OUTCONV") if $file eq "-";
    open($file, "| $OUTCONV > $file");
}

