#!/usr/bin/perl -w

use IO::Socket;
use lib '.';
use l3config;
use locale;

our @Command_Lines;
our @Command_Lines_Index;
our %Commands_Description;
our %Args_Description;
our $Mywi_Socket;
our %Sessions;

# vvv Инициализация переменных выполняется процедурой init_variables
our @Day_Name;
our @Month_Name;
our @Of_Month_Name;
our %Search_Machines;
our %Elements_Visibility;
# ^^^

our %Stat;
our %CommandsFDistribution;	# Сколько раз в журнале встречается какая команда

sub search_buy;
sub make_comment;
sub load_command_lines_from_xml;
sub load_sessions_from_xml;
sub print_command_lines;
sub sort_command_lines;
sub process_command_lines;
sub init_variables;
sub main;
sub collapse_list($);

main();

sub main
{
	$| = 1;

	init_variables();
	init_config();

	open_mywi_socket();
	load_command_lines_from_xml($Config{"backend_datafile"});
	load_sessions_from_xml($Config{"backend_datafile"});
	sort_command_lines;
	process_command_lines;
	print_command_lines($Config{"output"});
	close_mywi_socket;
}


sub search_by
{
	my $sm = shift;
	my $topic = shift;
	$topic =~ s/ /+/;
	
	return "<a href='".	$Search_Machines{$sm}->{"query"}."$topic'><img width='16' height='16' src='".
				$Search_Machines{$sm}->{"icon"}."' border='0'/></a>";
}

sub extract_from_cline
# Разобрать командную строку $_[1] и возвратить хэш, содержащий 
# номер первого появление команды в строке:
# 	команда => первая позиция
{
	my $what = $_[0];
	my $cline = $_[1];
	my @lists = split /\;/, $cline;
	
	
	my @commands = ();
	for my $list (@lists) {
		push @commands, split /\|/, $list;
	}

	my %commands;
	my %args;
	my $i=0;
	for my $command (@commands) {
		$command =~ s@^\s*\S+/@@;
		$command =~ /\s*(\S+)\s*(.*)/;
		if ($1 && $1 eq "sudo" ) {
			$commands{"$1"}=$i++;
			$command =~ s/\s*sudo\s+//;
		}
		$command =~ s@^\s*\S+/@@;
		$command =~ /\s*(\S+)\s*(.*)/;
		if ($1 && !defined $commands{"$1"}) {
				$commands{"$1"}=$i++;
		};	
		if ($2) {
			my $args = $2;
			my @args = split (/\s+/, $args);
			for my $a (@args) {
				$args{"$a"}=$i++
					if !defined $args{"$a"};
			};	

				
		}
	}

	if ($what eq "commands") {
		return \%commands;
	} else {
		return \%args;
	}
	
}

sub open_mywi_socket
{
	$Mywi_Socket = IO::Socket::INET->new(
				PeerAddr => $Config{mywi_server},
				PeerPort => $Config{mywi_port},
				Proto	 => "tcp",
				Type	 => SOCK_STREAM);
}

sub close_mywi_socket
{
	close ($Mywi_Socket);
}


sub mywi_client
{
	my $query = $_[0];
	my $mywi;

	open_mywi_socket;
	if ($Mywi_Socket) {
		local $| = 1;
		local $/ = "";
		print $Mywi_Socket $query."\n";
		$mywi = <$Mywi_Socket>;
		$mywi = "" if $mywi =~ /nothing app/;
	}
	close_mywi_socket;
	return $mywi;
}

sub make_comment
{
	my $cline = $_[0];
	#my $files = $_[1];

	my @comments=(); 
	my @commands = keys %{extract_from_cline("commands", $cline)};
	my @args = keys %{extract_from_cline("args", $cline)};
	return if (!@commands && !@args);
	#return "commands=".join(" ",@commands)."; files=".join(" ",@files);

	# Commands
	for my $command (@commands) {
		$command =~ s/'//g;
		$CommandsFDistribution{$command}++;
		if (!$Commands_Description{$command}) {
			my $mywi="";
			$mywi = mywi_client ($command);
			$mywi = join ("\n", grep(/\([18]\)/, split(/\n/, $mywi)));
			$mywi =~ s/\s+/ /;
			if ($mywi !~ /^\s*$/) {
				$Commands_Description{$command} = $mywi;
			}
			else {
				next;
			}
		}

		push @comments, $Commands_Description{$command};
	}
	return join("&#10;\n", @comments);
	
	# Files
	for my $arg (@args) {
		$arg =~ s/'//g;
		if (!$Args_Description{$arg}) {
			my $mywi;
			$mywi = mywi_client ($arg);
			$mywi = join ("\n", grep(/\([5]\)/, split(/\n/, $mywi)));
			$mywi =~ s/\s+/ /;
			if ($mywi !~ /^\s*$/) {
				$Args_Description{$arg} = $mywi;
			}
			else {
				next;
			}
		}

		push @comments, $Args_Description{$arg};
	}

}

