devi@0: #!/usr/bin/perl -w devi@0: devi@0: # devi@20: # (c) Igor Chubin, imchubin@mail.ru, 2004-2005 devi@0: # devi@0: devi@0: use strict; devi@0: use Getopt::Long; devi@0: use Term::VT102; devi@0: use Text::Iconv; devi@0: use Data::Dumper; devi@0: devi@0: our $Config_File = "labmaker.conf"; devi@0: our %Config = ( devi@0: "skip_empty" => "yes", devi@0: "skip_interrupted" => "no", devi@0: "skip_wrong" => "no", devi@0: "editors" => ["vi", "pico", "ee", "vim"], devi@0: "pagers" => ["more", "less", "zmore", "zless", "info", devi@0: "man", "mc", "trafshow", "screen", "cfdisk", devi@0: "trafshow-bsd", "yes", "lynx", "links", "centericq" devi@0: ], devi@0: "terminal" => ["mc"], devi@0: "suppress_editors" => "yes", devi@0: "suppress_pagers" => "yes", devi@0: "suppress_terminal" => "yes", devi@0: devi@0: "terminal_width" => 100, devi@0: "terminal_height" => 100, devi@0: "verbose" => "yes", devi@0: devi@0: "head_lines" => 5, devi@0: "tail_lines" => 5, devi@0: "skip_text" => "...", devi@0: "show_time" => "yes", devi@0: "show_diffs" => "yes", devi@0: "show_comments" => "yes", devi@0: devi@0: "input" => "/root/.labmaker", devi@0: "diffs" => "", devi@0: "input_mask" => "*.script", devi@0: "encoding" => "utf-8", devi@0: devi@0: "output" => "/var/www/lm/reportINDEX.html", devi@0: #"output" => "report.xml", devi@0: "output_mask" => "INDEX", devi@0: "output_format" => "html", devi@0: devi@0: "signature" => "#lm:", devi@0: "from" => "", devi@0: "to" => "", devi@0: "lab" => "", devi@0: "keywords" => "linux command", devi@0: "files_keywords" => "linux file", devi@0: devi@0: comment_width => "300", devi@0: time_width => "60", devi@0: devi@6: "course-name" => "", devi@6: "course-code" => "", devi@6: "course-date" => "", devi@6: "course-center" => "", devi@6: "course-trainer" => "", devi@6: "course-student" => "", devi@6: devi@0: ); devi@0: devi@0: our @Command_Lines; devi@0: our @Command_Lines_Index; devi@0: our @Diffs; devi@0: devi@0: our %Commands_Stat; # Statistics about commands usage devi@0: our %Files_Stat; # Statistics about commands usage devi@0: devi@0: our %Search_Machines = ( devi@0: "google" => { "query" => "http://www.google.com/search?q=" , devi@0: "icon" => "google.ico" }, devi@0: "freebsd" => { "query" => "http://www.freebsd.org/cgi/man.cgi?query=", devi@0: "icon" => "freebsd.ico" }, devi@0: "linux" => { "query" => "http://man.he.net/?topic=", devi@0: "icon" => "linux.ico"}, devi@0: "opennet" => { "query" => "http://www.opennet.ru/search.shtml?words=", devi@0: "icon" => "opennet.ico"}, devi@0: "local" => { "query" => "http://www.freebsd.org/cgi/man.cgi?query=", devi@0: "icon" => "freebsd.ico" }, devi@0: devi@0: ); devi@0: devi@0: our %Elements_Visibility = ( devi@0: "note" => "замечания", devi@0: "diff" => "редактор", devi@0: "time" => "время", devi@0: "ttychange" => "терминал", devi@0: "wrong_output wrong_cline wrong_root_output wrong_root_cline" devi@0: => "команды с ошибками", devi@0: "interrupted_output interrupted_cline interrupted_root_output interrupted_root_cline" devi@0: => "прерванные команды", devi@0: "tab_completion_output tab_completion_cline" devi@0: => "продолжение с помощью tab" devi@0: ); devi@0: devi@6: sub init_variables; devi@6: our $Html_Help; devi@6: our $Html_About; devi@6: devi@0: devi@0: sub load_diff_files devi@0: { devi@0: my @pathes = @_; devi@0: devi@0: for my $path (@pathes) { devi@0: my $template = "*.diff"; devi@0: my @files = <$path/$template>; devi@0: my $i=0; devi@0: for my $file (@files) { devi@0: my %diff; devi@0: devi@0: $diff{"path"}=$path; devi@0: $diff{"uid"}="SET THIS"; devi@0: devi@0: # Сейчас UID определяется из названия каталога devi@0: # откуда берутся diff-файлы devi@0: # Это неправильно devi@0: # devi@0: # ВАРИАНТ: devi@0: # К файлам жураналам должны прилагаться ситемны файлы, devi@0: # мз которых и будет определяться соответствие devi@0: # имён пользователей их uid'ам devi@0: # devi@0: $diff{"uid"} = 0 if $path =~ m@/root/@; devi@0: devi@0: $diff{"bind_to"}=""; devi@0: $diff{"time_range"}=-1; devi@0: devi@0: next if not $file=~m@/(D?[0-9][0-9]?[0-9]?)[^/]*?([0-9]*):([0-9]*):?([0-9]*)@; devi@0: $diff{"day"}=$1 || ""; devi@0: $diff{"hour"}=$2; devi@0: $diff{"min"}=$3; devi@0: $diff{"sec"}=$4 || 0; devi@0: devi@0: $diff{"index"}=$i; devi@0: devi@0: print "diff loaded: $diff{day} $diff{hour}:$diff{min}:$diff{sec}\n"; devi@0: devi@0: local $/; devi@0: open (F, "$file") devi@0: or return "Can't open file $file ($_[0]) for reading"; devi@0: my $text = <F>; devi@0: if ($Config{"encoding"} && $Config{"encoding"} !~ /^utf-8$/i) { devi@0: my $converter = Text::Iconv->new($Config{"encoding"}, "utf-8"); devi@0: $text = $converter->convert($text); devi@0: } devi@0: close(F); devi@0: $diff{"text"}=$text; devi@0: #print "$file loaded ($diff{day})\n"; devi@0: devi@0: push @Diffs, \%diff; devi@0: $i++; devi@0: } devi@0: } devi@0: } devi@0: devi@0: devi@0: sub bind_diff devi@0: { devi@0: # my $path = shift; devi@0: # my $pid = shift; devi@0: # my $day = shift; devi@0: # my $lab = shift; devi@0: devi@0: print "Trying to bind diff...\n"; devi@0: devi@0: my $cl = shift; devi@0: my $hour = $cl->{"hour"}; devi@0: my $min = $cl->{"min"}; devi@0: my $sec = $cl->{"sec"}; devi@0: devi@0: my $min_dt = 10000; devi@0: devi@0: for my $diff (@Diffs) { devi@0: # Check here date, time and user devi@0: next if ($diff->{"day"} && $cl->{"day"} && ($cl->{"day"} ne $diff->{"day"})); devi@0: #next if (!$diff->{"uid"} && $cl->{"euid"} != $diff->{"uid"}); devi@0: devi@0: my $dt=($diff->{"hour"}-$hour)*3600 +($diff->{"min"}-$min)*60 + ($diff->{"sec"}-$sec); devi@0: if ($dt >0 && $dt < $min_dt && ($diff->{"time_range"} <0 || $dt < $diff->{"time_range"})) { devi@0: print "Approppriate diff found: dt=$dt\n"; devi@0: if ($diff->{"bind_to"}) { devi@0: undef $diff->{"bind_to"}->{"diff"}; devi@0: }; devi@0: $diff->{"time_range"}=$dt; devi@0: $diff->{"bind_to"}=$cl; devi@0: devi@0: $cl->{"diff"} = $diff->{"index"}; devi@0: $min_dt = $dt; devi@0: } devi@0: devi@0: } devi@0: } devi@0: devi@0: devi@0: sub extract_from_cline devi@0: # Разобрать командную строку $_[1] и возвратить хэш, содержащий devi@0: # номер первого появление команды в строке: devi@0: # команда => первая позиция devi@0: { devi@0: my $what = $_[0]; devi@0: my $cline = $_[1]; devi@0: my @lists = split /\;/, $cline; devi@0: devi@0: devi@0: my @commands = (); devi@0: for my $list (@lists) { devi@0: push @commands, split /\|/, $list; devi@0: } devi@0: devi@0: my %commands; devi@0: my %files; devi@0: my $i=0; devi@0: for my $command (@commands) { devi@0: $command =~ /\s*(\S+)\s*(.*)/; devi@0: if ($1 && $1 eq "sudo" ) { devi@0: $commands{"$1"}=$i++; devi@0: $command =~ s/\s*sudo\s+//; devi@0: } devi@0: $command =~ /\s*(\S+)\s*(.*)/; devi@0: if ($1 && !defined $commands{"$1"}) { devi@0: $commands{"$1"}=$i++; devi@0: }; devi@0: if ($2) { devi@0: my $args = $2; devi@0: my @args = split (/\s+/, $args); devi@0: for my $a (@args) { devi@0: $files{"$a"}=$i++ devi@0: if !defined $files{"$a"}; devi@0: }; devi@0: devi@0: devi@0: } devi@0: } devi@0: devi@0: if ($what eq "commands") { devi@0: return %commands; devi@0: } else { devi@0: return %files; devi@0: } devi@0: devi@0: } devi@0: devi@0: sub load_command_lines devi@0: { devi@0: my $lab_scripts_path = $_[0]; devi@0: my $lab_scripts_mask = $_[1]; devi@0: devi@0: my $cline_re_base = qq' devi@0: (?:\\^?([0-9]*C?)) # exitcode devi@0: (?:_([0-9]+)_)? # uid devi@0: (?:_([0-9]+)_) # pid devi@0: (...?) # day devi@19: (.?.?) # lab devi@0: \\s # space separator devi@0: ([0-9][0-9]):([0-9][0-9]):([0-9][0-9]) # time devi@0: .\\[50D.\\[K # killing symbols devi@0: (.*?([\$\#]\\s?)) # prompt devi@0: (.*) # command line devi@0: '; devi@0: #my $cline_re = qr/$cline_re_base(?:$cline_re_base|$)/x; devi@0: #my $cline_re = qr/(?:$cline_re_base)*$cline_re_base$/x; devi@0: my $cline_re = qr/$cline_re_base/sx; devi@0: my $cline_re1 = qr/$cline_re_base\x0D/sx; devi@0: my $cline_re2 = qr/$cline_re_base$/sx; devi@0: devi@0: my $vt = Term::VT102->new ( 'cols' => $Config{"terminal_width"}, devi@0: 'rows' => $Config{"terminal_height"}); devi@0: my $cline_vt = Term::VT102->new ('cols' => $Config{"terminal_width"}, devi@0: 'rows' => $Config{"terminal_height"}); devi@0: devi@0: my $converter = Text::Iconv->new($Config{"encoding"}, "utf-8") devi@0: if ($Config{"encoding"} && $Config{"encoding"} !~ /^utf-8$/i); devi@0: devi@0: print "Loading lm-scripts...\n" if $Config{"verbose"} =~ /y/; devi@0: devi@0: my @lab_scripts = <$lab_scripts_path/$lab_scripts_mask>; devi@0: my $file; devi@0: my $files_number = $#lab_scripts; devi@0: my $ii = 0; devi@0: my $skip_info; devi@0: devi@0: my $commandlines_loaded =0; devi@0: my $commandlines_processed =0; devi@0: devi@0: for $file (@lab_scripts){ devi@0: #printf "\t%i %3.2f\n", $ii, (100*$ii++/$files_number) if $Config{"verbose"} =~ /y/; devi@0: devi@0: open (FILE, "$file"); devi@0: binmode FILE; devi@0: $file =~ m@.*/(.*?)-.*@; devi@0: devi@0: my $tty = $1; devi@0: my $first_pass = 1; devi@0: my %cl; devi@0: my $last_output_length=0; devi@0: while (<FILE>) { devi@0: $commandlines_processed++; devi@0: # time devi@0: devi@0: if (/[0-9][0-9]:[0-9][0-9]:[0-9][0-9].\[[0-9][0-9]D.\[K/ && m/$cline_re/) { devi@0: s/.*\x0d(?!\x0a)//; devi@0: # print "!!!",$_,"!!!\n"; devi@0: # next; devi@0: # while (m/$cline_re1/gs) { devi@0: # } devi@0: m/$cline_re2/gs; devi@0: devi@0: $commandlines_loaded++; devi@0: $last_output_length=0; devi@0: devi@0: # Previous command devi@0: my %last_cl = %cl; devi@0: my $err = $1 || ""; devi@0: devi@21: devi@21: =cut devi@21: devi@21: ТАБЛИЦА КОМАНД devi@21: devi@21: uid devi@21: Идентификатор пользователя devi@21: devi@21: tty devi@21: Идентификатор терминала, на котором была вызвана команда devi@21: devi@21: pid devi@21: PID-процесса командного интерпретатора, devi@21: в котором была вызвана команда devi@21: devi@21: lab devi@21: лабораторная работа, к которой относится команда. devi@21: Идентификатор текущей лабораторной работы devi@21: хранится в файле ~/.labmaker/lab devi@21: devi@21: pwd (!) devi@21: текущий каталог, из которого была вызвана команда devi@21: devi@21: day devi@21: время вызова, день devi@21: В действительности здесь хранится не время вызова команды, devi@21: а с момента появления приглашения командного интерпретатора devi@21: для ввода команды devi@21: devi@21: devi@21: hour devi@21: время вызова, час devi@21: devi@21: min devi@21: время вызова, минута devi@21: devi@21: sec devi@21: время вызова, секунда devi@21: devi@21: time (!) devi@21: время вызова команды в Unix-формате. devi@21: Предпочтительнее использовать этот формат чем hour:min:sec, devi@21: использовавшийся в Labmaker devi@21: devi@21: fullprompt devi@21: Приглашение командной строки devi@21: devi@21: prompt devi@21: Сокращённое приглашение командной строки devi@21: devi@21: cline devi@21: Командная строка devi@21: devi@21: output devi@21: Результат выполнения команды devi@21: devi@21: diff devi@21: Указатель на ассоциированный с командой diff devi@21: devi@21: note (!) devi@21: Текстовый комментарий к команде. devi@21: Может генерироваться из самого лога с помощью команд devi@21: #^ Комментарий devi@21: #v Комментарий devi@21: в том случае, если для комментирования достаточно одной строки, devi@21: или с помощью команд devi@21: cat > /dev/null #^ Заголовок devi@21: Текст devi@21: ^D devi@21: в том случае, если комментарий развёрнутый. devi@21: В последнем случае комментарий может содержать devi@21: заголовок, абзацы и несложное форматирование. devi@21: devi@21: Символ ^ или v после знака комментария # обозначает, devi@21: к какой команде относится комментарий: devi@21: к предыдущей (^) или последующей (v) devi@21: devi@21: err devi@21: Код завершения командной строки devi@21: devi@21: histnum (!) devi@21: Номер команды в истории командного интерпретатора devi@21: devi@21: status (!) devi@21: Является ли данная команда вызванной (r), запомненной (s) devi@21: или это подсказка completion (c). devi@21: devi@21: Команды, которые были вызваны и обработаны интерпретатором devi@21: имеют состояние "r". К таким командам относится большинство devi@21: команд вводимых в интерпретатор. devi@21: devi@21: Если команда набрана, но вызывать её по какой-либо причине devi@21: не хочется (например, команда может быть не полной, вредоносной devi@21: или просто бессмысленной в текущих условиях), devi@21: её можно сбросить с помощью комбинации клавиш Ctrl-C devi@21: (не путайте с прерыванием работающей команды! здесь она даже devi@21: не запускается!). devi@21: В таком случае она не выполняется, но попадает в журнал devi@21: со статусом "s". devi@21: devi@21: Если команда появилась в журнале благодаря автопроолжению devi@21: -- когда было показано несколько вариантов -- devi@21: она имеет статус "c". devi@21: devi@21: euid devi@21: Идентификатор пользователя от имени которого будет devi@21: выполняться команда. devi@21: Может отличаться от реального uid в том случае, devi@21: если вызывается с помощью sudo devi@21: devi@21: devi@21: version (!) devi@21: Версия lilalo-prompt использовавшаяся при записи devi@21: команды. devi@21: devi@21: 0 - версия использовавшая в labmaker. devi@21: Отсутствует информация о текущем каталоге и номере в истории. devi@21: Информация о версии также не указана в приглашении. devi@21: devi@21: devi@21: 1 - версия использующаяся в lilalo devi@21: devi@21: raw_file (*) devi@21: Имя файла, в котором находится бинарное представление журнала. devi@21: Может содержать ключевое слово HERE, devi@21: обозначающее что бинарное представление хранится devi@21: непосредственно в базе данных в атрибуте raw_data devi@21: devi@21: raw_start (*) devi@21: Начало блока командной строки в файле бинарного представления devi@21: devi@21: raw_end (*) devi@21: Конец блока командной строки в файле бинарного представления devi@21: devi@21: raw_cline (*) devi@21: Необработанная командная строка в бинарном виде devi@21: devi@21: raw_data (*) devi@21: Бинарное представление команды и результатов её выполнения devi@21: devi@21: devi@21: devi@21: devi@21: ТАБЛИЦА SESSION devi@21: devi@21: Информация о сеансах devi@21: devi@21: devi@21: devi@21: devi@21: =cut devi@21: devi@0: # Parse new command devi@0: $cl{"uid"} = $2; devi@0: $cl{"euid"} = $cl{"uid"}; # Если в команде обнаружится sudo, euid поменяем на 0 devi@0: $cl{"pid"} = $3; devi@0: $cl{"day"} = $4; devi@0: $cl{"lab"} = $5; devi@0: $cl{"hour"} = $6; devi@0: $cl{"min"} = $7; devi@0: $cl{"sec"} = $8; devi@0: $cl{"fullprompt"} = $9; devi@0: $cl{"prompt"} = $10; devi@0: $cl{"raw_cline"} = $11; devi@0: devi@0: $cl{"err"} = 0; devi@0: $cl{"output"} = ""; devi@0: $cl{"tty"} = $tty; devi@0: devi@0: $cline_vt->process($cl{"raw_cline"}."\n"); devi@0: $cl{"cline"} = $cline_vt->row_plaintext (1); devi@0: $cl{"cline"} =~ s/\s*$//; devi@0: $cline_vt->reset(); devi@0: devi@0: my %commands = extract_from_cline("commands", $cl{"cline"}); devi@0: $cl{"euid"}=0 if defined $commands{"sudo"}; devi@0: my @comms = sort { $commands{$a} cmp $commands{$b} } keys %commands; devi@0: $cl{"last_command"} = $comms[$#comms] || ""; devi@0: devi@0: if ( devi@0: $Config{"suppress_editors"} =~ /^y/i devi@0: && grep ($_ eq $cl{"last_command"}, @{$Config{"editors"}}) || devi@0: $Config{"suppress_pagers"} =~ /^y/i devi@0: && grep ($_ eq $cl{"last_command"}, @{$Config{"pagers"}}) || devi@0: $Config{"suppress_terminal"}=~ /^y/i devi@0: && grep ($_ eq $cl{"last_command"}, @{$Config{"terminal"}}) devi@0: ) { devi@0: $cl{"suppress_output"} = "1"; devi@0: } devi@0: else { devi@0: $cl{"suppress_output"} = "0"; devi@0: devi@0: } devi@0: $skip_info = 0; devi@0: devi@0: devi@0: print " ",$cl{"last_command"}; devi@0: devi@0: # Processing previous command line devi@0: if ($first_pass) { devi@0: $first_pass = 0; devi@0: next; devi@0: } devi@0: devi@0: # Error code devi@0: $last_cl{"err"}=$err; devi@0: $last_cl{"err"}=130 if $err eq "^C"; devi@0: devi@0: if (grep ($_ eq $last_cl{"last_command"}, @{$Config{"editors"}})) { devi@0: bind_diff(\%last_cl); devi@0: } devi@0: devi@0: # Output devi@0: if (!$last_cl{"suppress_output"} || $last_cl{"err"}) { devi@0: for (my $i=0; $i<$Config{"terminal_height"}; $i++) { devi@0: my $line= $vt->row_plaintext($i); devi@0: next if !defined ($line) || $line =~ /^\s*$/; devi@0: $line =~ s/\s*$//; devi@0: $last_cl{"output"} .= $line."\n"; devi@0: } devi@0: } devi@0: else { devi@0: $last_cl{"output"}= ""; devi@0: } devi@0: devi@0: $vt->reset(); devi@0: devi@0: devi@0: # Classifying the command line devi@0: devi@0: devi@0: # Save devi@0: if (!$Config{"lab"} || $cl{"lab"} eq $Config{"lab"}) { devi@0: # Changing encoding devi@0: for (keys %last_cl) { devi@0: $last_cl{$_} = $converter->convert($last_cl{$_}) devi@0: if ($Config{"encoding"} && devi@0: $Config{"encoding"} !~ /^utf-8$/i); devi@0: } devi@0: push @Command_Lines, \%last_cl; devi@0: } devi@0: next; devi@0: } devi@0: $last_output_length+=length($_); devi@0: #if (!$cl{"suppress_output"} || $last_output_length < 5000) { devi@18: if ($last_output_length < 50000) { devi@0: #print "(",length($_),")" if (length($_) > 2000) ; devi@0: $vt->process("$_"."\n") devi@0: } devi@0: else devi@0: { devi@0: if (!$skip_info) { devi@0: print "($cl{last_command})"; devi@0: $skip_info = 1; devi@0: } devi@0: } devi@0: } devi@0: close(FILE); devi@0: devi@0: } devi@0: if ($Config{"verbose"} =~ /y/) { devi@0: print "...finished." ; devi@0: print "Lines loaded: $commandlines_processed\n"; devi@0: print "Command lines: $commandlines_loaded\n"; devi@0: } devi@0: } devi@0: devi@0: sub search_by devi@0: { devi@0: my $sm = shift; devi@0: my $topic = shift; devi@0: $topic =~ s/ /+/; devi@0: devi@0: return "<a href='". $Search_Machines{$sm}->{"query"}."$topic'><img width='16' height='16' src='". devi@0: $Search_Machines{$sm}->{"icon"}."' border='0'/></a>"; devi@0: } devi@0: devi@0: sub make_comment devi@0: { devi@0: my $commands = $_[0]; devi@0: my $files = $_[1]; devi@0: chomp $commands; devi@0: chomp $files; devi@0: return if (!$commands && !$files); devi@0: devi@0: my $comment=""; devi@0: devi@0: # Commands devi@0: for my $command (split /\s+/,$commands) { devi@0: $command =~ s/'//g; devi@0: my $description=""; devi@0: eval { $description=`mywi-client '$command'`; } ; devi@0: $description = join ("<br>\n", grep(/\([18]\)/, split(/\n/, $description))); devi@0: $description =~ s/.*?-//; devi@0: next if $description =~ /^\s*$/; devi@0: devi@0: my $query=$command." ".$Config{"keywords"}; devi@0: $query =~ s/\ /+/g; devi@0: my $search= search_by("opennet",$query). devi@0: search_by("local",$command). devi@0: search_by("google",$query); devi@0: devi@0: $comment .= "<tr><td class='note_title'>$command</td>". devi@0: "<td class='note_search'>$search</td>". devi@0: "</tr><tr><td width='100%' colspan='2' class='note_text'>". devi@0: "$description</td></tr><tr/>"; devi@0: } devi@0: devi@0: # Files devi@0: for my $file (split /\s+/,$files) { devi@0: $file =~ s@.*/@@; devi@0: $file =~ s/'//g; devi@0: next if $file =~ /^\s*$/; devi@0: next if $file =~ /^-/; devi@0: devi@0: my $description=`mywi '$file'`; devi@0: $description = join ("<br>\n", grep(/\(5\)/, split(/\n/, $description))); devi@0: next if $description =~ /^\s*$/; devi@0: devi@0: my $query=$file." ".$Config{"files_keywords"}; devi@0: $query =~ s/\ /+/g; devi@0: my $search= search_by("opennet",$query). devi@0: search_by("local",$file). devi@0: search_by("google",$query); devi@0: devi@0: $comment .= "<tr><td class='note_title'>$file</td>". devi@0: "<td class='note_search'>$search</td>". devi@0: "</tr><tr><td width='100%' colspan='2' class='note_text'>". devi@0: "$description</td></tr><tr/>"; devi@0: } devi@0: devi@0: devi@0: return $comment; devi@0: } devi@0: devi@0: sub printq devi@0: { devi@0: my $TO = shift; devi@0: my $text = join "", @_; devi@0: $text =~ s/&/&/g; devi@0: $text =~ s/</</g; devi@0: $text =~ s/>/>/g; devi@0: print $TO $text; devi@0: } devi@0: devi@0: devi@0: sub sort_command_lines devi@0: { devi@0: print "Sorting command lines...\n" if $Config{"verbose"} =~ /y/; devi@0: devi@0: # Sort Command_Lines devi@0: # Write Command_Lines to Command_Lines_Index devi@0: devi@0: my @index; devi@0: for (my $i=0;$i<=$#Command_Lines;$i++) { devi@0: $index[$i]=$i; devi@0: } devi@0: devi@0: @Command_Lines_Index = sort { devi@0: $Command_Lines[$index[$a]]->{"day"} cmp $Command_Lines[$index[$b]]->{"day"} || devi@0: $Command_Lines[$index[$a]]->{"hour"} <=> $Command_Lines[$index[$b]]->{"hour"} || devi@0: $Command_Lines[$index[$a]]->{"min"} <=> $Command_Lines[$index[$b]]->{"min"} || devi@0: $Command_Lines[$index[$a]]->{"sec"} <=> $Command_Lines[$index[$b]]->{"sec"} devi@0: } @index; devi@0: devi@0: print "...finished\n" if $Config{"verbose"} =~ /y/; devi@0: devi@0: } devi@0: devi@0: sub process_command_lines devi@0: { devi@0: my $lab_scripts_path = $_[0]; devi@0: devi@0: for my $i (@Command_Lines_Index) { devi@0: devi@0: my $cl = \$Command_Lines[$i]; devi@0: @{${$cl}->{"new_commands"}} =(); devi@0: @{${$cl}->{"new_files"}} =(); devi@0: $$cl->{"class"} = ""; devi@0: devi@0: if ($$cl->{"err"}) { devi@0: $$cl->{"class"}="wrong"; devi@0: $$cl->{"class"}="interrupted" devi@0: if ($$cl->{"err"} eq 130); devi@0: } devi@0: if (!$$cl->{"euid"}) { devi@0: $$cl->{"class"}.="_root"; devi@0: } devi@0: devi@0: #tab# my @tab_words=split /\s+/, $$cl->{"output"}; devi@0: #tab# my $last_word= $$cl->{"cline"} =~ /(\S*)$/; devi@0: #tab# $last_word =~ s@.*/@@; devi@0: #tab# my $this_is_tab=1; devi@0: #tab# devi@0: #tab# if ($last_word && @tab_words >2) { devi@0: #tab# for my $tab_words (@tab_words) { devi@0: #tab# if ($tab_words !~ /^$last_word/) { devi@0: #tab# $this_is_tab=0; devi@0: #tab# last; devi@0: #tab# } devi@0: #tab# } devi@0: #tab# } devi@0: #tab# $$cl->{"class"}="tab" if $this_is_tab; devi@0: devi@0: devi@0: if ( !$$cl->{"err"}) { devi@0: # Command does not contain mistakes devi@0: devi@0: my %commands = extract_from_cline("commands", ${$cl}->{"cline"}); devi@0: my %files = extract_from_cline("files", ${$cl}->{"cline"}); devi@0: devi@0: # Searching for new commands only devi@0: for my $command (keys %commands) { devi@0: if (!defined $Commands_Stat{$command}) { devi@0: push @{$$cl->{new_commands}}, $command; devi@0: } devi@0: $Commands_Stat{$command}++; devi@0: } devi@0: devi@0: for my $file (keys %files) { devi@0: if (!defined $Files_Stat{$file}) { devi@0: push @{$$cl->{new_files}}, $file; devi@0: } devi@0: $Files_Stat{$file}++; devi@0: } devi@0: } devi@0: } devi@0: devi@0: } devi@0: devi@6: devi@6: =cut devi@6: Вывести результат обработки журнала. devi@6: =cut devi@6: devi@6: devi@0: sub print_command_lines devi@0: { devi@0: my $output_filename=$_[0]; devi@0: my $format = $Config{"output_format"}; devi@6: devi@6: my $course_name = $Config{"course-name"}; devi@6: my $course_code = $Config{"course-code"}; devi@6: my $course_date = $Config{"course-date"}; devi@6: my $course_center = $Config{"course-center"}; devi@6: my $course_trainer = $Config{"course-trainer"}; devi@6: my $course_student = $Config{"course-student"}; devi@0: devi@0: open(OUT, ">", $output_filename) devi@0: or die "Can't open $output_filename for writing\n"; devi@0: devi@0: devi@0: devi@0: if ($format eq "html") { devi@0: # vvvv HTML Header devi@0: print OUT <<HEADER; devi@0: <html> devi@0: <head> devi@0: <meta content='text/html; charset=utf-8' http-equiv='Content-Type' /> devi@0: <link rel='stylesheet' href='labmaker.css' type='text/css'/> devi@0: </head> devi@0: <body> devi@0: <script> devi@0: function getElementsByClassName(Class_Name) devi@0: { devi@0: var Result=new Array(); devi@0: var All_Elements=document.all || document.getElementsByTagName('*'); devi@0: for (i=0; i<All_Elements.length; i++) devi@0: if (All_Elements[i].className==Class_Name) devi@0: Result.push(All_Elements[i]); devi@0: return Result; devi@0: } devi@0: function ShowHide (name) devi@0: { devi@0: elements=getElementsByClassName(name); devi@0: for(i=0; i<elements.length; i++) devi@0: if (elements[i].style.display == "none") devi@0: elements[i].style.display = ""; devi@0: else devi@0: elements[i].style.display = "none"; devi@0: //if (elements[i].style.visibility == "hidden") devi@0: // elements[i].style.visibility = "visible"; devi@0: //else devi@0: // elements[i].style.visibility = "hidden"; devi@0: } devi@0: function filter_by_output(text) devi@0: { devi@0: devi@0: var jjj=0; devi@0: devi@0: elements=getElementsByClassName('command'); devi@0: for(i=0; i<elements.length; i++) { devi@0: subelems = elements[i].getElementsByTagName('pre'); devi@0: for(j=0; j<subelems.length; j++) { devi@0: if (subelems[j].className = 'output') { devi@0: var str = new String(subelems[j].nodeValue); devi@0: if (jjj != 1) { devi@0: alert(str); devi@0: jjj=1; devi@0: } devi@0: if (str.indexOf(text) >0) devi@0: subelems[j].style.display = "none"; devi@0: else devi@0: subelems[j].style.display = ""; devi@0: devi@0: } devi@0: devi@0: } devi@0: } devi@0: devi@0: } devi@0: </script> devi@6: <h2>Журнал лабораторных работ</h2> devi@6: devi@6: <p> devi@6: Выполнил $course_student<br/> devi@6: Проверил $course_trainer <br/> devi@6: Курс $course_name ($course_code), devi@6: $course_date<br/> devi@6: Учебный центр $course_center <br/> devi@6: </p> devi@6: devi@6: <ul> devi@6: <li><a href='#log'>Журнал</a></li> devi@6: <li><a href='#stat'>Статистика</a></li> devi@6: <li><a href='#help'>Справка</a></li> devi@6: <li><a href='#about'>О программе</a></li> devi@6: </ul> devi@6: devi@12: <h3 id="log">Журнал</h3> devi@0: HEADER devi@0: print OUT "<table class='visibility_form'><tr><td><form>\n"; devi@0: for my $element (keys %Elements_Visibility) devi@0: { devi@0: my @e = split /\s+/, $element; devi@0: my $showhide = join "", map { "ShowHide('$_');" } @e ; devi@0: print OUT "<input type='checkbox' name='$e[0]' onclick=\"$showhide\" checked>", devi@0: $Elements_Visibility{$element}, devi@0: "</input><br>\n"; devi@0: } devi@0: #print OUT "<input type='text' size='10' name=\"by_command\"/>". devi@0: #"<input type='button' value='фильтр по командам' onclick=\"filter_by_command()\"/> <br>\n"; devi@0: #print OUT "<input type='text' size='10' name=\"by_output\"/>". devi@0: #"<input type='button' value='фильтр по результату' ". devi@0: #"onclick=\"filter_by_output(this.form.by_output.value)\"/> <br>\n"; devi@0: devi@0: print OUT "</form></td></tr></table>\n"; devi@0: print OUT "<table width='100%'>\n"; devi@0: # ^^^^ HTML Header devi@0: } devi@0: else { devi@0: # XML Header devi@0: print OUT "<script>\n" devi@0: } devi@0: devi@0: my $cl; devi@0: my $last_tty=""; devi@13: my $last_day=""; devi@0: my $in_range=0; devi@0: for my $i (@Command_Lines_Index) { devi@0: devi@13: devi@0: $cl = $Command_Lines[$i]; devi@0: devi@0: if ($Config{"from"} && $cl->{"cline"} =~ /$Config{"signature"}\s*$Config{"from"}/) { devi@0: $in_range=1; devi@0: next; devi@0: } devi@0: if ($Config{"to"} && $cl->{"cline"} =~ /$Config{"signature"}\s*$Config{"to"}/) { devi@0: $in_range=0; devi@0: next; devi@0: } devi@0: next if ($Config{"from"} && $Config{"to"} && !$in_range) devi@0: || devi@0: ($Config{"skip_empty"} =~ /^y/i && $cl->{"cline"} =~ /^\s*$/ ) devi@0: || devi@0: ($Config{"skip_wrong"} =~ /^y/i && $cl->{"err"} != 0) devi@0: || devi@0: ($Config{"skip_interrupted"} =~ /^y/i && $cl->{"err"} == 130); devi@0: devi@0: my @new_commands=@{$cl->{"new_commands"}}; devi@0: my @new_files=@{$cl->{"new_files"}}; devi@0: devi@0: my $cl_class="cline"; devi@0: my $out_class="output"; devi@0: if ($cl->{"class"}) { devi@0: $cl_class = $cl->{"class"}."_".$cl_class; devi@0: $out_class = $cl->{"class"}."_".$out_class; devi@0: } devi@0: devi@0: my $output=""; devi@0: if ($Config{"head_lines"} || $Config{"tail_lines"}) { devi@0: # Partialy output devi@0: my @lines = split '\n', $cl->{"output"}; devi@0: # head devi@0: my $mark=1; devi@0: for (my $i=0; $i<= $#lines && $i < $Config{"head_lines"}; $i++) { devi@0: $output .= $lines[$i]."\n"; devi@0: } devi@0: # tail devi@0: my $start=$#lines-$Config{"tail_lines"}+1; devi@0: if ($start < 0) { devi@0: $start=0; devi@0: $mark=0; devi@0: } devi@0: if ($start < $Config{"head_lines"}) { devi@0: $start=$Config{"head_lines"}; devi@0: $mark=0; devi@0: } devi@0: $output .= $Config{"skip_text"}."\n" if $mark; devi@0: for (my $i=$start; $i<= $#lines; $i++) { devi@0: $output .= $lines[$i]."\n"; devi@0: } devi@0: } devi@0: else { devi@0: # Full output devi@0: $output .= $cl->{"output"}; devi@0: } devi@0: $output .= "^C\n" if ($cl->{"err"} eq "130"); devi@0: devi@0: devi@0: # Printing out devi@0: devi@0: # <command> devi@0: print OUT $format eq "html" devi@0: ? "<tr class='command'>\n" devi@0: : "\n<action time='$cl->{hour}:$cl->{min}:$cl->{sec}' tty='$cl->{tty}'>\n"; devi@0: devi@0: devi@0: if ($format eq "html") { devi@13: devi@13: # DAY CHANGE devi@13: if ( $last_day ne $cl->{"day"}) { devi@13: print OUT "<td colspan='6'><p></p><h3>День ",$cl->{"day"},"</h4></td></tr><tr>"; devi@13: $last_day=$cl->{"day"}; devi@13: } devi@13: devi@0: # CONSOLE CHANGE devi@0: if ( $last_tty ne $cl->{"tty"}) { devi@0: print OUT "<td colspan='6'><table><tr><td class='ttychange' width='140' align='center'>",$cl->{"tty"},"</td><td/></tr></table></td></tr><tr>"; devi@0: $last_tty=$cl->{"tty"}; devi@0: } devi@0: devi@0: # TIME devi@0: if ($Config{"show_time"} =~ /^y/i) { devi@0: print OUT "<td valign='top' class='time' width='$Config{time_width}'><pre>", devi@0: $cl->{"hour"}, ":", $cl->{"min"}, ":", $cl->{"sec"}, devi@0: "</td>"; devi@0: } else { devi@0: print OUT "<td width='0'/>" devi@0: } devi@0: } devi@0: devi@0: # COMMAND devi@0: devi@0: devi@0: if ($format eq "html") { devi@0: print OUT "<td class='script'>\n"; devi@0: print OUT "<pre class='$cl_class'>\n"; devi@0: my $cline = $cl->{"cline"}; devi@0: $cline =~ s/\n//; devi@0: printq(\*OUT,$cl->{"prompt"},$cl->{"cline"}); devi@0: # printq(\*OUT,"(sudo ".$cl->{"last_command"}.")\n") if !$cl->{"euid"}; devi@0: print OUT "</pre>\n"; devi@0: } devi@0: else { devi@0: print OUT "<line class='$cl_class'>\n"; devi@0: print OUT "<prompt>"; devi@0: printq(\*OUT,$cl->{"prompt"}); devi@0: print OUT "</prompt>"; devi@0: print OUT "<command>"; devi@0: printq(\*OUT,$cl->{"cline"}); devi@0: print OUT "</command>"; devi@0: print OUT "\n</line>\n"; devi@0: } devi@0: devi@0: my $last_command = $cl->{"last_command"}; devi@0: if (!( devi@0: $Config{"suppress_editors"} =~ /^y/i && grep ($_ eq $last_command, @{$Config{"editors"}}) || devi@0: $Config{"suppress_pagers"} =~ /^y/i && grep ($_ eq $last_command, @{$Config{"pagers"}}) || devi@0: $Config{"suppress_terminal"}=~ /^y/i && grep ($_ eq $last_command, @{$Config{"terminal"}}) devi@0: )) { devi@0: devi@0: if ($format eq "html") { devi@0: print OUT "<pre class='$out_class'>"; devi@0: printq(\*OUT,$output); devi@0: print OUT "</pre>\n"; devi@0: } devi@0: else { devi@0: print OUT "<output class='$out_class'>\n"; devi@0: printq(\*OUT,$output); devi@0: print OUT "</output>\n"; devi@0: } devi@0: } devi@0: devi@0: # DIFF devi@0: if ( $Config{"show_diffs"} =~ /^y/i && $cl->{"diff"}) { devi@0: if ($format eq "html") { devi@0: #print Dumper(%{$cl->{"diff"}}); devi@0: print OUT "<table><tr><td width='5'/><td class='diff'><pre>"; devi@0: printq(\*OUT,${$Diffs[$cl->{"diff"}]}{"text"}); devi@0: print OUT "</pre></td></tr></table>"; devi@0: } devi@0: else { devi@0: print OUT "<diff>\n"; devi@0: printq(\*OUT,${$Diffs[$cl->{"diff"}]}{"text"}); devi@0: print OUT "</diff>\n"; devi@0: } devi@0: } devi@0: devi@0: # COMMENT devi@0: if ( $Config{"show_comments"} =~ /^y/i) { devi@0: my $comment = make_comment(join(" ",@new_commands), join (" ",@new_files)); devi@0: if ($comment) { devi@0: if ($format eq "html") { devi@0: print OUT "<table width='$Config{comment_width}'>". devi@0: "<tr><td width='5'/><td>"; devi@0: print OUT "<table class='note' width='100%'>"; devi@0: print OUT $comment; devi@0: print OUT "</table>\n"; devi@0: print OUT "</td></tr></table>"; devi@0: } devi@0: # else { devi@0: # print OUT "<comment>"; devi@0: # printq(\*OUT,$comment); devi@0: # print OUT "</comment>"; devi@0: # } devi@0: } devi@0: } devi@0: devi@0: if ($format eq "html") { devi@0: print OUT "</td>\n"; devi@0: print OUT "</tr>\n"; devi@0: } devi@0: else { devi@0: print OUT "</action>\n"; devi@0: } devi@0: devi@0: } devi@0: if ($format eq "html") { devi@0: print OUT "</table>\n"; devi@6: devi@6: print OUT "<hr/>"; devi@6: print OUT "<h3 id='stat'>Статистика</h4>"; devi@6: print OUT "Статистическая информация о журнале<br/>"; devi@6: print OUT "<hr/>"; devi@6: print OUT "<h3 id='help'>Справка</h4>"; devi@6: print OUT "$Html_Help<br/>"; devi@6: print OUT "<hr/>"; devi@6: print OUT "<h3 a='about'>О программе</h4>"; devi@6: print OUT "$Html_About"; devi@0: print OUT "</body>\n"; devi@0: print OUT "</html>\n"; devi@0: } devi@0: else { devi@0: print OUT "</script>\n"; devi@0: } devi@0: close(OUT); devi@0: } devi@0: devi@0: sub read_config_file devi@0: { devi@0: my $config = $_[0]; devi@0: my $filename = $_[1]; devi@0: open(CONFIG, "$filename") devi@0: or return; devi@0: while (<CONFIG>) { devi@0: s/#.*//; devi@0: next if /^\s*$/; devi@0: my ($var, $val) = split /\s*=\s*/, $_, 2; devi@0: $var =~ s/\s*//; devi@0: $config->{$var} = $val; devi@0: } devi@0: close(CONFIG); devi@0: } devi@0: devi@18: devi@18: sub print_command_lines2 devi@18: { devi@18: my $output_filename=$_[0]; devi@18: open(OUT, ">", $output_filename) devi@18: or die "Can't open $output_filename for writing\n"; devi@18: devi@18: devi@18: print OUT <<OUT; devi@18: <log> devi@18: OUT devi@18: devi@18: my $cl; devi@18: for my $i (@Command_Lines_Index) { devi@18: devi@18: devi@18: $cl = $Command_Lines[$i]; devi@18: devi@18: devi@18: # Printing out devi@18: print OUT <<OUT; devi@18: <command> devi@18: <day>$cl->{day}</day> devi@18: <hour>$cl->{hour}</hour> devi@18: <min>$cl->{min}</min> devi@18: <sec>$cl->{sec}</sec> devi@18: <tty>$cl->{tty}</tty> devi@18: <uid>$cl->{uid}</uid> devi@18: <euid>$cl->{euid}</euid> devi@18: <prompt>$cl->{prompt}</prompt> devi@18: <cline>$cl->{cline}</cline> devi@18: <status>$cl->{err}</cline> devi@18: <output> devi@18: $cl->{output}</output> devi@18: </command> devi@18: OUT devi@18: } devi@18: devi@18: for my $diff (@Diffs) { devi@18: devi@18: print OUT <<OUT; devi@18: <diff> devi@18: <path>$diff->{path}</path> devi@18: <uid>$diff->{uid}</uid> devi@18: <day>$diff->{day}</day> devi@18: <hour>$diff->{hour}</hour> devi@18: <min>$diff->{min}</min> devi@18: <sec>$diff->{sec}</sec> devi@18: <text> devi@18: $diff->{text}</text> devi@18: </diff> devi@18: OUT devi@18: } devi@18: devi@18: print OUT <<OUT; devi@18: </log> devi@18: OUT devi@18: } devi@18: devi@18: devi@0: $| = 1; devi@0: devi@0: my %file_config; devi@0: my %argv_config; devi@6: init_variables; devi@0: read_config_file(\%file_config, $Config_File); devi@0: GetOptions(\%argv_config, map "$_=s", keys %Config); devi@0: %Config = (%Config, %file_config, %argv_config); devi@0: devi@0: my $i=0; devi@0: devi@0: for my $lab_log (split (/\s+/, $Config{"diffs"} || $Config{"input"})) devi@0: { devi@0: load_diff_files($lab_log); devi@0: } devi@0: devi@0: for my $lab_log (split /\s+/, $Config{"input"}) devi@0: { devi@0: my $tofile=$Config{"output"}; devi@0: $tofile =~ s/$Config{"output_mask"}/$i/; devi@0: #load_diff_files($lab_log); devi@0: load_command_lines($lab_log, $Config{"input_mask"}); devi@0: sort_command_lines; devi@0: process_command_lines($lab_log); devi@0: print_command_lines($tofile); devi@0: $i++; devi@0: } devi@0: devi@6: