#!/usr/local/bin/perl

$CGIPATH = '.';

# contents.cgi -- list article contents.
# Copyright (C) 1996-1998 nir@mvg.biglobe.ne.jp
#
# This file is part of WebForum.
# WebForum is free script; you can redistribute it and/or modify it
# for any purpose.

$rcsid = '@(#)$Id: contents.cgi,v 2.5 1998/12/26 03:24:55 nir Exp nir $';

require "$CGIPATH/forum.pl";

%http_args = &http'http_args;
$room = $http_args{'room'};
$area = $http_args{'area'};
$init_mes = $http_args{'mes'};
$http_args{'mode'} = 'onethread' if ($init_mes);
%http_cookie = &http'http_cookie;
%ext = &forum'get_ext($http_cookie{'ext'});
%gui = &forum'get_gui($http_cookie{'gui'});
%new_gui = &forum'merge_gui(*gui, %http_args);

$noframes = ($gui{'frame'} eq 'no');
$article_frame = qq{\nTARGET="article"} unless ($noframes);
$menu_frame = qq{\nTARGET="menu"} unless ($noframes);

$thread_order = ($gui{'sort'} eq 'order');
$child_order = ($gui{'sort'} ne 'reverse');
$sibling_order = (($gui{'sort'} eq 'order')
	       || ($gui{'sort'} eq 'new'));

$format = &forum'escape_format($gui{'listfmt'});
$indent = $gui{'indent'};
$step = $gui{'step'};
$max_indent = $indent * $step;
$suppress = ($gui{'suppress'} eq 'on');
$sep_str = '';
$sep_str = "\n" if ($gui{'sep'} eq 'nl');
$sep_str = '<HR>' if ($gui{'sep'} eq 'hr');
$mode = $gui{'mode'};

$title = &forum'get_title($room);
&WarningMsg('Bad Forum') if ($title eq '');

($req_min) = $area =~ m/^(\d+)$/;
($req_min, $req_max) = $area =~ m/^(\d*)-(\d*)$/ if ($req_min eq '');
$req_max++ if ($req_max);
&get_contents($room, *req_min, *req_max);
$article_area = sprintf("(記事番号[%d～%d])",
		$req_min, $req_max - 1) if ($req_min < $req_max);
$last_modified = (stat("$FORUMPATH/$room/contents"))[9];
$reload_menu = sprintf('<A HREF="%s/menu.%s%s"%s>%s会議室選択</A> ',
		$CGIROOT, $CGI,
		&forum'make_param_str(*new_gui),
		$menu_frame, $MENUICON);
$new_article = sprintf('<A HREF="%s/article.%s%s"%s>%s新規記事投稿</A> ',
		$CGIROOT, $CGI,
		&forum'make_param_str(*new_gui,
			'room', $room),
		$article_frame, $NEWICON);
$page_size = $req_max - $req_min;
$overlap = $gui{'overlap'};
$overlap = int($page_size / 2) if ($overlap > $page_size / 2);
if (($mode eq 'thread') || ($mode eq 'date')) {
	($alt_mode, $alt_icon) = ($mode eq 'thread')
				? ('date', $DATEICON)
				: ('thread', $THREADICON);
	$alt_mode = '' if ($new_gui{'mode'} eq $mode);
	$alt_contents = sprintf('<A HREF="%s/contents.%s%s">%s</A>|',
			$CGIROOT, $CGI,
			&forum'make_param_str(*new_gui,
				'room', $room,
				'mode', $alt_mode),
			$alt_icon);
}
if ($thread_order) {
	$top_icons = &prev_page($TOPICON, $PREVICON, $WIDEICON);
	$bot_icons = &next_page($BOTICON, $NEXTICON, $WIDEICON);
} else {
	$top_icons = &next_page($TOPICON, $PREVICON, $WIDEICON);
	$bot_icons = &prev_page($BOTICON, $NEXTICON, $WIDEICON);
}
$help = &forum'help('contents');
$line_extensions = &forum'make_extensions('contents', '', "\n", $noframes, *ext);
$icon_extensions = &forum'make_extensions('icon', '', '|', $noframes, *ext);

$output = sprintf("Content-Type: text/html\n%s\n%s\n\n",
		&forum'last_modified($last_modified),
		&forum'expires(time + $forum'expires));