=cut
Процедура load_command_lines_from_xml выполняет загрузку разобранного lab-скрипта
из XML-документа в переменную @Command_Lines

Предупреждение!
Процедура не в состоянии обрабатывать XML-документ любой структуры.
В действительности файл cache из которого загружаются данные 
просто напоминает XML с виду.
=cut
sub load_command_lines_from_xml
{
	my $datafile = $_[0];

	open (CLASS, $datafile)
		or die "Can't open file of the class ",$datafile,"\n";
	local $/;
	$data = <CLASS>;
	close(CLASS);

	for $command ($data =~ m@<command>(.*?)</command>@sg) {
		my %cl;
		while ($command =~ m@<([^>]*?)>(.*?)</\1>@sg) {
			$cl{$1} = $2;
		}
		push @Command_Lines, \%cl;
	}
}

sub load_sessions_from_xml
{
	my $datafile = $_[0];

	open (CLASS, $datafile)
		or die "Can't open file of the class ",$datafile,"\n";
	local $/;
	my $data = <CLASS>;
	close(CLASS);

	for my $session ($data =~ m@<session>(.*?)</session>@sg) {
		my %session;
		while ($session =~ m@<([^>]*?)>(.*?)</\1>@sg) {
			$session{$1} = $2;
		}
		$Sessions{$session{local_session_id}} = \%session;
	}
}



sub sort_command_lines
{
	# Sort Command_Lines
	# Write Command_Lines to Command_Lines_Index

	my @index;
	for (my $i=0;$i<=$#Command_Lines;$i++) {
		$index[$i]=$i;
	}

	@Command_Lines_Index = sort {
		$Command_Lines[$index[$a]]->{"time"} <=> $Command_Lines[$index[$b]]->{"time"}
	} @index;

}

