lilalo

annotate l3-frontend @ 25:ba4d6515b8fd

Выполнен шаг (3) в плане (N05) по построению распределённой системы lilalo.


Агент l3-агент в реальном времени анализирует скрипты в указанном ему
каталоге и по мере обнаружения новых завершённых команд записывает их
в кэш-файл.

Данные о том, докуда разобран каждый скрипт-файл сохраняются во временном
файле, для того чтобы при перезапуске агента он мог продолжить разбор
с того места, где он был остановлен в прошлый раз, а не копировал
данные в кэш-файл повторно.

Агент запускается для каждого пользователя системы.
Если агент обнаружил свою копию работающую от имени того же пользователя,
он автоматически завершается.
Поиск копии агента выполняется так:
просматривается pid-файл агента - если его нет, считается, что и агент не запущен
(Внимание! Не удаляйте pid-файл!! Работа нескольких агентов от имени одного
пользователя может быть некорректной!)
Если он есть, выполняется проверка, действительно ли процесс с таким идентификатором
это l3-агент текущего пользователя. Если нет, pid-файл удаляется, и агент запускается.

Нормальное завершение агента, работающего в режиме демона, выполняется
с помощью сигнала TERM. При завершении агент автоматически стирает свой pid-файл.



Добавлены атрибуты команды, хранящие информацию о участке бинарного файла скрипта,
соответствующей команды:
raw_start - начало блока команды
raw_output_start - начало вывода команды
raw_end - окончание вывода
raw_file - имя бинарного файла

Файлы:
(могут меняться с помощью конфигурационных параметров)

~/.labmaker/.cache.dat
~/.labmaker/cache.xml
~/.labmaker/l3-agent.pid

Конфигурационные параметры:
cache_stat Имя файла с информацией о текущей позиции разбора
в каждом файле

mode Режим, в котором работает агент.
Допустимые значения:
daemon - в режиме непрерывного опроса каталога
Программа не завершается после окончания анализа,
а ждёт появления новых данных
normal - однократный анализ каталога.
Программа завершается после окончания анализа данных

daemon_sleep_interval Интервал через который агент просматривает каталог скриптов
в поисках новых данных

detach Нужно ли выполнять отключение от терминала при работе в режиме демона?
(строго говоря, если процесс не отключился от терминала,
то и в режиме демона он работать не может. Здесь имеется в виду
режим непрерывного опроса каталога)

agent_pidfile Путь к файлу, который будет хранить идентификатор процесса агента.

l3-agent Имя, под которым будет известен процесс l3-agent

