
#
# hindex.pl v1.2 written by fuku
#
# 2001-05-08 v1.0 完成
# 2001-08-18 v1.1 内部番号 6 桁の制限を排除
# 2001-08-20 v1.2 idx($msgno) の不具合を修正
#
# ハッシュを使って記事番号管理をする
#
# $idx = new hindex('idx-db') ;	# 開始
# $msgno = $idx->add() ;		# インデックスを追加
# $msgno = $idx->get($dspno) ;	# 表示番号を与えて内部番号を得る
# $dspno = $idx->idx($msgno) ;	# 内部番号を与えて表示番号を得る
# $dspno = $idx->max() ;		# 総表示数 (=インデックスの大きさ)
# $idx->del($dspstrt,$dspcnt) ;	# 削除 (表示番号で削除)
# $dspno = $idx->ins($msgno) ;	# 挿入
# $idx->DESTROY() ;				# フラッシュ
#
# 表示プログラム例 (No.10 - No.20 を表示)
#
# require 'hindex.pl' ;
# $idx = new hindex('idx-db') ;
# for ( $i = 10 ; $i <= 20 ; $i ++ ) {
#    $msgno = $idx->get($i - 1) ;
#    open(MSG,"< $msgno.msg") ;
#    print <MSG> ;
#    close(MSG) ;
#    }
#

use strict ;

package hindex ;

sub new
{
	# コンストラクタ

	# 格納 DBM としてファイル名を指定しているが、
	# 実装によって
	# aaa.db だったり
	# aaa.dir,aaa.pag のペアだったりするので
	# 特に UNIX系 <-> WINDOWS のデータ互換性は絶望的
	# プログラムはどちらでも動きます

	my $self = {} ;

	shift ;
	my($indexfile) = @_ ;

	$self->{indexfile}  = $indexfile ;	# ハッシュファイル名

	my %INDEX ;
	dbmopen(%INDEX, "$indexfile", 0666) ;
	$self->{dspmax} = $INDEX{dspmax} + 0 ;	# 表示カウンタ
	$self->{msgcnt} = $INDEX{msgcnt} + 0 ;	# 内部カウンタ
	$self->{indexdb} = \%INDEX ;
	$self->{alive} = 1 ;

	bless $self ;

	return $self ;
}

sub DESTROY
{
	# デストラクタ

	# このパッケージでは管理値もハッシュに内蔵するので
	# 必ずここでセーブする

	my $self = shift ;
	my $hash ;

	if (defined $self->{alive})
		{
		$hash = $self->{indexdb} ;

		$hash->{dspmax} = $self->{dspmax} ;	# 表示カウンタ
		$hash->{msgcnt} = $self->{msgcnt} ;	# 内部カウンタ

		dbmclose(%$hash) ;
		}

	delete $self->{alive} ;
}

sub add
{
	# 内部番号を追加する
	# 「追加」なので内部番号の指定は出来ない
	# 挿入を行いたい場合は ins() を使われたし

	my $self = shift ;

	# 番号を追加する

	my $cntstr = $self->{msgcnt} + 0 ;

	my $hash = $self->{indexdb} ;
	$hash->{$self->{dspmax}} = $cntstr ;

	$self->{dspmax} ++ ;
	$self->{msgcnt} ++ ;

	return $cntstr ;	# 追加した内部番号を返す
}

sub get
{
	# 表示番号から内部番号を引き出す

	my $self = shift ;
	my($pos) = @_ ;
	my $hash ;

	# 内部番号を返す

	$hash = $self->{indexdb} ;
	return $hash->{$pos} ;
}

sub idx
{
	# 内部番号から表示番号を引き出す

	my $self = shift ;
	my($pos) = @_ ;
	my $hash ;
	my %TMP ;

	# 表示番号を返す

	$hash = $self->{indexdb} ;

	# ハッシュをひっくり返すので、
	# バッティングの危険を避けるために管理値を2つとも削除する
	#
	my $dspmax = $hash->{dspmax} ;
	my $msgcnt = $hash->{msgcnt} ;
	delete $hash->{dspmax} ;
	delete $hash->{msgcnt} ;

	%TMP = reverse %$hash ;

	$hash->{dspmax} = $dspmax ;
	$hash->{msgcnt} = $msgcnt ;

	return $TMP{$pos + 0} ;
}

sub max
{
	my $self = shift ;

	# 表示数を返す

	return $self->{dspmax} + 0 ;
}

sub del
{
	my $self = shift ;

	# 番号をつめた後に消す処理
	# (重い処理だが、やむなし)

	my($start,$cnt) = @_ ;

	$cnt = 1 if ($cnt eq '') ;

	return -1 if ($start eq '' || $cnt < 0) ;

	my $i ;
	my $hash ;

	$hash = $self->{indexdb} ;

	for ( $i = $start ; $i < ($self->{dspmax} - $cnt) ; $i ++ )
		{
		$hash->{$i} = $hash->{$i + 1} ;
		}
	for ( $i = 0 ; $i < $cnt ; $i ++ )
		{
		delete $hash->{$self->{dspmax} - 1 - $i} ;
		}
	$self->{dspmax} -= $cnt ;

	return 0 ;
}

sub ins
{
	my $self = shift ;
	my($msgno) = @_ ;
	my %TMP ;
	my $hash ;
	my $stat ;

	# 途中挿入を行う
	# (これも del と同様に重い処理だが、やむなし)

	$hash = $self->{indexdb} ;

	# ハッシュをひっくり返すので、
	# バッティングの危険を避けるために管理値を2つとも削除する
	#
	delete $hash->{dspmax} ;
	delete $hash->{msgcnt} ;

	%TMP = reverse %$hash ;

	if (defined $TMP{$msgno})
		{
		# 既にあるので挿入しない
		$stat = -1 ;
		}
	else
		{
		$TMP{$msgno} = '' ;

		# 番号を振りなおす
		# 内部番号に表示番号を付ける(0 ～ 最大値-1)
		my $no = 0 ;
		my $key ;
		foreach $key (sort keys %TMP)
			{
			$TMP{$key} = $no ;
			$no ++ ;
			}
		%$hash = reverse %TMP ;

		$self->{dspmax} = $no ;						# 表示カウンタ
		$self->{msgcnt} = $hash->{$no - 1} + 1 ;	# 内部カウンタ

		$stat = 1 ;
		}

	# さっき削除した管理値を2つとも戻す
	#
	$hash->{dspmax} = $self->{dspmax} ;
	$hash->{msgcnt} = $self->{msgcnt} ;

	return $stat ;
}

1;