$output .= <<"EOL";
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
<!-- $rcsid -->

<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html;charset=Shift_JIS">
<TITLE>$FORUMTITLE ($title)</TITLE>
<LINK REV="MADE" HREF="mailto:$forum'owner">
</HEAD>
$BODY
<H1><A HREF="$FORUMROOT/$room/"$article_frame>$title</A>一覧
$help</H1>
$line_extensions<PRE>
EOL

$output .= "${alt_contents}${icon_extensions}${reload_menu}${new_article}${top_icons}${article_area}<HR>";
&put_contents;
$output .= "<HR>${alt_contents}${icon_extensions}${reload_menu}${new_article}${bot_icons}${article_area}</PRE>\n";
$output .= "$FOOTER" if ($noframes);
$output .= "</BODY>\n</HTML>\n";

&forum'convert_print(*output);

exit(0);

sub prev_page {
	local($page);
	local(@icons) = @_;

	return('') if ($req_min <= $min_mes);
	$page .= &get_page($icons[0], $min_mes, $min_mes + $page_size);
	$page .= &get_page($icons[1], $req_min - $page_size + $overlap, $req_min + $overlap);
	$page .= &get_page($icons[2], $req_min - $gui{'articles'}, $req_max);
	return("$page ");
}

sub next_page {
	local($page);
	local(@icons) = @_;

	return('') if ($req_max >= $max_mes);
	$page .= &get_page($icons[0], $max_mes - $page_size, $max_mes);
	$page .= &get_page($icons[1], $req_max - $overlap, $req_max + $page_size - $overlap);
	$page .= &get_page($icons[2], $req_min, $req_max + $gui{'articles'});
	return("$page ");
}

sub get_page {
	local($icon, $min, $max) = @_;

	&get_area(*min, *max);
	sprintf('<A HREF="%s/contents.%s%s">%s</A>',
		$CGIROOT, $CGI,
		&forum'make_param_str(*new_gui,
			'room', $room,
			'area', sprintf('%d-%d', $min, $max - 1)),
		$icon);
}

sub get_area {
	local($size);
	local(*min, *max) = @_;

	$size = $max - $min;
	$size = $max_mes - $min_mes if (($size <= 0) && ($mode eq 'onethread'));
	$size = $gui{'articles'} if ($size <= 0);
	$size = $max_mes - $min_mes if (($size <= 0) || ($size > $max_mes - $min_mes));
	$max = ($min) ? $min + $size : $max_mes unless ($max);
	$min = $max - $size;
	if ($min < $min_mes) {
		$max += $min_mes - $min;
		$min = $min_mes;
	}
	if ($max > $max_mes) {
		$min -= $max - $max_mes;
		$max = $max_mes;
	}
}

sub make_dbm {
	local(%youngest);
	local($mes, $ref, $passwd, $from, $subject, $premes);
	local($sec, $min, $hour, $mday, $mon, $year);
	local(*dbm, $room, *req_min, *req_max) = @_;

	open(CONTENTS, "$FORUMPATH/$room/contents")
		|| &ErrorMsg('missing', "$room/contents");
	$_ = <CONTENTS>;
	($min_mes, $max_mes) = /^(\d{5}) (\d{5}) \S{3,}$/;
	$min_mes += 0; $max_mes += 0;
	(($min_mes) && ($max_mes) && ($min_mes <= $max_mes))
		|| &ErrorMsg('format', "$room/contents");
	&get_area(*req_min, *req_max);
	$premes = 0;
	while (<CONTENTS>) {
		($mes, $ref, $passwd, $year, $mon, $mday, $hour, $min, $sec, $from, $subject)
			= m#^(\d{5}) (\d{5}) (\S{3,}) (\d{4})/(\d{2})/(\d{2}) (\d{2}):(\d{2}):(\d{2}) <(.+)> (.+)$#;
		$mes += 0; $ref += 0;
		&ErrorMsg('format', "$room/contents")
			if (($mes < $min_mes) || ($mes >= $max_mes)
			 || ($mes <= $premes) || ($ref >= $mes));
		next if ($mes < $req_min);
		last if ($mes >= $req_max);
		$ref = 0 if ($ref < $min_mes);
		$dbm{'R', $mes} = $ref;
		$dbm{'P', $mes} = $passwd;
		$dbm{'A', $mes} = sprintf("%u <%s> %s",
			&forum'time_local($sec, $min, $hour, $mday, $mon - 1, $year - 1900),
			$from, $subject);
		if ($youngest{$ref}) {
			$dbm{'S', $youngest{$ref}} = $mes;
		} else {
			$dbm{'C', $ref} = $mes;
		}
		$youngest{$ref} = $mes;
		$premes = $mes;
	}
	close(CONTENTS);
	return(1);
}