sub process_command_lines
{
	for my $i (@Command_Lines_Index) {

		my $cl = \$Command_Lines[$i];
		#@{${$cl}->{"new_commands"}} =();
		#@{${$cl}->{"new_files"}} =();
		$$cl->{"class"} = ""; 

		if ($$cl->{"err"}) {
			$$cl->{"class"}="wrong";
			$$cl->{"class"}="interrupted"
				if ($$cl->{"err"} eq 130);
		}	
		if (!$$cl->{"euid"}) {
			$$cl->{"class"}.="_root";
		}
		
#tab#		my @tab_words=split /\s+/, $$cl->{"output"};
#tab#		my $last_word= $$cl->{"cline"} =~ /(\S*)$/;
#tab#		$last_word =~ s@.*/@@;
#tab#		my $this_is_tab=1;
#tab#
#tab#		if ($last_word && @tab_words >2) {
#tab#			for my $tab_words (@tab_words) {
#tab#				if ($tab_words !~ /^$last_word/) {
#tab#					$this_is_tab=0;
#tab#					last;
#tab#				}
#tab#			}
#tab#		}	
#tab#		$$cl->{"class"}="tab" if $this_is_tab;
		

#		if ( !$$cl->{"err"}) {
#			# Command does not contain mistakes
#			
#			my %commands = extract_from_cline("commands", ${$cl}->{"cline"});
#			my %files = extract_from_cline("files", ${$cl}->{"cline"});
#
#			# Searching for new commands only
#			for my $command (keys  %commands) {
#				if (!defined $Commands_Stat{$command}) {
#					push @{$$cl->{new_commands}}, $command;
#				}	
#				$Commands_Stat{$command}++;
#			}
#			
#			for my $file (keys  %files) {
#				if (!defined $Files_Stat{$file}) {
#					push @{$$cl->{new_files}}, $file;
#				}	
#				$Files_Stat{$file}++;
#			}
#		}	

		if ($$cl->{cline}=~ m@cat[^#]*#([\^=v])\s*(.*)@) {
			if ($1 eq "=") {
				$$cl->{"class"} = "note";
				$$cl->{"note"} = $$cl->{"output"};
				$$cl->{"note_title"} = $2;
			}
			else {
				my $j = $i;
				if ($1 eq "^") {
					$j--;
					$j-- while ($j >=0  && (!$Command_Lines[$j] || $Command_Lines[$j]->{tty} ne $$cl->{tty}));
				}
				elsif ($1 eq "v") {
					$j++;
					$j++ while ($j <= @Command_Lines  && (!$Command_Lines[$j] || $Command_Lines[$j]->{tty} ne $$cl->{tty}));
				}
				$Command_Lines[$j]->{note_title}="$2";
				$Command_Lines[$j]->{note}=$$cl->{output};
				$$cl=0;
			}
		}
		elsif ($$cl->{cline}=~ /#([\^=v])(.*)/) {
			if ($1 eq "=") {
				$$cl->{"class"} = "note";
				$$cl->{"note"} = $2;
			}
			else {
				my $j=$i;
				if ($1 eq "^") {
					$j--;
					$j-- while ($j >=0  && (!$Command_Lines[$j] || $Command_Lines[$j]->{tty} ne $$cl->{tty}));
				}
				elsif ($1 eq "v") {
					$j++;
					$j++ while ($j <= @Command_Lines  && $Command_Lines[$j]->{tty} ne $$cl->{tty} || !$Command_Lines[$j]);
				}
				$Command_Lines[$j]->{note}="$2";
				$$cl=0;
			}
		}
	}	

}


=cut
Процедура print_command_lines выводит HTML-представление
разобранного lab-скрипта. 

Разобранный lab-скрипт должен находиться в массиве @Command_Lines
=cut

sub print_command_lines
{
	my $output_filename=$_[0];

	my $course_name = $Config{"course-name"};
	my $course_code = $Config{"course-code"};
	my $course_date = $Config{"course-date"};
	my $course_center = $Config{"course-center"};
	my $course_trainer = $Config{"course-trainer"};
	my $course_student = $Config{"course-student"};
	

	# Результат выполнения процедуры равен 
	# join("", @Result{header,body,stat,help,about,footer})
	my %Result;
	my @toc;  # Хранит оглавление
	my $note_number=0;

	$Result{"body"} = "<table width='100%'>\n";
	
	my $cl;
	my $last_tty="";
	my $last_day="";
	my $in_range=0;

	my $i=0;

COMMAND_LINE:
	for my $k (@Command_Lines_Index) {

		my $cl=$Command_Lines[$Command_Lines_Index[$i++]];
		
		next unless $cl;


		if ($Config{filter}) {
			# Инициализация фильтра
			my %filter;
			for (split /&/,$Config{filter}) {
				my ($var, $val) = split /=/;
				$filter{$var} = $val;
			}

			if ($filter{hostname}) {
				next COMMAND_LINE unless $Sessions{$cl->{local_session_id}}->{hostname} eq $filter{hostname};
			}

			if ($filter{user}) {
				next COMMAND_LINE unless $Sessions{$cl->{local_session_id}}->{user} eq $filter{user};
			}

			#for my $filter_field (keys %filter) {
			#	next COMMAND_LINE unless $Sessions{$cl->{local_session_id}}->{$filter_field} eq $filter{$filter_field};
			#}
		}

		if ($Config{"from"} && $cl->{"cline"} =~ /$Config{"signature"}\s*$Config{"from"}/) {
			$in_range=1;
			next;
		}
		if ($Config{"to"} && $cl->{"cline"} =~ /$Config{"signature"}\s*$Config{"to"}/) {
			$in_range=0;
			next;
		}
		next if ($Config{"from"} && $Config{"to"} && !$in_range) 
			||
		    	($Config{"skip_empty"} =~ /^y/i && $cl->{"cline"} =~ /^\s*$/ )
			||
			($Config{"skip_wrong"} =~ /^y/i && $cl->{"err"} != 0)
			||
			($Config{"skip_interrupted"} =~ /^y/i && $cl->{"err"} == 130);
		
		#my @new_commands=@{$cl->{"new_commands"}};
		#my @new_files=@{$cl->{"new_files"}};

		if ($cl->{class} eq "note") {
			my $note = $cl->{note};
			$note = join ("\n", map ("<p>$_</p>", split (/-\n/, $note)));
			$note =~ s@(http:[a-zA-Z.0-9/?%-]*)@<a href='$1'>$1</a>@g;
			$note =~ s@(www\.[a-zA-Z.0-9/?%-]*)@<a href='$1'>$1</a>@g;
			$Result{"body"} .= "<tr><td colspan='6'>";
			$Result{"body"} .= "<h4 id='note$note_number'>".$cl->{note_title}."</h4>" if $cl->{note_title};
			$Result{"body"} .= "".$note."<p/><p/></td></td>";

			if ($cl->{note_title}) {
				push @{$toc[@toc]},"<a href='#note$note_number'>".$cl->{note_title}."</a>";
				$note_number++;
			}
			next;
		}

		my $cl_class="cline";
		my $out_class="output";
		if ($cl->{"class"}) {
			$cl_class = $cl->{"class"}."_".$cl_class;
			$out_class = $cl->{"class"}."_".$out_class;
		}

		my @new_commands;
		my @new_files;
		@new_commands = split (/\s+/, $cl->{"new_commands"}) if defined $cl->{"new_commands"};
		@new_files = split (/\s+/, $cl->{"new_files"}) if defined $cl->{"new_files"};

		my $output="";
		if ($Config{"head_lines"} || $Config{"tail_lines"}) {
			# Partialy output
			my @lines = split '\n', $cl->{"output"};
			# head
			my $mark=1;
			for (my $i=0; $i<= $#lines && $i < $Config{"head_lines"}; $i++) {
				$output .= $lines[$i]."\n";
			}
			# tail
			my $start=$#lines-$Config{"tail_lines"}+1;
			if ($start < 0) {
				$start=0;
				$mark=0;
			}	
			if ($start < $Config{"head_lines"}) {
				$start=$Config{"head_lines"};
				$mark=0;
			}	
			$output .= $Config{"skip_text"}."\n" if $mark;
			for (my $i=$start; $i<= $#lines; $i++) {
				$output .= $lines[$i]."\n";
			}
		} 
		else {
			# Full output
			$output .= $cl->{"output"};
		}	
		#$output .= "^C\n" if ($cl->{"err"} eq "130");

		#
		##
		## Начинается собственно вывод
		##
		#

		# <command>

		my ($sec,$min,$hour,$day,$mon,$year,$wday,$yday,$isdst) = localtime($cl->{time});
		$Stat{FirstCommand} = $cl->{time} unless $Stat{FirstCommand};
		$Stat{LastCommand} = 0 unless defined $Stat{LastCommand};	
		$Stat{TotalTime} += $cl->{time} - $Stat{LastCommand}
			if $cl->{time} - $Stat{LastCommand} < $Config{stat_inactivity_interval};
		$Stat{LastCommand} = $cl->{time};
		$Stat{TotalCommands} = 0 unless $Stat{TotalCommands};
		$Stat{TotalCommands}++;

		# Добавляем спереди 0 для удобочитаемости
		$min = "0".$min if $min =~ /^.$/;
		$hour = "0".$hour if $hour =~ /^.$/;
		$sec = "0".$sec if $sec =~ /^.$/;

		$class=$cl->{"out_class"};
		$class =~ s/output$//;

		$Stat{ErrorCommands}++
			if $class =~ /wrong/;
		
		$Result{"body"} .= "<tr class='command'>\n";
						
		
		# DAY CHANGE
		if ( $last_day ne $day) {
			#$Result{"body"} .= "<td colspan='6'><p></p><h3>День ",$day,"</h4></td></tr><tr>";
			$Result{"body"} .= "<td colspan='6'><p></p><h3 id='day$day'>".$Day_Name[$wday]."</h4></td></tr><tr>";
			push @toc, "<a href='#day$day'>".$Day_Name[$wday]."</a>\n";
			$last_day=$day;
		}

		# CONSOLE CHANGE
		if ( $last_tty ne $cl->{"tty"}) {
			my $host;
			#$host = $Sessions{$cl->{local_session_id}}->{user}."@".$Sessions{$cl->{local_session_id}}->{hostname};
			$Result{"body"} .= "<td colspan='6'><table><tr><td class='ttychange' width='140' align='center'>".$cl->{"tty"}."</td><td>$host</td></tr></table></td></tr><tr>";
			$last_tty=$cl->{"tty"};
		}

		# TIME
		if ($Config{"show_time"} =~ /^y/i) {
			$Result{"body"} .= "<td valign='top' class='time' width='$Config{time_width}'><pre>".
				$hour. ":". $min. ":". $sec.
				"</td>";
		} else {
			$Result{"body"} .= "<td width='0'/>"
		}

		# COMMAND
		$Result{"body"} .= "<td class='script'>\n";
		$Result{"body"} .= "<pre class='${class}cline'>\n";
		my $cline = $cl->{"prompt"}.$cl->{"cline"};
		$cline =~ s/\n//;

		#$cline .= "(".$Sessions{$cl->{local_session_id}}.")";
		
		my $hint = make_comment($cl->{"cline"});
		$cline = "<div title='$hint'>$cline</div>" if $hint;
		$Result{"body"} .= $cline;
		$Result{"body"} .= "</pre>\n";

		my $last_command = $cl->{"last_command"};
		if (!( 
		$Config{"suppress_editors"} =~ /^y/i && grep ($_ eq $last_command, @{$Config{"editors"}}) ||
		$Config{"suppress_pagers"}  =~ /^y/i && grep ($_ eq $last_command, @{$Config{"pagers"}}) ||
		$Config{"suppress_terminal"}=~ /^y/i && grep ($_ eq $last_command, @{$Config{"terminal"}})
			)) {

			$Result{"body"} .= "<pre class='".$cl->{out_class}."'>";
			$Result{"body"} .= $output;
			$Result{"body"} .= "</pre>\n";
		}	

		# DIFF
		if ( $Config{"show_diffs"} =~ /^y/i && $cl->{"diff"}) {
			$Result{"body"} .= "<table><tr><td width='5'/><td class='diff'><pre>";
			$Result{"body"} .= $cl->{"diff"};
			$Result{"body"} .= "</pre></td></tr></table>";
		}
		
		#NOTES
		if ( $Config{"show_notes"} =~ /^y/i && $cl->{"note"}) {
			my $note=$cl->{"note"};
			$note =~ s/\n/<br\/>\n/msg;
			$note =~ s@(http:[a-zA-Z.0-9/?%-]*)@<a href='$1'>$1</a>@g;
			$note =~ s@(www\.[a-zA-Z.0-9/?%-]*)@<a href='$1'>$1</a>@g;
		#	Ширину пока не используем
		#	$Result{"body"} .= "<table width='$Config{note_width}' class='note'>";
			$Result{"body"} .= "<table class='note'>";
			$Result{"body"} .= "<tr><td class='note_title'>".$cl->{note_title}."</td></tr>" if $cl->{note_title};
			$Result{"body"} .= "<tr><td width='100%' class='note_text'>".$note."</td></tr>";
			$Result{"body"} .= "</table>\n";
		}

		# COMMENT
		if ( $Config{"show_comments"} =~ /^y/i) {
			my $comment = make_comment($cl->{"cline"});
			if ($comment) {
				$Result{"body"} .= "<table width='$Config{comment_width}'>".
						"<tr><td width='5'/><td>";
				$Result{"body"} .= "<table class='note' width='100%'>";
				$Result{"body"} .= $comment;
				$Result{"body"} .= "</table>\n";
				$Result{"body"} .= "</td></tr></table>";
			}
		}

		# Вывод очередной команды окончен
		$Result{"body"} .= "</td>\n";
		$Result{"body"} .= "</tr>\n";
	}

	$Result{"body"} .= "</table>\n";

	#$Result{"stat"} = "<hr/>";

	%StatNames = (
		FirstCommand => "Время первой команды журнала",
		LastCommand => "Время последней команды журнала",
		TotalCommands => "Количество командных строк в журнале",
		ErrorsPercentage => "Процент команд с ненулевым кодом завершения, %",
		TotalTime => "Суммарное время работы с терминалом <sup><font size='-2'>*</font></sup>, час",
		CommandsPerTime => "Количество командных строк в единицу времени, команда/мин",
		CommandsFDistribution => "Частота использования команд",
		CommandsFDistribution => "Частота использования команд",
		RareCommands	=> "Частота использования этих команд < 0.5%",
	);
	@StatOrder = (
		FirstCommand,
		LastCommand,
		TotalCommands,
		ErrorsPercentage,
		TotalTime,
		CommandsPerTime,
		CommandsFDistribution,
		RareCommands,
	);

	# Подготовка статистики к выводу
	# Некоторые значения пересчитываются!
	# Дальше их лучше уже не использовать!!!

	my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($Stat{FirstCommand});
	$Stat{FirstCommand} = sprintf "%02i:%02i:%02i %04i-%2i-%2i", $hour, $min, $sec,  $year+1900, $mon+1, $mday;
	($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($Stat{LastCommand});
	$Stat{LastCommand} = sprintf "%02i:%02i:%02i %04i-%2i-%2i", $hour, $min, $sec,  $year+1900, $mon+1, $mday;
	$Stat{ErrorsPercentage} = sprintf "%5.2f", $Stat{ErrorCommands}*100/$Stat{TotalCommands}	
		if $Stat{TotalCommands};
	$Stat{CommandsPerTime} = sprintf "%5.2f", $Stat{TotalCommands}*60/$Stat{TotalTime}
		if $Stat{TotalTime};
	$Stat{TotalTime} = sprintf "%5.2f", $Stat{TotalTime}/60/60;

	my $total_commands=0;
	for $command (keys %CommandsFDistribution){
		$total_commands += $CommandsFDistribution{$command};
	}
	if ($total_commands) {
		for $command (reverse sort {$CommandsFDistribution{$a} <=> $CommandsFDistribution{$b}} keys %CommandsFDistribution){
			my $command_html;
			my $percentage = sprintf "%5.2f",$CommandsFDistribution{$command}*100/$total_commands;
			if ($percentage < 0.5) {
				my $hint = make_comment($command);
				$command_html = "$command";
				$command_html = "<span title='$hint' class='hint'>$command_html</span>" if $hint;
				my $command_html = "<tt>$command_html</tt>";
				$Stat{RareCommands} .= $command_html."<sub><font size='-2'>".$CommandsFDistribution{$command}."</font></sub> , ";
			}
			else {
				my $hint = make_comment($command);
				$command_html = "$command";
				$command_html = "<span title='$hint' class='hint'>$command_html</span>" if $hint;
				my $command_html = "<tt>$command_html</tt>";
				$percentage = sprintf "%5.2f",$percentage;
				$Stat{CommandsFDistribution} .= "<tr><td>".$command_html."</td><td>".$CommandsFDistribution{$command}."</td>".
					"<td>|".("="x int($CommandsFDistribution{$command}*100/$total_commands))."| $percentage%</td></tr>";
			}
		}
		$Stat{CommandsFDistribution} = "<table>".$Stat{CommandsFDistribution}."</table>";
		$Stat{RareCommands} =~ s/, $// if $Stat{RareCommands};
	}

	$Result{"stat"} .= "<h2 id='stat'>Статистика</h2>";
	$Result{"stat"} .= "<table>";
	for my $stat (@StatOrder) {
	$Result{"stat"} .= "<tr valign='top'><td width='300'>".$StatNames{"$stat"}."</td><td>".$Stat{"$stat"}."</td></tr>"
		if $Stat{"$stat"};
	}
	
	$Result{"stat"} .= "</table>";
	$Result{"stat"} .= "<font size='-2'>____<br/>*) Интервалы неактивности длительностью ".($Config{stat_inactivity_interval}/60)." минут и более не учитываются</font></br>";

	#$Result{"help"} .= "<hr/>";
	$Result{"help"} .= "<h2 id='help'>Справка</h2>";
	$Result{"help"} .= "$Html_Help<br/>";
	#$Result{"about"} .= "<hr/>";
	$Result{"about"} .= "<h2 id='about'>О программе</h2>";
	$Result{"about"} .= "$Html_About";
	$Result{"footer"} .= "</body>\n";
	$Result{"footer"} .= "</html>\n";

	$Result{"title"} = "Журнал лабораторных работ";
	$Result{"title"}.= " -- ".$course_student if $course_student;
	if ($course_date) {
		$Result{"title"}.= " -- ".$course_date; 
		$Result{"title"}.= "/".$course_code if $course_code;
	}
	else {
		$Result{"title"}.= " -- ".$course_code if $course_code;
	}

	# Заголовок генерируется позже всего
	# Тогда, когда известно уже, что должно быть написано в 
	# оглавлении
	$Result{"header"} = <<HEADER;
	<html>
	<head>
	<meta content='text/html; charset=utf-8' http-equiv='Content-Type' />
	<link rel='stylesheet' href='$Config{frontend_css}' type='text/css'/>
	<title>$Result{title}</title>
	</head>
	<body>
	<script>
	$Html_JavaScript
	</script>
	<h1>Журнал лабораторных работ</h1>

HEADER
	$Result{"header"} .= "<p>" if $course_student || $course_trainer || $course_name || $course_code || $course_date || $course_center;
	$Result{"header"} .= "Выполнил $course_student<br/>" if $course_student;
	$Result{"header"} .= "Проверил $course_trainer <br/>" if $course_trainer;
	$Result{"header"} .= "Курс " if $course_name || $course_code || $course_date;
	$Result{"header"} .= "$course_name " if $course_name;
	$Result{"header"} .= "($course_code)" if $course_code;
	$Result{"header"} .= ", $course_date<br/>" if $course_date;
	$Result{"header"} .= "Учебный центр $course_center <br/>" if $course_center;
	$Result{"header"} .= "</p>" if $course_student || $course_trainer || $course_name || $course_code || $course_date || $course_center;

	my $toc = collapse_list (\@toc);
	$Result{"header"} .= <<HEADER;
	<ul>
		<li><a href='#log'>Журнал</a></li>
		<ul>$toc</ul>
		<li><a href='#stat'>Статистика</a></li>
		<li><a href='#help'>Справка</a></li>
		<li><a href='#about'>О программе</a></li>
	</ul>

	<h2 id="log">Журнал</h2>
HEADER
	$Result{"header"} .= "<table id='visibility_form' class='visibility_form'><tr><td><form>\n";
	for my $element (keys %Elements_Visibility)
	{
		my @e = split /\s+/, $element;
		my $showhide = join "", map { "ShowHide('$_');" } @e ;
		$Result{"header"} .= "<input type='checkbox' name='$e[0]' onclick=\"$showhide\" checked>".
				$Elements_Visibility{$element}.
				"</input><br>\n";
	}

	$Result{"header"} .= "</form></td></tr></table>\n";

	if ($output_filename eq "-") {
		print $Result{"header"}, $Result{"body"}, $Result{"stat"}, $Result{"help"}, $Result{"about"}, $Result{"footer"};
	}
	else {
		open(OUT, ">", $output_filename)
			or die "Can't open $output_filename for writing\n";
		print OUT $Result{"header"}, $Result{"body"}, $Result{"stat"}, $Result{"help"}, $Result{"about"}, $Result{"footer"};
		close(OUT);
	}
}



sub collapse_list($)
{
	my $res = "";
	for my $elem (@{$_[0]}) {
		if (ref $elem eq "ARRAY") {
			$res .= "<ul>".collapse_list($elem)."</ul>";
		}
		else
		{
			$res .= "<li>".$elem."</li>";
		}
	}
	return $res;
}




sub init_variables
{
$Html_Help = <<HELP;
	Для того чтобы использовать LiLaLo, не нужно знать ничего особенного:
	всё происходит само собой.
	Однако, чтобы ведение и последующее использование журналов
	было как можно более эффективным, желательно иметь в виду следующее:
	<ul>
	<li><p> 
	В журнал автоматически попадают все команды, данные в любом терминале системы.
	</p></li>
	<li><p>
	Для того чтобы убедиться, что журнал на текущем терминале ведётся, 
	и команды записываются, дайте команду w.
	В поле WHAT, соответствующем текущему терминалу, 
	должна быть указана программа script.
	</p></li>
	<li><p>
	Если код завершения команды равен нулю, 
	команда была выполнена без ошибок.
	Команды, код завершения которых отличен от нуля, выделяются цветом.
<table>
<tr class='command'>
<td class='script'>
<pre class='wrong_cline'>
\$ l s-l</pre>
<pre class='wrong_output'>bash: l: command not found
</pre>
</td>
</tr>
</table>
<br/>
	</p></li>
	<li><p>
	Команды, ход выполнения которых был прерван пользователем, выделяются цветом.
<table>
<tr class='command'>
<td class='script'>
<pre class='interrupted_cline'>
\$ find / -name abc</pre>
<pre class='interrupted_output'>find: /home/devi-orig/.gnome2: Keine Berechtigung
find: /home/devi-orig/.gnome2_private: Keine Berechtigung
find: /home/devi-orig/.nautilus/metafiles: Keine Berechtigung
find: /home/devi-orig/.metacity: Keine Berechtigung
find: /home/devi-orig/.inkscape: Keine Berechtigung
^C
</pre>
</td>
</tr>
</table>
<br/>
	</p></li>
	<li><p>
	Команды, выполненные с привилегиями суперпользователя,
	выделяются слева красной чертой.
<table>
<tr class='command'>
<td class='script'>
<pre class='_root_cline'>
# id</pre>
<pre class='_root_output'>
uid=0(root) gid=0(root) Gruppen=0(root)
</pre>
</td>
</tr>
</table>
	<br/>
	</p></li>
	<li><p>
	Изменения, внесённые в текстовый файл с помощью редактора, 
	запоминаются и показываются в журнале в формате ed.
	Строки, начинающиеся символом "&lt;", удалены, а строки,
	начинающиеся символом "&gt;" -- добавлены.
<table>
<tr class='command'>
<td class='script'>
<pre class='cline'>
\$ vi ~/.bashrc</pre>
<table><tr><td width='5'/><td class='diff'><pre>2a3,5
&gt; 	if [ -f /usr/local/etc/bash_completion ]; then
&gt;         . /usr/local/etc/bash_completion
&gt;     	fi
</pre></td></tr></table></td>
</tr>
</table>
	<br/>
	</p></li>
	<li><p>
	Для того чтобы получить краткую справочную информацию о команде, 
	нужно подвести к ней мышь. Во всплывающей подсказке появится краткое
	описание команды.
	</p></li>
	<li><p>
	Время ввода команды, показанное в журнале, соответствует времени 
	<i>начала ввода командной строки</i>, которое равно тому моменту, 
	когда на терминале появилось приглашение интерпретатора
	</p></li>
	<li><p>
	Имя терминала, на котором была введена команда, показано в специальном блоке.
	Этот блок показывается только в том случае, если терминал
	текущей команды отличается от терминала предыдущей.
	</p></li>
	<li><p>
	Вывод не интересующих вас в настоящий момент элементов журнала,
	таких как время, имя терминала и других, можно отключить.
	Для этого нужно воспользоваться <a href='#visibility_form'>формой управления журналом</a>
	вверху страницы.
	</p></li>
	<li><p>
	Небольшие комментарии к командам можно вставлять прямо из командной строки.
	Комментарий вводится прямо в командную строку, после символов #^ или #v.
	Символы ^ и v показывают направление выбора команды, к которой относится комментарий:
	^ - к предыдущей, v - к следующей.
	Например, если в командной строке было введено:
<pre class='cline'>
\$ whoami
</pre>
<pre class='output'>
user
</pre>
<pre class='cline'>
\$ #^ Интересно, кто я?
</pre>
	в журнале это будет выглядеть так:
	
<pre class='cline'>
\$ whoami
</pre>
<pre class='output'>
user
</pre>
<table class='note'><tr><td width='100%' class='note_text'>
<tr> <td> Интересно, кто я?<br/> </td></tr></table> 
	</p></li>
	<li><p>
	Если комментарий содержит несколько строк,
	его можно вставить в журнал следующим образом:
<pre class='cline'>
\$ whoami
</pre>
<pre class='output'>
user
</pre>
<pre class='cline'>
\$ cat > /dev/null #^ Интересно, кто я?
</pre>
<pre class='output'>
Программа whoami выводит имя пользователя, под которым 
мы зарегистрировались в системе.
-
Она не может ответить на вопрос о нашем назначении 
в этом мире.
</pre>
	В журнале это будет выглядеть так:
<table>
<tr class='command'>
<td class='script'>
<pre class='cline'>
\$ whoami</pre>
<pre class='output'>user
</pre>
<table class='note'><tr><td class='note_title'>Интересно, кто я?</td></tr><tr><td width='100%' class='note_text'>
Программа whoami выводит имя пользователя, под которым<br/>
мы зарегистрировались в системе.<br/>
<br/>
Она не может ответить на вопрос о нашем назначении<br/>
в этом мире.<br/>
</td></tr></table>
</td>
</tr>
</table>
	Для разделения нескольких абзацев между собой
	используйте символ "-", один в строке.
	<br/>
</p></li>
	<li><p>
	Комментарии, не относящиеся непосредственно ни к какой из команд, 
	добавляются точно таким же способом, только вместо симолов #^ или #v 
	нужно использовать символы #=
	</p></li>
</ul>
HELP

$Html_About = <<ABOUT;
	<p>
	LiLaLo (L3) расшифровывается как Live Lab Log.<br/>
	Программа разработана для повышения эффективности обучения Unix/Linux-системам.<br/>
	(c) Игорь Чубин, 2004-2005<br/>
	</p>
ABOUT
$Html_About.='$Id$ </p>';

$Html_JavaScript = <<JS;
	function getElementsByClassName(Class_Name)
	{
		var Result=new Array();
		var All_Elements=document.all || document.getElementsByTagName('*');
		for (i=0; i<All_Elements.length; i++)
			if (All_Elements[i].className==Class_Name)
		Result.push(All_Elements[i]);
		return Result;
	}
	function ShowHide (name)
	{
		elements=getElementsByClassName(name);
		for(i=0; i<elements.length; i++)
			if (elements[i].style.display == "none")
				elements[i].style.display = "";
			else
				elements[i].style.display = "none";
			//if (elements[i].style.visibility == "hidden")
			//	elements[i].style.visibility = "visible";
			//else
			//	elements[i].style.visibility = "hidden";
	}
	function filter_by_output(text)
	{
		
		var jjj=0;
		
		elements=getElementsByClassName('command');
		for(i=0; i<elements.length; i++) {
			subelems = elements[i].getElementsByTagName('pre');
			for(j=0; j<subelems.length; j++) {
				if (subelems[j].className = 'output') {
					var str = new String(subelems[j].nodeValue);
					if (jjj != 1) { 
						alert(str);
						jjj=1;
					}
					if (str.indexOf(text) >0) 
						subelems[j].style.display = "none";
					else
						subelems[j].style.display = "";

				}
					
			}
		}		

	}
JS

%Search_Machines = (
		"google" => 	{ 	"query" => 	"http://www.google.com/search?q=" ,
					"icon" 	=> 	"$Config{frontend_google_ico}" },
		"freebsd" => 	{ 	"query" => 	"http://www.freebsd.org/cgi/man.cgi?query=",
					"icon"	=>	"$Config{frontend_freebsd_ico}" },
		"linux"  => 	{ 	"query" => 	"http://man.he.net/?topic=",
					"icon"	=>	"$Config{frontend_linux_ico}"},
		"opennet"  => 	{ 	"query" => 	"http://www.opennet.ru/search.shtml?words=",
					"icon"	=>	"$Config{frontend_opennet_ico}"},
		"local" => 	{ 	"query" => 	"http://www.freebsd.org/cgi/man.cgi?query=",
					"icon"	=>	"$Config{frontend_local_ico}" },

	);

%Elements_Visibility = (
		"note"		=>	"замечания",
		"diff"		=>	"редактор",
		"time"		=>	"время",
		"ttychange" 	=>	"терминал",
		"wrong_output wrong_cline wrong_root_output wrong_root_cline" 
				=>	"команды с ошибками",
		"interrupted_output interrupted_cline interrupted_root_output interrupted_root_cline" 
				=>	"прерванные команды",
		"tab_completion_output tab_completion_cline"	
				=> 	"продолжение с помощью tab"
);

@Day_Name      = qw/ Воскресенье Понедельник Вторник Среда Четверг Пятница Суббота /;
@Month_Name    = qw/ Январь Февраль Март Апрель Май Июнь Июль Август Сентябрь Октябрь Ноябрь Декабрь /;
@Of_Month_Name = qw/ Января Февраля Марта Апреля Мая Июня Июля Августа Сентября Октября Ноября Декабря /;
}