Незначительные исправления:
* убрана отладочная информация о new_commands и new_files из frontend'а
author devi
date Thu Nov 03 17:49:56 2005 +0200 (2005-11-03)
parents 6d93c5f1d0e5
children 098664cf339c
rev   line source
devi@23 1 #!/usr/bin/perl -w
devi@23 2
devi@23 3 use lib '.';
devi@23 4 use l3config;
devi@23 5
devi@23 6 our @Command_Lines;
devi@23 7
devi@23 8 # vvv Инициализация переменных выполняется процедурой init_variables
devi@23 9 our @Day_Name;
devi@23 10 our @Month_Name;
devi@23 11 our @Of_Month_Name;
devi@23 12 our %Search_Machines;
devi@23 13 our %Elements_Visibility;
devi@23 14 # ^^^
devi@23 15
devi@23 16 sub search_buy;
devi@23 17 sub make_comment;
devi@23 18 sub load_command_lines_from_xml;
devi@23 19 sub print_command_lines;
devi@23 20 sub init_variables;
devi@23 21 sub main;
devi@23 22
devi@23 23 main();
devi@23 24
devi@23 25 sub main
devi@23 26 {
devi@23 27 $| = 1;
devi@23 28
devi@23 29 init_variables();
devi@23 30 init_config();
devi@23 31
devi@23 32 load_command_lines_from_xml($Config{"cache"});
devi@23 33 print_command_lines($Config{"output"});
devi@23 34 }
devi@23 35
devi@23 36
devi@23 37 sub search_by
devi@23 38 {
devi@23 39 my $sm = shift;
devi@23 40 my $topic = shift;
devi@23 41 $topic =~ s/ /+/;
devi@23 42
devi@23 43 return "<a href='". $Search_Machines{$sm}->{"query"}."$topic'><img width='16' height='16' src='".
devi@23 44 $Search_Machines{$sm}->{"icon"}."' border='0'/></a>";
devi@23 45 }
devi@23 46
devi@23 47 sub make_comment
devi@23 48 {
devi@23 49 my $commands = $_[0];
devi@23 50 my $files = $_[1];
devi@23 51 chomp $commands;
devi@23 52 chomp $files;
devi@23 53 return if (!$commands && !$files);
devi@23 54
devi@23 55 my $comment="";
devi@23 56
devi@23 57 # Commands
devi@23 58 for my $command (split /\s+/,$commands) {
devi@23 59 $command =~ s/'//g;
devi@23 60 my $description="";
devi@23 61 eval { $description=`mywi-client '$command'`; } ;
devi@23 62 $description = join ("<br>\n", grep(/\([18]\)/, split(/\n/, $description)));
devi@23 63 $description =~ s/.*?-//;
devi@23 64 next if $description =~ /^\s*$/;
devi@23 65
devi@23 66 my $query=$command." ".$Config{"keywords"};
devi@23 67 $query =~ s/\ /+/g;
devi@23 68 my $search= search_by("opennet",$query).
devi@23 69 search_by("local",$command).
devi@23 70 search_by("google",$query);
devi@23 71
devi@23 72 $comment .= "<tr><td class='note_title'>$command</td>".
devi@23 73 "<td class='note_search'>$search</td>".
devi@23 74 "</tr><tr><td width='100%' colspan='2' class='note_text'>".
devi@23 75 "$description</td></tr><tr/>";
devi@23 76 }
devi@23 77
devi@23 78 # Files
devi@23 79 for my $file (split /\s+/,$files) {
devi@23 80 $file =~ s@.*/@@;
devi@23 81 $file =~ s/'//g;
devi@23 82 next if $file =~ /^\s*$/;
devi@23 83 next if $file =~ /^-/;
devi@23 84
devi@23 85 my $description=`mywi '$file'`;
devi@23 86 $description = join ("<br>\n", grep(/\(5\)/, split(/\n/, $description)));
devi@23 87 next if $description =~ /^\s*$/;
devi@23 88
devi@23 89 my $query=$file." ".$Config{"files_keywords"};
devi@23 90 $query =~ s/\ /+/g;
devi@23 91 my $search= search_by("opennet",$query).
devi@23 92 search_by("local",$file).
devi@23 93 search_by("google",$query);
devi@23 94
devi@23 95 $comment .= "<tr><td class='note_title'>$file</td>".
devi@23 96 "<td class='note_search'>$search</td>".
devi@23 97 "</tr><tr><td width='100%' colspan='2' class='note_text'>".
devi@23 98 "$description</td></tr><tr/>";
devi@23 99 }
devi@23 100
devi@23 101
devi@23 102 return $comment;
devi@23 103 }
devi@23 104
devi@23 105 =cut
devi@23 106 Процедура load_command_lines_from_xml выполняет загрузку разобранного lab-скрипта
devi@23 107 из XML-документа в переменную @Command_Lines
devi@23 108
devi@23 109 Предупреждение!
devi@23 110 Процедура не в состоянии обрабатывать XML-документ любой структуры.
devi@23 111 В действительности файл cache из которого загружаются данные
devi@23 112 просто напоминает XML с виду.
devi@23 113 =cut
devi@23 114 sub load_command_lines_from_xml
devi@23 115 {
devi@23 116 my $datafile = $_[0];
devi@23 117
devi@23 118 open (CLASS, $datafile)
devi@23 119 or die "Can't open file of the class ",$datafile,"\n";
devi@23 120 local $/;
devi@23 121 $data = <CLASS>;
devi@23 122 close(CLASS);
devi@23 123
devi@23 124 for $command ($data =~ m@<command>(.*?)</command>@sg) {
devi@23 125 my %cl;
devi@23 126 while ($command =~ m@<([^>]*?)>(.*?)</\1>@sg) {
devi@23 127 $cl{$1} = $2;
devi@23 128 }
devi@23 129 push @Command_Lines, \%cl;
devi@23 130 }
devi@23 131 }
devi@23 132
devi@23 133 =cut
devi@23 134 Процедура print_command_lines выводит HTML-представление
devi@23 135 разобранного lab-скрипта.
devi@23 136
devi@23 137 Разобранный lab-скрипт должен находиться в массиве @Command_Lines
devi@23 138 =cut
devi@23 139
devi@23 140 sub print_command_lines
devi@23 141 {
devi@23 142 my $output_filename=$_[0];
devi@23 143
devi@23 144 my $course_name = $Config{"course-name"};
devi@23 145 my $course_code = $Config{"course-code"};
devi@23 146 my $course_date = $Config{"course-date"};
devi@23 147 my $course_center = $Config{"course-center"};
devi@23 148 my $course_trainer = $Config{"course-trainer"};
devi@23 149 my $course_student = $Config{"course-student"};
devi@23 150
devi@23 151
devi@23 152 # Результат выполнения процедуры равен
devi@23 153 # join("", @Result{header,body,stat,help,about,footer})
devi@23 154 my %Result;
devi@23 155 my $toc =""; # Хранит оглавление по дням
devi@23 156
devi@23 157 $Result{"body"} = "<table width='100%'>\n";
devi@23 158
devi@23 159 my $cl;
devi@23 160 my $last_tty="";
devi@23 161 my $last_day="";
devi@23 162 my $in_range=0;
devi@23 163
devi@23 164 for my $cl (@Command_Lines) {
devi@23 165
devi@23 166 if ($Config{"from"} && $cl->{"cline"} =~ /$Config{"signature"}\s*$Config{"from"}/) {
devi@23 167 $in_range=1;
devi@23 168 next;
devi@23 169 }
devi@23 170 if ($Config{"to"} && $cl->{"cline"} =~ /$Config{"signature"}\s*$Config{"to"}/) {
devi@23 171 $in_range=0;
devi@23 172 next;
devi@23 173 }
devi@23 174 next if ($Config{"from"} && $Config{"to"} && !$in_range)
devi@23 175 ||
devi@23 176 ($Config{"skip_empty"} =~ /^y/i && $cl->{"cline"} =~ /^\s*$/ )
devi@23 177 ||
devi@23 178 ($Config{"skip_wrong"} =~ /^y/i && $cl->{"err"} != 0)
devi@23 179 ||
devi@23 180 ($Config{"skip_interrupted"} =~ /^y/i && $cl->{"err"} == 130);
devi@23 181
devi@23 182 #my @new_commands=@{$cl->{"new_commands"}};
devi@23 183 #my @new_files=@{$cl->{"new_files"}};
devi@23 184
devi@23 185 my $cl_class="cline";
devi@23 186 my $out_class="output";
devi@23 187 if ($cl->{"class"}) {
devi@23 188 $cl_class = $cl->{"class"}."_".$cl_class;
devi@23 189 $out_class = $cl->{"class"}."_".$out_class;
devi@23 190 }
devi@23 191
devi@23 192 my @new_commands;
devi@23 193 my @new_files;
devi@23 194 @new_commands = split (/\s+/, $cl->{"new_commands"}) if defined $cl->{"new_commands"};
devi@23 195 @new_files = split (/\s+/, $cl->{"new_files"}) if defined $cl->{"new_files"};
devi@23 196
devi@23 197 my $output="";
devi@23 198 if ($Config{"head_lines"} || $Config{"tail_lines"}) {
devi@23 199 # Partialy output
devi@23 200 my @lines = split '\n', $cl->{"output"};
devi@23 201 # head
devi@23 202 my $mark=1;
devi@23 203 for (my $i=0; $i<= $#lines && $i < $Config{"head_lines"}; $i++) {
devi@23 204 $output .= $lines[$i]."\n";
devi@23 205 }
devi@23 206 # tail
devi@23 207 my $start=$#lines-$Config{"tail_lines"}+1;
devi@23 208 if ($start < 0) {
devi@23 209 $start=0;
devi@23 210 $mark=0;
devi@23 211 }
devi@23 212 if ($start < $Config{"head_lines"}) {
devi@23 213 $start=$Config{"head_lines"};
devi@23 214 $mark=0;
devi@23 215 }
devi@23 216 $output .= $Config{"skip_text"}."\n" if $mark;
devi@23 217 for (my $i=$start; $i<= $#lines; $i++) {
devi@23 218 $output .= $lines[$i]."\n";
devi@23 219 }
devi@23 220 }
devi@23 221 else {
devi@23 222 # Full output
devi@23 223 $output .= $cl->{"output"};
devi@23 224 }
devi@23 225 #$output .= "^C\n" if ($cl->{"err"} eq "130");
devi@23 226
devi@23 227 #
devi@23 228 ##
devi@23 229 ## Начинается собственно вывод
devi@23 230 ##
devi@23 231 #
devi@23 232
devi@23 233 # <command>
devi@23 234
devi@23 235 my ($sec,$min,$hour,$day,$mon,$year,$wday,$yday,$isdst) = localtime($cl->{time});
devi@23 236 # Добавляем спереди 0 для удобочитаемости
devi@23 237 $min = "0".$min if $min =~ /^.$/;
devi@23 238 $hour = "0".$hour if $hour =~ /^.$/;
devi@23 239 $sec = "0".$sec if $sec =~ /^.$/;
devi@23 240
devi@23 241 $class=$cl->{"out_class"};
devi@23 242 $class =~ s/output$//;
devi@23 243
devi@23 244
devi@23 245 $Result{"body"} .= "<tr class='command'>\n";
devi@23 246
devi@23 247
devi@23 248 # DAY CHANGE
devi@23 249 if ( $last_day ne $day) {
devi@23 250 #$Result{"body"} .= "<td colspan='6'><p></p><h3>День ",$day,"</h4></td></tr><tr>";
devi@23 251 $Result{"body"} .= "<td colspan='6'><p></p><h3 id='day$day'>".$Day_Name[$wday]."</h4></td></tr><tr>";
devi@23 252 $toc .= "<li><a href='#day$day'>".$Day_Name[$wday]."</a></li>\n";
devi@23 253 $last_day=$day;
devi@23 254 }
devi@23 255
devi@23 256 # CONSOLE CHANGE
devi@23 257 if ( $last_tty ne $cl->{"tty"}) {
devi@23 258 $Result{"body"} .= "<td colspan='6'><table><tr><td class='ttychange' width='140' align='center'>".$cl->{"tty"}."</td><td/></tr></table></td></tr><tr>";
devi@23 259 $last_tty=$cl->{"tty"};
devi@23 260 }
devi@23 261
devi@23 262 # TIME
devi@23 263 if ($Config{"show_time"} =~ /^y/i) {
devi@23 264 $Result{"body"} .= "<td valign='top' class='time' width='$Config{time_width}'><pre>".
devi@23 265 $hour. ":". $min. ":". $sec.
devi@23 266 "</td>";
devi@23 267 } else {
devi@23 268 $Result{"body"} .= "<td width='0'/>"
devi@23 269 }
devi@23 270
devi@23 271 # COMMAND
devi@23 272 $Result{"body"} .= "<td class='script'>\n";
devi@23 273 $Result{"body"} .= "<pre class='${class}cline'>\n";
devi@23 274 my $cline = $cl->{"cline"};
devi@23 275 $cline =~ s/\n//;
devi@23 276 $Result{"body"} .= $cl->{"prompt"}.$cl->{"cline"};
devi@23 277 $Result{"body"} .= "</pre>\n";
devi@23 278
devi@23 279 my $last_command = $cl->{"last_command"};
devi@23 280 if (!(
devi@23 281 $Config{"suppress_editors"} =~ /^y/i && grep ($_ eq $last_command, @{$Config{"editors"}}) ||
devi@23 282 $Config{"suppress_pagers"} =~ /^y/i && grep ($_ eq $last_command, @{$Config{"pagers"}}) ||
devi@23 283 $Config{"suppress_terminal"}=~ /^y/i && grep ($_ eq $last_command, @{$Config{"terminal"}})
devi@23 284 )) {
devi@23 285
devi@23 286 $Result{"body"} .= "<pre class='".$cl->{out_class}."'>";
devi@23 287 $Result{"body"} .= $output;
devi@23 288 $Result{"body"} .= "</pre>\n";
devi@23 289 }
devi@23 290
devi@23 291 # DIFF
devi@23 292 if ( $Config{"show_diffs"} =~ /^y/i && $cl->{"diff"}) {
devi@23 293 $Result{"body"} .= "<table><tr><td width='5'/><td class='diff'><pre>";
devi@23 294 $Result{"body"} .= $cl->{"diff"};
devi@23 295 $Result{"body"} .= "</pre></td></tr></table>";
devi@23 296 }
devi@23 297
devi@23 298 # COMMENT
devi@23 299 if ( $Config{"show_comments"} =~ /^y/i) {
devi@23 300 my $comment = make_comment(join(" ",@new_commands), join (" ",@new_files));
devi@23 301 if ($comment) {
devi@23 302 $Result{"body"} .= "<table width='$Config{comment_width}'>".
devi@23 303 "<tr><td width='5'/><td>";
devi@23 304 $Result{"body"} .= "<table class='note' width='100%'>";
devi@23 305 $Result{"body"} .= $comment;
devi@23 306 $Result{"body"} .= "</table>\n";
devi@23 307 $Result{"body"} .= "</td></tr></table>";
devi@23 308 }
devi@23 309 }
devi@23 310
devi@23 311 # Вывод очередной команды окончен
devi@23 312 $Result{"body"} .= "</td>\n";
devi@23 313 $Result{"body"} .= "</tr>\n";
devi@23 314 }
devi@23 315
devi@23 316 $Result{"body"} .= "</table>\n";
devi@23 317
devi@23 318 $Result{"stat"} = "<hr/>";
devi@23 319 $Result{"stat"} .= "<h2 id='stat'>Статистика</h2>";
devi@23 320 $Result{"stat"} .= "Статистическая информация о журнале<br/>";
devi@23 321 $Result{"help"} .= "<hr/>";
devi@23 322 $Result{"help"} .= "<h2 id='help'>Справка</h2>";
devi@23 323 $Result{"help"} .= "$Html_Help<br/>";
devi@23 324 $Result{"about"} .= "<hr/>";
devi@23 325 $Result{"about"} .= "<h2 id='about'>О программе</h2>";
devi@23 326 $Result{"about"} .= "$Html_About";
devi@23 327 $Result{"footer"} .= "</body>\n";
devi@23 328 $Result{"footer"} .= "</html>\n";
devi@23 329
devi@23 330 # Заголовок генерируется позже всего
devi@23 331 # Тогда, когда известно уже, что должно быть написано в
devi@23 332 # оглавлении
devi@23 333 $Result{"header"} = <<HEADER;
devi@23 334 <html>
devi@23 335 <head>
devi@23 336 <meta content='text/html; charset=utf-8' http-equiv='Content-Type' />
devi@23 337 <link rel='stylesheet' href='labmaker.css' type='text/css'/>
devi@23 338 </head>
devi@23 339 <body>
devi@23 340 <script>
devi@23 341 $Html_JavaScript
devi@23 342 </script>
devi@23 343 <h2>Журнал лабораторных работ</h2>
devi@23 344
devi@23 345 <p>
devi@23 346 Выполнил $course_student<br/>
devi@23 347 Проверил $course_trainer <br/>
devi@23 348 Курс $course_name ($course_code),
devi@23 349 $course_date<br/>
devi@23 350 Учебный центр $course_center <br/>
devi@23 351 </p>
devi@23 352
devi@23 353 <ul>
devi@23 354 <li><a href='#log'>Журнал</a></li>
devi@23 355 <ul>$toc</ul>
devi@23 356 <li><a href='#stat'>Статистика</a></li>
devi@23 357 <li><a href='#help'>Справка</a></li>
devi@23 358 <li><a href='#about'>О программе</a></li>
devi@23 359 </ul>
devi@23 360
devi@23 361 <h2 id="log">Журнал</h2>
devi@23 362 HEADER
devi@23 363 $Result{"header"} .= "<table class='visibility_form'><tr><td><form>\n";
devi@23 364 for my $element (keys %Elements_Visibility)
devi@23 365 {
devi@23 366 my @e = split /\s+/, $element;
devi@23 367 my $showhide = join "", map { "ShowHide('$_');" } @e ;
devi@23 368 $Result{"header"} .= "<input type='checkbox' name='$e[0]' onclick=\"$showhide\" checked>".
devi@23 369 $Elements_Visibility{$element}.
devi@23 370 "</input><br>\n";
devi@23 371 }
devi@23 372
devi@23 373 $Result{"header"} .= "</form></td></tr></table>\n";
devi@23 374
devi@23 375 open(OUT, ">", $output_filename)
devi@23 376 or die "Can't open $output_filename for writing\n";
devi@23 377 print OUT $Result{"header"}, $Result{"body"}, $Result{"stat"}, $Result{"help"}, $Result{"about"}, $Result{"footer"};
devi@23 378 close(OUT);
devi@23 379 }
devi@23 380
devi@23 381
devi@23 382
devi@23 383
devi@23 384
devi@23 385
devi@23 386 sub init_variables
devi@23 387 {
devi@23 388 $Html_Help = <<HELP;
devi@23 389 Справка по использованию журнала
devi@23 390 HELP
devi@23 391
devi@23 392 $Html_About = <<ABOUT;
devi@23 393 <p>
devi@23 394 LiLaLo (L3) расшифровывается как Live Lab Log.<br/>
devi@23 395 Программа разработана для повышения эффективности обучения<br/>
devi@23 396 Unix/Linux-системам.<br/>
devi@23 397 (c) Игорь Чубин, 2004-2005<br/>
devi@23 398 </p>
devi@23 399 ABOUT
devi@23 400 $Html_About.='$Id$ </p>';
devi@23 401
devi@23 402 $Html_JavaScript = <<JS;
devi@23 403 function getElementsByClassName(Class_Name)
devi@23 404 {
devi@23 405 var Result=new Array();
devi@23 406 var All_Elements=document.all || document.getElementsByTagName('*');
devi@23 407 for (i=0; i<All_Elements.length; i++)
devi@23 408 if (All_Elements[i].className==Class_Name)
devi@23 409 Result.push(All_Elements[i]);
devi@23 410 return Result;
devi@23 411 }
devi@23 412 function ShowHide (name)
devi@23 413 {
devi@23 414 elements=getElementsByClassName(name);
devi@23 415 for(i=0; i<elements.length; i++)
devi@23 416 if (elements[i].style.display == "none")
devi@23 417 elements[i].style.display = "";
devi@23 418 else
devi@23 419 elements[i].style.display = "none";
devi@23 420 //if (elements[i].style.visibility == "hidden")
devi@23 421 // elements[i].style.visibility = "visible";
devi@23 422 //else
devi@23 423 // elements[i].style.visibility = "hidden";
devi@23 424 }
devi@23 425 function filter_by_output(text)
devi@23 426 {
devi@23 427
devi@23 428 var jjj=0;
devi@23 429
devi@23 430 elements=getElementsByClassName('command');
devi@23 431 for(i=0; i<elements.length; i++) {
devi@23 432 subelems = elements[i].getElementsByTagName('pre');
devi@23 433 for(j=0; j<subelems.length; j++) {
devi@23 434 if (subelems[j].className = 'output') {
devi@23 435 var str = new String(subelems[j].nodeValue);
devi@23 436 if (jjj != 1) {
devi@23 437 alert(str);
devi@23 438 jjj=1;
devi@23 439 }
devi@23 440 if (str.indexOf(text) >0)
devi@23 441 subelems[j].style.display = "none";
devi@23 442 else
devi@23 443 subelems[j].style.display = "";
devi@23 444
devi@23 445 }
devi@23 446
devi@23 447 }
devi@23 448 }
devi@23 449
devi@23 450 }
devi@23 451 JS
devi@23 452
devi@23 453 %Search_Machines = (
devi@23 454 "google" => { "query" => "http://www.google.com/search?q=" ,
devi@23 455 "icon" => "google.ico" },
devi@23 456 "freebsd" => { "query" => "http://www.freebsd.org/cgi/man.cgi?query=",
devi@23 457 "icon" => "freebsd.ico" },
devi@23 458 "linux" => { "query" => "http://man.he.net/?topic=",
devi@23 459 "icon" => "linux.ico"},
devi@23 460 "opennet" => { "query" => "http://www.opennet.ru/search.shtml?words=",
devi@23 461 "icon" => "opennet.ico"},
devi@23 462 "local" => { "query" => "http://www.freebsd.org/cgi/man.cgi?query=",
devi@23 463 "icon" => "freebsd.ico" },
devi@23 464
devi@23 465 );
devi@23 466
devi@23 467 %Elements_Visibility = (
devi@23 468 "note" => "замечания",
devi@23 469 "diff" => "редактор",
devi@23 470 "time" => "время",
devi@23 471 "ttychange" => "терминал",
devi@23 472 "wrong_output wrong_cline wrong_root_output wrong_root_cline"
devi@23 473 => "команды с ошибками",
devi@23 474 "interrupted_output interrupted_cline interrupted_root_output interrupted_root_cline"
devi@23 475 => "прерванные команды",
devi@23 476 "tab_completion_output tab_completion_cline"
devi@23 477 => "продолжение с помощью tab"
devi@23 478 );
devi@23 479
devi@23 480 @Day_Name = qw/ Воскресенье Понедельник Вторник Среда Четверг Пятница Суббота /;
devi@23 481 @Month_Name = qw/ Январь Февраль Март Апрель Май Июнь Июль Август Сентябрь Октябрь Ноябрь Декабрь /;
devi@23 482 @Of_Month_Name = qw/ Января Февраля Марта Апреля Мая Июня Июля Августа Сентября Октября Ноября Декабря /;
devi@23 483 }
devi@23 484