sub get_contents {
	local($mes, $top);
	local($room, *req_min, *req_max) = @_;

	if ($forum'use_dbm) {
		dbmopen(%dbm, "$FORUMPATH/$room/cont_dbm", undef)
			|| &ErrorMsg('missing', "$room/cont_dbm");
		$min_mes = $dbm{'min', 0};
		$max_mes = $dbm{'max', 0};
		(($min_mes) && ($max_mes) && ($min_mes <= $max_mes))
			|| &ErrorMsg('format', "$room/cont_dbm");
		&get_area(*req_min, *req_max);
	} else {
		&make_dbm(*dbm, $room, *req_min, *req_max);
	}
	if ($mode eq 'onethread') {
		foreach $mes (split(/,/, $init_mes)) {
			$mes += 0;
			while ($dbm{'R', $mes} >= $req_min) {
				$mes = $dbm{'R', $mes};
			}
			next unless (($mes >= $req_min) && ($mes < $req_max)
				&& (defined($dbm{'P', $mes})));
			foreach $top (@top) {
				$mes = 0, last if ($mes == $top);
			}
			push(@top, $mes) if ($mes);
		}
		@top = sort {$a <=> $b} @top;
	} else {
		foreach $mes ($req_min .. $req_max - 1) {
			next unless (defined($dbm{'P', $mes}));
			push(@top, $mes) if (($dbm{'R', $mes} < $req_min)
				|| ($mode eq 'date'));
		}
	}
}

sub put_contents {
	local($mes, $i, $prelines);

	$lines = 0;
	if ($mode eq 'date') {
		foreach $mes (($thread_order) ? (@top) : (reverse @top)) {
			&article_out($mes, 0);
		}
	} else {
		@top = (reverse @top) unless ($thread_order);
		for $i (0 .. $#top) {
			$mes = $top[$i];
			$prelines = $lines;
			&article_out($mes, 0) if ($child_order);
			&article($dbm{'C', $mes}, 1);
			&article_out($mes, 0) unless ($child_order);
			$output .= "$sep_str" if (($i != $#top) && ($lines != $prelines));
		}
	}
	if ($lines == 0) {
		$output .= ($mes) ? '選択範囲には' : 'この会議室には';
		$output .= 'ひとつも記事がありません。';
	}
}

sub article {
	local($mes, $level) = @_;

	return if (($mes < $req_min) || ($mes >= $req_max));
	&article($dbm{'S', $mes}, $level) unless ($sibling_order);
	&article($dbm{'C', $mes}, $level + 1) unless ($child_order);
	&article_out($mes, $level);
	&article($dbm{'C', $mes}, $level + 1) if ($child_order);
	&article($dbm{'S', $mes}, $level) if ($sibling_order);
}

sub article_out {
	local($I, $i, $s);
	local($mes, $level) = @_;

	local($mtime, $from, $subject)
		= $dbm{'A', $mes} =~ /^(\d+) <(.+)> (.*)$/;
	return if (&forum'is_canceled($dbm{'P', $mes}, *from, *subject) && ($suppress));
	$I = ($level >= $indent) ? sprintf("%${max_indent}d", $level) : ' 'x($level * $step);
	$i = ($mtime > $^T - $forum'new) ? $NEWTEXTICON : $TEXTICON;
	$s = sprintf(qq(<A HREF="%s/%s/%05d.html"%s>%s</A>),
		$FORUMROOT, $room, $mes, $article_frame, $subject);
	$output .= &forum'eval_var($format, 'NnRrIifsYymdHMS', $mtime, $mes, $dbm{'R', $mes}, $I, $i, $from, $s);
	$output .= "\n";
	$lines++;
}

