#!/usr/bin/perl -w use lib '.'; use l3config; our @Command_Lines; # vvv Инициализация переменных выполняется процедурой init_variables our @Day_Name; our @Month_Name; our @Of_Month_Name; our %Search_Machines; our %Elements_Visibility; # ^^^ sub search_buy; sub make_comment; sub load_command_lines_from_xml; sub print_command_lines; sub init_variables; sub main; main(); sub main { $| = 1; init_variables(); init_config(); load_command_lines_from_xml($Config{"backend_datafile"}); print_command_lines($Config{"output"}); } 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 make_comment { my $commands = $_[0]; my $files = $_[1]; chomp $commands; chomp $files; return if (!$commands && !$files); my $comment=""; # Commands for my $command (split /\s+/,$commands) { $command =~ s/'//g; my $description=""; eval { $description=`/home/devi/bin/mywi-client '$command'`; } ; $description = join ("<br>\n", grep(/\([18]\)/, split(/\n/, $description))); $description =~ s/.*?-//; next if $description =~ /^\s*$/; my $query=$command." ".$Config{"keywords"}; $query =~ s/\ /+/g; my $search= search_by("opennet",$query). search_by("local",$command). search_by("google",$query); $comment .= "<tr><td class='note_title'>$command</td>". "<td class='note_search'>$search</td>". "</tr><tr><td width='100%' colspan='2' class='note_text'>". "$description</td></tr><tr/>"; } # Files for my $file (split /\s+/,$files) { $file =~ s@.*/@@; $file =~ s/'//g; next if $file =~ /^\s*$/; next if $file =~ /^-/; my $description=`mywi '$file'`; $description = join ("<br>\n", grep(/\(5\)/, split(/\n/, $description))); next if $description =~ /^\s*$/; my $query=$file." ".$Config{"files_keywords"}; $query =~ s/\ /+/g; my $search= search_by("opennet",$query). search_by("local",$file). search_by("google",$query); $comment .= "<tr><td class='note_title'>$file</td>". "<td class='note_search'>$search</td>". "</tr><tr><td width='100%' colspan='2' class='note_text'>". "$description</td></tr><tr/>"; } return $comment; } =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; } } =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 =""; # Хранит оглавление по дням $Result{"body"} = "<table width='100%'>\n"; my $cl; my $last_tty=""; my $last_day=""; my $in_range=0; for my $cl (@Command_Lines) { 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"}}; 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}); # Добавляем спереди 0 для удобочитаемости $min = "0".$min if $min =~ /^.$/; $hour = "0".$hour if $hour =~ /^.$/; $sec = "0".$sec if $sec =~ /^.$/; $class=$cl->{"out_class"}; $class =~ s/output$//; $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>"; $toc .= "<li><a href='#day$day'>".$Day_Name[$wday]."</a></li>\n"; $last_day=$day; } # CONSOLE CHANGE if ( $last_tty ne $cl->{"tty"}) { $Result{"body"} .= "<td colspan='6'><table><tr><td class='ttychange' width='140' align='center'>".$cl->{"tty"}."</td><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->{"cline"}; $cline =~ s/\n//; $Result{"body"} .= $cl->{"prompt"}.$cl->{"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>"; } # COMMENT if ( $Config{"show_comments"} =~ /^y/i) { my $comment = make_comment(join(" ",@new_commands), join (" ",@new_files)); 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/>"; $Result{"stat"} .= "<h2 id='stat'>Статистика</h2>"; $Result{"stat"} .= "Статистическая информация о журнале<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{"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'/> </head> <body> <script> $Html_JavaScript </script> <h2>Журнал лабораторных работ</h2> <p> Выполнил $course_student<br/> Проверил $course_trainer <br/> Курс $course_name ($course_code), $course_date<br/> Учебный центр $course_center <br/> </p> <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 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 init_variables { $Html_Help = <<HELP; Справка по использованию журнала 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/ Января Февраля Марта Апреля Мая Июня Июля Августа Сентября Октября Ноября Декабря /; }