PERL - статьи

         

Переменные среды CGI


Предыдущий скрипт не содержал ничего особенно замечательного,так просто вываливал HTMLый текст который благополучно и отбражался на екране браузера.Но По настоящему мощь придает CGI возможность обработки параметров,которые переданы скрипту.например вы можете набрать

http://www.somehost.ru/somedir/cgi-bin/my_cgi.cgi?param=value

то есть вы хотите чтоб скрипт my_cgi.cgi

обработал для вас параметер param

со значением value (ну это например) или когда вы заполнили запрос в форме (в например yahoo

или altavista).Ну это с точки зрения пользователя... А на сервере при запуске CGI-скрипта сервер формирует среду окружения в которой скрипт может найти всю доступную информацию о HTTP-соединении и о запросе.

Вот эти переменные:

REQUEST_METHOD

Это одно из самых главных поле используемое для определения метода запроса HTTP Протокол HTTP

использует методы GET и POST

для запроса к серверу.Они отличаются тем что при методе GET

запрос является как-бы частью URL

т.е. http://www..../myscript.cgi?request а при методе POST данные передаются в теле HTTP-запроса (при GET

тело запроса пусто) и следовательно для CGI тоже есть различие при GET запрос идет в переменную QUERY_STRING а при POST подается на STDIN

скрипта.

Пример:REQUEST_METHOD=GET

QUERY_STRING

Это строка запроса при методе GET. Вам всем известно что запрос из формы кодируется браузером поскольку не все символы разрешены в URL некоторые имеют специальное назначение. Теперь о методе urlencode: неплохо бы чисто формально напомнить,что все пробелы заменяются в URL на знак '+', а все специальные и непечатные символы на последовательность %hh ,где hh-шестнадцатиричный код символа,разделитель полей формы знак '&',так что при обработке форм надо произвести декодирование.

Пример:QUERY_STRING= name=quake+doomer&age=20&hobby=games

CONTENT_LENGTH



Длина в байтах тела запроса.При методе запроса POST необходимо считать со стандартного входа STDIN

CONTENT_LENGTH байт,а потом производить их обработку.Обычно методом POST пользуются для передачи форм,содержащих потенциально большие области ввода текста TEXTAREA.При этом методе нет никаких ограничений,а при методе GET существуют ограничения на длину URL .


Пример:CONTENT_LENGTH=31

CONTENT_TYPE

Тип тела запроса( для форм кодированых выше указаным образом он application/x-www-form-urlencoded)

GATEWAY_INTERFACE

Версия протокола CGI.

Пример:GATEWAY_INTERFACE=CGI/1.1

REMOTE_ADDR

IP-Адрес удаленого хоста,делающего данный запрос.

Пример:REMOTE_ADDR=139.142.24.157

REMOTE_HOST

Если запрашивающий хост имеет доменное имя,то эта переменная содержит его, в противном случае -тот же самый IP-адресс что и REMOTE_ADDR

Пример:REMOTE_HOST=idsoftware.com

SCRIPT_NAME

Имя скрипта,исполизованое в запросе.Для получения реального пути на сервере используйте SCRIPT_FILENAME

Пример:SCRIPT_NAME=/~paaa/guestbook.cgi

SCRIPT_FILENAME

Имя файла скрипта на сервере.

Пример:SCRIPT_FILENAME=/home/p/paaa/public_html/cgi-bin/guestbook.cgi

SERVER_NAME

Имя серера ,чаще всего доменное как www.microsoft.com ,но в редких случаях за неимением такового может быть IP-адресом как 157.151.74.254

Пример:SERVER_NAME=www.uic.nnov.ru

SERVER_PORT

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

Пример:SERVER_PORT=80

SERVER_PROTOCOL

Версия протокола сервера.

Пример:SERVER_PROTOCOL=HTTP/1.1

SERVER_SOFTWARE

Програмное обеспечение сервера.

Пример:Apache/1.0

AUTH_TYPE, REMOTE_USER

Эти переменные определены в том случае,когда запрошеный ресурс требует аутентификации пользователя.

Переменные заголовка HTTP-запроса.

За исключением тех строк из заголовка HTTP-запроса которые были включены в другие переменные,сервер приделывает строкам префикс HTTP_ и заменяет знаки '-' на '_':

HTTP_ACCEPT

Давая запрос на сервер браузер обычно расчитывает получить информацию определеного формата,и для этого он в заголовке запроса указывает поле Accept:,Отсюда скрипту поступает cписок тех MIME,которые браузер готов принять в качестве ответа от сервера.

Пример:HTTP_ACCEPT=text/html,text/plain,image/gif

HTTP_USER_AGENT

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



Пример:HTTP_USER_AGENT=Mozila/2.01 Gold(Win95;I)

Ну, начнем применять на практике усвоеные уроки.



#!/usr/bin/perl #vars.cgi sub urldecode{ #очень полезная функция декодирования local($val)=@_; #запроса,будет почти в каждой вашей CGI-программе $val=~s/\+/ /g; $val=~s/%([0-9A-H]{2})/pack('C',hex($1))/ge; return $val; } print "Content-Type: text/html\n\n"; print "<HTML><HEAD><TITLE>CGI-Variables</TITLE></HEAD>\n"; print "<BODY>\n"; print "Enter here something:<ISINDEX><BR>\n"; print "Your request is:$ENV{'REQUEST_STRING'}<BR>\n"; print "Decoded request is:urldecode($ENV{'REQUEST_STRING'})<BR>\n"; print "<HR>\n"; print "Variables:<BR>\n"; print "<I><B>REQUEST_METHOD</B></I>=$ENV{'REQUEST_METHOD'}<BR>\n"; print "<I><B>QUERY_STRING</B></I>=$ENV{'QUERY_STRING'}<BR>\n"; print "<I><B>CONTENT_LENGTH</B></I>=$ENV{'CONTENT_LENGTH'}<BR>\n"; print "<I><B>CONTENT_TYPE</B></I>=$ENV{'CONTENT_TYPE'}<BR>\n"; print "<I><B>GATEWAY_INTERFACE</B></I>=$ENV{'GATEWAY_INTERFACE'}<BR>\n"; print "<I><B>REMOTE_ADDR</B></I>=$ENV{'REMOTE_ADDR'}<BR>\n"; print "<I><B>REMOTE_HOST</B></I>=$ENV{'REMOTE_HOST'}<BR>\n"; print "<I><B>SCRIPT_NAME</B></I>=$ENV{'SCRIPT_NAME'}<BR>\n"; print "<I><B>SCRIPT_FILENAME</B></I>=$ENV{'SCRIPT_FILENAME'}<BR>\n"; print "<I><B>SERVER_NAME</B></I>=$ENV{'SERVER_NAME'}<BR>\n"; print "<I><B>SERVER_PORT</B></I>=$ENV{'SERVER_PORT'}<BR>\n"; print "<I><B>SERVER_PROTOCOL</B></I>=$ENV{'SERVER_PROTOCOL'}<BR>\n"; print "<I><B>SERVER_SOFTWARE</B></I>=$ENV{'SERVER_SOFTWARE'}<BR>\n"; print "<I><B>HTTP_ACCEPT</B></I>=$ENV{'HTTP_ACCEPT'}<BR>\n"; print "<I><B>HTTP_USER_AGENT</B></I>=$ENV{'HTTP_USER_AGENT'}<BR>\n"; print "<HR>\n"; print "All enviroment:<BR>\n"; foreach $env_var (keys %ENV){ print "<I>$env_var=$ENV{$env_var}</I><BR>\n"; } print "</BODY></HTML>\n";

Так как все ваши .cgi -файлы должны быть исполняемыми то чтоб облегчить себе жизнь заведите себе в директории cgi-bin командный файл mkcgi ,содержащий



chmod +x *.cgi

и сделайте его в свою очередь исполняемым chmod +x mkcgi -он сильно упростит вам жизнь.

Ну а теперь запускайте скрипт......

Изучив информацию,выдаваемую данным скриптом вы сможете лучше ориентироваться в переменных окружения CGI.


Perl+Windows


Q: Как прикрутить perl к винде, чтобы можно было скрипты выполнять локально?

A: www.activestate.com, download perl. Устанавливаете, и запускаете:

perl myscript.pl

Q: У меня перл от Activestate, как мне поставить такой-то модуль?

A1: ppm

ppm> install

A2: www.activestate.com/packages/zips/

Hаходите нужный архив, скачиваете, читаете readme.

Если нужного модуля нет - идёте на CPAN, ищете его там, пытаетесь понять, как

его прикрутить. С некоторой вероятностью он чисто перловый, и тогда есть шанс,

что просто положив его в директорию с модулями, вы получите работающий модуль.

Если же у него есть С-часть, ставите MS Visual C, и мучаетесь, мучаетесь...

Q: А из браузера как смотреть на вывод скрипта? Почему-то показывается

исходник.

A: Потому что между браузером и perl должен стоять сервер, который и

запускает скрипт на выполнение, отдавая результат браузеру. Сервер не

обязательно подразумевает под собой отдельный компьютер - вы можете поставить

себе программу, и обращаться к ней через браузер, наблюдая за работой

скриптов. Вариантов масса:

Apache-W32, www.apache.org

Personal Web Server из поставки Win9x

Sambar, www.sambar.com

Устанавливаете сервер, и либо указываете в настройках сервера ассоциацию на

файлы cgi и pl - запуск perl.exe, либо устанавливаете такую ассоциацию на эти

файлы в windows.

Q: Как заставить работать связку perl + MySQL под виндой?

A: Скачать дистрибутив MySQL с сайта (www.mysql.com), установить необходимые модули - DBI и DBD:MySQL.

Неплохой тьюториал по установке Perl+Apache+MySql

http://userguide.webservis.ru/



Постраничный вывод новостей с разбиением по датам


Предположим есть файл news.dat со строками(не суть что разделитель, разделитель определяется переменной $/, которую можно в начале кода переопределить) вида:

20010717<A href="http://www.netoscope.ru/news/" target=_new>news1</a>&nbsp;&nbsp;&nbsp;&nbsp;[Нетоскоп] 20010717<A href="http://www.utro.ru/news/" target=_new>news2</a>&nbsp;&nbsp;&nbsp;&nbsp;[Утро] 20010718<A href="http://www.compulenta.ru/" target=_new>news3</a>&nbsp;&nbsp;&nbsp;&nbsp;[Компьюлента] 20010718<A href="http://www.compulenta.ru" target=_new>news4</a>&nbsp;&nbsp;&nbsp;&nbsp;[Компьюлента] 20010718<A href="http://www.kommersant.ru/news/" target=_new>news5</a>&nbsp;&nbsp;&nbsp;&nbsp;[КоммерсантЪ] 20010719<A href="http://www.echo.msk.ru/7news/" target=_new>news6</a>&nbsp;&nbsp;&nbsp;&nbsp;[Эхо Москвы] 20010719<A href="http://www.echo.msk.ru/7news/" target=_new>news7</a>&nbsp;&nbsp;&nbsp;&nbsp;[Эхо Москвы]

где даты поставлены задом наперед, сначала год, затем месяц, затем день, так сравнивать легче, ибо год максимален, месяц тоже максимален, и деть тоже максимален. Иначе для сортировки по дате пришлось бы вводить двенадцатиричную систему счисления для месяцев и затем 60-тиричную систему счисления для часов, минут и секунд.

Приведенная ниже программа позволяет реализовать постраничный вывод текста, с линейкой прокрутки текста наподобие результатов, выдаваемых обычными поисковиками:

#!/usr/bin/perl -wT print "content-type: text/html\n\n"; use CGI 'param'; $pos = param('pos'); $n = 5; $k = 5; $url = "q.pl"; open F, "<news.dat"; @mass=<F>; close F;

@m1=grep{!$_{$_}++} map{/^(\d\d\d\d\d\d\d\d)/} @mass;

foreach $u(0 .. $#m1){ foreach $n(@mass){ chomp $m1[$u]; push @{$ha{$m1[$u]}}, $n if($n=~m/^$m1[$u]/) } } print "<a name=top>"; for $k(reverse sort keys %ha){ $k=~s|^(\d\d\d\d)(\d\d)(\d\d)|$3\.$2\.$1|; push(@re, " <a href=\"#$k\">$k</a> "); } print "<center>"; &res(@re); print "</center>"; for $k(reverse sort keys %ha){ $m=$k; $m=~s|^(\d\d\d\d)(\d\d)(\d\d)|$3\.$2\.$1|; $tr="<b><a name=\"$m\">$m</b> <a href=\"#top\">top</a><ul>"; foreach $im(@{$ha{$k}}){ $im=~s!$k|<br>!!; $tr.="<li>$im";} $tr.="</ul>"; push @res, "$tr\n"; } push @res, ""; &res(@res); sub res{ local @res=@_; if($#res>$n-1){ print "<p><center><font size=-1><b>"; foreach($j=0; $j<=$#res; $j++){ push(@pervij,"$j") if($j<=$pos && $j % $n == 0); push(@vtoroj,"$j") if($j>=$pos+$n && $j % $n == 0); } foreach $elem(@pervij){ if($elem/$n>=$pervij[$#pervij]/$n-$k && $res[$#res] ne '<!--end-->'){ if($elem==$pervij[$#pervij] && $res[$#res] ne '<!--end-->') {push(@nach ,($elem/$n+1));} else{ push(@nach, " <a href=\"$url?pos=$elem\">".($elem/$n+1)."<\/a> |\n");} } if($#pervij > $k && $res[$#res] ne '<!--end-->'){ push(@back, "<a href=\"$url?pos=".($pos-$n)."\"><<<\/a>") } } print $back[$#back]; print @nach; foreach $elem1(@vtoroj){ if ($elem1/$n<=$pos/$n+$k && $res[$#res] ne '<!--end-->'){ print " | <a href=\"$url?pos=$elem1\">".($elem1/$n+1)."<\/a> \n"; } if($#vtoroj > $k-1 && $res[$#res] ne '<!--end-->'){ push(@back1, "<a href=\"$url?pos=".($elem1)."\">>><\/a>") } } print "$back1[0]</b></font></center>"; } $#pervij=-1; $#vtoroj=-1; $#back=-1; $#nach=-1; $#back1=-1; print "<p>"; for ($i=$pos; $i<$pos+$n; $i++) { print $res[$i]} }


Разберем работу программы:

@m1=grep{!$_{$_}++} map{/^(\d\d\d\d\d\d\d\d)/} @mass;

составляем массив цифр, т.е. массив дат, map{/^(\d\d\d\d\d\d\d\d)/} @ mass составляет список цифр, указанных в регулярном выражении. Следующая строчка grep{!$_{$_}++} - убирает одинаковые даты, т.к. в списке новостей может быть несколько новостей за один день. Получаем массив @m1 с днями, которые были с новостями.

foreach $u(0 .. $#m1){ foreach $n(@mass){ chomp $m1[$u]; push @{$ha{$m1[$u]}}, $n if($n=~m/^$m1[$u]/) } }

создаем хеш массивов @{$ha{$m1[$u]}}, в которых определенному дню будет соответствовать несколько новостей.

for $k(reverse sort keys %ha){ $k=~s|^(\d\d\d\d)(\d\d)(\d\d)|$3\.$2\.$1|; push(@re, " <a href=\"#$k\">$k</a> "); } print "<center>"; &res(@re); print "</center>";

выводим линейку дат, если при выводе 5 дней(значение числа дней содержится в переменной $n = 5;) число новостей таково, что будет помещаться на более чем одной странички, вобщем для быстрой навигации.

for $k(reverse sort keys %ha){ $m=$k; $m=~s|^(\d\d\d\d)(\d\d)(\d\d)|$3\.$2\.$1|; $tr="<b><a name=\"$m\">$m</b> <a href=\"#top\">top</a><ul>"; foreach $im(@{$ha{$k}}){ $im=~s!$k|<br>!!; $tr.="<li>$im";} $tr.="</ul>"; push @res, "$tr\n"; } push @res, ""; &res(@res);

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

sub res{ local @res=@_; if($#res>$n-1){ print "<p><center><font size=-1><b>"; foreach($j=0; $j<=$#res; $j++){ push(@pervij,"$j") if($j<=$pos && $j % $n == 0); push(@vtoroj,"$j") if($j>=$pos+$n && $j % $n == 0); } foreach $elem(@pervij){ if($elem/$n>=$pervij[$#pervij]/$n-$k && $res[$#res] ne '<!--end-->'){ if($elem==$pervij[$#pervij] && $res[$#res] ne '<!--end-->') {push(@nach ,($elem/$n+1));} else{ push(@nach, " <a href=\"$url?pos=$elem\">".($elem/$n+1)."<\/a> |\n");} } if($#pervij > $k && $res[$#res] ne '<!--end-->'){ push(@back, "<a href=\"$url?pos=".($pos-$n)."\"><<<\/a>") } } print $back[$#back]; print @nach; foreach $elem1(@vtoroj){ if ($elem1/$n<=$pos/$n+$k && $res[$#res] ne '<!--end-->'){ print " | <a href=\"$url?pos=$elem1\">".($elem1/$n+1)."<\/a> \n"; } if($#vtoroj > $k-1 && $res[$#res] ne '<!--end-->'){ push(@back1, "<a href=\"$url?pos=".($elem1)."\">>><\/a>") } } print "$back1[0]</b></font></center>"; } $#pervij=-1; $#vtoroj=-1; $#back=-1; $#nach=-1; $#back1=-1; print "<p>"; for ($i=$pos; $i<$pos+$n; $i++) { print $res[$i]} }



локализуем массив @res: local @res=@_; - получили массив, переданный попрограмме. Далее if($#res>$n-1){blah blah blah} если число элементов массива больше, чем 5, то показываем строчку прокрутки.

Разбиваем массив @res на массивы до текущей страницы(@pervij) и после текущей страницы(@vtoroj):

foreach($j=0; $j<=$#res; $j++){ push(@pervij,"$j") if($j<=$pos && $j % $n == 0); push(@vtoroj,"$j") if($j>=$pos+$n && $j % $n == 0); }

размерность массивов @pervij и @vtoroj кратна(кратность определяется оператором %) числу $n, т.е. это массивы до текущей страницы и после текущей страницы, в броузере, если пользователь находится допустим на 10 странице результатов до текущей все то, что до цифры без ссылки, после текужей это все то, что после цифры без ссылки, включая линейки прокрутки << и >>:



| | | | | 7 | | | | | >>



Эти массивы содержат число пятерок(для $n=5;) элементов массива @res.

foreach $elem(@pervij){ if($elem/$n>=$pervij[$#pervij]/$n-$k && $res[$#res] ne '<!--end-->'){ if($elem==$pervij[$#pervij] && $res[$#res] ne '<!--end-->') {push(@nach ,($elem/$n+1));} else{ push(@nach, " <a href=\"$url?pos=$elem\">".($elem/$n+1)."<\/a> |\n");} } if($#pervij > $k && $res[$#res] ne '<!--end-->'){ push(@back, "<a href=\"$url?pos=".($pos-$n)."\"><<<\/a>") } }

Цикл для показа элементов предыдущих страниц | | | | | 7 | |

т.е. если $elem/$n>=$pervij[$#pervij]/$n-$k, то это значит, что выбираются страницы элементов до текущей позиции, включая е?, т.е. 2, 3, 4, 5 и 6 - цифра 7 это текущая позиция, которая определяется условием if($elem==$pervij[$#pervij] && $res[$#res] ne '<!--end-->'). Зачем тут стоит <!--end--> будет видно позднее в разборе админской части этой ленты новостей.

Условие

if($#pervij > $k && $res[$#res] ne '<!--end-->'){ push(@back, "<a href=\"$url?pos=".($pos-$n)."\"><<<\/a>") }



значит что если существуют элементы массива @pervij больше, чем $k=5, то для таких елементов нужно ставить знички прокрутки << и >>. Для массива @vtoroj условия определяются аналогичным образом, учитывая, что элементы для него должны быть больше определенного числа $pos, которое определяет текущее значение показываемой пятерки дат. Вывод самих новостей осуществляется циклом

for ($i=$pos; $i<$pos+$n; $i++) { print $res[$i]}

который берет элементы массива $res[$i] до элемента $n, т.е. допустим пользователь находится на странице 10, значит должен производится вывод новостей с 50 по 55-ю включительно.

Вывод результатов помещен в подпрограмму, т.е. существует некая переносимость кода из одного скрипта в другой. В скрипте подпрограмма используется дважды, когда нужно вывести число дат по $n штук и когда нужно вывести сами новости в количестве $n штук на страничку.

Автор: Dmitriy Kuvshinov

E-mail: dmitriy@genphys.phys.msu.su

URL: http://genphys.phys.msu.su/~dmitriy/perl/


Права Доступа


Я бы ни за что не написал этот раздел,если бы он не был так важен.Сидя в пределах своей домашней директории и занимаясь только тем,что качаете с Инета всякую херню,вы возможно и не задавались некоторыми вопросами....а зря.......... Ведь немного надо,чтоб попортить нервы начинающему CGI -програмисту.

Одна из таких вещей это права доступа......

Начнем с того ,что в системе Unix каждый пользователь имеет свой идентификатор- число,уникально идентифицирующее его в этой системе.(Мой логин paaa а ему соответсвует число 1818).Это число внутреннее для операционной системы,для пользования оно представлено как логин,который и соответствует пользователю.

Только не надо думать о пользователе,как о конкретном человеке сидящим за клавиатурой, пользователем может быть и какой-нибудь процесс.Важно отметить что пользователь-это определенная область прав доступа,которая ему соответствует.(Вы например не можете удалить файлы из каталога другого пользователя). Это и дает возможность стабильной работы всей системы.

Итак есть идентификатор пользователя.Также имеется идентификатор группы.

Группа служит для выделения пользователей по группам. Например у пользователей группы users (Обычные пользователи) не такие права как у группы wheels (административная группа).

Каждый процесс который вами запущен(Будь то Netscape,терминал,или текстовый редактор)получают ваши идентификаторы пользователя и группы. таким образом исполняются от вашего имени.

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

Следующее новшество по сравнению с DOS это права доступа к файлу.Их может сменить только тот пользователь которому принадлежит файл,или супервизор.(Это в отличии от DOS где каждая дрянь типа вируса может снять атрибут readonly)


Права доступа задаются обычно числом в восьмеричном коде и разбиты на 3 части по 3 бита: Каждая часть задает права доступа для конкретной группы:

1я -права доступа для пользователя,которому принадлежит файл
2я -для группы которой принадлежит файл
3я -для всех остальных
В каждой такой категории выделяются 3 права: Право на чтение,Право на запись,и право на исполнение. (все права и аттрибуты очень наглядно показаваютя командой ls с ключом -l) Так как исполнять каталоги бессмыслено,то право на исполнение для них означает право обращатся к файлам из этого каталога.

Бит Описание
8 Право на чтение для пользователя
7 Право на запись для пользователя
6 Право на исполнение для пользователя
5 Право на чтение для группы
4 Право на запись для группы
3 Право на исполнение для группы
2 Право на чтение для всех остальных
1 Право на запись для всех остальных
0 Право на исполнение для всех остальных
Изменяются права командой chmod,ее синтаксис такой:

chmod [u|g|o]{+|-}{r|w|x} file

chmod number file

,где u-user,g-group,o-other,r-read,w-write,x-execute;--удалить,+-установить

Примеры:

chmod +r file.txt #разрешает всем право на чтения файла

chmod u+w file.txt #устанавливает для владельца файла право на запись в него

chmod +x gbook.cgi #право на исполнение для всех,как для ползователя,группы,и для других

chmod 0777 cgi-bin #Разрешает самые широкие права доступа для cgi-bin

Приоткрытии файла программой,операционная система сравнивает идентификатор пользователя с идентификатором пользователя владельца файла, если они равны,то действуют права пользователя,если не равны то сравниваются идентификаторы группы,если и они не равны,то действуют права доступа для остальных

остальных.В том случае если у процесса нет достаточных прав,система возвращает ошибку. Следует заметить ,что для супервизора root права доступа не проверяются.

Можно выполнить скрипт,только если есть права на его исполнение. Вот почему следует давать chmod +x *.cgi иначе ваши скрипты станут просто недоступными. Но и это еще не все.....



Ваш скрипт может обращатся к вашим файлам ( например ведет базу данных гостевой книги). Все выглядит нормально,но только ничего не работает,файл в который вы намеревались писать, не открывается,знакомая проблема ;(( ?.Так вот чтобы вы не мучались в догадках Ваш скрипт не может получить доступ к вашим файлам,потому что он выполняется не вами (не с вашим идентификатором), а от имени nobody (непривелигированый пользователь).Это мера предосторожности направлена на то, чтоб скрипты ,взбесившись из-за неправильно переданых параметров(или вообще от глюков) не могли ничего повредить ценного и важного на сервере.

Поэтому к тем файлам,к которым скрипт по смыслу должен обращатся нужно присвоить самые широкие права доступа 0777

Например в случае гостевой книги chmod 0777 guestbook.dat

Если также важно чтоб скрипты могли заводить новые файлы в cgi-bin то надо дать также права на это chmod 0777 cgi-bin

Если вы видите что ваш скрипт не может обратится к какому-то файлу,то это в 99% случаев из-за вашей забывчивости.!!!

На самый крайний случай воспользуйтесь setuid-скриптами (к этому делу ,если вы на это решились,отнеситесь ОЧЕНЬ серьезно,так как целые тома по безопасности в Unix посвящены именно setuid-скриптам). Хочу сразу предупредить ,сам я таких не писал,да и вам не особенно советую.Но для общего как говорится развития,имейте в виду следующую информацыю.

Кроме указания прав доступа,существуют специальные биты у файла.Это биты установки пользователя и группы. Когда процесс выполняется(простой процесс) то его реальный и эффективный идентификаторы пользователей совпадают,идентификаторы групп тоже. На самом деле значение имеют как раз эффективные значения пользователя и группы,они учавствуют в сравнении прав доступа. Нельзя ли их как-то изменить,когда уж совсем нужда заставит? Можно! .На этот вопрос дают ответ программы с установленым битом пользователя.Когда система запускает такую программу,она присваивает новому процессу не идентификатор того пользователя,что запустил ее, а идентификатор пользователя-владельца исполняемого файла.



Самый классический пример setuid-программ это программа passwd , предназначеная для смены пароля пользователя. Такие данные как пароль и прочие характеристики пользователей хранятся в специальном файле,который имеет огромное значение при входе в систему. Так как это системный файл,то открыть к нему доступ на запись всем-значит подвергнуть ВСЮ систему риску,ведь любое неправильное изменение его повлечет катастрофические последствия(в конце концов бывает просто хулиганство). Поэтому доступ к этому файлу закрыт для всех пользователей.А что если надо сменить пароль?

Запускаем программу passwd ,если глянуть на ее аттрибуты ,то видно что она принадлежит root -супервизору, и еще имеет установленый бит setuid. Так корректно обходится эта проблема.

Если вы все-же решили попытаться ,то знайте ,что сделать программу setuid можно

коммандой : chmod +s myprogramm

И как всгда Примерчик напоследок:

Эта программа выдает содержимое вашей директории public_html в том случае,если она доступна для чтения,и для каждого файла указывает ,можно ли его читать,писать и исполнять. Попробуйте ее сделать setuid и посмотрите как изменится результат.



#!/usr/bin/perl #listmydir.cgi print "Content-Type: text/html\n\n"; if(!(-r '..')){ print ".. is not allowed for reading ;)))))\n"; } else{ @list=glob('../*'); foreach(@list){ print "<A href=\"$_\">$_</A>"; print "&nbsp;readable" if -r; print "&nbsp;writable" if -w; print "&nbsp;executable" if -x; print "<BR>\n"; } }


Права доступа к файлам и скриптам


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

Настраивая права доступа к файлам, вы должны знать, под какими именами веб сервер выполняет ваши программы и считывает веб страницы. Все CGI-программы, а также asp-страницы выполняются с именами пользователя и группы - владельца сайта.

Все остальное - HTML страницы, программы mod_perl, mod_php, java-сервлеты, jsp-страницы, ... - выполняется с именами пользователя и группы www. SSI-страницы вызываются под именами www.www. Однако запускаемые из SSI-страниц CGI-скрипты имеют имя и группу владельца сайта.

Исходя из этих сведений и настраиваются права доступа: Те файлы и скрипты, которые запускаются под именем www.www, должны иметь признаки чтения всеми. Если это исполняемые файлы (mod_perl), то должны еще быть и признаки выполнения всеми. Файлы, выполняемые под вашими именами пользователя и группы, не обязаны содержать признаки чтения и выполнения всеми. Вы можете даже убрать разрешение чтения/выполнения членами вашей группы (FTP-пользователями вашего сайта). В этом случае вы полностью застрахованы от того, что ваш файл прочитают посторонние пользователи. Вы можете смело располагать прямо в файле такие сведения, например, как пароли доступа к базам данных. Кроме суперпользователя root и самого веб сервера, эти файлы сможете прочитать или исполнить только вы сами. Если файл создан не вами, а одним из ваших FTP-пользователей, то имя владельца не будет совпадать с тем, под которым его будет запускать веб сервер. В этом случае признак чтения (и исполнения, если требуется) должен быть проставлен для группы тоже. Но открывать чтение (и исполнение) для всех в этом случае все равно не требуется.

Варианты настройки прав доступа к различным типам файлов вы можете проследить из следующей таблицы.

CGI Perlrwx --- ---700user.user
rwx r-x ---750
CGI Pythonrwx --- ---700user.user
rwx r-x ---750
CGI TCLrwx --- ---700user.user
rwx r-x ---750
CGI PHPrw- --- ---600user.user
rw- r-- ---640
ASP (VBScript,JavaScript)rw- --- ---600user.user
rw- r-- ---640
ASP (PerlScript)rw- r-- r--644www.www
Java сервлетыrw- r-- r--644www.www
JSP-страницыrw- r-- r--644www.www
HTML-страницыrw- r-- r--644www.www
SSI-страницыrw- r-- r--644www.www
mod_php скриптыrw- r-- r--644www.www
mod_perl скриптыrwx r-x r-x755www.www



Прекрасный язык Perl


Вы наверное обратили свое внимание что CGI скрипты пишутся обычно на языке Perl (Practical Extraction and Report Language)- очень удобном языке,впитавшем из других все лучшие черты.Может у вас возникнуть сомнение :Ну вот!Изучать новый язык программирования!? Спешу вас успокоить,изучение Perl не будет в тягость (я сужу по своему опыту!). Вы даже сами не заметите как выучите его.Если вы хоть когда-нибудь программировали скажем на C и использовали утилиту grep для поиска регулярных выражений в тексте,то вам будет еще легче.Мое изучение Perl

началось с того что я скачал Perl

под Windows (фирмы ActiveWare) и изучения той HTMLой документации которая к нему прилагалась хватило чтоб этот язык стал моим любимым....

Все в нем сделано для удобства программиста (в отличии например от Java;( )

Начнем с переменных,они в Perl

бывают 3х типов скаларные,списковые(массивы)

и хэши(ассоциативные массивы). Для указания компилятору(да и для немалого удобства программиста) перед именем скалярной переменной стоит знак '$' перед массивом '@',перед хешем '%'. т.е. например $scalar_var,@array_var,%hash_var

Скалярные переменные могут быть как числовые так и строковые,но это не надо указывать Perl сам по контексту в зависимости от операций может привести одно к другому.

Например: "123"+"4" будет 127 (или "127") так как операция '+' действует над числами а вот если применить операцию конкатенации строк '.'

то строковое "test" . 1 будет "test1"

Ну а вот операции над скалярными переменными:

Операцыи Описание Пример
+ - * / % Арифметические print 2*7+4/(8%3);

print int(127/15); #целая часть

** Возведение в степень print 2**16;
++ -- Инкремент-декремент $i++;
& | ^ ~ << >> Побитовые $x=3;$y=4;

print $x|$y;

print $x&$y;

== != < > <= >= <=> Числовые операции сравнения if($x==9){print "Ok!";}
eq ne lt gt le ge cmp стрковые операции сравнения if($game eq 'doom'){print "You are doomer!\n";}
&& ! Логические if(($x==9)($game eq 'doom')){print "hello you!\n";}
?: Условный оператор $x=($game eq 'quake'?9:8);
, Последовательное вычисление $x=10,$y=20;
. Конкатенация $x='http://'.'www.uic.nnov.ru';
x Повторение $x='1234'x5; #$x='12341234123412341234'
=~ Сопоставление с образцом if($url=~/http/){print "HTTP";}
!~ То же но с отрицанием if($url!~/http/){print "No HTTP";}
= += -= *= /= %= **= |= &= ^= ~= <<= >>= .= x= Присваивание $x+=$y;
   
<
Пусь это будет вам справочником ,да кстати насчет строк,вы заметили,что они могут быть в двойных и одинарных кавычках, разница между ними состоит в том ,что в одинарных не осуществляется подстановка переменных, а в двойных осущестляется, Например:



$x='qwerty'; print 'my var is $x'; #выведет my var is $x print "my var is $x"; #выведет my var is qwerty

Списки: Спискочные переменные начинаются с символа '@'

конструируются следующим образом



@List1=(1,2,5,70); @List2=(12,23,@List1); #12,23,1,2,5,70 @Rgb=($r,$g,$b);

Также можно список использовать как lvalue:



@List=(1,2,3..8,15); ($x,$y,$z)=@List; #$x=1,$y=2,$z=3 ($x,$y,$z,@list2)=@List; #$x=1,$y=2,$z=3,@list2=(4,5,6,7,8,15); ($r,$g,$b)=@Rgb;

Можно обращаться к нескольким,выбраным элементам массива(срезу массива):



@list=(1..10); @list[2,3,5,9]=(100,200,300,400); #@list=(1,100,200,4,300,6,7,8,400,10) @list[1,10]=@list[10,1];#меняет местами элементы

Обратится к скаларному значению -элементу массива можно $имя_массива[индекс], сдесь обратите внимание на знак '$'- мы ведь обращаемся к скаляру-элементу.

Теперь немного о хешах:

хеш это такой массив который состоит из пар ключ-значение, весь хеш обозначается %хеш ,к отдельным элементам доступ $хеш{скалярное выражение} конструируется хеш так:



$my_hash{1}="doom"; $my_hash{'quake'}="www.idsoftware.com"; $my_hash{1+2}=100;

Хеш может быть также сконструирован из массива с четным числом элементов где пары превращаются в ключ-значение



%hash=(1,20,2,100);#аналогично $hash{1}=20;$hash{2}=100;

удаление из хеша -операция delete:



delete $hash{1};

есть функции выдающие ключи и значения соответственно.



%hash=(1,20,2,100,3,'doom'); @k=keys %hash; #@k=(1,2,3); @v=values %hash;#@v=(20,100,'doom');

Операторы:

Набор операторов в Perl Очень широк,многие из них прямые аналоги имеющихся в других языках,например if,for,while;но есть и значительные улучшения имеюшихся и конечно новые...

Тот же самый оператор if имеет две формы (как когда удобнее):





if(условие)оператор; оператор if условие;

В пару к оператору if имеется оператор unless : означающий if

с отрицанием:



unless(($method eq 'GET')($method eq 'POST')){print "Unsupported method";} print "Ok" unless $x < $y;

Также в пару while существует until

синтаксис оператора for

полностью аналогичен C:



for($i=0;$i

новшеством(и приятным) является foreach позволяющий пройтись по всем элементам массива,присваивая по очереди его элементы какой-то переменной, его синтаксис такой:



foreach $переменная (@массив){ блок операторов; } или foreach (@массив){ операторы; }

Последний пример особенно важен для упрощения вашего тяжкого труда програмиста и демонтстрирует интересную особенность Perl-переменную по умолчанию $_: в оргомном количестве операторов и функций при опускании аргумента она подразумевается по умолчанию. Она также по умолчанию сопоставляется с регулярным выражением:



следующий пример @Data=<STDIN>; foreach(@Data){ chomp; print if /^From:/; } аналогичен такому: @Data=<STDIN>; foreach $_ (@Data){ chomp($_); print $_ if $_ =~ /^From:/;

как видите затраты труда значительно сокращаются,благодаря этому маленькому трюку. Регулярные выражения.

регулярное выражение записывается между двух слэшей /рег_выр/



if(/abc/){ print '$_ содержит abc\n'; }

это самый простой пример применения регулярного выражения а теперь посложнее вот тут в табличке (из того что я помню наизусть):

Символ Значение Пример применения
. Соответствует любому символу print if /ab.c/;
[мн-во симв] Соответствует любому символу из данного мн-ва /[abc]d/;#соответствует ad,bd,cd
[^мн-во] Отрицание мн-ва символов /[^xyz]/;#
(....) Группировка элементов(и также запоминание в переменных $1 $2 $3 ...) /(xyz)*/

/([abc].[^xy]qwerty)/
(..|..|..) Одна из альтернатив  
* повторение образца 0 или более раз /.*/;#соответствует всему
? Повторение 0 или 1 раз /(http:\/\/)?.*\.cgi/
+ Повторение 1 или более раз  
{n,m} повторение от n до m раз  
{n} повторение точно n раз  
{n,} повторение n и более раз  
Спец символы:    
\t \r \n ... Управляющие символы:табуляции,возврат каретки,перевод строки.....  
\d Соответствует цифре,Аналог [0-9]  
\D Соответствует нецифровому симсволу,аналог[^0-9]  
\w Соответствует букве  
\W Соответствует небуквеному символу  
\s Соответствует пробельным символам(пробелы,табуляции,новые строки..)  
\S Соответствует непробельному символу  
\b Соответствует границе слова $test1="this is test";

$test2="wise";

if($test1=~/\bis\b/){print "1";}#соответствует

if($test2=~/\bis\b/){print "2";}#нет
\B Соответствует не границе слова /\Bis\B/ соответсвует 'wise' но не 'is'
<


Для того чтоб поместить в регулярное выражение любой специальный символ,поставьте реред ним обратный слэш Заставить Perl игнорировать регистр можно поставив i после регулярного выражения



print "Are you sure?:"; $answer=<STDIN>; if($answer=~/Y/i){ #че-нибудь сделаем... }

Полезные функции.

В Perl очень много различных функций ,как говорится на все случаи жизни,все о них я конечно не опишу,но обо многих. Начну с тех,которые больше относятся к операторам. Операция замены s/рег.выражение/строка/ игнорировать регистр - опция i

глобальная(по всей строке) замена -опция g; Пример:



$x="This is test"; $x=~s/ /_/g; print $x; #This_is_test

Очень полезная опция у s/// e -она означает что вторая строка не строка а выражение, результат которого и будет подставлен. Например,у вас есть файл в котором все записи о возрасте через год надо менять



open OLD,"oldfile.txt" die "Cannot open oldfile.txt $!\n"; open NEW,">newfile.txt" die "Cannot open newfile.txt $!\n"; foreach(){ s/(\d+)(\s+год)/($1+1).$2/gie; s/(\d+)(\s+лет)/($1+1).$2/gie; print NEW $_; } close NEW; close OLD;

или более показательным примером послужит функция urldecode,которая будет встречатся в каждой вашей программе,обрабатывающей формы:



sub urldecode{ local($val)=@_; $val=~s/\+/ /g; $val=~s/%([0-9A-H]{2})/pack('C',hex($1))/ge; return $val; }

Также важным удобством в Perl являются операции для работы с файлами для выполнения схожих функций в других языках приходиться проделывать огромную массу работы. Аргументами могут быть как Файловые переменные,так и строки,представляющие имя файла.

Операция Описание Пример использоввания
-r Доступен для чтения unless(-r "myfile"){print "Cannot read myfile\n";}
-w Доступен для записи  
-x Для исполнения  
-o Принадлежит пользователю if(-o "index.htm"){chmod 0777,"index.htm";}
-R Доступен для чтения реальным

пользователем,а не только "эффективным".

Имеет значения для set-uid -скриптов
if(-r FILE){unless(-R FILE){die "Its not allowed to read this\n";}}
-W Доступен для записи реальным пользователем  
-X Доступен для исполнения реальным пользователем  
-O Принадлежит реальному пользователю  
-e Файл или каталог Существует unless(-e $htmlfile){

open HTML,">$htmlfile";

print HTMLFILE "<HTML><BODY></BODY></HTML>";

close HTMLFILE;

}
-z Существует,но имеет нулевую длину if(-z 'tmpfile'){unlink 'tmpfile';}
-s Размер файла в байтах system "rar m -m5 archive.rar $myfile" if -s $myfile > 1000;
-f Файл существует и является простым файлом  
-d Файл существует и является каталогом if(-d 'public_html'){chdir 'public_html';}
-l Символической ссылкой  
-p Каналом FIFO  
-u Имеет бит установки пользователя  
-g Имеет бит установки группы  
-k Установлен sticky-бит  
-t Является терминальным устройством  
-M Время с последнего изменения (в днях) while(defiled($file=glob('*'))){

 if(-M $file >= 7.0){

  unlink($file);#удаляем слишком старые файлы

  }

}
-A Время последнего доступа(в днях) if(-A "$ENV{'HOME'}/public_html/index.html"
-C Время последнего обновления файлового индекса(в днях)  
     
<


Еще есть и другие

функция open открывает файл



open ФАЙЛОВАЯ_ПЕРЕМЕННАЯ,"имя файла"; # открыть файл для чтения open ФАЙЛОВАЯ_ПЕРЕМЕННАЯ,">имя файла"; #для записи open ФАЙЛОВАЯ_ПЕРЕМЕННАЯ,">>имя файла";#для записи в конец open ФАЙЛОВАЯ_ПЕРЕМЕННАЯ,"+

Что какается открытия файлов,то вам как програмистам все очевидно, но с коммандами тоже все здорово,что пояснит хороший пример(из практики):



open MAIL,"|mail paaa@uic.nnov.ru";#Пошлем информацию по почте print MAIL "Hello\n"; print MAIL "...\n"; print MAIL "...\n"; close MAIL;

когда вы открыли файл вы можете считать из него строку в скалярную переменную Вот так:$str=<FILE>

избавиться от символа новой строки на конце поможет функция chomp, ведь этот символ может помешаться например в имени файла или при выводе на экран



print "Введите имя файла:"; $fname=<STDIN>; chomp($fname); open F,$fname die "Cannot open $fname $!\n"; .....

Если также подставить списочную переменную,то получим список строк файла от текущей строки и до конца



print "Что искать:"; $search=<STDIN>; chomp($search); @L=<F>; foreach(@L){ print if /$search/; } а можно и так: print "Что искать:"; $search=<STDIN>; chomp($search); foreach(<F>){ print if /$search/; }

бинарный файл можно читать и писать функциями sysread и syswrite:

sysread ФАЙЛОВАЯ_ПЕРЕМЕННАЯ,$скалярная_перемменая,сколько_байт

syswrite ФАЙЛОВАЯ_ПЕРЕМЕННАЯ,$скалярная_перемменая,сколько_байт

функции split и join: @Список=split /рег.выр/,$скаляр;

$скаляр=join строка,@Список;



#Разбить строку слов,разделенных пробелами в список вы можете @WordList=split / /,$String; #После обработки снова обьединить $String=join ' ',@WordList;

Встроеные функции Perl можно вызывать со скобками или без (как вам удобно), скобки программисты указывают или для красоты,или чаще,что устранить возможную неоднозначность в выраженнии:



printf "x=%d",$x; printf ("x=%d",$x);#аналогично



Надеюсь что я вас позабавил примерами функций ;).

Примеры применения Perl для различных нужд...

Следующая программа переводит текстовый файл в формат HTML (вспомните сколько хлопот вам доставит отлов во всем файле '<', '>' и '&' чтоб заменить их на &tl; , &gt; и &amp;

а как неплохо чтоб автоматически все http://www.... превратились в <A href="http://www...." >http://www....</A>)



#!/usr/bin/perl #txt2html die "Usage: txt2html Infile OutFile\n" unless(@ARGV); open IN,"$ARGV[0]" die "Cannot open $ARGV[0] $! \n"; open OUT,">$ARGV[1]" die "Cannot open $ARGV[1] $! \n"; while(<IN>){ s/&/&amp;/g; s/</&lt;/g; s/>/&gt;/g; s/\n/<BR>\n/g; s/(http:\/\/\S+)/<A href="$1">$1<\/A>/g; print OUT $_; } close IN; close OUT;

Более подробную информацию о Perl вы можете получить по адресам:

http://www.metronet.com/0/perlinfo/perl5/manual/perl.html


Рассмотрим сценарий регистрации пользователя на


Рассмотрим сценарий регистрации пользователя на веб-сервере.Имя пользователя и его пароль записываются в текстовый файл и используются для его последующей аутентификации.
#!/usr/local/bin/perl #Объявляем глобальные переменные.
$request=$ENV{'REQUEST_METHOD'}; $content=$ENV{'CONTENT_LENGTH'}; $basedir="http://www.mydomain.com/~"; $userdir="f:/home";
#Подпрограммы для декодирования данных из формы.
sub urldecode { local($val)=@_; $val=~ s/\+/ /g; $val=~ s/%[0-9a-hA-H] {2}/pack('C',hex($1))/ge; return $val; }
sub strhtml { local($val)=@_; $val=~s//>/g; $val=~s/(http:\/\/\+S)/<A href="$1">$1<\/A>/g; return $val; } ######################################################################
if ($request eq 'GET') { $query=$ENV{'QUERY_STRING'}; } else { sysread(STDIN,$query,$content); }
#Генерируем форму,если никакие данные не введены. print "Content-type:text/html\n\n"; print <<HTML_gen; <HTML><BODY bgcolor="e6e8fa">
HTML_gen
if ($query eq '') { print <<HTML; <h2 align=center><font color="ff0000">Registration.</font></h2> <p><font face="serif" size=2> Please,fill in the form below. <p>After registration you will receive your personal directory and unique URL.Fill all fields carefully. Form fields marked as <font color="ff0000">*</font>are required.</font> <p><FORM ACTION="../cgi-bin/addlogin.cgi" METHOD="POST" name="reg"> <center><TABLE BGCOLOR="bfbfbf"> <TR><td><font color="ff0000">*</font> <TD><b>Login:</b><TD><INPUT TYPE="text" NAME="login" SIZE="20"> <TR><td><font color="ff0000">*</font> <TD><b>Password:</b> <TD><INPUT TYPE="password" NAME="pass" SIZE="20"> <TR><td><font color="ff0000">*</font><TD><b>E-mail:</b> <TD><INPUT TYPE="text" NAME="email" SIZE="20"> <TR><TD colspan=3><p><center> <INPUT TYPE="submit" VALUE="Submit"></center> </TABLE></center> </FORM> HTML


#Декодируем поля формы
else { foreach (@fields=split(/&/,$query)) { if (/^login=(.*)/) { $login=&urldecode ($1); } if (/^pass=(.*)/) { $password=&urldecode ($1); } if (/^email=(.*)/) { $email=&urldecode ($1); } }
#Проверяем, не существует ли данное имя в системе. open(INFO,"login.txt") die; @data=<INFO>;#Читаем строки в массив. close(INFO);
foreach $string(@data) { @item=split(/&/,$string);#Разбиваем строку на части. foreach (@item) { if ($item[0] eq $login) { #Сравниваем полученное имя с первым полем файла #для каждой строки и если такое найдено выдаем #ошибку. print <<HTML; <h2 align=center><font color="ff0000">Error!</font></h2>
<p><center><b>The name <font color="ff0000">$login</font> already exists in the system. <p>Please,go back and choose another name.</b>
<p><form><input type="button" value="Back" onClick="history.back()"></form>
</center>
HTML exit; } } }
#Если имя не найдено,открываем базу данных и добавляем информацию.
if ($item[0] ne $login) { open(DATA,">>login.txt"); $string=join('&',$login,$password,$email,scalar localtime,$ENV {'REMOTE_ADDR'}; print DATA "$string\n"; close(DATA);
#Создаем домашний каталог пользователя и переходим в него.
mkdir("$userdir/$login",0700); chdir("$userdir/login"); opendir(USER,"$userdir/$login");
#Помещаем файл index.html в каталог пользователя.
open(IN,">$userdir/$login/index.html"); print IN "This is the test!\n"; close(IN); closedir(USER);
#Содержание файла может быть любым,это только для примера.
#Генерируем ответ пользователю.
print <<HTML; <p><h1 align=center><font color="ff0000">Congratulations!</font></h1>
<p><b>Your registration was successful and your data were added to our database.Thank you for your time.</b>
<p><center><b><font color="ff0000">
You entered:</font>(print this page and keep it in safe place)</b>
<p><table>
<tr><td><b>Your login name:</b><td><font color="0000ff">$login</font>
<tr><td><b>Your password:</b><td><font color="0000ff">$password</font>
<tr><td><b>Your e-mail address:</b><td><font color="0000ff">$email</font>
</table></center>
HTML } }
Скрипт выдает ответ в виде html-страницы,содержащей всю информацию,введенную пользователем.

Начало скрипта можно взять из


Рассмотрим пример открытия и чтения каталога и вывод списка файлов,содержащихся в нем. Начало скрипта можно взять из предыдущего примера.Предположим,что пользователь,зарегистрированный на веб-сервере,хочет войти в свой домашний каталог.
#!/usr/local/bin/perl #Объявляем глобальные переменные.
$request=$ENV{'REQUEST_METHOD'}; $content=$ENV{'CONTENT_LENGTH'}; $basedir="http://www.mydomain.com/~"; $file="login.txt"; $url="http://www.mydomain.com"; $dir="f:/home/"; $cgi="f:/usr/local/apache/cgi-bin";
#Подпрограммы для декодирования данных из формы.
sub urldecode { local($val)=@_; $val=~ s/\+/ /g; $val=~ s/%[0-9a-hA-H] {2}/pack('C',hex($1))/ge; return $val; }
sub strhtml { local($val)=@_; $val=~s//>/g; $val=~s/(http:\/\/\+S)/<A href="$1">$1<\/A>/g; return $val; } ######################################################################
if ($request eq 'GET') { $query=$ENV{'QUERY_STRING'}; } else { sysread(STDIN,$query,$content); }
#Генерируем форму,если никакие данные не введены. print "Content-type:text/html\n\n"; print <<HTML_gen; <HTML><BODY bgcolor="e6e8fa">
HTML_gen
if ($query eq '') { print "Content-type:text/html\n\n"; print <<HTML; <HTML><HEAD>
</HEAD><BODY bgcolor="e6e8fa">
<FORM ACTION="../cgi-bin/fileman.cgi" name="form1" METHOD="POST">
<h2 align=center><font color="ff0000">System login.</font></h2>
<p><center>Please,enter your login name and password: <p><TABLE BGCOLOR="cccccc">
<tr><td colspan=2 align=center bgcolor="99cccc"><b><font color="ff0000">
I am registered user</font></b>
<TR><TD><p><b>Login:</b><TD><INPUT TYPE="text" NAME="login" SIZE="20">
<TR><TD><p><b>Password:</b><TD><INPUT TYPE="password" NAME="pass" SIZE="20">


<tr><td colspan=2 align=center><input type=submit value="Submit"></center>
HTML } #Если информация получена,декодируем поля формы.
else { foreach (@fields=split(/&/,$query)) { if (/^login=(.*)/) { $login=&urldecode ($1); } if (/^pass=(.*)/) { $password=&urldecode ($1); } }
#Открываем базу данных и проверяем логин и пароль. open(INFO,$file) die; @data=; close(INFO);
foreach $string(@data) { @item=split(/&/,$string); foreach (@item) { if (($item[0] eq $login) && ($item[1] eq $password)) {
#Если все нормально,переходим в пользовательский каталог.
print "Content-type:text/html\n\n"; print <<HTML; <html><body bgcolor="e6e8fa">
#Приветствуем пользователя. <p><h2 align=center><font color="ff0000">Hello,$login!</font></h2>
<p><center> Welcome to your home directory! <p>Your URL is <a href="$basedir$login">$basedir$login.</a></center>
HTML
######################### # Directories list # #########################
$userdir=$dir.$login; chdir ("$userdir");
#Открываем каталог и читаем список файлов в массив. opendir(DIR,"$userdir") die "Cannot open $userdir!"; while (@files=readdir(DIR)) {
#Если каталог содержит подкаталоги,выводим их отдельно,а также не показываем #каталоги "." и ".." Печатаем шапку таблицы. print <<HTML; <p><center>
<table bgcolor=\"bfbfbf\" width=600 border cellspacing=0 cellpadding=0 nowrap>
<tr><td colspan=5 align=center nowrap><b><font color="ff0000">Directories</font></b></td></tr>
<tr><td>.</td><td align=center><b>List</b></td><td><b>Size</b><td><td><b>Last accessed</b></td><td><b>Last modified</b></td>
HTML foreach $file(@files) {


#Стстистика файлов-размер,время последнего обращения и модификации. $size=(stat("$userdir/$file"))[7]; $atime=localtime((stat("$userdir/$file"))[8]); $mtime=localtime((stat("$userdir/$file"))[9]);
#Печатаем список подкаталогов. if ( -d "$userdir/$file" && "$file" ne "." && "$file" ne "..") {
print "<tr><td width=30><img src=\"$url/image/folder.gif\"></td><td width=100 align=left>$file</td>\n"; print "<td width=50>",$size,"</td><td width=200>",$atime,"</td><td width=200>",$mtime,"</td></tr>\n"; } } print "</table>\n";
################ # Files list # ################
# Ту же операцию проводим для файлов.Печатаем шапку таблицы. print <<HTML; <p><table bgcolor=\"bfbfbf\" width=600 border cellspacing=0 cellpadding=0>
<tr><td colspan=5 align=center><b><font color="ff0000">Files</font></b></td></tr>
<tr><td>.</td><td><b>List</b><td><b>Size</b><td><b>Last accessed</b><td><b>Last modified</b></tr>
HTML
foreach $file(@files) { $size=(stat("$userdir/$file"))[7]; $atime=localtime((stat("$userdir/$file"))[8]); $mtime=localtime((stat("$userdir/$file"))[9]);
if (!-d "$userdir/$file" && "$file" ne "." && "$file" ne "..") { push (@dir,"$userdir/$file");#Помещаем найденные файлы в массив $number=@dir; #Подсчитываем их количество.
#Выдаем информацию. print "<tr><td width=30><img src=\"$url/image/page.gif\"></td><td width=100><a href=\"$basedir$login/$file\">",$file,"</a></td>\n"; print "<td width=50>",$size ,"</td>\n"; print "<td width=200>",$atime,"</td><td width=200>",$mtime,"</td></tr>\n"; } } print "</table>\n"; print "<p><center><b><font color=\"0000ff\">There are ",$number," files in this directory.</b></font></center>\n"; }
Надеюсь,я объяснил все достаточно подробно.Я выбрал намеренно сложные примеры,чтобы показать все операции,которые можно производить с файлами и каталогами.Файлы еще можно загружать на сервер через веб.Этому посвящен следующий раздел.

Примеры функций, разное


Q: Как округлить число?

A: sprintf("%3.2f",$i)

Здесь 3 - кол-во знаков до запятой, 2 - после запятой.

Q: Как получить текущую дату и время?

A: Функция time() возвращает время в unix-формате - количество секунд,

прошедших с 1 января 1970 года. Функция localtime() возвращает дату и время.

В контексте массива - значения секунд, минут, и т.п. раздельно, в скалярном

контексте - строку определенного формата. Подробнее см. perldoc -f localtime

Q: Функция time() возвращает время с точностью до секунды. Как работать с

меньшими промежутками?

A: Time::HiRes

Q:Как по дате вычислить день недели?

A1: perldoc -f localtime

A2: Date::Calc

Q: Как, зная дату и время, получить количество секунд, прошедших до этого

времени с 1970 года (unix-time format)?

A: Time::Local

Q: Можно ли сделать GUI-интерфейс в перловой программе, или вывод в GUI-окно

ее результатов?

A: модуль Tk. он и под windows, и под linux есть - можно писать GUI-программы

для разных платформ.

Q: Как устроить загрузку файла с удаленного сервера? Проще говоря, скачать

скриптом файл с http или ftp

A: LWP, Net::Ftp

Q: Как передать из одного скрипта данные другому скрипту методом GET, вроде

бы понятно. А как передавать данные методом POST?

A: Внимательно почитать документацию о LWP::UserAgent и HTTP::Request

+ perldoc lwpcook

Q: Как создавать графические файлы на лету?

A: модуль GD, или внешние программы: Imagemagick, fly,..



Примеры скриптов


Q: Как одновременно поставить куки и сделать редирект?

A:

sub ReLocate

{

my($url,$cookie)=@_;

print "Status: 302 Moved\n";

if ($cookie ne '') { print "Set-Cookie: $cookie\n" }

print "Location: $url\n\n";

}

Примечание:

Не забудьте, что URL должен быть полным, частичные "не прокатывают" под некоторыми ОС и веб-серверами.

Q: Вечная тема - закачка файла на сервер(upload).

A:

#!/usr/bin/perl # используйте -T для CGI-скриптов

$SIG{ALRM} = sub { die "$0 timed out" }; # эти 2 строки не работают под

alarm 900; # Windows, но они необязательны

use strict; # спасает от глупых ошибок

use Fcntl; # O_EXCL, O_CREAT и O_WRONLY

use CGI qw (:standard); # читайте "perldoc CGI"

use CGI::Carp qw (fatalsToBrowser); # вывод ошибок к browser-у

$CGI::POST_MAX = 131072; # максимальный ввод = 128 KB

my $foto = param ('foto'); # имя файла и одноврем. handle

my $DIR = '/home/alex/pics' # не забудьте "chmod 777 pics"

my $JS = " // JavaScript-функция для пред-

// варительной проверки формы

function check (form)

{

if (form.elements['foto'].value.length < 4)// foto должно быть заполнено

{

alert ('A gde zhe foto?'); // показать JavaScript-окошко

form.elements['foto'].select (); // выделить текст в поле foto

form.elements['foto'].focus (); // и прыгнуть туда курсором

return false; // false запретит browser-у

} // отправлять данные к серверу

return true; // все OK - можно отправлять

}";

print header (-type => 'text/html; charset=koi8-r'),

start_html (-title => 'МИСС ИHТЕРHЕТ', # напечатать заголовок и $JS

-script => $JS);

# regex внизу проверяет, есть ли .gif или .jp(e)g в конце и кладет имя

# файла в $1

if ($foto !~ /([\w-]+ \. (?: gif | jpe?g ))$/ix) #только имя,путь выкидывается

{

print h1 ('Отправьте нам фото!'), #start_form работать не будет

start_multipart_form (-onsubmit => 'return check (this)'),

filefield (-name => 'foto'),

submit (-value => 'Go baby go!'), # напечатать форму с кнопкой


end_form; # напечатать

}

# Этот скрипт никому не мешает вводить мусор (например xxxx.gif) в поле foto,

# что приведет к созданию пустых файлов; если это Вас беспокоит, установите

# новую версию CGI.pm и прочтите

# http://stein.cshl.org/WWW/software/CGI/#upload

elsif (not sysopen FILE, "$DIR/$1", O_EXCL | O_CREAT | O_WRONLY, 0444)

{

print h1 ("Hе могу создать $DIR/$1: $!"); # ошибка или такой файл уже есть

}

# Портятся файлы при посылке? См. ниже!

else # все в порядке - файл создан

{

binmode FILE; # включить binmode для Windows

print FILE while (<$foto>); # и скопировать данные в файл

close FILE;

print h1 ('Сердечное Вам спасибо!'); # воспитывать как собак Павлова

}

print end_html; # напечатать

==========================

Если закачка некоторых файлов происходит с глюками - видимо, файл

перекодируется на лету русским апачем.

Варианты решения:

1.запретите все перекодировки для скрипта, который разбирает FileUpload,

например таким способом:

CharsetDisable On

и делайте перекодировку сами.

2.Используйте директиву CharsetRecodeMultipartForms, которая появилась в PL23,

но при этом вам все равно придется перекодировать вручную текстовые части

запросов. Для этого можно использовать Russian Apache API, доступное в других

модулях или Russian Apache Perl API, доступное из mod_perl.

Программа вывода номеров журнала


Еще один пример использования хеша массивов для вывода содержания журнала за несколько лет календарем. Есть директория с файлами содержания журнала по номерам вида:

1.1996.txt

2.1996.txt

3.1996.txt

4.1996.txt

5.1996.txt

...

и т.д. до

5.2001.txt

где первая цифра в названии файла это содержание журнала за данный месяц, а вторая это год.

Читаем эту директорию в массив и вызываем подпрограмму:

while(<$dir/journal/*.txt>){push(@files, $_)}

&calendar;

sub calendar{

print qq~<center><font><b>Содержание

по номерам</b></font><p></center>~;

@year1=grep{!$test{$_}++ if(/^(\d+)$/)} #выделяет число лет, за которые есть номера журнала

#и заодно удаляет одинаковыен года, т.к.

#на каждый год приходится не больше 12

#файлов: 1.1996, 2.1996, 3.1996...

map{/\.(\d+)/, $_} @files; #выделяет года и заносит их во временный массив

foreach $line(@year1){ #цикл по годам.

foreach $files(@files){

push @{$numbers{$line}}, $files if($files=~m/$line/);

#здесь производится заполнение хеша массивов ключами,

#которые являются годами, а значениями хешей буду являться

#массивы номеров журнала за данный год, который является ключом.

#т.е. в результате должно получиться что-то вида:

#%hash = (

# 1996 => ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"],

# 1997 => ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"],

# ...

# 2001 => ["1", "2", "3", "4", "5"] #до пятого номера потому, что

#шестой номер на момент написания этого

#примера еще не вышел.

#);

}

}

print "<center><table>"; #открываем табличку для вывода результатов

for $key (sort keys %numbers){#цикл по отсортированным в порядке возрастания годам


print "<tr><td><font size=\"1\"><b>$key: </b></font>"; #печатаем год

foreach $elem(@{$numbers{$key}}){ # вытаскиваем массив номеров журнала из хеша, ключем

#которому должен являться определенный год

if($elem=~m/\/(\d+).(\d+)\.txt/){$nj=$1; $yj=$2;

if($nj eq $nomer && $yj eq $year){#текущий номер для просмотра выделяем красным:

$temp1=qq~<font size="1" color=red><b>$nj</a> </b></font>\n~;

push(@results123, $temp);

}

else{#остальные выделяем ссылкой

$temp2=qq~<font size="1"><b><a

href="$url?month=$nj&year=$yj" class="menu">$nj</a>

</b></font>\n~;

push(@results123, $temp2)

}

}

}

#дальнейшая конструкция называется преобразованием Рэндела

#Шварца, смысл которой заключается

#в том, чтобы отсортировать массив номеров журнала по возрастанию,

#т.к. при извлечении из хеша они будут выстраиваться в порядке

#1,11,12,2,3,4...

@sort123= map{ $_ -> [1]}

sort{$a->[0] <=> $b->[0]}

map{[/>(\d+)<\/a>/, $_]} @results123;

print @sort123; #печатаем табличку номеров журнала за 1995 год

print "</td></tr>";#закрываем строку таблички

#обнуляем временные массивы

$#results123=-1;

$#sort123-1;

#возвращаемся наверх и начинаем печатать строчку

#таблицы для следующих номеров следующего года.

}

print qq~</table></center>~;

}

Все вышеописанное выглядит в виде html примерно так:

Содержание по номерам

1995:

1996:























1997:























1998:























1999:























2000:























2001:







5


Протокол HTTP





Что такое протокол HTTP и для чего он нужен, я думаю расказывать не нужно, поэтому сразу приступим к более подробному рассмотрению. Какое отношение HTTP протокол имеет к Perl? Да самое прямое. Без знания HTTP протокола, нельзя написать практически никакого CGI скрипта.

Итак, договоримся о терминах.
Веб-сервер - програмное обеспечение, которое следит за 80 (возможны и другие варианты, но данный наиболее популярен) портом и в случае обнаружения запроса, выдает некоторую информацию (сообщение об ошибке, html документ).
Клиент - программное обеспечение осуществляющее подключение к 80 порту сервера и посылающее туда запрос.

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

В процессе прочтения данного документа, вам наверно захочется попробовать все предложенные примеры. Используйте для этого любой telnet клиент. Просто соединитесь с 80 портом сервера, на котором расположен ваш любимый сайт.

Взаимодействие, между веб-сервером и клиентом просто до неприличия. Клиент посылает запрос состоящий из заголовка и (иногда) тела, а веб-сервер выдает ответ, который так же состоит из заголовка и тела. Рассмотрим более детально, как же выглядят эти запросы и ответы.



Любой запрос начинается строкой:

МЕТОД РЕСУРС HTTP/версия


МЕТОД - один из поддерживаемых веб-сервером методов. Наиболее распространены:

GET - в этом случае выдается запрошенный ресурс. Самый старый и самый распространенный метод.

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

POST - аналогичен методу GET, но не только запрашивает ресурс, но и передает ему некоторую информацию. Если ресурс - CGI скрипт, то информация поступает на его стандартный ввод. Метод GET тоже умеет передавать информацию для CGI скриптов, но передает он ее в самом имени ресурса. Дело в том, что если мы вызовем CGI скрипт таким образом:



script.cgi?ПАРАМЕТРЫ



то веб-сервер запустит на выполнение script.cgi, а тот в свою очередь сможет заполучить ПАРАМЕТРЫ. А вот о том, откуда он их получит, позже.

Кроме вышеперечисленных методов, существуют еще методы PUT - для сохранения данных в указанном ресурсе и DELETE - для удаления указанного ресурса. Как правило они не поддерживаются веб-серверами, сами понимаете почему.

Теперь пример запроса:

GET /~user/cgi-bin/test.pl HTTP/1.0

Это конечно самый простой пример. Обычно, запрос выглядит гораздо сложнее и состоит из нескольких строк. Чтобы веб-сервер знал, что ввод данных закончен, нужно послать ему пустую строку. Т.е в данном примере, нужно нажать Enter два раза (один раз - переход на новую строчку, второй раз - пустая строка).

Если вы запрашиваете главную страницу сайта (т.е. в браузере это www.perl.ru) запрос будет выглядеть так:

GET / HTTP/1.0

Практически все переданные данные можно узнать непосредственно в скрипте на перле. Для этого служит массив %ENV, в котором хранятся так называемые переменные среды CGI. Например:

$ENV{REQUEST_METHOD} = GET

$ENV{QUERY_STRING} = те самые
параметры (script.cgi?параметры)

Узнать все пременные очень просто:

foreach (keys %ENV){
 print "$_ = $ENV{$_}\n";
}

Какую же еще информацию, мы можем передавать веб-серверу?

ПолеПримерОписание
Date:Date: Sun, 30 Dec 2000 23:59:59 GMTДата запроса.
MIME-version:MIME-version: 1.0Версия MIME.
Pragma:Pragma: no-cacheИнформация для шлюзов и прокси-серверов.
Authorization:Authorization: Basic QWxhZGRpbj pvcGVuIHNlc2FtZQ==Информация для авторизации.
From:From: Pupkin@mail.rue-mail пользователя браузера.
If-Modified-Since:If-Modified-Since: Sun, 30 Dec 2000 23:59:59 GMTИспользуется при методе GET. Документ возвращается только в том случае, если он изменился с указанного момента. Как правило браузер, запрашивает документ подобным образом, если копия документа содержится у него в кеше. И соответственно выдает пользователю информацию из кеша или обновленный документ.
Referer:Referer: http://www.perl.ru/URL предшествующего ресурса. Именно по нему на некоторых сайтах определяется, насколько хорошо их рекламирует тот или иной ресурс.
User-Agent:User-Agent: Mozilla/5.0Имя клиента. Может стоять что угодно, но некоторые, особенно гадкие сервера, не выдают страницы "не браузерам".
Host:Host: vasya.narod.ruИмя хоста. Дело в том, что в сети существует очень большое количество виртуальных серверов, т.е. серверов с разными именами, но одним IP адресом. Именно по этой переменной веб-сервер определяет какую же информацию вам дать.
Accept: Accept: text/htmlУказывает серверу, выдавать только данные указанного типа.
Вот основные и наиболее часто используемые данные, передаваемые веб-серверу.

Теперь пара примеров запросов:

GET /cgi-bin/test.cgi?name=Vasily&age=18 HTTP/1.0
User-Agent: Mozilla/5.0

И тоже, но методом POST

POST /~user/cgi-bin/test.cgi HTTP/1.0
User-Agent: Mozilla/5.0
Accept: text/html
Accept: image/gif
Content-Type: application/x-www-form-urlencoded
Content-Length: 18

name=Vasily
&age=18

Теперь рассмотрим, что же отвечает веб-сервер. Ответ веб-сервера выглядит следующим образом:

HTTP/ВЕРСИЯ КОД_ОТВЕТА ФРАЗА_ОТВЕТА

КОД_ОТВЕТА - 3-хзначное число. Указывает что все прошло успешно (200) или код ошибки.

ФРАЗА_ОТВЕТА - Тот же код но по-русски (шучу, по-английски)

Пример:

HTTP/1.1 200 OK




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

Location: http://www.perl.ru/

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

Обязательно должна присутствовать строчка:

Content-Type: text/html

указывающая тип переданных данных.

Ну и напоследок, список кодов возвращаемых веб-серверами и их значений

 Код статуса  Значение 
200OK
201Успешная команда POST
202Запрос принят
203    Запрос GET или HEAD выполнен
204Запрос выполнен но нет содержимого
300Ресурс обнаружен в нескольких местах
301Ресурс удален навсегда
302Ресурс отсутствует временно
304Ресурс был изменен
400Плохой запрос от клиента
401Неавторизованный запрос
402Необходима оплата за ресурс
403Доступ Запрещен
404Ресурс не найден
405Метод не применим для данного ресурса
406Недопустимый тип ресурса
410Ресурс Недоступен
500Внутренняя ошибка сервера
501Метод не выполнен
502Неисправный шлюз либо перегруз сервера
503Сервер недоступен/тайм-аут шлюза
504Вторичный шлюз/тайм-аут сервера
Philip A. Koryaka

e-mail:

Copyright c 2001


Рабочие программы, использующие регулярные выражения


В принципе регулярные выражениея это вовсе не вещь в себе, хотя иногда и может встретится задача, фактически полностью реализуемая при помощи regex. Ниже приведены программы, иллюстрирующие использование реглуярных выражений:



Работа с cookies на Perl


Материал взят с сайта http://spgroup.km.ru/

Cookies - небольшие файлы сохраняемые на машине клиента, позволяющие хранить персональную информацию для каждого пользователя на стороне клиента. В Perl для работы с Cookies используется модуль CGI::Cookie

С помощью Cookies Вы можете сохранять на стороне клиента информацию ввиде ключ-занчение. В какой-то степени Cookies можно сравнивать с передачей параметров запроса методом POST, но передаваемые значения в случае Cookies будут храниться на компьютере пользователя, что позволяет идентифицировать любого клиента, зашедшего на Ваш сайт второй или более раз.

В основном Cookies применяются для аутентификации пользователя ресурса. Т.е. в Cookies можно хранить имя пользователя, пароль, или идентификатор сессии пользователя. Таким образом, зарегистрированный на вашем сайте пользователь может входить в свою область идентификации не вводя каждый раз пароль.

Единтефикация пользователя, конечно, не единственное применение Cookies. Уважаемый читатель может пользоваться Cookies для каких-то своих, ему ведомых целей.

Итак, как можно (и нужно) работать с Cookies на Perl?

Во-первых, для работы необходимо подключить модуль:

use CGI::Cookie;

Этот модуль позволит Вам получить доступ для манипуляций с Cookies.

Вторым шагом может служить запись Cookies:

Сначала надо создать Cookie:

$c = new CGI::Cookie(-name => 'SOMENAME',

    -value => 'SOMEVALUE',

    -expires => '+3M',

    -domain => '.someserver.com',

    -path => '/cgi-bin'

    -secure => 1

);

Ключи -name и -value передают пользователю данные которые Вы хотите записать пользователю в формате Ключ - Значение соответственно.

Ключ -expires указывает время жизни Cookie на машине пользователя. В данном примере время жизни 3 месяца. Если время жизни устанавливается в ноль то Cookie существует только тогда, когда запущен браузер и не записывается на диск. После закрытия браузера такой Cookie аннулируется.


Ключ -domain указывает полное или частичное имя сервера, для которого Cookie имеет силу. Браузер возвратит Cookie любому хосту, который соответствует полному или частичному имени указанному в этом ключе. На-пример, если Вы укажете имя домена .somesrver.com, то Cookie будет возвращено любому поддомену этого домена, будь то www.somesrver.com или vasya.somesrver.com. Если же этот ключ не определен, то будет установлено имя хоста по умолчанию, с которого был записан Cookie.

Ключ -path служит для указания пути к скриптам которые используют этот Cookie. Если Вы указываете путь /cgi-bin, то все скрипты в этом каталоге и в его подкаталогах получат Cookie, но скрипты расположенные в других каталогах, не являющихся подкаталогами /cgi-bin этот Cookie не получат.

Если установлен ключ -secure, то Ваш Cookie будет передаваться только по протоколу SSL. По обычному HTTP протоколу он передаваться не будет.

Дальше нобходимо передать созданный Cookie браузеру пользователя:


print header(-cookie=>$c);

Теперь как получить Cookie...



%cookies = fetch CGI::Cookie;

В результате этой операции мы получим хэш со значениями переданного от пользователя Cookie. Далее мы можем манипулировать полученными значениями


$SOMECOOKIE = $cookies{'SOMENAME'}->value;

После этой операции переменная $SOMECOOKIE будет иметь значение 'SOMEVALUE'.


Работа с файлами и каталогами.


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

Кратко напомню основные положения:

Файл можно открывать для чтения,записи,добавления либо поиска определенных строк.

Открыть файл для чтения: open (FILE,"$file");

Открыть файл для записи: open (FILE,">$file");

Открыть файл для добавления: open (FILE,">>$file");

Открыть файл для чтения и добавления: open (FILE,"+<$file");

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

$file-переменная для файла,содержит обычно имя файла и путь к нему.Предпочтительно объявить эту переменную в начале скрипта.Это удобно,если файл находится где-то глубоко в подкаталогах,к примеру,неудобно все время набирать /usr/local/htdocs/main/pages/file.html.

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

#!/usr/local/bin/perl open (STAT,"$file");#Прочесть одну строку из файла. $count=; close (STAT);

------------------------------------------------- #!/usr/local/bin/perl open (STAT,"$file");#Прочесть файл в массив. @count=; close (STAT);

Что касается каталога,то его тоже можно открывать для чтения командой readdir.Для того,чтобы понять как все это происходит,рассмотрим практические примеры.



Работа со строками в файле


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

Для чтения строк из файла используется файловый манипулятор "поставленный" в <>.

Например:

open(FIL,"data.txt"); while() { # делаем что-то с каждой строкой файла }

Если не указано иначе, то внутри такого цикла используется стандартная переменная "$_", а номер строки записывается в "$.". Так как конец строки обозначается спецсимволом, например "\n", для получения самой строки (без эдакого "хвоста") нужно ее усечь функцией chomp.

open(FIL,"data.txt"); while() { chomp; # отрезается спецсимвол от переменной $_ print; # пишем $_ на STDOUT }

Можно прочитать строки в массив:

@strings=; foreach $line (@strings) { print $list; }

Для передвижения по файлу используются функции tell и seek.

open(FIL,"data.txt"); $position=tell(FIL); print "Текущая позиция в файле $position. \n"; seek(FIL,$position+10,1); print "А теперь переместились на 10 байт к концу файла от текущей позиции. \n"; $position=tell(FIL); print "Теперь текущая позиция в файле $position. \n";

Результат:

Текущая позиция в файле 0. А теперь переместились на 10 байт к концу файла. Теперь текущая позиция в файле 10.

Функция tell принимает в качестве параметра файловый манипулятор, а seek берет три параметра. Первый - файловый манипулятор, второй - смещение в байтах, третий - направление смещение. Есть три направления смещения: 0 - от начала файла, 1 - от текущей позиции, 2 - с конца файла.

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

Буду очень благодарен за сообщения о возможных ошибках и неточностях в этой статье.

С уважением. © 2001



Работаем с файлами на Perl


О том, что такое файл писать я надеюсь ненужно, но думаю нужно подумать над тем, что с ними можно делать, как видно из заголовка статьи, на Perl. Ну, приступим . . .

В этой статье обсудим: Что такое файловые манипуляторы, и с чем их едят Манипуляции с файлом Работа со строками в файле



Regular expressions (регулярные выражения)


Q: А что это за закорючки такие в скрипте - s/^[^\w]{1,3}(\d+)/$1/

A: Это регулярные выражения, одна из мощнейших возможностей perl. Средства

поиска и замены подстрок в строках

Q: Как ими пользоваться?

A: Этому учатся всю жизнь. Даже книга есть - "Mastering regular

expressions". Также следует прочесть perldoc perlre

А начать можно со следующего: // - поиск

s/// - поиск и замена

tr/// - трансляция

Если мы хотим узнать, нет ли в строке $_ подстроки 'my', мы пишем:

/my/;

Если мы имеем дело не с переменной по умолчанию, а с любой другой, пишем:

$str=~/my/;

Если мы хотим заменить в строке подстроку tree на root, используем поиск и

замену:

$str=~s/tree/root/; В конце выражения могут стоять опции g, i и т.п. g означает проводить замену

не 1 раз, а по всей подстроке. i означает не учитывать регистр символов. В подстроке для поиска можно использовать управляющиесимволы. Есть

несколько видов управляющих символов- мета-символы, обозначающие какой-то

символиз набора букв, цифр,и т.п., мета-символы, управляющие количеством

символов, и т.п. Hапример, \d в подстроке обозначает любую цифру, \w -

любую букву, \s - пробел, . - любой символ. Стоящая после мета-символа *

означает, что предыдущий символ может повторяться 0 или более раз. + означает

повторение 1 или более раз. То есть, строка вида $str=~s/\s\d+\s/ 1 /g; означает "заменить все числа в строке $str, отделенные пробелами с двух

сторон, на цифру 1, отделенную пробелами". Более подробное описание регулярных выражений, список управляющих символов и

других возможностей смотрите в документации.

Q: Как не учитывать регистр в русском тексте? С английским регекспы с ключом

i работают, а с русским - нет.

A: Hа UNIX'оподобных системах с корректной локализацией (FreeBSD, Linux на

glibc 2.1) это делается так:

use locale;

use POSIX qw (locale_h);

setlocale(LC_CTYPE, 'ru_RU.KOI8-R'); (можно еще выставить переменную окружения export LANG=ru_RU.KOI8-R

тогда будет достаточно вызова use locale; в Perl-скрипте)



Set permissions:


chmod 755 (-rwxr-xr-x) on all .cgi files.

chmod 666 (-rw-rw-rw-) on all files in the data directory.

chmod 777 (drwxrwxrwx) on the public directory

Тоже многие тут спотыкаются - а что я вытворял!!! Бедный сервер наверное свихнулся - о существовании таких прав доступа, что я ставил, он и не подозревал! Итак, теперь надо установить права доступа: 755 - наивысший приоритет, всем файлам *.cgi, *.pl надо установить его. В инструкциях все пишут - следуйте им! Если есть сомнения, то можно действовать по такому принципу - всем файлам, которые для массового использования ставим 777 , всем исполнительным (*.cgi, *.pl) -755, а тем, которые непонятно какие - 666. Если скрипт не запускается и выдает ошибку (500 Server Errors - очень часто ;-) ), то возможно вы не правильно расставили права, проверьте все (*.cgi, *.pl) в первую очередь! Как эти права расставлять? Опять с помощью FTP клиента: Change File Attributes - и пишете в появившемся окошке необходимые права! Теперь дополнение одного из моих читателей:

................

Было бы неплохо не просто порекомендовать какие права доступа ставить (и для каких файлов), а подробно объяснить, что обозначает каждая буковка в записи "drwxrwxrwx" и цифра в команде "CHMOD" . Это позволило бы новичкам, прочитавшим статью, сэкономить кучу времени и нервов (GK: как он прав!).

Так вот, права доступа определяют: кто и что он может сделать с файлами/папками, размещенными на сервере.

"Кто" подразделяются на три вида:

"Owner" - владелец файла/папки

"Group" - группа, к которой относится владелец

"Everyone" - все остальные пользователи

"Что" подразумевает: какие действия с файлами/папками могут производить Пользователи, Группа или Владелец, а именно:

"Read" - чтение

"Write" - запись

"Execute/Search" - выполнение (для файлов) и поиск (для каталогов).

Число, следующее после команды "chmod xyz имя_файла/папки" расшифровывается следующим образом:


- первая цифра (x) - это права владельца

- вторая (y) - права группы

- третья (z) - права доступа для остальных пользователей.

Каждая цифра - состоит из суммы чисел, которыми эти права обозначаются:

Чтение - число 4

Запись - число 2

Выполнение/поиск - число 1

То есть, если нужно предоставить владельцу право читать, записывать и выполнять файл, то число x получается из суммы 4+2+1 = 7. Если нужно установить права доступа только для чтения и выполнения, то 4+0+1 = 5.

Рассмотрим конкретный пример: что означает это преславутое число 755?

Первая цифра - 7 - право доступа для Владельца (4+2+1) чтение+запись+выполнение.

Вторая цифра - 5 - право доступа для Группы (4+0+1) чтение+выполнение.

Третья цифра - 5 - право доступа для остальных Пользователей (4+0+1) чтение+выполнение. Данное число (755) рекомендуется устанавливать для CGI скриптов, а остальным файлам - 660 (GK: подразумеваются только скриптовые файлы, а не все остальные! Ваши публичные документы имеют доступ 777 - то есть самые широкие права). Также, если есть файлы, в которых хранятся данные "не для чужих глаз" (а именно: шаблоны, файлы регистрации и т.д), то для них я бы порекомендовал установить права доступа - 600 (GK: а часто им ставят 666).

Теперь рассмотрим, что означают буковки "drwxrwxrwx" при выдаче листинга файлов в каталоге. Тут все очень просто:

d - указывает на то, что это папка. Если стоит "-" - значит это файл.

rwx - права доступа для владельца - "r"ead, "w"rite и e"x"ecute, если вместо какой-либо буквы стоит "-", то это означает, что данное право отсутствует.

Cледующая троица "rwx" аналогична предыдущей, только она определяет права доступа для группы.

И последняя троица "rwx" также определяет права доступа для остальных пользователей. Следовательно, права доступа к файлу 755 выглядят так: "-rwxr-x-r-x"

. .........

Я могу лишь выразить благодарность!


Сheck that the Path to perl is correct. Links defaults with #!/usr/local/bin/perl".


Многие прекращают знакомство со скриптами после подобной фразы. Вас просят убедиться, что правильно прописан путь к perl (а этот путь вы посмотрели - в пред.пункте). Теперь, чем вскрыть скрипт (*.cgi, *.pl)? Можно сделать это в текстовом редакторе, но это не лучший способ для новичков. Я делаю это с помощью PerlBuilder (для его функционирования вам придется скачать ActivePerl, его предлагают прям там). Чем он хорош (т.е. Perl Builder)? Проверка синтаксиса, вскрывает все: *.cgi, *.pl, *.html, *.cfg, *.def, *.php, *.html и т.д. Вы сможете проверить работоспособность скрипта прямо у себя дома на компьютере! Правда, особо не обольщайтесь - топ дома вы не запустите, но добьетесь вывода кода html - значит работает!

Итак, открываете файл, там в самом верху строка: #!/usr/bin/perl - исправляете ее на нужную! И все! Если, что-то надо прописать в самом скрипте (опции, например, или пути к папкам(!) - весьма часто встречается) - то внимательно следуйте инструкциям! Если что-то добавляете в код, то перед этим делайте резервную копию - а вдруг? Мы то не программисты!



Шпионская программа


Допустим в Пентагоне нужно ввести авторизованный доступ к данным по определенным правилам:

есть полный файл(data.crypt) всех записей вида(расшифрованный)

London#явка1#доступ#прочее#логин1

Damask#явка2#доступ#прочее#логин2

Peru#явка3#доступ#прочее#логин3

И есть файл скажем для агента agent007.crypt, в котором описан вид доступа только

к определенным данным:

логин1

логин3

т.е. из файла data.crypt нужно выбрать совпадения строк с подстроками файла agent007.crypt и выбрать из data.crypt все параметры спецзадания(-ий). Результат нужно вывести в виде:

<A NAME=\"a\">

aasd

abf

Absc

afgh

<A NAME=\"b\">

bcd

bgh

bfe

и т.д.

т.е. в отсортированном виде. Шпионы тоже люди.

Скрипт ниже решает данную задачу при помощи операций с массивами и использованием хеша массивов(хотя наверное можно было сделать и проще, здесь же главное показать использование хеша массивов):

#!/usr/bin/perl -wT #ключ -T означает повышенную безопасность, Пентагон все-таки

use strict;

my(@data, @new, $line, $m, @res, @sort, $k, %ha);

my($autor, $pesniya, $position, $u, $n, $im);

open F, "<data.crypt" or die "Err: $!"; @data=<F>; close F;

open F, "<agent007.crypt" or die "Err: $!" @data=<F>; close F;

foreach $line(@new){

foreach $m (@data){push @res, "$m" if($m=~m/^(.*)#$line$/)}

}

@sort=map{$_ -> [1]}

sort{$a->[0] cmp $b->[0]}

map{[/^(.*)#/, $_]}

grep{!$_{$_}++} @res;

foreach $u('a' .. 'z'){

foreach $n(@sort){push @{$ha{$u}}, $n if($n=~m/^$u/)}

}

for $k(sort keys %ha){print "<a href=\"#$k\">$k</a> "}

print "\n<p><center>\n";

for $k(sort keys %ha){

print "<a name=\"$k\"></a><br>\n";

foreach $im(@{$ha{$k}}){

($autor, $pesniya, $position)=split /#/, $im;

print "$autor, $pesniya, $position<br>\n";

}

}

print "</center>";

Разберем работу программы:


foreach $line(@new){

foreach $m (@data){push @res, "$m" if($m=~m/^(.*)#$line$/)}

}

Отсеять из файла data.crypt разрешенные данные для agent007.crypt при помощи сравнения подстрок.

@sort=map{$_ -> [1]}

sort{$a->[0] cmp $b->[0]}

map{[/^(.*)#/, $_]}

grep{!$_{$_}++} @res;

Отсортировать данные для данного агента в алфавитном порядке по первой ячейке из общей таблицы data.crypt и убрать повторения одинаковых строк.

foreach $u('a' .. 'z'){

foreach $n(@sort){push @{$ha{$u}}, $n if($n=~m/^$u/)}

}

Создавть хеш массивов, где ключем будет буква, а значением будет массив из строк спецзаданий.

for $k(sort keys %ha){print "<a href=\"#$k\">$k</a> "}

Вывести линейку начальных букв, по которым будут сортироваться результаты.

for $k(sort keys %ha){

print "<a name=\"$k\"> </a><br>\n";

foreach $im(@{$ha{$k}}){

($autor, $pesniya, $position)=split /#/, $im;

print "$autor, $pesniya, $position<br>\n";

}

}

Вывести массивы, ассоциированные со значениями ключей букв. @{$ha{$k}} - просто обычный массив, доступ к которому зависит от значения ключа $k. Каждый элемент массива стостоит из строки с разделителями #, по нему и разделяет функция split ($autor, $pesniya, $position)=split /#/, $im;


Сортировки


Сортировка хэша:

%hash = (

'шляпа' => 'серая',

'водка' => 'горькая',

'вобла' => 'вкусная');

foreach $key(sort keys %hash){

print "$key => $hash{$key}\n"; #отсортирует в алфавитном порядке по значениям ключа

}

foreach $value(sort values %hash){

print "$value\n"; #сортировка по значению

}

Сортировка ключей по алфавиту ассоциированных значений:

foreach $key(sort {$hash{$a} cmp $hash{$b}} keys %hash){

print $key, " => ", $hash{$key},"\n";

}

Сортировка по длинне ассоциированных значений:

@massive = sort{length($hash{$a}) length($hash{$b})}

keys %hash;

foreach $key(@massive){

print $key, $hash{$key},"\n";

}

Пример из :

>> Есть хеш массив (слово, частота), необходимо вывести в файл пары

>> (слово,частота) отсортированные по частоте. По ключю - просто, а вот

>> по значению затрудняюсь. Подскахите как проще сделать?

my %hash=('for'=>1000,'to'=>1500,'in'=>2000,'do'=>500);

foreach(sort {$hash{$a} $hash{$b}} keys %hash) {

print $_,'=',$hash{$_},"\n";

}

Как можно хеш положить в строку? Например :

tnc> имеются логи http-gw. Плохость их

tnc> заключается в том, что на каждый запрос в логе появляется 4 строки,

tnc> совершенно не обязательно идущие подряд:

Создаешь хэш и держишь в нем те строчки, которые еще, грубо говоря,

не закрыты, а как закроешь - удаляй. Конечно, некий заметный кусок

файла в памяти будет присутствовать, но не полностью же.

Что-нибудь типа

while(<IN>) {

($key, $no, $data) = parse($_); # расковыряли строчку

$buf{$key}-<[$no] = $data; # запихнули в хэш в виде массива

next if $no > 3; # нумеруем, ессно с нуля

analyze($buf{$key}); # обработали

delete $buf{key}; # удалили

}

Слияние хешей выполняется как и слияние массивов:

%hash1 = (

'шляпа' => 'серая',

'водка' => 'горькая',

'вобла' => 'вкусная');

%hash2 = (

'штаны' => 'широкие',

'пиво' => 'темное',

'игрушка' => 'любимая');


%allhash = (%hash1, %hash2);

Чтобы сэкономить память, можно воспользоваться таким кодом:

%hash=();

while (($key, $values) = each(%hash1)){

$hash{$key} = $values;

}

while (($key, $values) = each(%hash2)){

$hash{$key} = $values;

}

Пример работы с хэшем и базой данных из (cм. ответ):

VS> Скажите, можно ли ввести в dbm-базу данных уже заполненный значениями

VS> хеш? Если да, то как?

%hash1=("dfadf", "dfadfd");

dbmopen (%hash, "hash", 0666);

%hash=%hash1;

dbmclose (%hash);

Как передать хэш в функцию? (см. всю дискуссию)

> PK> Вобщем то все передаеться, но использовать его нельзяю

> PK> Hа %_[0] или $_[0]{??} компилер ругаеться.

> %a;

> &f(\%a);

> sub f {

> my $a = $($_[0]){'key'};

> }

> кажется так, мог ошибится в написании. Смысл в передаче ссылки, а не значения.

Это можно сделать также:

1. Неявной передачей ссылки (использование прототипов):

sub f(\%) {

my $a=$_[0]->{'key'};

}

f(%a);

2. Передачей хэша как массива с четным числом

элементов:

sub f {

my $a={@_}->{'key'};

}

f(%a);

Пример использования хеша для транслитерации :

RK> Как сочнить такой tr/../../ (и возможно ли), чтобы

RK> "аб я" -> "abyoya"

RK> Т.е. транслитерацию сделать...

самый простой и топорный вариант - сделать хеш

с соответствиями типа я => ya сплитить строчки и заменять элементы

полученного массива на нужные. что-то на подобие:

@letters = split //, $str;

for (@letters){$_ = $hash{$_}};


Split


Если необходимо разделить данные из STDIN по нужному разделителю, то можно воспользоваться локализацией $/:

sub example_local{ local $/ = undef; @mass= split /pattern/, <>; return 1; } print scalar(@mass);

Можно разделять данные из файла и так:

undef $/; @res=split /pattern/, <F>;

что эквивалентно:

while (<F>) {push @asdf, split}

После split можно ставить вместо запятой и стрелочку:

@mass = split /(\d){4}/ => $file;

В функции сплит можно воспользоваться макисмальным квантификатором *, который в том числе и о символов, позволит разделить строку на символы, которых там нет(в силу того, что * это 0 и более символов), т.е. посимвольно:

@ruru = split /\001*/ => "lalalalalala"; #массив @ruru будет содержать элементы по одной букве.

Если строка состоит из нескольких строк, то можно поставить разделителем и символ начала новой строки:

$str = "asdf\nghjk\nqwer\n"; @lines = split /^/ => $str;

Вобщем, в split можно вставлять любой поиск по шаблону.



Сщетчик посещений


Наверное тоже одним из часто встречающихся приложений CGI являются счетчики посещений. Они стоят практически на каждой страничке, возможно даже и у вас. Но иногда вас не устраивает тот факт, что счетчик лежит где-то в другом месте.Из-за этого скажем невозможно начать счет с произвольного числа.Или еще некоторые счетчики по разному фильтруют 'Reload'. Да и мало ли? Ну а иногда вам хочется просто сделать другой дизайн цифр. То если вы CGI-програмист то возможно имеет смысл написать свой счетчик. И делать с ним что захочется. Вот я так-же написал.....

Скрипт данного счетчика обслужевает несколько счетчиков ,им вы присваиваете идентификаторы. Поэтому вы спокойно можете втыкать независимые счетчики в разные страницы сайта и даже давать это делать друзьям. В общем он прост в использовании:<IMG src="cgi-bin/counter.cgi?id=name">, Где name -любое уникальное имя идентифицирующее счетчик.Вытакже можете задать необязательный параметр dig

который задает количество цифр в счетчике ,Например:

<IMG src="cgi-bin/counter.cgi?id=doom2&dig=9">.

Получится примерно вот так:

.gif'ы в счетчике с прозрачными областями.Что дает дополнительную гибкость к примеру для улучшения внешнего вида с помощью другого фона его иногда имеет смысл запихнуть в "таблицу":

<TABLE><TR><TD bgcolor="white"><IMG src="counter.gif"></TD></TR></TABLE>

Свои данные он пишет примерно в такой файл counter.dat:

doom2 4 127.0.0.1 906992351 quake2 1 127.0.0.1 906992700 quake 3 127.0.0.1 906992668 doom 1 127.0.0.1 906991960

Вы спросите,зачем столько информации? Чтобы отфильтровывать нажатия Reload. Если с одного IP-адреса между заходами промежуток меньше чем 30 секунд,то счетчик не инкрементируется (Так например поступает счетчик в Rambler'е).

Теперь об исходнике.Скрипт получился великоват,потому,что я вместо чтения из файлов решил .gif'ы запихнуть в сам скрипт.Выглядит громоздко , зато пашет как трактор ;))!!




#!/usr/bin/perl #counter.cgi $gifheader="GIF89a"; # Использую 14x16 .gif $digx_size=14; $digy_size=16; $datafile="counter.dat"; ###################################################### $GifData[0]= "GIF89a\x0E\x00\x10\x00\xEF\x00\x00C\x03\x03\x03\xFF\xFF\x2F\x2F\x2F\x8B\x03\x03\x7F\x03\x03\x9B\x03\x03\xA7". "\x03\x03\x5B\x03\x03g\x03\x03\xBF\x03\x03s\x03\x03\xB3\x03\x03O\x03\x03\xCB\x03\x03\xFF\xB7\xB7\x3F\x2B\x1BG3\x23". "\xF3\xA3\xA3\xEB\x97\x97O\x3B\x2B\xF7\xAB\xAB\xDF\x87\x87\xE7\x8F\x8F\xD3ss\xCBkk\xC7cc\xBF\x5B\x5B\xBBW". "W\xDB\x7B\x7B\xAFGG\xA7\x3F\x3F\xA3\x3B\x3B\x9B33\xB3OO\x8F\x2B\x2B\x8B\x23\x23\x83\x1F\x1F\x7F\x1B\x1Bw". "\x17\x17s\x13\x13k\x0F\x0Fg\x0B\x0B_\x07\x07\x5B\x07\x07S\x07\x07\x97\x2F\x2FG\x03\x03C\x03\x03\xFF\xEB\xDF". "\xFF\xE3\xD3\xFF\xDB\xC7\xFF\xD3\xBB\xFF\xCF\xB3\xFF\xC7\xA7\xFF\xBF\x9B\xFF\xBB\x93\xFF\xB3\x83\xF7\xAB\x7B\xEF\xA3s\xE7\x9B". "k\xDF\x93c\xD7\x8B\x5B\xCF\x83S\xCB\x7FO\xBF\x7BK\xB3sG\xABoC\xA3k\x3F\x9Bc\x3B\x8F_7\x87". "W3\x7FS\x2FwO\x2BkG\x27_C\x23S\x3F\x1FK7\x1B\x3F\x2F\x173\x2B\x13\x2B\x23\x0F\xEF\xEF\xEF". "\xE7\xE7\xE7\xDF\xDF\xDF\xDB\xDB\xDB\xD3\xD3\xD3\xCB\xCB\xCB\xC7\xC7\xC7\xBF\xBF\xBF\xB7\xB7\xB7\xB3\xB3\xB3\xAB\xAB\xAB\xA7\xA7". "\xA7\x9F\x9F\x9F\x97\x97\x97\x17\x1F\x07\x2F7\x1F\x0F\x17\x03\x23\x2B\x0F\x07\x07\x07oookkkccc\x5B". "\x5B\x5BWWWOOOGGGCCC\x3B\x3B\x3B777\x13\x13\x13\x27\x27\x27\x23\x23\x23w\xFFo". "o\xEFgg\xDF__\xCFW\x5B\xBFOS\xAFGK\x9F\x3FC\x937\x3F\x83\x2F7s\x2B\x2Fc\x23\x1B\x1B". "\x1B\x1FC\x17\x173\x0F\x13\x23\x0B\x0B\x17\x07\x03\x03\x03\xB7\x9F\x87\xAF\x97\x7F\xA7\x8Fw\x9F\x87o\x9B\x7Fk\x93". "\x7Bc\x8Bs\x5B\x83kW\x7BcOw_KoWCgS\x3F_K7WC3S\x3F\x2F\x9F\x83c". "\x8FwS\x83kKw_\x3FgS3\x5BG\x2BO\x3B\x23C3\x1B\x7B\x7FcosWgkO\x5Bc". "GSW\x3BGO3\x3FG\x2B7\x3F\x27\xFF\xFFs\xEB\xDBW\xD7\xBBC\xC3\x9B\x2F\xAF\x7B\x1F\x9B\x5B\x13\x87". "C\x07s\x2B\x03\xFF\xFF\xFF\xFF\xDB\xDB\xFF\xBB\xBB\xFF\x9B\x9B\xFF\x7B\x7B\xFF__\xFF\x3F\x3F\xFF\x1F\x1F\xFF\x03\x03". "\xEF\x03\x03\xE3\x03\x03\xD7\x03\x03\x83\x83\x83\x0B\x0B\x0B\x7F\x7F\x7F\xFF\xFF\xFFKKK\x17\x0F\x07\x1F\x17\x0Bww". "w\x93\x93\x93\x8B\x8B\x8BO\x03\x03\x27S\x1B\xE7\xE7\xFF\xC7\xC7\xFF\xAB\xAB\xFF\x8F\x8F\xFFss\xFFSS\xFF7". "7\xFF\x1B\x1B\xFF\x03\x03\xFF\x03\x03\xE3\x03\x03\xCB\x03\x03\xB3\x03\x03\x9B\x03\x03\x83\x03\x03k\x03\x03S\xFF\xFF\xFF". "\xFF\xEB\xDB\xFF\xD7\xBB\xFF\xC7\x9B\xFF\xB3\x7B\xFF\xA3\x5B\xFF\x8F\x3B\xFF\x7F\x1B\xF3s\x17\xEBo\x0F\xDFg\x0F\xD7_". "\x0B\xCBW\x07\xC3O\x03\xB7G\x03\xAFC\x03\xFF\xFF\xFF\xFF\xFF\xD7\xFF\xFF\xB3\xFF\xFF\x8F\xFF\xFFk\xFF\xFFG\xFF". "\xFF\x23\xFF\xFF\x03\xA7\x3F\x03\x9F7\x03\x93\x2F\x03\x87\x23\x03O\x3B\x27C\x2F\x1B7\x23\x13\x2F\x1B\x0B\x03\x03S". "\x03\x03G\x03\x03\x3B\x03\x03\x2F\x03\x03\x23\x03\x03\x17\x03\x03\x0B\xBF\xA7\x8F\xFF\x9FC\xFF\xE7K\xFF\x7B\xFF\xFF\x03". "\xFF\xCF\x03\xCF\x9F\x03\x9Bo\x03k\xA7kk\x21\xF9\x04\x01\x00\x00\x01\x00\x2C\x00\x00\x00\x00\x0E\x00\x10\x00\x00\x08". "\x94\x00\x03\x08\x0C\x00\xA0\x60\xC1\x81\x08\x0B\x12\x28\xB0\x60\x40\x81\x83\x03\x01\x1C\x28\xA0\xC0\xE1\x01\x05\x06\x0E\x00\x88". "h\xC0\x00\x81\x01\x03\x10\x20H\xF0P\x00A\x02\x1D\x09\x20\x00\xC0\xE0\x00I\x02\x1B\x01\x60\x1C\x00S\x00\x80\x91". "\x05\x06\x00\xB0y\xC0\x00\xCD\x98\x0A\x16\x14\xA8\x291\xA7N\x82\x0A\x12\xFC\xE4i4\x26\x81\x069w\xCA4\xD0". "\xD0\x29I\x9D6Q6\x7C\xD8\xF0\xE5\xCE\x93\x0B\x3E\xA6L\x60\x60\xA7I\x96\x08\x14\x20\x28\xE0\x91\x80F\x93\x02". "\x0B\x228\x10R\xA3Y\x846\x0D\x9A\x85\x8B0\x80\x80\xBF\x7F\x11\x06\x04\x00\x3B"; $GifData[1]= "GIF89a\x0B\x00\x10\x00\xEF\x00\x00C\x03\x03\x03\xFF\xFF\x2F\x2F\x2F\xBF\x03\x03\x8B\x03\x03\xCB\x03\x03\x9B". "\x03\x03\xA7\x03\x03\xB3\x03\x03\x7F\x03\x03\xD7\x03\x03s\x03\x03G3\x23\x3F\x2B\x1B\x0F\x17\x03O\x3B\x2B\xFF\xB7\xB7". "\xF7\xAB\xAB\xF3\xA3\xA3\xEB\x97\x97\xE7\x8F\x8F\xDF\x87\x87\xDB\x7B\x7B\xD3ss\xCBkk\xC7cc\xBF\x5B\x5B\xBBW". "W\xB3OO\xAFGG\xA7\x3F\x3F\xA3\x3B\x3B\x9B33\x97\x2F\x2F\x8F\x2B\x2B\x8B\x23\x23\x83\x1F\x1F\x7F\x1B\x1Bw". "\x17\x17s\x13\x13k\x0F\x0Fg\x0B\x0B_\x07\x07\x5B\x07\x07S\x07\x07O\x03\x03G\x03\x03C\x03\x03\xFF\xEB\xDF". "\xFF\xE3\xD3\xFF\xDB\xC7\xFF\xD3\xBB\xFF\xCF\xB3\xFF\xC7\xA7\xFF\xBF\x9B\xFF\xBB\x93\xFF\xB3\x83\xF7\xAB\x7B\xEF\xA3s\xE7\x9B". "k\xDF\x93c\xD7\x8B\x5B\xCF\x83S\xCB\x7FO\xBF\x7BK\xB3sG\xABoC\xA3k\x3F\x9Bc\x3B\x8F_7\x87". "W3\x7FS\x2FwO\x2BkG\x27_C\x23S\x3F\x1FK7\x1B\x3F\x2F\x173\x2B\x13\x2B\x23\x0F\xEF\xEF\xEF". "\xE7\xE7\xE7\xDF\xDF\xDF\xDB\xDB\xDB\xD3\xD3\xD3\xCB\xCB\xCB\xC7\xC7\xC7\xBF\xBF\xBF\xB7\xB7\xB7\xB3\xB3\xB3\xAB\xAB\xAB\xA7\xA7". "\xA7\x9F\x9F\x9F\x23\x2B\x0F\x93\x93\x93\x2F7\x1F\x83\x83\x83\x7F\x7F\x7F\x17\x1F\x07oookkkccc\x5B". "\x5B\x5BWWWOOOGGGCCC\x3B\x3B\x3B777\x07\x07\x07\x27\x27\x27\x23\x23\x23w\xFFo". "o\xEFgg\xDF__\xCFW\x5B\xBFOS\xAFGK\x9F\x3FC\x937\x3F\x83\x2F7s\x2B\x2Fc\x23\x0B\x0B". "\x0B\x1FC\x17\x173\x0F\x13\x23\x0B\x0B\x17\x07\x03\x03\x03\xB7\x9F\x87\xAF\x97\x7F\xA7\x8Fw\x9F\x87o\x9B\x7Fk\x93". "\x7Bc\x8Bs\x5B\x83kW\x7BcOw_KoWCgS\x3F_K7WC3S\x3F\x2F\x9F\x83c". "\x8FwS\x83kKw_\x3FgS3\x5BG\x2BO\x3B\x23C3\x1B\x7B\x7FcosWgkO\x5Bc". "GSW\x3BGO3\x3FG\x2B7\x3F\x27\xFF\xFFs\xEB\xDBW\xD7\xBBC\xC3\x9B\x2F\xAF\x7B\x1F\x9B\x5B\x13\x87". "C\x07s\x2B\x03\xFF\xFF\xFF\xFF\xDB\xDB\xFF\xBB\xBB\xFF\x9B\x9B\xFF\x7B\x7B\xFF__\xFF\x3F\x3F\xFF\x1F\x1F\xFF\x03\x03". "\xEF\x03\x03\xE3\x03\x03www\x13\x13\x13\x1B\x1B\x1B\xFF\xFF\xFFKKK\x17\x0F\x07\x1F\x17\x0B\x97\x97\x97\x8B\x8B". "\x8Bg\x03\x03\x5B\x03\x03O\x03\x03\x27S\x1B\xE7\xE7\xFF\xC7\xC7\xFF\xAB\xAB\xFF\x8F\x8F\xFFss\xFFSS\xFF7". "7\xFF\x1B\x1B\xFF\x03\x03\xFF\x03\x03\xE3\x03\x03\xCB\x03\x03\xB3\x03\x03\x9B\x03\x03\x83\x03\x03k\x03\x03S\xFF\xFF\xFF". "\xFF\xEB\xDB\xFF\xD7\xBB\xFF\xC7\x9B\xFF\xB3\x7B\xFF\xA3\x5B\xFF\x8F\x3B\xFF\x7F\x1B\xF3s\x17\xEBo\x0F\xDFg\x0F\xD7_". "\x0B\xCBW\x07\xC3O\x03\xB7G\x03\xAFC\x03\xFF\xFF\xFF\xFF\xFF\xD7\xFF\xFF\xB3\xFF\xFF\x8F\xFF\xFFk\xFF\xFFG\xFF". "\xFF\x23\xFF\xFF\x03\xA7\x3F\x03\x9F7\x03\x93\x2F\x03\x87\x23\x03O\x3B\x27C\x2F\x1B7\x23\x13\x2F\x1B\x0B\x03\x03S". "\x03\x03G\x03\x03\x3B\x03\x03\x2F\x03\x03\x23\x03\x03\x17\x03\x03\x0B\xBF\xA7\x8F\xFF\x9FC\xFF\xE7K\xFF\x7B\xFF\xFF\x03". "\xFF\xCF\x03\xCF\x9F\x03\x9Bo\x03k\xA7kk\x21\xF9\x04\x01\x00\x00\x01\x00\x2C\x00\x00\x00\x00\x0B\x00\x10\x00\x00\x08". "g\x00\x03\x08\x04\x40\xB0\x20\x00\x81\x01\x00\x1C\x40P\xA0\x80\x82\x03\x00\x04\x24\x24p\xC0\xC0\x80\x01\x08\x08D\x04". "\x40\x80\xE2\x81\x86\x034\x0A0\xC8\xB0\x40\xC8\x88\x01\x04\x8C\xFC\xD8P\x24B\x85\x03Z\xA2\x1Ch\xF1\xA2K\x9A". "\x17OJ\xC4isf\xC2\x9A\x03\x12\xF8\x04\x60\x00\x81Q\xA1\x3B\x7F\x22\xB8\x88\xF4\x25\x01\x03\x1D\x17\x0C5H". "0iJ\x95X\x05\x04\x04\x00\x3B"; $GifData[2]= "GIF89a\x0E\x00\x10\x00\xEF\x00\x00C\x03\x03\x03\xFF\xFF\x2F\x2F\x2F\x7F\x03\x03\xA7\x03\x03\x9B\x03\x03\xB3". "\x03\x03\xBF\x03\x03\x8B\x03\x03\xEF\x03\x03\xCB\x03\x03s\x03\x03\xD7\x03\x03\xE3\x03\x03g\x03\x03\xFF\xB7\xB7\xF7\xAB\xAB". "\xF3\xA3\xA3\x3F\x2B\x1B\xEB\x97\x97\xE7\x8F\x8F\xDF\x87\x87\xDB\x7B\x7B\xD3ss\xCBkk\xC7cc\xBF\x5B\x5B\xBBW". "W\xB3OO\xAFGG\xA7\x3F\x3F\xA3\x3B\x3B\x9B33\x97\x2F\x2F\x8F\x2B\x2B\x8B\x23\x23\x83\x1F\x1F\x7F\x1B\x1Bw". "\x17\x17s\x13\x13k\x0F\x0Fg\x0B\x0B_\x07\x07\x5B\x07\x07S\x07\x07O\x03\x03G\x03\x03C\x03\x03\xFF\xEB\xDF". "\xFF\xE3\xD3\xFF\xDB\xC7\xFF\xD3\xBB\xFF\xCF\xB3\xFF\xC7\xA7\xFF\xBF\x9B\xFF\xBB\x93\xFF\xB3\x83\xF7\xAB\x7B\xEF\xA3s\xE7\x9B". "k\xDF\x93c\xD7\x8B\x5B\xCF\x83S\xCB\x7FO\xBF\x7BK\xB3sG\xABoC\xA3k\x3F\x9Bc\x3B\x8F_7\x87". "W3\x7FS\x2FwO\x2BkG\x27_C\x23S\x3F\x1FK7\x1B\x3F\x2F\x173\x2B\x13\x2B\x23\x0F\xEF\xEF\xEF". "\xE7\xE7\xE7\xDF\xDF\xDF\xDB\xDB\xDB\xD3\xD3\xD3\xCB\xCB\xCB\xC7\xC7\xC7\xBF\xBF\xBF\xB7\xB7\xB7\xB3\xB3\xB3\xAB\xAB\xAB\xA7\xA7". "\xA7\x9F\x9F\x9F\x2F7\x1FO\x3B\x2B\x0F\x17\x03G3\x23\x7F\x7F\x7F\x23\x2B\x0Foookkkccc\x5B". "\x5B\x5BWWWOOOGGGCCC\x3B\x3B\x3B777\x0B\x0B\x0B\x27\x27\x27\x23\x23\x23w\xFFo". "o\xEFgg\xDF__\xCFW\x5B\xBFOS\xAFGK\x9F\x3FC\x937\x3F\x83\x2F7s\x2B\x2Fc\x23\x13\x13". "\x13\x1FC\x17\x173\x0F\x13\x23\x0B\x0B\x17\x07\x03\x03\x03\xB7\x9F\x87\xAF\x97\x7F\xA7\x8Fw\x9F\x87o\x9B\x7Fk\x93". "\x7Bc\x8Bs\x5B\x83kW\x7BcOw_KoWCgS\x3F_K7WC3S\x3F\x2F\x9F\x83c". "\x8FwS\x83kKw_\x3FgS3\x5BG\x2BO\x3B\x23C3\x1B\x7B\x7FcosWgkO\x5Bc". "GSW\x3BGO3\x3FG\x2B7\x3F\x27\xFF\xFFs\xEB\xDBW\xD7\xBBC\xC3\x9B\x2F\xAF\x7B\x1F\x9B\x5B\x13\x87". "C\x07s\x2B\x03\xFF\xFF\xFF\xFF\xDB\xDB\xFF\xBB\xBB\xFF\x9B\x9B\xFF\x7B\x7B\xFF__\xFF\x3F\x3F\xFF\x1F\x1F\xFF\x03\x03". "\x17\x1F\x07\x83\x83\x83\x07\x07\x07www\x1B\x1B\x1B\xFF\xFF\xFFKKK\x17\x0F\x07\x1F\x17\x0B\x97\x97\x97\x93\x93". "\x93\x8B\x8B\x8B\x5B\x03\x03O\x03\x03\x27S\x1B\xE7\xE7\xFF\xC7\xC7\xFF\xAB\xAB\xFF\x8F\x8F\xFFss\xFFSS\xFF7". "7\xFF\x1B\x1B\xFF\x03\x03\xFF\x03\x03\xE3\x03\x03\xCB\x03\x03\xB3\x03\x03\x9B\x03\x03\x83\x03\x03k\x03\x03S\xFF\xFF\xFF". "\xFF\xEB\xDB\xFF\xD7\xBB\xFF\xC7\x9B\xFF\xB3\x7B\xFF\xA3\x5B\xFF\x8F\x3B\xFF\x7F\x1B\xF3s\x17\xEBo\x0F\xDFg\x0F\xD7_". "\x0B\xCBW\x07\xC3O\x03\xB7G\x03\xAFC\x03\xFF\xFF\xFF\xFF\xFF\xD7\xFF\xFF\xB3\xFF\xFF\x8F\xFF\xFFk\xFF\xFFG\xFF". "\xFF\x23\xFF\xFF\x03\xA7\x3F\x03\x9F7\x03\x93\x2F\x03\x87\x23\x03O\x3B\x27C\x2F\x1B7\x23\x13\x2F\x1B\x0B\x03\x03S". "\x03\x03G\x03\x03\x3B\x03\x03\x2F\x03\x03\x23\x03\x03\x17\x03\x03\x0B\xBF\xA7\x8F\xFF\x9FC\xFF\xE7K\xFF\x7B\xFF\xFF\x03". "\xFF\xCF\x03\xCF\x9F\x03\x9Bo\x03k\xA7kk\x21\xF9\x04\x01\x00\x00\x01\x00\x2C\x00\x00\x00\x00\x0E\x00\x10\x00\x00\x08". "\x8F\x00\x03\x00\x18Hp\x60\x80\x83\x08\x01\x20\x20\xC0\x40\x81\x02\x03\x04\x1C\x00\x40\x28p\xC0B\x03\x05\x0AX\x1C". "0\xF1\x20\x80\x05\x08\x16\x0C\x18\xB0\x20\xE3\x01\x8E\x1E\x0B\x0E\x1C\x40\xE0\x40\x01\x00\x02\x0E\x0AP9\xC0\x80\x82\x97". "1\x2B\x2Ah\xD0\xF0\xC0\x01\x03\x1Cc\x02\x18P\x00\x28\x01\x03\x3E\x83\xCE\x24z\xD4\x81\xC5\x900\x85b\xD4\xA8". "R\x40N\x00\x10\x09pT\xD9\x11\x00\x81\xA3\x04\x12\x88\x15k\x00\xA6\xC0\xAF\x07Z\xFAd\xD0\x00g\xC5\x91\x20". "\x11\x14\xF8\x8A\xC0\x2CW\xAE9\xAD\xEA\xDD\xAB7\x40\x40\x00\x3B"; $GifData[3]= "GIF89a\x0E\x00\x10\x00\xEF\x00\x00C\x03\x03\x03\xFF\xFF\x2F\x2F\x2Fs\x03\x03\xBF\x03\x03\xB3\x03\x03\xE3". "\x03\x03\xCB\x03\x03\x8B\x03\x03\x9B\x03\x03\xD7\x03\x03\xEF\x03\x03\xA7\x03\x03g\x03\x03\x7F\x03\x03G3\x23\xFF\xB7\xB7". "\xF7\xAB\xAB\xF3\xA3\xA3\xEB\x97\x97\xE7\x8F\x8F\xDF\x87\x87\xDB\x7B\x7B\xD3ss\xCBkk\xC7cc\xBF\x5B\x5B\xBBW". "W\xB3OO\xAFGG\xA7\x3F\x3F\xA3\x3B\x3BO\x3B\x2B\x3F\x2B\x1B\x8F\x2B\x2B\x8B\x23\x23\x83\x1F\x1F\x7F\x1B\x1Bw". "\x17\x17s\x13\x13k\x0F\x0Fg\x0B\x0B_\x07\x07\x5B\x07\x07S\x07\x07O\x03\x03G\x03\x03C\x03\x03\xFF\xEB\xDF". "\xFF\xE3\xD3\xFF\xDB\xC7\xFF\xD3\xBB\x9B33\x97\x2F\x2F\xFF\xCF\xB3\xFF\xBB\x93\xFF\xB3\x83\xF7\xAB\x7B\xEF\xA3s\xE7\x9B". "k\xDF\x93c\xD7\x8B\x5B\xCF\x83S\xCB\x7FO\xBF\x7BK\xB3sG\xABoC\xA3k\x3F\x9Bc\x3B\x8F_7\x87". "W3\x7FS\x2FwO\x2BkG\x27_C\x23S\x3F\x1FK7\x1B\x3F\x2F\x173\x2B\x13\x2B\x23\x0F\xEF\xEF\xEF". "\xE7\xE7\xE7\xDF\xDF\xDF\xDB\xDB\xDB\xD3\xD3\xD3\xCB\xCB\xCB\xC7\xC7\xC7\xBF\xBF\xBF\xB7\xB7\xB7\xB3\xB3\xB3\xAB\xAB\xAB\xA7\xA7". "\xA7\x9F\x9F\x9F\xFF\xC7\xA7\xFF\xBF\x9B\xFF\xFF\xFF\x83\x83\x83\x17\x1F\x07\x0F\x17\x03oookkkccc\x5B". "\x5B\x5BWWWOOOGGGCCC\x3B\x3B\x3B777\x17\x0F\x07\x27\x27\x27\x23\x23\x23w\xFFo". "o\xEFgg\xDF__\xCFW\x5B\xBFOS\xAFGK\x9F\x3FC\x937\x3F\x83\x2F7s\x2B\x2Fc\x23\x1F\x17". "\x0B\x1FC\x17\x173\x0F\x13\x23\x0B\x0B\x17\x07\x03\x03\x03\xB7\x9F\x87\xAF\x97\x7F\xA7\x8Fw\x9F\x87o\x9B\x7Fk\x93". "\x7Bc\x8Bs\x5B\x83kW\x7BcOw_KoWCgS\x3F_K7WC3S\x3F\x2F\x9F\x83c". "\x8FwS\x83kKw_\x3FgS3\x5BG\x2BO\x3B\x23C3\x1B\x7B\x7FcosWgkO\x5Bc". "GSW\x3BGO3\x3FG\x2B7\x3F\x27\xFF\xFFs\xEB\xDBW\xD7\xBBC\xC3\x9B\x2F\xAF\x7B\x1F\x9B\x5B\x13\x87". "C\x07s\x2B\x03\xFF\xFF\xFF\xFF\xDB\xDB\xFF\xBB\xBB\xFF\x9B\x9B\xFF\x7B\x7B\xFF__\xFF\x3F\x3F\xFF\x1F\x1F\xFF\x03\x03". "\x7F\x7F\x7F\x23\x2B\x0F\x2F7\x1F\x07\x07\x07\x0B\x0B\x0B\x13\x13\x13www\x1B\x1B\x1BKKK\x97\x97\x97\x93\x93". "\x93\x8B\x8B\x8B\x5B\x03\x03O\x03\x03\x27S\x1B\xE7\xE7\xFF\xC7\xC7\xFF\xAB\xAB\xFF\x8F\x8F\xFFss\xFFSS\xFF7". "7\xFF\x1B\x1B\xFF\x03\x03\xFF\x03\x03\xE3\x03\x03\xCB\x03\x03\xB3\x03\x03\x9B\x03\x03\x83\x03\x03k\x03\x03S\xFF\xFF\xFF". "\xFF\xEB\xDB\xFF\xD7\xBB\xFF\xC7\x9B\xFF\xB3\x7B\xFF\xA3\x5B\xFF\x8F\x3B\xFF\x7F\x1B\xF3s\x17\xEBo\x0F\xDFg\x0F\xD7_". "\x0B\xCBW\x07\xC3O\x03\xB7G\x03\xAFC\x03\xFF\xFF\xFF\xFF\xFF\xD7\xFF\xFF\xB3\xFF\xFF\x8F\xFF\xFFk\xFF\xFFG\xFF". "\xFF\x23\xFF\xFF\x03\xA7\x3F\x03\x9F7\x03\x93\x2F\x03\x87\x23\x03O\x3B\x27C\x2F\x1B7\x23\x13\x2F\x1B\x0B\x03\x03S". "\x03\x03G\x03\x03\x3B\x03\x03\x2F\x03\x03\x23\x03\x03\x17\x03\x03\x0B\xBF\xA7\x8F\xFF\x9FC\xFF\xE7K\xFF\x7B\xFF\xFF\x03". "\xFF\xCF\x03\xCF\x9F\x03\x9Bo\x03k\xA7kk\x21\xF9\x04\x01\x00\x00\x01\x00\x2C\x00\x00\x00\x00\x0E\x00\x10\x00\x00\x08". "\x85\x00\x01\x08\x1C80\x80A\x83\x00\x10\x280\xC0\xF0\x00\x81\x01\x02\x0F\x02p\xC0\xA0\x40\x01\x02\x16\x19\x24h". "\x00\x00\xE1\x80\x8F\x20\x07\x10\x20\x90\x00\x80\x80\x00\x04\x09\x128\x90\x00\xA2A\x01\x02R\xAEd\x80\xC0\xA4\xC4\x03". "\x0D\x09\x28\x20P\xF3\x24\xC2\x91\x16u\xF2\xB4\xE9\xF1\x23\x82\x02\x06\x86\xFAD\xA9\xD2\x40\x81\x9E\x29\x07\x12H\xCA". "1f\x81\x05X\x15\x1Cp\xA8\xA0\x24L\x00\x09\x82Z\xBC\xB8\xD1\x26\x80\x90\x21\x05\xC2d\x1A\xD5\xE4\xDA\x970". "\xE3\xC6\x3D\x18\x20\x20\x00\x3B"; $GifData[4]= "GIF89a\x0E\x00\x10\x00\xEF\x00\x00C\x03\x03\x03\xFF\xFF\x2F\x2F\x2F\x7F\x03\x03s\x03\x03\x8B\x03\x03g". "\x03\x03\xA7\x03\x03\x9B\x03\x03\xB3\x03\x03\xBF\x03\x03\xD7\x03\x03\xE3\x03\x03\xEF\x03\x03S\x07\x07\x5B\x03\x03C\x03\x03". "\xCBkk\xEB\x97\x97\xE7\x8F\x8F\xDF\x87\x87\xF7\xAB\xAB\xFF\xB7\xB7\xDB\x7B\x7B\xF3\xA3\xA3\xC7cc\xBF\x5B\x5B\xBBW". "W\xB3OO\xD3ss\xA7\x3F\x3F\xAFGG\x9B33\x97\x2F\x2F\x8F\x2B\x2B\x8B\x23\x23\x83\x1F\x1F\x7F\x1B\x1Bw". "\x17\x17s\x13\x13k\x0F\x0Fg\x0B\x0B_\x07\x07\x5B\x07\x07\xA3\x3B\x3BO\x03\x03G\x03\x03\x83\x83\x83\xFF\xEB\xDF". "\xFF\xE3\xD3\xFF\xDB\xC7\xFF\xD3\xBB\xFF\xCF\xB3\xFF\xC7\xA7\xFF\xBF\x9B\xFF\xBB\x93\xFF\xB3\x83\xF7\xAB\x7B\xEF\xA3s\xE7\x9B". "k\xDF\x93c\xD7\x8B\x5B\xCF\x83S\xCB\x7FO\xBF\x7BK\xB3sG\xABoC\xA3k\x3F\x9Bc\x3B\x8F_7\x87". "W3\x7FS\x2FwO\x2BkG\x27_C\x23S\x3F\x1FK7\x1B\x3F\x2F\x173\x2B\x13\x2B\x23\x0F\xEF\xEF\xEF". "\xE7\xE7\xE7\xDF\xDF\xDF\xDB\xDB\xDB\xD3\xD3\xD3\xCB\xCB\xCB\xC7\xC7\xC7\xBF\xBF\xBF\xB7\xB7\xB7\xB3\xB3\xB3\xAB\xAB\xAB\xA7\xA7". "\xA7\x9F\x9F\x9F\x97\x97\x97\x0F\x17\x03G3\x23\x3F\x2B\x1B\x7F\x7F\x7FwwwoooO\x3B\x2Bccc\x5B". "\x5B\x5BWWWOOOGGGCCC\x3B\x3B\x3B777\x23\x2B\x0F\x27\x27\x27\x23\x23\x23w\xFFo". "o\xEFgg\xDF__\xCFW\x5B\xBFOS\xAFGK\x9F\x3FC\x937\x3F\x83\x2F7s\x2B\x2Fc\x23\x2F7". "\x1F\x1FC\x17\x173\x0F\x13\x23\x0B\x0B\x17\x07\x03\x03\x03\xB7\x9F\x87\xAF\x97\x7F\xA7\x8Fw\x9F\x87o\x9B\x7Fk\x93". "\x7Bc\x8Bs\x5B\x83kW\x7BcOw_KoWCgS\x3F_K7WC3S\x3F\x2F\x9F\x83c". "\x8FwS\x83kKw_\x3FgS3\x5BG\x2BO\x3B\x23C3\x1B\x7B\x7FcosWgkO\x5Bc". "GSW\x3BGO3\x3FG\x2B7\x3F\x27\xFF\xFFs\xEB\xDBW\xD7\xBBC\xC3\x9B\x2F\xAF\x7B\x1F\x9B\x5B\x13\x87". "C\x07s\x2B\x03\xFF\xFF\xFF\xFF\xDB\xDB\xFF\xBB\xBB\xFF\x9B\x9B\xFF\x7B\x7B\xFF__\xFF\x3F\x3F\xFF\x1F\x1F\xFF\x03\x03". "\x17\x1F\x07kkk\x07\x07\x07\xCB\x03\x03\x0B\x0B\x0B\x13\x13\x13\x1B\x1B\x1B\xFF\xFF\xFFKKK\x17\x0F\x07\x1F\x17". "\x0B\x93\x93\x93\x8B\x8B\x8BO\x03\x03\x27S\x1B\xE7\xE7\xFF\xC7\xC7\xFF\xAB\xAB\xFF\x8F\x8F\xFFss\xFFSS\xFF7". "7\xFF\x1B\x1B\xFF\x03\x03\xFF\x03\x03\xE3\x03\x03\xCB\x03\x03\xB3\x03\x03\x9B\x03\x03\x83\x03\x03k\x03\x03S\xFF\xFF\xFF". "\xFF\xEB\xDB\xFF\xD7\xBB\xFF\xC7\x9B\xFF\xB3\x7B\xFF\xA3\x5B\xFF\x8F\x3B\xFF\x7F\x1B\xF3s\x17\xEBo\x0F\xDFg\x0F\xD7_". "\x0B\xCBW\x07\xC3O\x03\xB7G\x03\xAFC\x03\xFF\xFF\xFF\xFF\xFF\xD7\xFF\xFF\xB3\xFF\xFF\x8F\xFF\xFFk\xFF\xFFG\xFF". "\xFF\x23\xFF\xFF\x03\xA7\x3F\x03\x9F7\x03\x93\x2F\x03\x87\x23\x03O\x3B\x27C\x2F\x1B7\x23\x13\x2F\x1B\x0B\x03\x03S". "\x03\x03G\x03\x03\x3B\x03\x03\x2F\x03\x03\x23\x03\x03\x17\x03\x03\x0B\xBF\xA7\x8F\xFF\x9FC\xFF\xE7K\xFF\x7B\xFF\xFF\x03". "\xFF\xCF\x03\xCF\x9F\x03\x9Bo\x03k\xA7kk\x21\xF9\x04\x01\x00\x00\x01\x00\x2C\x00\x00\x00\x00\x0E\x00\x10\x00\x00\x08". "\x8B\x00\x01\x08\x1C\x18\x60\x20A\x00\x05\x184H\x00\xA0\xE0\x81\x05\x0B\x10\x00\x10\x00\x60\x40\x82\x04\x05\x1AV\x3C". "\x80\x20\x23\xC5\x01\x07\x0E\x0C\xD08\x00A\xC7\x89\x08C\x8E\x2C8\xA0\x40\x81\x91\x14\x0B\x98\x1C\x29\x90\x40K\x98". "\x08\x2F\x16H\x10\x92\x80O\x9C\x05\x14\x28\xE0\xF8r\x80O\x03\x28\x83\xF2\x1C\x60\xC0\xC0O\xA4\x14\x0D\x0Et\xCA". "tb\x80\x00\x02\xB2f\x05\x40\x95\x80\xD5\xAB\x60\x0BR\x85\x1A\x16\x2CW\x02\x05\xBC\x0A\x28\x7B\x15\x80\x83\x07\x04". "\x0C\x40X\xCBV\xEAW\xB6Z\xB5\x06\x04\x00\x3B"; $GifData[5]= "GIF89a\x0E\x00\x10\x00\xEF\x00\x00C\x03\x03\x2F\x2F\x2F\x03\xFF\xFF\x7F\x03\x03\x9B\x03\x03\xA7\x03\x03\x8B". "\x03\x03s\x03\x03\xBF\x03\x03\xB3\x03\x03\xD7\x03\x03\xCB\x03\x03\xEF\x03\x03\xE3\x03\x03\x3F\x2B\x1B\x23\x2B\x0F\xFF\xB7\xB7". "\xF7\xAB\xAB\xF3\xA3\xA3\xEB\x97\x97\xE7\x8F\x8F\xDF\x87\x87\xDB\x7B\x7B\xD3ss\xCBkk\xC7cc\xBF\x5B\x5B\xBBW". "W\xB3OO\xAFGG\xA7\x3F\x3F\xA3\x3B\x3B\x9B33\x97\x2F\x2F\x8F\x2B\x2B\x8B\x23\x23\x83\x1F\x1F\x7F\x1B\x1Bw". "\x17\x17s\x13\x13k\x0F\x0Fg\x0B\x0B_\x07\x07\x5B\x07\x07S\x07\x07O\x03\x03G\x03\x03C\x03\x03\xFF\xEB\xDF". "\xFF\xE3\xD3\xFF\xDB\xC7\xFF\xD3\xBB\xFF\xCF\xB3\xFF\xC7\xA7\xFF\xBF\x9B\xFF\xBB\x93\xFF\xB3\x83\xF7\xAB\x7B\xEF\xA3s\xE7\x9B". "k\xDF\x93c\xD7\x8B\x5B\xCF\x83S\xCB\x7FO\xBF\x7BK\xB3sG\xABoC\xA3k\x3F\x9Bc\x3B\x8F_7\x87". "W3\x7FS\x2FwO\x2BkG\x27_C\x23S\x3F\x1FK7\x1B\x3F\x2F\x173\x2B\x13\x2B\x23\x0F\xEF\xEF\xEF". "\xE7\xE7\xE7\xDF\xDF\xDF\xDB\xDB\xDB\xD3\xD3\xD3\xCB\xCB\xCB\xC7\xC7\xC7\xBF\xBF\xBF\xB7\xB7\xB7\xB3\xB3\xB3\xAB\xAB\xAB\xA7\xA7". "\xA7\x9F\x9F\x9F\x1B\x1B\x1B\x17\x1F\x07O\x3B\x2BG3\x23\x7F\x7F\x7Fwwwoookkkccc\x5B". "\x5B\x5BWWWOOOGGGCCC\x3B\x3B\x3B777KKK\x27\x27\x27\x23\x23\x23w\xFFo". "o\xEFgg\xDF__\xCFW\x5B\xBFOS\xAFGK\x9F\x3FC\x937\x3F\x83\x2F7s\x2B\x2Fc\x23\x17\x0F". "\x07\x1FC\x17\x173\x0F\x13\x23\x0B\x0B\x17\x07\x03\x03\x03\xB7\x9F\x87\xAF\x97\x7F\xA7\x8Fw\x9F\x87o\x9B\x7Fk\x93". "\x7Bc\x8Bs\x5B\x83kW\x7BcOw_KoWCgS\x3F_K7WC3S\x3F\x2F\x9F\x83c". "\x8FwS\x83kKw_\x3FgS3\x5BG\x2BO\x3B\x23C3\x1B\x7B\x7FcosWgkO\x5Bc". "GSW\x3BGO3\x3FG\x2B7\x3F\x27\xFF\xFFs\xEB\xDBW\xD7\xBBC\xC3\x9B\x2F\xAF\x7B\x1F\x9B\x5B\x13\x87". "C\x07s\x2B\x03\xFF\xFF\xFF\xFF\xDB\xDB\xFF\xBB\xBB\xFF\x9B\x9B\xFF\x7B\x7B\xFF__\xFF\x3F\x3F\xFF\x1F\x1F\xFF\x03\x03". "\x0F\x17\x03\x0B\x0B\x0B\x2F7\x1F\x13\x13\x13\x07\x07\x07\x93\x93\x93\xFF\xFF\xFF\x1F\x17\x0B\x83\x83\x83\x97\x97\x97\x8B\x8B". "\x8Bg\x03\x03\x5B\x03\x03O\x03\x03\x27S\x1B\xE7\xE7\xFF\xC7\xC7\xFF\xAB\xAB\xFF\x8F\x8F\xFFss\xFFSS\xFF7". "7\xFF\x1B\x1B\xFF\x03\x03\xFF\x03\x03\xE3\x03\x03\xCB\x03\x03\xB3\x03\x03\x9B\x03\x03\x83\x03\x03k\x03\x03S\xFF\xFF\xFF". "\xFF\xEB\xDB\xFF\xD7\xBB\xFF\xC7\x9B\xFF\xB3\x7B\xFF\xA3\x5B\xFF\x8F\x3B\xFF\x7F\x1B\xF3s\x17\xEBo\x0F\xDFg\x0F\xD7_". "\x0B\xCBW\x07\xC3O\x03\xB7G\x03\xAFC\x03\xFF\xFF\xFF\xFF\xFF\xD7\xFF\xFF\xB3\xFF\xFF\x8F\xFF\xFFk\xFF\xFFG\xFF". "\xFF\x23\xFF\xFF\x03\xA7\x3F\x03\x9F7\x03\x93\x2F\x03\x87\x23\x03O\x3B\x27C\x2F\x1B7\x23\x13\x2F\x1B\x0B\x03\x03S". "\x03\x03G\x03\x03\x3B\x03\x03\x2F\x03\x03\x23\x03\x03\x17\x03\x03\x0B\xBF\xA7\x8F\xFF\x9FC\xFF\xE7K\xFF\x7B\xFF\xFF\x03". "\xFF\xCF\x03\xCF\x9F\x03\x9Bo\x03k\xA7kk\x21\xF9\x04\x01\x00\x00\x02\x00\x2C\x00\x00\x00\x00\x0E\x00\x10\x00\x00\x08". "\x8D\x00\x01\x08\x1CHP\xA0\x00\x01\x00\x0E\x20\x28\x90\x60\x81\x82\x87\x05\x0C\x00\x08\x80P\xE1\x00\x02\x04\x06\x18\x88". "8\x60\x22\xC2\x01\x08\x08\x1C\x18p\xA0\xA4\x81\x03\x1E\x01\x10\x40\xD0\xB1\xA0\x40\x8A\x2A\x17\xB4\x24\x18\xA0\xE6\xC7\x04". "\x08\x1C6P\xC0\x12\xC0\xC1\x8A\x08\x10\x24\x88\xC80\xA3\xCF\x8A\x07\x0C\x18\x20i\x60\xA8\xD1\x835\x0B\x5E\x24\x90". "\x60\xA2K\x81\x03\x0A0\xB4\x9A\x80\x81W\x06\x0B6\x260\x1A\x40\xE5\xCE\xA0\x18\x0B\xF4\x8C\xBA\xB1\x00\x01\xA5\x24". "\x3B\xDA\xBC\xFA\xD2\x26\xD4\x9Ax\xF1\xFE\x14\x10\x10\x00\x3B"; $GifData[6]= "GIF89a\x0E\x00\x10\x00\xEF\x00\x00C\x03\x03\x03\xFF\xFF\x2F\x2F\x2F\xBF\x03\x03s\x03\x03\x8B\x03\x03\x9B". "\x03\x03\xA7\x03\x03\xB3\x03\x03g\x03\x03\xCB\x03\x03\xD7\x03\x03\x7F\x03\x03\xE3\x03\x03\xFF\xB7\xB7\x3F\x2B\x1B\xF7\xAB\xAB". "G3\x23\xF3\xA3\xA3\xEB\x97\x97\xE7\x8F\x8F\xDF\x87\x87\xDB\x7B\x7B\xD3ss\xCBkk\xC7cc\xBF\x5B\x5B\xBBW". "W\xB3OO\xAFGG\xA7\x3F\x3F\xA3\x3B\x3B\x9B33\x97\x2F\x2F\x8F\x2B\x2B\x8B\x23\x23\x83\x1F\x1F\x7F\x1B\x1Bw". "\x17\x17s\x13\x13k\x0F\x0Fg\x0B\x0B_\x07\x07\x5B\x07\x07S\x07\x07O\x03\x03G\x03\x03C\x03\x03\xFF\xEB\xDF". "\xFF\xE3\xD3\xFF\xDB\xC7\xFF\xD3\xBB\xFF\xCF\xB3\xFF\xC7\xA7\xFF\xBF\x9B\xFF\xBB\x93\xFF\xB3\x83\xF7\xAB\x7B\xEF\xA3s\xE7\x9B". "k\xDF\x93c\xD7\x8B\x5B\xCF\x83S\xCB\x7FO\xBF\x7BK\xB3sG\xABoC\xA3k\x3F\x9Bc\x3B\x8F_7\x87". "W3\x7FS\x2FwO\x2BkG\x27_C\x23S\x3F\x1FK7\x1B\x3F\x2F\x173\x2B\x13\x2B\x23\x0F\xEF\xEF\xEF". "\xE7\xE7\xE7\xDF\xDF\xDF\xDB\xDB\xDB\xD3\xD3\xD3\xCB\xCB\xCB\xC7\xC7\xC7\xBF\xBF\xBF\xB7\xB7\xB7\xB3\xB3\xB3\xAB\xAB\xAB\xA7\xA7". "\xA7\x9F\x9F\x9F\x0F\x17\x03\x17\x1F\x07O\x3B\x2B\x83\x83\x83\x23\x2B\x0Fwwwoookkkccc\x5B". "\x5B\x5BWWWOOOGGGCCC\x3B\x3B\x3B777\x17\x0F\x07\x27\x27\x27\x23\x23\x23w\xFFo". "o\xEFgg\xDF__\xCFW\x5B\xBFOS\xAFGK\x9F\x3FC\x937\x3F\x83\x2F7s\x2B\x2Fc\x23\x1F\x17". "\x0B\x1FC\x17\x173\x0F\x13\x23\x0B\x0B\x17\x07\x03\x03\x03\xB7\x9F\x87\xAF\x97\x7F\xA7\x8Fw\x9F\x87o\x9B\x7Fk\x93". "\x7Bc\x8Bs\x5B\x83kW\x7BcOw_KoWCgS\x3F_K7WC3S\x3F\x2F\x9F\x83c". "\x8FwS\x83kKw_\x3FgS3\x5BG\x2BO\x3B\x23C3\x1B\x7B\x7FcosWgkO\x5Bc". "GSW\x3BGO3\x3FG\x2B7\x3F\x27\xFF\xFFs\xEB\xDBW\xD7\xBBC\xC3\x9B\x2F\xAF\x7B\x1F\x9B\x5B\x13\x87". "C\x07s\x2B\x03\xFF\xFF\xFF\xFF\xDB\xDB\xFF\xBB\xBB\xFF\x9B\x9B\xFF\x7B\x7B\xFF__\xFF\x3F\x3F\xFF\x1F\x1F\xFF\x03\x03". "\xEF\x03\x03\x2F7\x1F\xFF\xFF\xFF\x7F\x7F\x7F\x07\x07\x07\x0B\x0B\x0B\x13\x13\x13\x1B\x1B\x1BKKK\x97\x97\x97\x93\x93". "\x93\x8B\x8B\x8B\x5B\x03\x03O\x03\x03\x27S\x1B\xE7\xE7\xFF\xC7\xC7\xFF\xAB\xAB\xFF\x8F\x8F\xFFss\xFFSS\xFF7". "7\xFF\x1B\x1B\xFF\x03\x03\xFF\x03\x03\xE3\x03\x03\xCB\x03\x03\xB3\x03\x03\x9B\x03\x03\x83\x03\x03k\x03\x03S\xFF\xFF\xFF". "\xFF\xEB\xDB\xFF\xD7\xBB\xFF\xC7\x9B\xFF\xB3\x7B\xFF\xA3\x5B\xFF\x8F\x3B\xFF\x7F\x1B\xF3s\x17\xEBo\x0F\xDFg\x0F\xD7_". "\x0B\xCBW\x07\xC3O\x03\xB7G\x03\xAFC\x03\xFF\xFF\xFF\xFF\xFF\xD7\xFF\xFF\xB3\xFF\xFF\x8F\xFF\xFFk\xFF\xFFG\xFF". "\xFF\x23\xFF\xFF\x03\xA7\x3F\x03\x9F7\x03\x93\x2F\x03\x87\x23\x03O\x3B\x27C\x2F\x1B7\x23\x13\x2F\x1B\x0B\x03\x03S". "\x03\x03G\x03\x03\x3B\x03\x03\x2F\x03\x03\x23\x03\x03\x17\x03\x03\x0B\xBF\xA7\x8F\xFF\x9FC\xFF\xE7K\xFF\x7B\xFF\xFF\x03". "\xFF\xCF\x03\xCF\x9F\x03\x9Bo\x03k\xA7kk\x21\xF9\x04\x01\x00\x00\x01\x00\x2C\x00\x00\x00\x00\x0E\x00\x10\x00\x00\x08". "\x93\x00\x03\x08\x0C\x00\xA0\xA0A\x83\x03\x0B\x12\x18\xA0\x60\x81\xC3\x01\x05\x00\x08\x20\x98\xC0\xC0\x01\x04\x08\x0E\x1C\x28". "\xC0\x80\x81D\x82\x06\x06\x0C\x20\x40\xB2\x24\x01\x89\x00\x08\x18P0\xE0\xE0A\x01\x00\x0A\x1C\x60\xE9R\x80M\x98". "\x05\x06\x2C\x60\xD8\x40\xE4\x81\x82\x02c\xEA\x1C\x80\xD1\xA2\xC5\x88\x04s6\x40P\x80\x24\x83\xA3\x28\x0B\x20\xE8y". "\xB0\x80\x81\x880\x13\x0C\xA0j\xD0\x2AV\x90\x3B\x15\x288\x60\x80\x40\x81\xAF\x00\x2A\x12E\xC0\x80d\x82\x93\x13". "\x09\xA6t\x9B\x20AA\x9B\x03a\xD6\xC4\x3BP\xE0\xCD\x9B\x03\x03\x02\x00\x3B"; $GifData[7]= "GIF89a\x0E\x00\x10\x00\xEF\x00\x00\x03\xFF\xFFC\x03\x03\x2F\x2F\x2F\x8B\x03\x03s\x03\x03\x9B\x03\x03\xB3". "\x03\x03\xA7\x03\x03\x7F\x03\x03\xBF\x03\x03g\x03\x03\x0F\x17\x03O\x3B\x2BG3\x23\x3F\x2B\x1B\x7F\x7F\x7F\xFF\xB7\xB7". "\xF7\xAB\xAB\xF3\xA3\xA3\xEB\x97\x97\xE7\x8F\x8F\xDF\x87\x87\xDB\x7B\x7B\xD3ss\xCBkk\xC7cc\xBF\x5B\x5B\xBBW". "W\xB3OO\xAFGG\xA7\x3F\x3F\xA3\x3B\x3B\x9B33\x97\x2F\x2F\x8F\x2B\x2B\x8B\x23\x23\x83\x1F\x1F\x7F\x1B\x1Bw". "\x17\x17s\x13\x13k\x0F\x0Fg\x0B\x0B_\x07\x07\x5B\x07\x07S\x07\x07O\x03\x03G\x03\x03C\x03\x03\xFF\xEB\xDF". "\xFF\xE3\xD3\xFF\xDB\xC7\xFF\xD3\xBB\xFF\xCF\xB3\xFF\xC7\xA7\xFF\xBF\x9B\xFF\xBB\x93\xFF\xB3\x83\xF7\xAB\x7B\xEF\xA3s\xE7\x9B". "k\xDF\x93c\xD7\x8B\x5B\xCF\x83S\xCB\x7FO\xBF\x7BK\xB3sG\xABoC\xA3k\x3F\x9Bc\x3B\x8F_7\x87". "W3\x7FS\x2FwO\x2BkG\x27_C\x23S\x3F\x1FK7\x1B\x3F\x2F\x173\x2B\x13\x2B\x23\x0F\xEF\xEF\xEF". "\xE7\xE7\xE7\xDF\xDF\xDF\xDB\xDB\xDB\xD3\xD3\xD3\xCB\xCB\xCB\xC7\xC7\xC7\xBF\xBF\xBF\xB7\xB7\xB7\xB3\xB3\xB3\xAB\xAB\xAB\xA7\xA7". "\xA7\x9F\x9F\x9F\x23\x2B\x0F\x2F7\x1F\x1B\x1B\x1B\x83\x83\x83\x17\x1F\x07wwwoookkkccc\x5B". "\x5B\x5BWWWOOOGGGCCC\x3B\x3B\x3B777KKK\x27\x27\x27\x23\x23\x23w\xFFo". "o\xEFgg\xDF__\xCFW\x5B\xBFOS\xAFGK\x9F\x3FC\x937\x3F\x83\x2F7s\x2B\x2Fc\x23\x17\x0F". "\x07\x1FC\x17\x173\x0F\x13\x23\x0B\x0B\x17\x07\x03\x03\x03\xB7\x9F\x87\xAF\x97\x7F\xA7\x8Fw\x9F\x87o\x9B\x7Fk\x93". "\x7Bc\x8Bs\x5B\x83kW\x7BcOw_KoWCgS\x3F_K7WC3S\x3F\x2F\x9F\x83c". "\x8FwS\x83kKw_\x3FgS3\x5BG\x2BO\x3B\x23C3\x1B\x7B\x7FcosWgkO\x5Bc". "GSW\x3BGO3\x3FG\x2B7\x3F\x27\xFF\xFFs\xEB\xDBW\xD7\xBBC\xC3\x9B\x2F\xAF\x7B\x1F\x9B\x5B\x13\x87". "C\x07s\x2B\x03\xFF\xFF\xFF\xFF\xDB\xDB\xFF\xBB\xBB\xFF\x9B\x9B\xFF\x7B\x7B\xFF__\xFF\x3F\x3F\xFF\x1F\x1F\xFF\x03\x03". "\xEF\x03\x03\xE3\x03\x03\xD7\x03\x03\xCB\x03\x03\x07\x07\x07\x0B\x0B\x0B\x13\x13\x13\xFF\xFF\xFF\x1F\x17\x0B\x97\x97\x97\x93\x93". "\x93\x8B\x8B\x8B\x5B\x03\x03O\x03\x03\x27S\x1B\xE7\xE7\xFF\xC7\xC7\xFF\xAB\xAB\xFF\x8F\x8F\xFFss\xFFSS\xFF7". "7\xFF\x1B\x1B\xFF\x03\x03\xFF\x03\x03\xE3\x03\x03\xCB\x03\x03\xB3\x03\x03\x9B\x03\x03\x83\x03\x03k\x03\x03S\xFF\xFF\xFF". "\xFF\xEB\xDB\xFF\xD7\xBB\xFF\xC7\x9B\xFF\xB3\x7B\xFF\xA3\x5B\xFF\x8F\x3B\xFF\x7F\x1B\xF3s\x17\xEBo\x0F\xDFg\x0F\xD7_". "\x0B\xCBW\x07\xC3O\x03\xB7G\x03\xAFC\x03\xFF\xFF\xFF\xFF\xFF\xD7\xFF\xFF\xB3\xFF\xFF\x8F\xFF\xFFk\xFF\xFFG\xFF". "\xFF\x23\xFF\xFF\x03\xA7\x3F\x03\x9F7\x03\x93\x2F\x03\x87\x23\x03O\x3B\x27C\x2F\x1B7\x23\x13\x2F\x1B\x0B\x03\x03S". "\x03\x03G\x03\x03\x3B\x03\x03\x2F\x03\x03\x23\x03\x03\x17\x03\x03\x0B\xBF\xA7\x8F\xFF\x9FC\xFF\xE7K\xFF\x7B\xFF\xFF\x03". "\xFF\xCF\x03\xCF\x9F\x03\x9Bo\x03k\xA7kk\x21\xF9\x04\x01\x00\x00\x00\x00\x2C\x00\x00\x00\x00\x0E\x00\x10\x00\x00\x08". "l\x00\x03\x08\x1C8\x10\x80A\x83\x01\x10\x14H\xC0\xF0\x40\x01\x02\x02\x0F\x06\x18P\xA0\xC0\x80\x03\x18\x0F\x0C\x80". "\x88\x90\x80\xC7\x8F\x04\x0CP\x0C\x20\x00\x00A\x82\x16\x0Dp\x04\x20\xA0\xA5\xCB\x00\x29\x0B\x90\x3CH\x13\xE6\x00\x91". "3i\x22\xD4\x88\xB3\xA4N\x93\x3Ce\xFA\xD49\xF1\xE6\x80\x9CD\x11\x18EZS\xC1M\x04L\x25\x3A5\x00". "uhM\x90Q\x11\x9E\xB4J\xD3\xA5\xCB\x80\x00\x3B"; $GifData[8]= "GIF89a\x0E\x00\x10\x00\xEF\x00\x00C\x03\x03\x03\xFF\xFF\x9B\x03\x03\x2F\x2F\x2F\x8B\x03\x03s\x03\x03g". "\x03\x03\xCB\x03\x03\xA7\x03\x03\xB3\x03\x03\xBF\x03\x03\xE3\x03\x03\x5B\x03\x03\xD7\x03\x03\x7F\x03\x03\xCBkk\xFF\xB7\xB7". "\xF7\xAB\xAB\xF3\xA3\xA3\xEB\x97\x97\xE7\x8F\x8F\xDF\x87\x87\xDB\x7B\x7BG3\x23\x3F\x2B\x1B\xC7cc\xBF\x5B\x5B\xBBW". "W\xB3OO\xAFGG\xA7\x3F\x3F\xA3\x3B\x3B\x9B33\xD3ss\x97\x2F\x2F\x8B\x23\x23\x83\x1F\x1F\x7F\x1B\x1Bw". "\x17\x17s\x13\x13k\x0F\x0Fg\x0B\x0B_\x07\x07\x5B\x07\x07S\x07\x07O\x03\x03G\x03\x03C\x03\x03\xFF\xEB\xDF". "\xFF\xE3\xD3\xFF\xDB\xC7\xFF\xD3\xBB\xFF\xCF\xB3\xFF\xC7\xA7\x8F\x2B\x2B\xFF\xBB\x93\xFF\xBF\x9B\xF7\xAB\x7B\xEF\xA3s\xE7\x9B". "k\xDF\x93c\xD7\x8B\x5B\xCF\x83S\xCB\x7FO\xBF\x7BK\xB3sG\xABoC\xA3k\x3F\x9Bc\x3B\x8F_7\x87". "W3\x7FS\x2FwO\x2BkG\x27_C\x23S\x3F\x1FK7\x1B\x3F\x2F\x173\x2B\x13\x2B\x23\x0F\xEF\xEF\xEF". "\xE7\xE7\xE7\xDF\xDF\xDF\xDB\xDB\xDB\xD3\xD3\xD3\xCB\xCB\xCB\xC7\xC7\xC7\xBF\xBF\xBF\xB7\xB7\xB7\xB3\xB3\xB3\xAB\xAB\xAB\xA7\xA7". "\xA7\x9F\x9F\x9F\x97\x97\x97\x0F\x17\x03\x17\x1F\x07\xFF\xB3\x83\x7F\x7F\x7Fwww\x83\x83\x83O\x3B\x2Bccc\x5B". "\x5B\x5BWWWOOOGGGCCC\x3B\x3B\x3B777\x23\x2B\x0F\x27\x27\x27\x23\x23\x23w\xFFo". "o\xEFgg\xDF__\xCFW\x5B\xBFOS\xAFGK\x9F\x3FC\x937\x3F\x83\x2F7s\x2B\x2Fc\x23\x2F7". "\x1F\x1FC\x17\x173\x0F\x13\x23\x0B\x0B\x17\x07\x03\x03\x03\xB7\x9F\x87\xAF\x97\x7F\xA7\x8Fw\x9F\x87o\x9B\x7Fk\x93". "\x7Bc\x8Bs\x5B\x83kW\x7BcOw_KoWCgS\x3F_K7WC3S\x3F\x2F\x9F\x83c". "\x8FwS\x83kKw_\x3FgS3\x5BG\x2BO\x3B\x23C3\x1B\x7B\x7FcosWgkO\x5Bc". "GSW\x3BGO3\x3FG\x2B7\x3F\x27\xFF\xFFs\xEB\xDBW\xD7\xBBC\xC3\x9B\x2F\xAF\x7B\x1F\x9B\x5B\x13\x87". "C\x07s\x2B\x03\xFF\xFF\xFF\xFF\xDB\xDB\xFF\xBB\xBB\xFF\x9B\x9B\xFF\x7B\x7B\xFF__\xFF\x3F\x3F\xFF\x1F\x1F\xFF\x03\x03". "\xEF\x03\x03\x07\x07\x07ooo\x0B\x0B\x0B\x13\x13\x13\x1B\x1B\x1B\xFF\xFF\xFFKKK\x17\x0F\x07kkk\x1F\x17". "\x0B\x93\x93\x93\x8B\x8B\x8BO\x03\x03\x27S\x1B\xE7\xE7\xFF\xC7\xC7\xFF\xAB\xAB\xFF\x8F\x8F\xFFss\xFFSS\xFF7". "7\xFF\x1B\x1B\xFF\x03\x03\xFF\x03\x03\xE3\x03\x03\xCB\x03\x03\xB3\x03\x03\x9B\x03\x03\x83\x03\x03k\x03\x03S\xFF\xFF\xFF". "\xFF\xEB\xDB\xFF\xD7\xBB\xFF\xC7\x9B\xFF\xB3\x7B\xFF\xA3\x5B\xFF\x8F\x3B\xFF\x7F\x1B\xF3s\x17\xEBo\x0F\xDFg\x0F\xD7_". "\x0B\xCBW\x07\xC3O\x03\xB7G\x03\xAFC\x03\xFF\xFF\xFF\xFF\xFF\xD7\xFF\xFF\xB3\xFF\xFF\x8F\xFF\xFFk\xFF\xFFG\xFF". "\xFF\x23\xFF\xFF\x03\xA7\x3F\x03\x9F7\x03\x93\x2F\x03\x87\x23\x03O\x3B\x27C\x2F\x1B7\x23\x13\x2F\x1B\x0B\x03\x03S". "\x03\x03G\x03\x03\x3B\x03\x03\x2F\x03\x03\x23\x03\x03\x17\x03\x03\x0B\xBF\xA7\x8F\xFF\x9FC\xFF\xE7K\xFF\x7B\xFF\xFF\x03". "\xFF\xCF\x03\xCF\x9F\x03\x9Bo\x03k\xA7kk\x21\xF9\x04\x01\x00\x00\x01\x00\x2C\x00\x00\x00\x00\x0E\x00\x10\x00\x00\x08". "\x95\x00\x03\x08\x04\x40\xB0\x20\x00\x81\x08\x010\x28\x20\x60\xC1\x82\x04\x02\x0A08\x18\x00\x80\x01\x02\x04\x04\x24P". "\x20\x40\x00F\x03\x07\x010\xCC\x98\xD1\x81\xC7\x88\x00\x06\x88\xF4x\xA0\xA0\x82\x8C\x05RZD\x40\xA0\x25\xC1\x03". "\x04\x10\x80\x1CP\xF1b\xC3\x06\x1C\x09\xEC\xE4Y1\x27\x82\xA34S\x12\xB5\x28\x00A\x81\xA7\x08\x04\x80\x1Ch". "\x40\xA3M\x00\x07\x0EH\x0D\x991\xC1\xD5\x03\x09\x12\x10\x90I\x00\xA2\x80\x03\x0B\x0E\x20P\x20VfU\x8Fl". "\x3BB\xDCY\xF1\xA9\xDD\xBBJ\x07\x1A48\x80\xA8\xC0\xBE\x80\x03\x0B\x0C\x08\x00\x3B"; $GifData[9]= "GIF89a\x0E\x00\x10\x00\xEF\x00\x00C\x03\x03\x03\xFF\xFF\x2F\x2F\x2F\xB3\x03\x03\x8B\x03\x03s\x03\x03\x9B". "\x03\x03\xBF\x03\x03g\x03\x03\xCB\x03\x03\xA7\x03\x03\xE3\x03\x03\x7F\x03\x03\xD7\x03\x03\xEF\x03\x03\x2F7\x1F\xFF\xB7\xB7". "\xF7\xAB\xAB\xF3\xA3\xA3\xEB\x97\x97\xE7\x8F\x8F\xDF\x87\x87\xDB\x7B\x7B\xD3ss\xCBkk\xC7cc\xBF\x5B\x5B\xBBW". "W\xB3OO\xAFGG\xA7\x3F\x3F\xA3\x3B\x3B\x9B33\x97\x2F\x2F\x8F\x2B\x2B\x8B\x23\x23\x83\x1F\x1F\x7F\x1B\x1Bw". "\x17\x17s\x13\x13k\x0F\x0Fg\x0B\x0B_\x07\x07\x5B\x07\x07S\x07\x07O\x03\x03G\x03\x03C\x03\x03\xFF\xEB\xDF". "\xFF\xE3\xD3\xFF\xDB\xC7\xFF\xD3\xBB\xFF\xCF\xB3\xFF\xC7\xA7\xFF\xBF\x9B\xFF\xBB\x93\xFF\xB3\x83\xF7\xAB\x7B\xEF\xA3s\xE7\x9B". "k\xDF\x93c\xD7\x8B\x5B\xCF\x83S\xCB\x7FO\xBF\x7BK\xB3sG\xABoC\xA3k\x3F\x9Bc\x3B\x8F_7\x87". "W3\x7FS\x2FwO\x2BkG\x27_C\x23S\x3F\x1FK7\x1B\x3F\x2F\x173\x2B\x13\x2B\x23\x0F\xEF\xEF\xEF". "\xE7\xE7\xE7\xDF\xDF\xDF\xDB\xDB\xDB\xD3\xD3\xD3\xCB\xCB\xCB\xC7\xC7\xC7\xBF\xBF\xBF\xB7\xB7\xB7\xB3\xB3\xB3\xAB\xAB\xAB\xA7\xA7". "\xA7\x9F\x9F\x9FG3\x23\x0F\x17\x03O\x3B\x2B\x3F\x2B\x1B\x7F\x7F\x7Fwwwoookkkccc\x5B". "\x5B\x5BWWWOOOGGGCCC\x3B\x3B\x3B777\xFF\xFF\xFF\x27\x27\x27\x23\x23\x23w\xFFo". "o\xEFgg\xDF__\xCFW\x5B\xBFOS\xAFGK\x9F\x3FC\x937\x3F\x83\x2F7s\x2B\x2Fc\x23KK". "K\x1FC\x17\x173\x0F\x13\x23\x0B\x0B\x17\x07\x03\x03\x03\xB7\x9F\x87\xAF\x97\x7F\xA7\x8Fw\x9F\x87o\x9B\x7Fk\x93". "\x7Bc\x8Bs\x5B\x83kW\x7BcOw_KoWCgS\x3F_K7WC3S\x3F\x2F\x9F\x83c". "\x8FwS\x83kKw_\x3FgS3\x5BG\x2BO\x3B\x23C3\x1B\x7B\x7FcosWgkO\x5Bc". "GSW\x3BGO3\x3FG\x2B7\x3F\x27\xFF\xFFs\xEB\xDBW\xD7\xBBC\xC3\x9B\x2F\xAF\x7B\x1F\x9B\x5B\x13\x87". "C\x07s\x2B\x03\xFF\xFF\xFF\xFF\xDB\xDB\xFF\xBB\xBB\xFF\x9B\x9B\xFF\x7B\x7B\xFF__\xFF\x3F\x3F\xFF\x1F\x1F\xFF\x03\x03". "\x23\x2B\x0F\x17\x1F\x07\x07\x07\x07\x13\x13\x13\x0B\x0B\x0B\x17\x0F\x07\x1B\x1B\x1B\x83\x83\x83\x1F\x17\x0B\x97\x97\x97\x93\x93". "\x93\x8B\x8B\x8B\x5B\x03\x03O\x03\x03\x27S\x1B\xE7\xE7\xFF\xC7\xC7\xFF\xAB\xAB\xFF\x8F\x8F\xFFss\xFFSS\xFF7". "7\xFF\x1B\x1B\xFF\x03\x03\xFF\x03\x03\xE3\x03\x03\xCB\x03\x03\xB3\x03\x03\x9B\x03\x03\x83\x03\x03k\x03\x03S\xFF\xFF\xFF". "\xFF\xEB\xDB\xFF\xD7\xBB\xFF\xC7\x9B\xFF\xB3\x7B\xFF\xA3\x5B\xFF\x8F\x3B\xFF\x7F\x1B\xF3s\x17\xEBo\x0F\xDFg\x0F\xD7_". "\x0B\xCBW\x07\xC3O\x03\xB7G\x03\xAFC\x03\xFF\xFF\xFF\xFF\xFF\xD7\xFF\xFF\xB3\xFF\xFF\x8F\xFF\xFFk\xFF\xFFG\xFF". "\xFF\x23\xFF\xFF\x03\xA7\x3F\x03\x9F7\x03\x93\x2F\x03\x87\x23\x03O\x3B\x27C\x2F\x1B7\x23\x13\x2F\x1B\x0B\x03\x03S". "\x03\x03G\x03\x03\x3B\x03\x03\x2F\x03\x03\x23\x03\x03\x17\x03\x03\x0B\xBF\xA7\x8F\xFF\x9FC\xFF\xE7K\xFF\x7B\xFF\xFF\x03". "\xFF\xCF\x03\xCF\x9F\x03\x9Bo\x03k\xA7kk\x21\xF9\x04\x01\x00\x00\x01\x00\x2C\x00\x00\x00\x00\x0E\x00\x10\x00\x00\x08". "\x8E\x00\x03\x08\x0C\x00\xA0\x60\xC1\x81\x08\x0B\x2AH\x90\xE0\x40\x81\x83\x03\x01\x10P\x60\x40\xC1\x80\x01\x14\x11\x00\x10". "\x08\x00\xC1D\x03\x0C\x0A\x14\x18p\xC0\x00\x00\x01\x04\x0D\x10Pip\x40\x02\x03\x0FS\xAE\x24\xD02\x81\x02\x9A". "\x02\x3A\x12\xD89\x60A\xC3\x06\x07p\x12\x2C\xE0\xF1\x22I\xA0B\x09vDP\x80\x00\x83\x05AO\x0E\xCCi". "\xF0\xE9\x00\x9C\x06\xB3\x02xz\x40cN\x97\x0D\x168\x60x\xA0\x81I\x019\x0D\x185Z\xD2\xEBP\x91p". "E\x16D\xABT\xEB\x5C\xBA\x02\xD1\xEA\xDD\x8B0\x20\x00\x3B"; ###################################################### sub Number2gif{ my($Number,$digits)=@_; my($Ascii_Num,$Zeropad,$n_gif,$Gif_Answer,$logsrc,$pal); my($gif_log_scr_packed,$gif_transps,$gif_img_desc,$gif_dat,$packed_was); $Gif_Answer="$gifheader"; #GIF89a $logsrc=pack('S2C3',$digx_size*$digits,$digy_size,pack('B8','01100000'),0,0); $Gif_Answer .=$logsrc; $Ascii_Num=''.$Number; $digits=($digits>length($Ascii_Num)?$digits:length($Ascii_Num)); $Zeropad='0' x $digits; substr($Zeropad,- length($Ascii_Num),length($Ascii_Num))=$Ascii_Num; $Ascii_Num=$Zeropad; foreach(0..length($Ascii_Num)-1){ $n_gif=0+substr($Ascii_Num,$_,1); $pal=substr($GifData[$n_gif],13,256*3); $gif_log_scr_packed=unpack('B8',substr($GifData[$n_gif],10,1)); $gif_transps=substr($GifData[$n_gif],13+256*3,8); $gif_img_desc=substr($GifData[$n_gif],13+256*3+8,10); $gif_dat=substr($GifData[$n_gif],13+256*3+8+10,length($GifData[$n_gif])); $gif_dat=substr($gif_dat,0,length($gif_dat)-1); substr($gif_img_desc,1,2)=pack('S',$_*$digx_size); $packed_was=unpack('B8',substr($gif_img_desc,9,1)); substr($gif_img_desc,9,1)=pack('B8','1'. substr($packed_was,1,1). substr($gif_log_scr_packed,4,1).'00'.substr($gif_log_scr_packed,5,3)); $Gif_Answer.=$gif_transps.$gif_img_desc.$pal.$gif_dat; } $Gif_Answer.='\x3B'; return $Gif_Answer; } ###################################################### sub urldecode{ local($val)=@_; $val=~s/\+/ /g; $val=~s/%([0-9A-H]{2})/pack('C',hex($1))/ge; return $val; } sub debug_err{ open DEBUGFILE,">>debug.txt"; print DEBUGFILE $ENV{'SCRIPT_NAME'}.' '.scalar(localtime).' '.@_."\n"; close DEBUGFILE; } $query=$ENV{'QUERY_STRING'}; if($query eq ''){print "Content-Type: image/gif\n\n$GifData[0]";} else{ @fields=split /&/,$query; foreach(@fields){ if(/^id=(.*)/){$id=urldecode($1);} if(/^dig=(.*)/){$dig=urldecode($1);} } $digits=$dig; $digits=9 unless($dig); $cur_ip=$ENV{'REMOTE_ADDR'}; $cur_time=time; open DATA,"+<$datafile" debug_err("Cannot open $datafile $!"); @Dat=<DATA>; chomp(@Dat); %Counters=@Dat; ($count,$ip,$t)=split /\s+/,$Counters{$id}; $count++ if(($ip!=$cur_ip)($cur_time-$t>30)); $ip=$cur_ip; $t=$cur_time; $Counters{$id}=join ' ',$count,$ip,$t; seek DATA,0,0; foreach(keys %Counters){ print DATA "$_\n"; print DATA "$Counters{$_}\n"; } truncate(DATA,tell(DATA)); close DATA; print "Content-Type: image/gif\n\n"; print Number2gif($count,$digits); }

Если вам циферки не понравились вы их легко сможете заменить.Новые .gif'ы либо считайте из файла, либо можете продолжить мою традицию и запихнуть их в строку, в чем вам поможет такой скрипт file2str. (Да и еще не забудьте сменить тогда в скрипте константы $digx_size и $digy_size на новый размер цифр.)



#!/usr/bin/perl #file2str unless(@ARGV){die "Usage: file2str file strfile\n";} open F1,"$ARGV[0]" die "Cannot open $ARGV[0] $!\n"; open F2,">$ARGV[1]" die "Cannot open $ARGV[1] $!\n"; binmode(F1); sysread F1,$filedata,-s F1; close(F1); for($i=0;$i<=length($filedata)/32;$i++){ $data=substr($filedata,$i*32,32); $data=~s/(\W)/sprintf("\\x%02X",unpack('C',$1))/eg; print F2 "\"$data\".(length($data)==32?".":";")."\n";" } close(F2);


Ssi


Теперь посмотрите, что вы натворили. Мда, натворили. Допустим, вы уже сделали сайт. К примеру, на нем 30 страниц с общим дизайном, причем на каждой странице есть навигация, например, по разделу. Что произойдет, если вы добавите в раздел новую статью? Правильно, вам понадобится изменять все страницы, входящие в раздел. Хорошо если их 10. А если больше? А если вам необходимо дизайн поменять?

Мда, ситуация незавидная. С такой ситуацией я столкнулся, когда пытался сделать похожий ресурс на бесплатном сервере. Это кончилось тем, что я просто не смог обновлять сайт. К счастью, есть выход! Этим выходом является технология Server-Side Includes (включения на стороне сервера), в просторечии SSI. Она, если сервер ее поддерживает, позволяет прямо-таки творить чудеса. Суть в следующем - включив в свою страничку код

<!--#include virtual="header.ssi" -->

Вы тем самым включаете в нее все, что находится внутри этого файла. Кроме того, вы можете включить в страницу исполняемый файл

<!--#exec cgi="cgi.pl" -->

Вот так. После этого вы можете переделать свои страницы так, что смену дизайна можно будет произвести за 5 минут, изменив только два-три файла. Именно это я могу сделать со своим сервером. В моем случае страница, которую вы видите как чистый HTML, на самом деле состоит из заголовка с META-тэгами, особыми для каждой страницы, заголовка страницы, потом идет общий заголовок (SSI), потом подзаголовок в темно-серой линии, потом навигация по разделу (SSI), потом текст, потом "стопа" документа (SSI). Вот так:

<html>

<head>

<meta http-equiv="Content-Language" content="ru">

<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">

<title>Кормушка :: Локальная копия :: HTML учебник</title>

<!--#include virtual="header.ssi" -->

<p><b><a href="index.shtml">Кормушка</a>

 :: <a href="local.shtml">Локальная копия</a>

 :: HTML учебник</b>

</p>

<!--#include virtual="leftnav.ssi" -->

<!--#include virtual="localheader.ssi" -->

Тут лежит текст

<!--#include virtual="rightnav.ssi" -->

</body>

</html>

Как видите, все гениальное просто. Попробуйте аналогичные преобразования на своих страницах. Не получилось? Еще бы! Это же Server-Side Includes. Для просмотра SSI-документов на своей машине необходимо установить сервер. О том, как это сделать, читайте в .

В связи с разрастанием проекта структура немного изменилась, но, поскольку это не меняет сути дела, статью я переписывать не стал.


<!--#include file="file.html" --> или



<!--#include virtual="URL" -->



Первый случай используется при подключении файла, находящегося в том же самом каталоге, второй – для подключения файла, находящегося в любом месте, даже на другом сервере. (Но не все бесплатные сервера разрешат вам подключить файл, находящийся на другом сервере). Все говорят, что предпочтительнее использовать virtual, и я не буду спорить. В этом случае можно использовать как относительные, так и абсолютные адреса. Само собой разумеется, что подключаемый файл тоже, в свою очередь, может содержать директивы SSI. Обратите внимание: пробелы есть только после слова include и перед -->. Желательно, чтобы файл, в котором есть директивы SSI, имел вид name.shtml, где name – любая комбинация латинских букв, цифр и знаков подчеркивания. На некоторых серверах разрешают вставлять директивы SSI и в файлы с расширением *.htm, *.html.

Данные

Для хранения данных существуют переменные. Они объявляются так:



<!--#set var="имя" value="значение" -->



Передача данных

Передавать данные можно двумя способами. Даже тремя.

Используя метод get формы.

Через URL вызываемого файла в виде
file.html?param=value[&param=value].

Используя возможности SSI.

В принципе, первые два различаются лишь тем, самостоятельно будете вы кодировать строку запроса (2-й способ) или предоставите это делать пользователю (1-й способ), который будет заполнять форму и жать на ОК или SUBMIT. Этот способ рассмотрен мною подробно в другой статье. Для передачи данных средствами SSI достаточно объявить переменную и присвоить ей значение. Теперь она доступна из любого файла, подключающего данный.

Получение данных

Получить и затем использовать данные можно двумя способами.

Стандартными средствами DHTML (HTML+JavaScript)

Средствами SSI.

Первый способ рассмотрен все в той же статье "Передача данных…", да и не интересует он нас сейчас. А вот средствами SSI получить данные можно опять-таки двумя способами.



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

Если вы самостоятельно сформировали строку запроса так, как описано выше, или передавали данные из формы, это равносильно объявлению переменных. То есть строка file.shtml?param=value эквивалентна объявлению переменной param, равной value.

<!--#set var="param" value="value" -->

Чтобы проверить, передались ли вам какие-либо данные или нет, есть директива

<!--#printenv -->

Она выводит на экран список всех переменных и их значения.

Обработка данных

Нет смысла передавать данные и не использовать их потом. Разве что делать это с целью заморочить голову посетителю. Самое простое, что можно сделать, это вставить переменную в текст документа.



<!--#echo var="$name" --> -

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



<a href="<!--#echo var="$url" -->">

Ссылка</a>.



Или, например, посетитель сказал нам, как его зовут, нажал на кнопочку, и на следующей странице к нему будем обращаться по имени. Строка запроса у нас получилась
file.shtml?name=Alexey и используем мы эти бесценные сведения



Привет,

<!--#echo var="$name" -->.



В результате на экране отобразится Привет, Alexey.

Если передавались данные, требующие аналитической обработки, на это есть директива if.

<!--#if expr=(условие) -->

<!--#elif expr=(условие) -->



<!--#else -->

<!--#endif -->

endif является обязательным элементом. elif может повторяться сколько угодно раз. else между if и endif может быть лишь единожды, причем после else уже не может использоваться elif. После каждого условия можете вставлять неограниченное количество строчек кода HTML. Например, предыдущий пример (с именем) можно модернизировать





<!--#if expr=($name="Alexey") -->

<h1>Привет, <!--#echo var="$name" --></h1>

<!--#else -->

<h2>Привет, <!--#echo var="$name" --></h2>

<!--#endif -->



Теперь, если посетитель окажется моим тезкой, приветствие будет написано чуть крупнее, чем для всех остальных.

Организация сайта

Наконец-то я добрался до того, ради чего и затеял-то писать эту статью. Мы же хотим организовать свой сайт так, чтобы в случае чего модернизация заняла бы минимум усилий. Возможны два пути для достижения этой цели. Видите, какой я нынче демократичный – всегда предоставляю вам право выбора. Цените это J

. Следует учесть, что HTML – очень терпимый к ошибкам язык. Если вы что-то неправильно написали, к фатальным последствиям это не приведет. Так, например, не смотря на то, что во всех руководствах заголовок документа (head) рекомендуют писать в начале файла, броузеры правильно понимают его в любой части документа, даже если вы забудете заключить его в контейнер <head>. Единственное (имхо) неудобство заключается в том, что, располагая заголовок, содержащий title, в конце файла, страница броузера остается безымянной до тех пор, пока не прочитает этот заголовок.

Способ первый

Пишем статью в формате HTML, как обычно, но не указывая никакие стили, избегая изменения цветов (потому что мало ли какой цвет фона нам потом приспичит сделать – вдруг видно не будет) и даже опуская тег body. Остаются только font size, b, big, img, a, i, table и некоторые другие. Сохраняем как filename.html. Имя, естественно, у каждой статьи разное. Самое главное – запомнить, в каком файле какая статья лежит, а лучше записывать это. Адрес этой статьи никому не говорим – он для сугубо внутреннего использования.

Далее создаем публичный файл статьи. В начале документа пишем <html> и затем объявляем переменные, в которых будем передавать информацию о статье



<html>

<!--#set var="description" value="описание" -->



<!--#set var="title" value="Заголовок" -->

<!--#set var="keywords" value="ключевые слова" -->

<!--#set var="file" value="url статьи" -->

<!--#include virtual="файл интерфейса.shtml" -->



Сохраняем это все и запоминаем адрес – это и будет адрес статьи, который вы будете регистрировать в каталогах и рекомендовать посетить друзьям. Можно еще написать то, что вы там еще обычно пишете в заголовке. Я чаще всего ограничиваюсь одним title. Как-то спокойно отношусь к тому, что релевантность маленькая окажется J

.

Теперь создаем файл интерфейса (*,shtml). Начало его выглядит так:



<html><head>

<title><!--#echo var="$title" --></title>

<meta name="description" content="<!--#echo var="$description" -->">

<meta name="keywords" content="<!--#echo var="$keywords" -->">

</head>



Далее оформляем внешний вид – вешаем баннеры, вставляем счетчики, добавляем новости и т.д. Наконец в том уголке, где должна быть статья, пишем



<!--#include virtual="$file" -->



Все. Теперь, как и обещалось, для радикального изменения дизайна достаточно изменить лишь наш файл интерфейса.

Способ второй

Честно говоря, он мне не очень нравится, и поэтому я его приводить не буду. Идея его заключается в том, чтобы вызывать все статьи как, например, index.shtml?sub=filename. Выигрыш можно получить в том, что одна и та же статья теперь имеет два адреса. Кому интересно, можете сами организовать такую структуру.

Заключение

Некоторые тонкости. Обращение к переменным я делал как "$name". На сервере, где мой хоумпейдж, работает именно так. На других может быть "${name}" (скорее всего) или как-нибудь по-другому (маловероятно).

При тестировании дома могут возникнуть сложности. Для получения данных нужен сервер. Если у вас не стоит Apache или какой-нибудь другой и вы не хотите его ставить, и не надо. Есть хорошая программка Small HTTP Server (около 60кб). Последний раз я видел ее по адресу http://wwwkoi.wplus.net/pp/mrdoors/srv/rdesc.htm. После установки вы сможете тестировать дома CGI, SSI, PHP и т.д.

Статья взята с сайта . Автор - .


Ссылки на найденный текст


Иногда нужно сослаться на подстроку текста, для которой получено совпадение с некоторой частью шаблона. Например, при обработке файла, HTML может потребоваться выделять фрагменты текста, ограниченные открывающими и закрывающими метками HTML (например, <А> и </А>). В начале уже приводился пример, в котором выделялся текст, ограниченнуй метками HTML <А> и <B>. Следующий пример позволяет выделять текст, расположенный между любыми правильно закрытыми метками:

$text = "<А>Here is an anct1or.</А>"; if($text=~m%<([A-Za-z]+)>[\w\s\.]+</\1>%i){ }

Вместо косой черты в качестве ограничителя шаблона использован другой символ. Это позволяет использовать символ косой черты внутри шаблона без предшествующей ему обратной косой черты. Каждому фрагменту шаблона, заключенному в круглые скобки, соответствует определенная внутренняя переменная. Переменные пронумерованы, так что на них можно ссылаться внутри шаблона, поставив перед номером обратную косую черту (\1, \2, \3,...). На значения переменных можно ссылаться внутри шаблона, как на обычный текст, поэтому </\1> соответствует </\A>, если открываю-щей меткой служит, и , если открывающей меткой служит .

Эти же самые внутренние переменные можно использовать и вне шаблона, ссылаясь на них как на скаляры с именами $1, $2, $3,..., $n: $text = "I have 4 apples."; if ($text =- /(\(\d+)/) { print "Here Is the number of apples: $1.\n"; Here is the number of apples: 4.

Каждой паре скобок внутри шаблона после завершения операции поиска будет соответствовать скалярная переменная с соответствующим номером. Это можно 'использовать при выделении нужных для последующей работы фрагментов ана-лизируемой строки. В следующем примере мы изменяем порядок трех слов в тек-стовой строке с помощью команды s/.../.../:

$text = "I see you."; $text=-s/^(\w+) *(\w+) *(\w+)/$3 $2 $1/; print $text; you see I.

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

$text = "ABCDEFGH"; $text =- m/(\w(\w)(\w))((\w)(\w))/; print "$1/$2/$3/$4/$5/$6/"; ABC/B/C/DE/D/E

Кроме переменных, ссылающихся на найденный текст, можно использовать специальные переменные perl.



Trics and traps


Я так решил назвать эту часть,потому что это название больше всего соответствует.

Я вам в этой части расскажу о всяких тонких и неочевидных местах и о том как с этим боротся.

Продолжая тему отладки, я столкнулся с такой неочевидной проблеммой, помимо того,что надо перевести раскладку Windows (или DOS) в koi8-r

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

Методом решения (этот процесс как и все я максимально автоматизирую) стал коротенький скрипт delcr .Просто "натравливайте" его на ваши скрипты:delcr *.cgi

#!/usr/bin/perl #delcr unless(@ARGV){die "Usage: delcr file ....\n";} foreach $file(@ARGV){ if(! -r $file ! -w $file){print "$file: access denied\n";} else{ open F,"+<$file" die "Cannot open $file $!\n"; binmode F die "Cannot binmode $file $!\n"; @D=<F>; seek F,0,0; foreach(@D){ s/\r//g; print F; } truncate F,tell(F); close F; } }

Саму же взаимную перекодировку Dos<->koi-8<->Win поможет выполнить вот такая прога. Написал я ее очень давно, даже раньше чем занялся CGI программированием и с тех давних пор она меня все время выручала, став моим по-настоящему незаменимым и верным другом. Итак, знакомьтесь: txtconv -Утилита перевода раскладок символов.

#include<stdio.h> #include<string.h> /*****************/ char tbldw[256]={ 0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xa,0xb,0xc,0xd,0xe,0xf, 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f, 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f, 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f, 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,


0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf, 0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf, 0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef, 0x5f,0x5f,0x5f,0xa6,0xa6,0xa6,0xa6,0x2b,0x2b,0xa6,0xa6,0x2b,0x2b,0x2b,0x2b,0x2b, 0x2b,0x2d,0x2d,0x2b,0x2d,0x2b,0xa6,0xa6,0x2b,0x2b,0x2d,0x2d,0xa6,0x2d,0x2b,0x2d, 0x2d,0x2d,0x2d,0x2b,0x2b,0x2b,0x2b,0x2b,0x2b,0x2b,0x2b,0x5f,0x5f,0x5f,0x5f,0x5f, 0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff, 0xa8,0xb8,0xaa,0xba,0xaf,0xbf,0xa1,0xa2,0xb0,0x95,0xb7,0x5f,0xb9,0xa4,0x5f,0x5f }; char tblwd[256]={ 0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xa,0xb,0xc,0xd,0xe,0xf, 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f, 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f, 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f, 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,

0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x5f, 0x5f,0x5f,0x5f,0x5f,0x5f,0xf9,0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x5f, 0x5f,0xf6,0xf7,0x5f,0xfd,0x5f,0x7c,0x15,0xf0,0x63,0xf2,0x11,0x2d,0x2d,0x72,0xf4, 0xf8,0x5f,0x49,0x69,0x5f,0x5f,0x14,0xfa,0xf1,0xfc,0xf3,0x10,0x5f,0x5f,0x5f,0xf5, 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f, 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f, 0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf, 0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef }; char tbl_asc[256]={ 0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xa,0xb,0xc,0xd,0xe,0xf, 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f, 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f, 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f, 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f }; char tbldu[256]={ 0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xa,0xb,0xc,0xd,0xe,0xf, 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f, 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f, 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f, 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,



0xE1,0xE2,0xF7,0xE7,0xE4,0xE5,0xF6,0xFA,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0, 0xF2,0xF3,0xF4,0xF5,0xE6,0xE8,0xE3,0xFE,0xFB,0xFD,0xFF,0xF9,0xF8,0xFC,0xE0,0xF1, 0xC1,0xC2,0xD7,0xC7,0xC4,0xC5,0xD6,0xDA,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0, 0x90,0x91,0x92,0x81,0x87,0xB2,0xB4,0xA7,0xA6,0xB5,0xA1,0xA8,0xAE,0xAD,0xAC,0x83, 0x84,0x89,0x88,0x86,0x80,0x8A,0xAF,0xB0,0xAB,0xA5,0xBB,0xB8,0xB1,0xA0,0xBE,0xB9, 0xBA,0xB6,0xB7,0xAA,0xA9,0xA2,0xA4,0xBD,0xBC,0x85,0x82,0x8D,0x8C,0x8E,0x8F,0x8B, 0xD2,0xD3,0xD4,0xD5,0xC6,0xC8,0xC3,0xDE,0xDB,0xDD,0xDF,0xD9,0xD8,0xDC,0xC0,0xD1, 0xB3,0xA3,0x99,0x98,0x93,0x9B,0x9F,0x97,0x9C,0x95,0x9E,0x96,0xBF,0x9D,0x94,0x9A }; char tblud[256]={ 0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xa,0xb,0xc,0xd,0xe,0xf, 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f, 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f, 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f, 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,

0xC4,0xB3,0xDA,0xBF,0xC0,0xD9,0xC3,0xB4,0xC2,0xC1,0xC5,0xDF,0xDC,0xDB,0xDD,0xDE, 0xB0,0xB1,0xB2,0xF4,0xFE,0xF9,0xFB,0xF7,0xF3,0xF2,0xFF,0xF5,0xF8,0xFD,0xFA,0xF6, 0xCD,0xBA,0xD5,0xF1,0xD6,0xC9,0xB8,0xB7,0xBB,0xD4,0xD3,0xC8,0xBE,0xBD,0xBC,0xC6, 0xC7,0xCC,0xB5,0xF0,0xB6,0xB9,0xD1,0xD2,0xCB,0xCF,0xD0,0xCA,0xD8,0xD7,0xCE,0xFC, 0xEE,0xA0,0xA1,0xE6,0xA4,0xA5,0xE4,0xA3,0xE5,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE, 0xAF,0xEF,0xE0,0xE1,0xE2,0xE3,0xA6,0xA2,0xEC,0xEB,0xA7,0xE8,0xED,0xE9,0xE7,0xEA, 0x9E,0x80,0x81,0x96,0x84,0x85,0x94,0x83,0x95,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E, 0x8F,0x9F,0x90,0x91,0x92,0x93,0x86,0x82,0x9C,0x9B,0x87,0x98,0x9D,0x99,0x97,0x9A }; char tbluw[256]={ 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F, 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F, 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F, 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F, 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F, 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F, 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F, 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F,



0x2D,0xA6,0x2B,0x2B,0x2B,0x2B,0x2B,0xA6,0x2D,0x2D,0x2B,0x5F,0x5F,0x5F,0x5F,0x5F, 0x5F,0x5F,0x5F,0xAF,0x5F,0x95,0x5F,0xA2,0xBA,0xAA,0x5F,0xBF,0xB0,0xA4,0xB7,0xA1, 0x2D,0xA6,0x2B,0xB8,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0xA6, 0xA6,0xA6,0xA6,0xA8,0xA6,0xA6,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2B,0x2B,0x2B,0xB9, 0xFE,0xE0,0xE1,0xF6,0xE4,0xE5,0xF4,0xE3,0xF5,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE, 0xEF,0xFF,0xF0,0xF1,0xF2,0xF3,0xE6,0xE2,0xFC,0xFB,0xE7,0xF8,0xFD,0xF9,0xF7,0xFA, 0xDE,0xC0,0xC1,0xD6,0xC4,0xC5,0xD4,0xC3,0xD5,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE, 0xCF,0xDF,0xD0,0xD1,0xD2,0xD3,0xC6,0xC2,0xDC,0xDB,0xC7,0xD8,0xDD,0xD9,0xD7,0xDA }; char tblwu[256]={ 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F, 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F, 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F, 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F, 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F, 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F, 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F, 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F,

0x5F,0x5F,0x5F,0x5F,0x5F,0x5F,0x5F,0x5F,0x5F,0x5F,0x5F,0x5F,0x5F,0x5F,0x5F,0x5F, 0x5F,0x5F,0x5F,0x5F,0x5F,0x95,0x5F,0x5F,0x5F,0x5F,0x5F,0x5F,0x5F,0x5F,0x5F,0x5F, 0x5F,0x9F,0x97,0x5F,0x9D,0x5F,0x7C,0x15,0xB3,0x63,0x99,0x11,0x2D,0x2D,0x72,0x93, 0x9C,0x5F,0x49,0x69,0x5F,0x5F,0x14,0x9E,0xA3,0xBF,0x98,0x10,0x5F,0x5F,0x5F,0x9B, 0xE1,0xE2,0xF7,0xE7,0xE4,0xE5,0xF6,0xFA,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0, 0xF2,0xF3,0xF4,0xF5,0xE6,0xE8,0xE3,0xFE,0xFB,0xFD,0xFF,0xF9,0xF8,0xFC,0xE0,0xF1, 0xC1,0xC2,0xD7,0xC7,0xC4,0xC5,0xD6,0xDA,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0, 0xD2,0xD3,0xD4,0xD5,0xC6,0xC8,0xC3,0xDE,0xDB,0xDD,0xDF,0xD9,0xD8,0xDC,0xC0,0xD1 }; /*****************/ int convert(unsigned char *buff,unsigned char *Tbl,int count) { int i; for(i=0;i<count;i++)buff[i]=Tbl[(unsigned)buff[i]]; return 0; } /*****************/ char szHelp[]= "Text file converter (c)lesha 1998\n" "Usage:txtconv <options> <srcfile> <dstfile>\n" " options: -ud koi8->dos\n" " -du dos->koi8\n" " -uw koi8->win\n" " -wu win->koi8\n" " -dw dos->win\n" " -wd win->dos\n" " -? -This help\n"; int main(int argc,char *argv[]) { FILE *f1; FILE *f2; char tmpbuff[1024]; char *xtbl=NULL; int nr; if((argc>1)&&(strcmp(argv[1],"-?")==0)){printf(szHelp);return 0;} if(argc<4){printf(szHelp);return 0;} if (strcmp(argv[1],"-ud")==0)xtbl=tblud; else if(strcmp(argv[1],"-du")==0)xtbl=tbldu; else if(strcmp(argv[1],"-uw")==0)xtbl=tbluw; else if(strcmp(argv[1],"-wu")==0)xtbl=tblwu; else if(strcmp(argv[1],"-dw")==0)xtbl=tbldw; else if(strcmp(argv[1],"-wd")==0)xtbl=tblwd; if(xtbl==NULL){printf("unknown option:%s",argv[1]);return 1;} if((f1=fopen(argv[2],"rb"))==NULL){perror(argv[2]);return 1;} if((f2=fopen(argv[3],"wb"))==NULL){fclose(f1);perror(argv[3]);return 1;} while((nr=fread(tmpbuff,1,sizeof(tmpbuff),f1))>0) { convert(tmpbuff,xtbl,nr); fwrite(tmpbuff,1,nr,f2); } fclose(f1); fclose(f2); return 0; }

Это еще одна утилита,соторая поможет вам в тяжелой реальности Интернета ;). Скомпилить ее можно под все три системы и используется она после этого очень легко, особенно когда всегда под рукой.

В общем что я могу сказать ,у вас на пути будет немало трудностей, но их можно всех преодолеть используя нехитрые приспособления.

Желаю вам удачи. А пока посмотрите на некоторые примеры приложений, возможно они вам будут полезны.


"...Upload everything in cgi-bin in ASCII mode to a directory on your server that can run cgi..."


Теперь надо создать на сервере необходимые директории и загрузить туда файлы - четко следуйте инструкциям! Загружать надо в ASCII режиме! Что это? Есть два режима: ASCII и Binary - обычно для закачки/перекачки используется второй, но со *.cgi, *.pl файлами это не пройдет: 500 Server Errors - эта ошибка чаще всего появляется из-за неправильного режима загрузки! Выбор режима загрузки производится с помощью вашего ftp клиента (у меня Сutftp, скачать можно здесь: ) - есть там нечто вроде Transfer Type - это оно самое, часто стоит на автомате (Auto), но иногда выбирает не тот режим (убедился лично) - лучше поставьте ASCII и не мучайтесь!



Установка CGI скриптов - не вляпайся!


Эту статью я написал, т.к. почувствовал необходимость в этом: сам я с cgi скриптами намучался, и многие читатели меня спрашивали. Скажу сразу, что я вовсе не профессионал (как думают некоторые мои читатели), а простой Вася Пупкин, который вознамерился покорить Интернет. После некоторых потуг, у меня что-то стало получаться (далеко не все!), я заметил, что с проблемой "первого знакомства" со скриптами (и не только cgi) постоянно сталкивается множество людей - меня постоянно просят "помочь со скриптами" … И вот я решил изложить основные моменты по их установке, ничего особенного здесь нет, все это вы сможете найти и в инструкции, прилагаемой к каждому скрипту, но я попытался написать это нормальным человеческим языком...

Когда я сказал, что я не профи, то соврал! Есть у меня одно умение, в котором я признанный профессионал - это совершать все возможные и самые наиглупейшие ошибки!!! Поковыпявшись в CGI скриптах всего три недельки, я умудрился совершить почти все ошибки при их усановке: все что можно сделать неправильно - я сделал! Поэтому, чтоб уберечь вас от неверных действий, я изложу вам основные моменты по установке CGI скриптов. Не пугайтесь, это не так сложно как кажется - даже у меня что-то получилось, а я сам услышал о них недавно! Приступим....



Установка cookie.


Как выставлять cookies клиенту зависит от того, как они будут

использоваться в дальнейшем. Это можно делать как с помощью скриптов,

так и с помощью META-тагов HTML. Можно манипулировать временем

жизни выставленных cookies и устанавливать место, в котором установки

действительны. Общий формат установки таков:

Set-Cookie: NAME=value; EXPIRES=date; DOMAIN=domain_name; PATH=path; SECURE

Более подробно об этом можно прочитать в статье



Установка cookie с использованием Perl/CGI.


Другой способ выставить cookie - с помощью серверного скрипта.

На Perl это будет выглядеть примерно следующим образом: перед

тем как выдавать серверный ответ генерируется HTTP заголовок

print "Content-type: text/html\n";

print "Set-Cookie: username=aaa13; expires=Friday,31-Dec-99 23:59:59 GMT; path=/win/internet/html/; domain=citforum.ru;\n\n";

Чтобы прочитать скриптом значение cookie, которое было установлено

ранее, и соответствующим образом выполнить скрипт, используется

переменная окружения HTTP_COOKIE. На Perl это будет выглядеть

так:

$cookie = $ENV{'HTTP_COOKIE'};

При использовании SSI для просмотра значения cookie можно применить

директиву:

<!--#echo var="HTTP_COOKIE"-->



Установка cookie с помощью HTML.


Простейший способ выставить cookie - использовать соответствующий

META-таг в заголовке <HEAD> </HEAD> любого статического

HTML документа. Это выглядит следующим образом:

<META HTTP-EQUIV="Set-Cookie" CONTENT="NAME=value; EXPIRES=date; DOMAIN=domain_name; PATH=path; SECURE">



Установка нескольких cookie одновременно.


Как с помощью HTML, так и с помощью скриптов можно устанавливать

несколько cookie разом:

HTML:

<META HTTP-EQUIV="Set-Cookie" CONTENT="NAME=value; EXPIRES=date; DOMAIN=domain_name; PATH=path; SECURE">

<META HTTP-EQUIV="Set-Cookie" CONTENT="NAME=value; EXPIRES=date; DOMAIN=domain_name; PATH=path; SECURE">

Perl/CGI:

print "Content-type: text/html\n";

print "Set-Cookie: NAME=value; EXPIRES=date; PATH=path; DOMAIN=domain_name; SECURE\n";

print "Set-Cookie: NAME=value; EXPIRES=date; PATH=path; DOMAIN=domain_name; SECURE\n\n";



Встроенные хеши


Переменные окружения, использующие встроенные хэши %SIG, %ENV, %FORM{}.

%SIG - хэш, в котором хранятся обработчики различных

ситуаций, возникающих в perl. Например строка local $SIG{__WARN__} = sub{}; отключает предупреждающие сообщения.

%ENV содержит значения переменных среды(окружения), заданных на момент запуска сценария(скрипта).

Ключами обычно бывают имена переменных среды(но их состав зависит от операционной системы), изменение этих значений

вызовет изменение окружения для процессов потомков.

#!/usr/bin/perl/ -w

while (($key, $value) = each(%ENV)){

print "$key => $value\n";

}

программа выдает:

SERVER_SOFTWARE => Apache/1.3.11 (FreeBSD) mod_perl/1.21 PHP/3.0.14

GATEWAY_INTERFACE => CGI/1.1

DOCUMENT_ROOT => /usr/local/www/data

UNIQUE_ID => OZaSFsHofQoAAEd@Cn8

REMOTE_ADDR => 195.202.122.14

SERVER_PROTOCOL => HTTP/1.0

SERVER_SIGNATURE => Apache/1.3.11 Server at www.mojdodir.ru Port 80

REQUEST_METHOD => GET

REMOTE_HOST => www.mojdodir.ru

QUERY_STRING =>

HTTP_USER_AGENT => Mozilla/4.73 [en] (Win98; I)

PATH => /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin

HTTP_ACCEPT => image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png, */*

HTTP_CONNECTION => keep-alive

REMOTE_PORT => 3633

SERVER_ADDR => 195.202.122.14

HTTP_ACCEPT_LANGUAGE => en,ru

HTTP_CACHE_CONTROL => max-age=259200

SCRIPT_NAME => /cgi-bin/1.pl

SCRIPT_FILENAME => /usr/local/www/cgi-bin/1.pl

HTTP_ACCEPT_ENCODING => gzip

SERVER_NAME => www.mojdodir.ru

HTTP_PRAGMA => no-cache

REQUEST_URI => /cgi-bin/1.pl

HTTP_ACCEPT_CHARSET => iso-8859-1,*,utf-8

HTTP_X_FORWARDED_FOR => 15.0.0.23

SERVER_PORT => 30

HTTP_HOST => www.mojdodir.ru

SERVER_ADMIN => webmaster@www.mojdodir.ru

HTTP_VIA => 1.0 cache.www.mojdodir.ru:3363 (Squid/2.3.STABLE1)

Непосредственно из скрипта элементы хэша %ENV можно вызывать

$ENV{'HTTP_CACHE_CONTROL'} или $ENV{'HTTP_USER_AGENT'},

смотря что нужно вызывать.

%FORM содержит данные, вводимые из формы методом POST: html форма такая:

<form action="/cgi-bin/1.pl" method="post">

<input type="text" name="name1" size=10 maxlength=10>

<input type="text" name="name2" size=10 maxlength=10>

<input type="text" name="name3" size=10 maxlength=10>

<input type="submit" value="send">

<input type="reset" value="reset"></form>

Если мы введем в поле name1 qwe, name2 rty, name3 asd и нажмем send, то через STDIN передаются данные в виде: name1=qwe&name2=rty&name3=asd и содержимое хэша

%FORM(

name1 => qwe,

name2 => rty,

name3 => asd);

Значения полей name* можно получать $FORM{'name1'}, $FORM{'name2'} и т.д.



Выделение чисел в математической записи


Пример использования логических условий для нахождения любых чисел в том числе и в общепринятой математической записи:

#!/usr/bin/perl $_=qq~ 1234 34 -4567 3456 -0.35e-0,2 56grf45 -.034 E20 -.034 e2,01 -,045 e-,23 -,034 e201 3e-.20 -,045 e-,23 e-0.88

4 E-0.20 22 E-21 -0.2 w 4 3 345 2 ^-,3 ~; print "$1\n" while m%(([+-]?(?=\d|[\.,]\d)\d*([\.,]\d*)?((\se|e|\s?\^) ([-+]?\d*[,\.]?)\d+)?)|([+-]?e[+-]?\d*[,.]?\d+))%gxi;

программа исправно выводит все числа. Разберем регулярное выражение

m%(([+-]?(?=\d|[\.,]\d)\d*([\.,]\d*)?((\se|e|\s?\^) ([-+]?\d*[,\.]?)\d+)?)|([+-]?e[+-]?\d*[,.]?\d+))%gxi;

в переменной $1 содержится то, что регулярное выражение находит в результате, т.е. m%(...)%gmi. m%((что-то)|([+-]?e[+-]?\d*[,.]?\d+))%gmi нужно для того, чтобы находить числа вида e-20 или E21(так в математике обозначают десятку в какой-то степени, например e-0,20 = 10-0,20 или E20 = 1021). Рассмотрим левое регулярное выражение "что-то" для чисел вида не e20 или E21:

([+-]?(?=\d|[\.,]\d)\d*([\.,]\d*)?((\se|e|\s?\^)([-+]?\d*[,\.]?)\d+)?)

[+-]? - есть ли в перед числом знак + или -. ? - если вообще есть что-то, находящееся внутри впереди стоящего [...]. Выкинем проверку знака, регексп сократится до

(?=\d|[\.,]\d)\d*([\.,]\d*)?((\se|e|\s?\^)([-+]?\d*[,\.]?)\d+)?

рассмотрим regex (?=\d|[\.,]\d)\d* логический оператор (?=B) требует, чтобы перед числов было B. В данном случае B представляет из себя regex \d|[\.,]\d Regex \d|[\.,]\d значит, что перед каждым числом должно быть что-то либо просто число, либо число, перед которым стоит либо запятая, либо точка, т.е. находим все числа вида ,2 .2 или просто числа 2(2 выбрано для примера, может быть и 3). Далее скобка закрывается и идет \d*, т.е. число вида ,2 точно пройдет(например ,2 e-,23 где перед запятой забыли поставить нолики, но мало ли бывает, забыли, надо и это предусмотреть. Вообще когда пишешь программу, надо предполагать, что е использовать будет ленивый склеротический чайник, правда не всегда возможно предугадать что учудит юзер, но к этому надо стремится), а вот число вида ,223 не пройдет. Да и regex (?=\d|[\.,]\d) говорит о том, что нужно найти только одну цифру после запятой. Для остальных цифр и нужен квантификатор \d*, который значит любое количество цифр, в том числе и ноль, т.е. оно работает и для числе вида .2 или ,2 Далее идет регулярное выражение ([\.,]\d*)? которое говорит о том, есть ли вообще точка и запятая(здесь всю полную строчку в принципе можно усовершенствовать) и число \d*(в том числе и его отсутствие, ведь квантификатор * значит любой символ в том числе и ноль). Отбрасывая все что было выше от этого большого регулярного выражения остается строчка:


((\se|e|\s?\^)([-+]?\d*[,\.]?)\d+)?

Эта строчка отвечает за поиск в строке $_ математических обозначений степеней типа e201, E,20(число в степени 0,20 например a-0,20) и т.д. но только для подстрок вида -,034 e201. Заметьте, что в конце стоит знак вопроса, т.е. если степенное обозначение вообще существует. (\se|e|\s?\^) есть ли числа вида -,034 e201 или -,034e201 и числа в "компьютерной" записи вида 2 ^-,3 = 2-0,3, т.е. этим регекспом мы разрешили пользователю ставить или не ставить пробел при указании степени и разрешили писать значек ^ с пробелом перед ним(если есть). Далее идет выражение ([-+]?\d*[,\.]?), которое говорит о том, что степень может быть с + или - (типа e,-23 где юзер забыл поставть нолик, а на самом деле хотел написать a-0,23). Дальше идет цифра \d* (а может и не идет, т.к. квантификатор то *). Потом идет либо точка либо запятая(причем тут негласно введено ограничение на использование запятой/точки, после e, если степень дробная или вообще есть, точка или запятая должна быть, иными словами не имеет смысла написать -2,34e-,23, хотя юзер на самом деле хотел написать число -2,34-0,23). Наконец мы добрались до конца: идет \d+, но тут уж, пользователь, будь добр напиши хотя бы одно число, т.к. квантификатор +, а не * после \d. Т.е. наложили своего рода ограничения здравого смысла, можно просто написать 2, а можно написать и 2e,- что суть бессмыленно. И еще, m%(что-то)%igm стоит квантификатор i, который разрешает e быть и заглавным и квантификатор x, который разрешает разносить регулярное выражение на несколько строк.

Прошу прошения что не ставил иногда знаки препинания, которые есть точка и запятая, тогда Вы бы подумали, что что-то лишнее написно и не подсечено как спецсимвол при помощи бэкслэша \.

Итак, регулярным выражением m%(([+-]?(?=\d|[\.,]\d)\d*([\.,]\d*)?((\se|e|\s?\^) ([-+]?\d*[,\.]?)\d+)?)|([+-]?e[+-]?\d*[,.]?\d+))%gxi;

были предусмотрены числа степенного порядка, просто числа, числа со знаком, нецелые числа вида ,3(которое есть 0,3 или 0.3), ошибки пользователя при вводе чисел( типа -.034 e2,01 хотя надо бы писать либо -,034 e2,01 либо -.034 e2.01 хотя по смыслу перед точками и запятыми нужно ставить нули, но мы предусмотрели и это) и числа в "компьютерном" представлении.

Конечно, данное регулярное выражение не претендует на абсолютную работу, т.к. оно успешно не работает на подстроках вида -,045 e -,23 e-0.88 считая -,045 отдельным числом, а -,23 возводит в степень e-0.88, хотя по идее должно было бы быть два числа -,045 e -,23 и e-0.88, в таком случае еще одно ограничение пользователю: если хочется, чтобы степенные числа понимались корректно(для этой программы), то нельзя ставить пробел перед степенью e.


Для того чтобы организовать циклы


Для того чтобы организовать циклы по элементам хэша, нужно использовать функцию each:
%hash = (
'шляпа' => 'серая',
'водка' => 'горькая',
'вобла' => 'вкусная');
while(($key,$value) = each %hash){
print "$key => $value\n";
};
Для перебора элементов не очень большого хеша можно воспользоваться foreach:
%hash = (
'шляпа' => 'серая',
'водка' => 'горькая',
'вобла' => 'вкусная');
foreach $key(keys %hash){
print $key,"\n"; #возвращает список ключей хеша
}
%hash = (
'шляпа' => 'серая',
'водка' => 'горькая',
'вобла' => 'вкусная');
foreach $value(values %hash){
print "$value\n"; #возвращает список значений хеша
}
Преимущество each заключается в том, что пары ключ/значение извлекаются по одной, Если хэш содержит слишком много ключей, отказ от предварительного построения полного списка существенно экономит память и время, но функция each не позволяет управлять порядком обработки пар. Перебор хэша при помощи while затрачивает мало памяти. Можно любым образом форматировать выходные данные, при этом нужны всего лишь две скалярные переменные, ключ и значение.
Цикл foreach перебирает заранее построенный список ключей, поэтому после начала цикла он ничего не знает о добавленных или удаленных ключах, ключи, добавляемые внутри цикла, не включаются автоматически в список перебираемых ключей, а удаленные внутри цикла ключи не удаляются из этого списка.
Содержимое хэша можно вывести и так:
while (@Res = each %hash){
print "$Res[0] = $Res[1]\n"
}
Вывод хэша одной строкой.
можно воспользоваться функцией map:
print map {"$_ => $hash{$_}\n"} keys %hash;
функция map позволяет работать с элементами в произвольном порядке, в этом случае создается список строк(ключ => значение), передаваемый функции print. Если сохранить хэш во временном массиве, то он будет выведен как строка:
{
my @temp = %hash;
print "@temp";
}
также можно вывести хэш и таким образом:
print "@{[%hash]}\n";


в двух последних случаях мы интерполируем хэш как список, что не позволяет предсказать или управлять порядком вывода пар "ключ/значение". Данные в последних двух случаях выводятся в виде списка ключей и значений, элементы которого разделяются текущим содержимым переменной $", т.е. не удастся вывести каждую пару значений на новой строке или отделить ключи от значений каким-либо символом. Приведем программу, которая читает файл почтового ящика и выводит количество сообщений от каждого отправителя, отправитель определяется по строке From(цель программы только проиллюстрировать операции с хешами):
#!/usr/bin/perl -w
$file = $ARGV[0] "-";
open(FILE, "
Запускаем программу чтения почтового ящика: bash-2.03$ ./1.pl /usr/home/vovka/mbox
Инвертирование хэша производится при помощи функции reverse, в котором ассоциированные значения исходного хэша являются ключами и наоборот. Воспользуемся списковой эквивалентностью хэшей. В списковом контексте reverse иетерпретирует %hash как список и меняет местами составляющие его элементов. Например: имеем хэш %Glavfish = ("seledka"=>"mokraia","skat"=>"elektricheskij"), если его интерпретировать как список, то получим следующее ("seledka","mokraia","skat","elektricheskij"), после инвертирования список выглядит так: ("elektricheskij","skat","mokraia","seledka"), интерпретация его как хэша дает следующее: ("elektricheskij"=>"skat","mokraia"=>"seledka").

Вывод хеша в порядке вставки без использования Tie::IxHash


Без модуля Tie::IxHash вывод хеша в порядке вставки можно сделать при помощи дописывания числовой информации в хеш. Есть файл news.dat, который выводится скриптом в таком порядке, в каком данные занесены в файл. Необходимо удалить одновременно больше одной строчки из текста(не важно строка это, или разделитесь, это однозначно определяется переменной $/).

#!/usr/bin/perl -wT

use CGI 'param';

@del=param;

sub del{

pop @del; $mass=~s!"|&(.*?);!!g;

open F, "<news.dat" or die "Err: $!"; @mass=<F>; close F;

open F, ">news.dat";

foreach $un(@mass){ $as=$un; $i++; chomp $as;

$un=~s|(.*)new>(.*?)</a>(.*)\[(.*?)\]|$2$4|i;

$un=~s!"|&(.*?);!!g; chomp $un;

$u{"$un"}="$as#$i#\n";

}

foreach $del(@del){

$del=~s!"|&(.*?);!!g; chomp $del;

$terr="Link $u{$del} was deleted<p>\n" if (exists $u{"$del"});

$terr=~s!\d{8}|#(.*?)#!!ig;

print $terr; $terr="";

delete $u{$del} if (exists $u{"$del"});

}{ local $_;

while (($km, $_) = each %u){ push @tmp, "$u{$km}"}

}

@temp=grep{s/#(\d+?)#//}

map{ $_ -> [1]}

sort{$a->[0] <=> $b->[0]}

map{[/#(\d+?)#/, $_]}

grep{!$_{$_}++} @tmp;

print F reverse @temp;

close F;

}

Разъясним принцип работы скрипта. Исходная задача такова: на входе есть несколько checkbox из формы, в которых может быть поставлено больше одной галочки. Требуется найти и вычеркнуть отмеченные строчки. Файл news.dat модержит строки вида:

12345678<a href="lalalal">tra-ta-ta</a>&nbsp;&nbsp;&nbsp;[AAA]

чекбокс отмечается текстом tra-ta-ta, т.е. что-то вида

for ($i=$pos; $i<$pos+$n; $i++) {

$res[$i]=~s|^(\d\d\d\d)(\d\d)(\d\d)|$3\.$2\.$1 |;

print qq~<tr><td>$res[$i]</td><td>

<input type=checkbox name="$1$3" value="$1$3"></td></tr>~

if($res[$i]=~m!>(.*?)</a>(.*?)\[(.*?)\]!);

}

т.е. name="$1$3" value="$1$3" => name=tra-ta-ta&value=tra-ta-ta. Идея заключается в том, что элементы хеша можно пронумеровать в исходном порядке вставки, который будет исходным в силу того, что хеш определяется foreach, который последовательно читает данные из массива. поэтому говорим $i++; в цикле, ставим цифру в разделителе #\d+# и получаем на выходе хеш:


foreach $un(@mass){ $as=$un; $i++; chomp $as;
$un=~s|(.*)new>(.*?)</a>(.*)\[(.*?)\]|$2$4|i;
$un=~s!"|&(.*?);!!g; chomp $un;
$u{"$un"}="$as#$i#\n";
}
Дальше начинаем в хеше искать данные, которые передались через @del=param;
foreach $del(@del){
$del=~s!"|&(.*?);!!g; chomp $del;
$terr="Link $u{$del} was deleted<p>\n" if (exists $u{"$del"});
$terr=~s!\d{8}|#(.*?)#!!ig;
print $terr; $terr="";
delete $u{$del} if (exists $u{"$del"});
}
при помощи функции exists проводится проверка на наличие элемента в хеше. Итак, получили хеш с ключами, являющимися подстроками строк из файла news.dat, и значениями самих строк, т.е. в памяти точно лежит файл, превосходящий по размеру news.dat чуть меньше чем в два раза.
Далее идет вытаскивание значений из файла, уже без удаленных(было сравнение по подстроке):
{ local $_;
while (($km, $_) = each %u){ push @tmp, "$u{$km}"}
}
Замечательно, проверили, занесли в массив @tmp. Здесь локализация local $_; применена для того, чтобы убрать при использовании ключа -w лишнего warning из серверного лог-файла ошибок. Вытащили новый массив, который нужно соранить в файл news.dat. Теперь нужно убрать из массива @tmp повторяющиеся элементы, отсортировать по номерам #(\d+)#, убрать эти номера из элементов массива @tmp и сохранить массив в прежнем виде:
@temp=grep{s/#(\d+?)#//}
map{ $_ -> [1]}
sort{$a->[0] <=> $b->[0]}
map{[/#(\d+?)#/, $_]}
grep{!$_{$_}++} @tmp;
print F reverse @temp;
операция grep{!$_{$_}++} удаляет из массива повторяющиеся элементы, map{[/#(\d+?)#/, $_]} создает временный список анонимных массивов, которые затем сортируются sort{$a->[0] <=> $b->[0]}, затем map{ $_ -> [1]} приводит элементы массива в удобоваримый вид и grep{s/#(\d+?)#//} вырезает нуумерацию массива, оставшуюся от начального формирования хеша %u.
Далее оборачиваем конечный массив @temp функцией reverse и получам такой-же файл news.dat, только без элементов, отмеченных пользователем в чекбоксе.
Еще один вывод хеша в порядке вставки без использования приспособленных
для этого модулей:
my @qq = qw(a s d f g h j y f d e e t y u i v f s a e);
my @del = qw(f h u);
my (%to, %del, %exist);
map {$del{$_} = 1} @del;
for (my $i=$#qq; $i>=0; $i--){
if (!exists $exist{$qq[$i]}){
$exist{$qq[$i]} = 1;
$to{$i} = $qq[$i] unless(exists $del{$qq[$i]});
}
}
my @tmp;
foreach (sort{$a$b} keys %to){
push @tmp, $to{$_};
print "$to{$_}\n";
}
автор: Monax from

Вывод хеша в порядке вставки с использованием Tie::IxHash


Для перебора элементов хэша в порядке вставки, т.к. keys и each выводят элементы хеша неупорядоченно, можно воспользоваться модулем(либо операциями с массивами) Tie::IxHash

use Tie::IxHash

tie %hash, "Tie::IxHash";

#операции с %hash

@keys = keys %hash;

Модуль Tie::IxHash заставляет функции keys, each и values возвращать элементы в порядке занесения их в хэш. Если у Вас нет такого модуля IxHash.pm то нужно зайти на , найти его и установить, если у вас нет прав на установку библиотек, то в первой строчке скрипта нужно написать #!/put'/do/perl'a -wT -I/put'/do/nugnogo/modulia и установить модуль в Вышей домашней директории. Пример использования Tie::IxHash:

use Tie::IxHash

tie %hash, "Tie::IxHash";

%hash = (

'шляпа' => 'серая',

'водка' => 'горькая',

'вобла' => 'вкусная');

print "В упорядоченной вставке список хеша такой:\n";

foreach $qwerty (keys %hash){

print " $qwerty\n";

}

print "Кроме того, предметы обладают некоторыми свойствами:\n";

while(($predmet, $opredelenie) = each %hash){

print $predmet $opredelenie,"\n";

}

см. тех документацию по модулю Tie::IxHash.



Вывод результатов поиска


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

$sn=4; { local $_=$description1; print "...$1<font color=red>$3</font>$4..." while(m/(([\s,\.\n^]*\w*){$sn})(\s*$query\s*)(([\s,\.\n^]*\w+){$sn})/ig); } $_="";

Исходная задача состоит в следующем: вывести по 4 слова спереди и сзади результата поиска, причем так, чтобы если слово находится первым, то будет видно 4 слова позади него. В точности такое-же условие и для последнего слова.

Соответственно из вида регекспа понятно, что разделителями слов могут быть символы [\s,\.\n^]*, в том числе и символ перевода каретки ^. Комбинация (\d\d\d){$sn} значит что нужно начти 3 цифры три раза.



WWW,CGI-специфика


Q: Я положил скрипт на сервер, ввожу его путь в браузер, но вместо того,

чтобы выполнять скрипт, браузер выводит его исходный текст.

A: Чтобы сервер запускал скрипт и выдавал результаты его работы, необходимо

объяснить серверу, что этот файл -исполняемый. Существующее в

unix-системах понятие "атрибуты файла" отличается от атрибутов в DOS/Windows,

и в unix исполняемый файл определяется не расширением, а атрибутом.

Если сервер работает на unix-системе, необходимо проставить на

файлатрибуты исполняемого файла. Это делает команда "chmod 0755 file",

если вы работаете через telnet или ssh, или же продвинутый FTP-клиент типа

CuteFTP или FAR ftp plugin. Атрибуты файла, который могут исполнять

все желающие (в том числе и сервер), выглядят в буквенном представлении, как

-rwxr-xr-x

Если вы работаетев win32 среде, достаточно определить программу,

запускающую файлы с расширением, которое вы даете скриптам - например, cgi.

Для этого нужно в windows explorer'е дважды кликнуть по файлу .cgi, и

выбрать perl\bin\perl.exe в качестве запускающей программы.

Q: При попытке запустить скрипт происходит 500 Internal Server Error! Что это за

ошибка?

A: Это, строго говоря, просто сообщение об одной из ошибок, приведших к

невозможности нормального выполнения скрипта. Узнать что же привело к этому можно в большинстве случаев из логов сервера.(*)

Чаще всего возникает в следующих случаях:

1) в первой строке отсутствует или неправильно указан путь к perl. обычно это

#!/usr/local/bin/perl или #!/usr/bin/perl

2) Файл со скриптом содержит DOS-овые концы строк 0x0D 0x0A. В юниксе конец

строки- только 0x0A. Чтобы исправить это, воспользуйтесь соотв. возможностью

редактора FAR(shift-f2, asUnix text), или призакачке скрипта на ftp

используйте ASCII-режим вместо бинарного.

3) Скрипт невыводит ничего во время работы, или не выводит заголовок.

Hеобходимо, чтобы он выводил хотя бы content-type заголовок, например:

print "Content-type: text/html\n\n";


print "Ok";

Обратите внимание на двойной перевод строки в конце заголовка.

(*)Примечание:

Если у вас нет доступа к логу ошибок сервера, для отладки скрипта используйте

директиву use CGI::Carp qw (fatalsToBrowser); В этом случае при фатальной ошибке, приводящей к завершению скрипта, эта ошибка будет видна вам в браузере.

Q: Как узнать IP человека, вызвавшего скрипт?

A: $ENV{'REMOTE_ADDR'}

Q: Как узнать, с какой страницы идет ссылка на скрипт?

A: $ENV{'HTTP_REFERER'}

Q: Как узнать IP прокси, если через нее запустили скрипт?

A1 $ENV{'HTTP_VIA'}

A2: Hекоторые прокси добавляют в конец заголовка строку типа

", via ProxyName", и в результате вместо переменной HTTP_VIA эту строку можно

наблюдать в конце переменной $ENV{'HTTP_USER_AGENT'}

Q: А можно ли узнать IP юзера, запустившего скрипт через прокси?

A: $ENV{'HTTP_X_FORWARDED_FOR'}. Hо не все прокси обеспечивают IP сидящего за

ними юзера. "Честные" прокси,- у провайдеров, например,- это делают. А

некоторые халявные (анонимные) прокси-сервера, могут и не показывать IP сидящего "за" ними пользователя.

Q: А какие еще значения есть у этого %ENV ?

A: for (keys %ENV) { print "$_ = ${ENV{$_}}\n" }

A1: То же самое, но по-другому записанное (более "перловое" решение):

print join("
",map("$_ = $ENV{$_}", keys %ENV));

Q: Как запускать скрипт на сервере через определенные промежутки времени, или

в определенное время суток, день недели, месяц?

A: Это возможно, если есть шелл-доступ на сервер. Читайте man crontab

Q: Как, зная ip, получить имя (DNS), за которым этот ip закреплен?

A: Встроенная функция gethostbyaddr()

Q: Что такое method GET, и что такое method POST?

A1: rfc2616

A2: GET - передача параметров скрипту из строки location браузера. Пример:

script.cgi?mode=aa&type=bb&length=12

Минусы этого метода:

1. Все параметры видны в строке. Если в кач-ве параметров передается пароль

- получается дырка в безопасности



2. Длина данных, передаваемых таким методом, ограничена.

3. Hельзя передавать multipart form data, в т.ч. файлы через кнопочку

"Browse"

POST - передача данных скрипту через STDIN. Отсутствуют минусы метода GET.

Обычно метод GET используется, когда скрипт вызывается со страницы через линк

, а POST - при нажатии кнопки submit в форме.

О получении параметров внутри скрипта читайте в следующем Q.

Q: Как обрабатывать параметры, переданные скрипту?

A1: Используйте модуль CGI. Прочтите perldoc CGI , а для начала простой

пример:


use CGI;

$q=new CGI();

$mode=$q->param('mode');

$q - объект, который имеет несколько методов и полей, относящихся к

параметрам вызова, переменным окружения, cookie, и пр. В $mode содержится значение параметра 'mode'.


или проще: use CGI qw/:standard/;

$mode=param('mode'); если вам не нужно несколько объектов.


A2: Некоторые предпочитают пользоваться библиотекой cgi-lib (она тоже входит в модуль CGI, хотя существуют индивидуумы, которые включают ее отдельным файлом).

Пример:

use CGI qw(:cgi-lib);

ReadParse(); #Функция возвращает все параметры в хэш.

$mode=$in{'mode'};

A3: Можно пойти по пути изобретения велосипеда и начать переписывать модуль CGI, т.е. разбор параметров метода GET, потом наверняка понадобиться POST, а потом multipart form data. В написанный код закрадуться ошибки, которые придется отлавливать на последующих этапах. Это конечно дает неплохую практику программирования, но лучше просто посмотрите как написан модуль CGI.

(Высказанное в последнем ответе является тем самым ИМХО ).

Q: Посоветуйте халявный хостинг с поддержкой CGI-perl.

A:

Примечание:

В последнее время почти все халявные хостинги перестали поддерживать исходящие запросы, т.е. скачать откуда-нибудь файл используя LWP или Net::Ftp не получиться.

Если знаете хостинг с поддержкой исходящих запросов, напишите мне, буду благодарен.

Q: Что надо указатьв заголовках HTTP-ответа CGI скрипта, выводящего

содержимое, которое надо не показать на экране, а сохранить в файле с заданным

именем, например 'download.zip' ? А то браузер сохраняет файл с именем скрипта.

A: print "Content-Type: application/octet-stream\nContent-Length: ", -s 'download.zip' ,"\n";

print "Content-Disposition: attachment; filename=download.zip\n\n";

Q: Директивы SSI не обрабатываются в выводе скрипта. Что делать?

A: Есть специальный модуль - CGI::SSI.


Зачем нам нужны эти скрипты?


Форум, чат, доска объявлений, голосование, гостевуха, каталог, топ, магазин и аукцион - все это можно сделать с помощью CGI. Возникает вопрос, что такое скрипт? Грубо говоря, скрипт - это программка, которая запускается не на вашем компьютере, а на сервере. В результате работы скрипта, сайт превращается из существа пассивного, в существо активное, у вас появляется больше возможностей как для общения с посетителями (форумы, чаты, mail формы, опросы, голосования, рассылки), так и в управлении сайтом (организация оперативно обновляемых новостей, баннерокрутилка, публикация статей через веб и т.д.) Кроме того, некоторые скрипты могут стать основой вашего сайта (например, каталог, топ-рейтинг, служба поздравительных открыток, поисковая машина…) - у вас уже загорелись глаза? То-то же, скрипты открывают новые горизонты, у кого-то появляются нездоровые желания создать свой Yahoo!, а кто-то просто хочет сделать на сайте гостевую книгу.



Задачки


Hello, world!

$_=q$qsjou<vd<r<aia+<rr<bfmmp+<xpsme=\ob$;

y?<-@*-.b-z? -$+-/a-y?;s=\D+=$&=ee

Заголовки запросов и ответов


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

Этапы соедирения.

Первый этап это когда HTTP -клиент(браузер) соединяется с сервером.для этого он использует протокол TCP/IP соединение происходит к известному клиенту TCP-порту (80 -номер порта HTTP) (другие сервисы сидят на других портах ,например FTP и SMTP на 21 и 25)

Вторым этапом идет запрос клиента:клиент передает заголовок запроса и возможно(в зависимости от метода) тело сообщения запроса.В заголовке обязательно указывается метод ,URI,и версия HTTP,и может быть еще несколько необязательных полей

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

Четвертым этапом происходит разрыв TCP/IP соединения.

HTTP -запрос.

Запрос состоит из Строки запроса(она обязательна) и остальных полей. Синтаксис строки :МЕТОД <SP> URI <SP> HTTP/версия <CRLF>

где <SP> -пробел ,<CRLF> -переход на новую строку

Методы HTTP.

GET

Самый часто применяемый метод,в протоколе HTTP/0.9 был единственным методом,и применяется для извлечения информации по заданому URI

Может быть условным если в заголовке указано поле If-Modified-Since:

HEAD

Почти идентичен GET но отличается тем что сервер не возвращает тело обьекта а только его заголовок (метаинформацию) программы могут применять его для проверки гиперссылок на правильность,доступность и изменения.

POST

передает данные для обработки их программой ,указаной в URIсдесь обязательно указывается поле Content-Length:

Сушествуют и другие ,реже применяемые методы,например PUT -для сохранения передавемых данных в указаном URI

и DELETE для удаления ресурса.

Поля заголовка запроса.

После строки запроса идут поля заголовка запроса.

Поля общего(general-header) заголовка (он общий как для запросов так и для ответов):


Date:

Указывает дату запроса,например:

Date: Sun, 20 Nov 1994 08:12:31 GMT

MIME-version:

Указывает версию MIME (по умолчанию 1.0)

MIME-version: 1.0

Pragma:

Содержит указания для таких промежуточных агентов как прокси и шлюзы,

Pragma: no-cache

Поля относящиеся к запросу(Request-Header):

Authorization:

Содержит информацию аутентификации

Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==

From:

Браузер может посылать адрес пользователя серверу

From: quake@doom.ru

If-Modified-Since:

используется при методе GET ресурс возвращается ,если он был изменен с указаного момента, может использоваться при кешировании.

If-Modified-Since:Mon 15 Jul 1997 00:15:24 GMT

Referer:

Содержит URL предшествующего ресурса.

Referer: http://www.uic.nnov.ru/~paaa/index.html

User-Agent:

Програмное обеспечение клиента.

User-Agent: Mozilla/3.0

Заголовок информации сообщения (Entity-Header) применяется как в запросах так и в ответах (при этом некоторые поля только в ответах):

Allow: (в ответе сервера)

Список методов,поддерживаемых ресурсом.

Allow: GET, HEAD

Content-Encoding:

идентифицирует метод кодировки,которым был закодирован ресурс

Content-Encoding: x-gzip

Content-Length:

Длина тела сообщения

Content-Length: 102

Content-Type:

Содержит тип ресурса(),для текстовых еще и кодировку символов(необязательно)

Content-Type: text/html; charset=windows-1251

Expires: (в ответе сервера)

Дата окончания действия ресурса,применяется в кешировании для запрета кеширования устаревших ресурсов (в ответе)

Expires: Tue, 24 Sep 1998 23:00:15 GMT

Last-Modified: (в ответе сервера)

Время последнего обновления ресурса

Last-Modified: Tue, 23 sep 1998 13:48:40 GMT

Другие поля:

Поля Accept: указывают серверу выдавать только указаные форматы данных,которые клиент может распознать.

Accept: text/html

Accept: text/plain

Accept: image/gif

Примеры запросов:



Простейший запрос: GET /index.html HTTP/1.0

Посложнее: GET /somedir/somedoc.html HTTP/1.0 User-Agent: Mozilla/2.0 Accept: text/html Accept: text/plain Accept: image/gif



Передача данных CGI- скрипту через метод GET GET /~paaa/cgi-bin/test.cgi?name=Dmitry&organization=%D3%ED%E8%E2%E5%F0%F1%E8%F2%E5%F2+%CD%E8%E6%ED%E5%E3%EE+%CD%EE%E2%E3%EE%F0%EE%E4%E0&Name=&email=&comment= HTTP/1.0 User-Agent: Mozila/2.0 Accept: text/html Accept: image/gif

Используя метод POST данные передаются в теле сообщения запроса: GET /~paaa/cgi-bin/test.cgi HTTP/1.0 User-Agent: Mozila/2.0 Accept: text/html Accept: image/gif Content-Type: application/x-www-form-urlencoded Content-Length: 131

name=Lesha &organization=%D3%ED%E8%E2%E5%F0%F1%E8%F2%E5%F2+%CD%E8%E6%ED%E5%E3%EE+%CD%EE%E2%E3%EE%F0%EE%E4%E0&Name= &email= &comment=

Ответ HTTP-сервера.

Ответ идет от сервера.Состоит он из строки состояния и затем поля ответа Общий заголовок() и заголовок тела сообщения (Entity-Header),которые уже описаны при обсуждении запроса. и еще идет заголовок ответа(Response-Header).

Строка состояния имеет следующий формат:

HTTP/version <SP> Status-Code <SP> Status-Phrase

где HTTP/version версия,Status-Code -3х значный код,и Status-Phrase текстовая фраза, поясняющая код ,пример: HTTP/1.0 200 Ok

,200 -код означающий успешную обработку запроса,что и поясняет "Ok"

Заголовок ответа состоит из полей:

Location:

Содержит URI ресурса,может быть использован для переключения клиента в другое место, если например ресурс был перемещен в другое место или на другой сервер.

Location: http://www.uic.nnov.ru/newlocation/index.html

Server:

Информация о програмном обеспечении сервера

Server: Apache/1.1

WWW-Autenticate:

Параметры аутентификации.

WWW-Autenticate: Basic realm="doomsday"

Коды ответов HTTP.

Код статуса Значение
200 OK
201 Успешная команда POST
202 Запрос принят
203 Запрос GET или HEAD

выполнен
204 Запрос выполнен но нет содержимого
300 Ресурс обнаружен в нескольких местах
301 Ресурс удален навсегда
302 Ресурс отсутствует временно
304 Ресурс был изменен
400 Плохой запрос от клиента
401 Неавторизованый запрос
402 Необходима оплата за ресурс
403 Доступ Запрещен
404 Ресурс не найден
405 Метод не применим для данного ресурса
406 Недопустимый тип ресурса
410 Ресурс Недоступен
500 Внутренняя ошибка сервера

(это по вашу душу,юные CGI-программисты ;( )
501 Метод не выполнен
502 Неисправный шлюз либо перегруз сервера
503 Сервер недоступен/тайм-аут шлюза
504 Вторичный шлюз/тай-аут сервера
<


Более подробное описание всех кодов можно найти в RFC-1945

Несколько примеров:



HTTP/1.0 200 Ok Date: Wed, 25 Sep 1998 23:00:00 GMT Server: Apache/1.1 MIME-version: 1.0 Last-Modified: Mon 15 Nov 1996 15:20:12 GMT Content-Type: text/html Content-Length: 2000

<HTML><HEAD><TITLE>Hello</TITLE></HEAD> <BODY bgcolor="green" text="yellow"> ...... </HTML> А вот такое сервер выдаст в неудачном случае: HTTP/1.0 404 Not Found



CGI-заголовок.

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

Заголовок (CGI-Header) состоит из полей:

Content-Type:

Должно обязательно присутствовать,если есть тело.

Content-Type: text/html

Location:

Содержит URL ресурса на который скрипт перенаправляет запрос.Как правило,если присутствует это поле больше ничего не указывается.

Location: http://www.idsoftware.com/index.html

Status:

Позволяет CGI скрипту вернуть статус обработки,если это поле не задано,то сервер подразумевает "200 Ok"

Status: 404 Not found

На базе этой информации сервер и формирует окончательный заголовок,который и передается клиенту.

Примеры:



Обычно такое выдает скрипт: Content-Type: text/html

<HTML><HEAD>....... Но иногда такое(когда он служит для перенаправления): Location: http://www.mustdie.ru/

А вот пример возврата статуса: Content-Type: image/gif Status: 190 Its seems great like a playing doom! WOW!

GIF89a........

nph-скрипты.

Иногда возникает необходимость чтобы CGI -скрипт сам отвечал напрямую клиенту, минуя разбор заголовка.Это во-первых уменьшает нагрузку на сервер,и во вторых, что самое главное такой прямой ответ клиенту позволяет скрипту полностью контролировать транзакцию.Для этого существуют nph-скрипты(Not Parse Header) ,имя скрипта должно начинаться с префикса "nph-" ,Например "nph-animate.cgi" .Такие скрипты сами формируют HTTP-ответ клиенту,что полезно при анимации:





#!/usr/bin/perl #nph-animate.cgi

$times = 20; #Заготовте несколько небольних gif-файлов для этой программы @files = qw(img0.gif img1.gif img2.gif img3.gif);

select (STDOUT); $|=1; #autoflush mode on #Generate header print "HTTP/1.0 200 Okay\n"; print "Content-Type: multipart/x-mixed-replace;boundary=myboundary\n\n";

print "--myboundary\n"; for ($num=1;$num<=$times;$num++) { foreach $file (@files) { print "Content-Type: image/gif\n\n"; open(PIC,"$file"); print <PIC>; close(PIC); print "\n--myboundary\n"; sleep(3); } } print "\n--myboundary--\n";

Этот пример вам выдаст анимацию ,составленую из нескольких .gif -файлов.Если же вы получили вместо анимации сообщение об ошибках,то вам следует,может быть перейти к следующей главе, которая поведает вам о правах доступа- того,без чего Unix не был бы Unixом.


Загрузка файлов на сервер через Интернет.


Файлы можно загружать на веб-сервер через Интернет,используя формы.Вы,наверное,сами не раз это делали.Разберем более подробно,как это делается. Нужно создать форму с полем типа file и методом кодировки multipart/form-data.

#!/usr/local/bin/perl

print "Content-type:text/html\n\n"; print <<HTML; <html><head>

<script language="javascript">

<!-- function fill () { if (fn==document.form.entry.value) { document.form.file.value=fn; } } //-->

</script>

</head>

<body bgcolor="e6e8fa">

HTML print "<p><table width=300 bgcolor=\"bfbfbf\">\n"; print "<h3 align=center><font color=\"0000ff\">File upload:</font></h3>\n"; print "<center><FORM action=\"../cgi-bin/upload.cgi\" name=\"form\" METHOD=\"POST\" ENCTYPE=\"multipart/form-data\">\n"; print "<tr><td align=center><b>Select file:</b></td>\n"; print "<tr><td><input type=\"file\" name=\"entry\" onBlur=\"fill()\"></td>\n"; print "<tr><td><input type=\"hidden\" name=\"file\" value=\"1\"></td>\n"; print "<tr><td align=center><input type=\"Submit\" value=\"Upload\"></td></table>\n"; print "</form></center></table></body></html>\n";

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

$content_type = $ENV{'CONTENT_TYPE'}; binmode STDIN; read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'}); if ((!$content_type) ($content_type =~ m#^multipart/form-data#)){ ($boundary = $content_type) =~ s/^.*boundary=(.*)$/\1/; @pairs = split(/--$boundary/, $buffer); @pairs = splice(@pairs,1,$#pairs-1); for $part (@pairs) { ($dump,$fline,$value) = split(/\r\n/,$part,3); next if $fline =~ /filename=\"\"/; $fline =~ s/^Content-Disposition: form-data; //; (@columns) = split(/;\s+/, $fline); ($name = $columns[0]) =~ s/^name="([^"]+)"$/\1/g;


if ($#columns > 0) { if ($value =~ /^Content-Type:/) { ($dump,$dump,$value) = split(/\r\n/,$value,3); } else {($dump,$value) = split(/\r\n/,$value,2);}} else {($dump,$value) = split(/\r\n/,$value,2); if (grep(/^$name$/, keys(%CGI))) { if (@{$FORM{$name}} > 0) { push(@{$FORM{$name}}, $value);} else { $arrvalue = $FORM{$name}; undef $FORM{$name}; $FORM{$name}[0] = $arrvalue; push(@{$FORM{$name}}, $value);}} else { next if $value =~ /^\s*$/; $FORM{$name} = $value;} next;} $FORM{$name} = $value;}}

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

$upfile=$FORM {'entry'}; #Имя загружаемого файла. $destfile=$FORM {'file'}; #Имя,под которым он будет записан в каталог назначения. $destdir="/home/upload"; #Имя каталога для загрузки. chdir ("$destdir");

#Отбрасываем путь,оставляя только имя. $destfile=~s/\w+//; $destfile=~s/([^\/\\]+)$//; $destfile=$1;

#Далее записываем файл в каталог назначения. open(FILE, ">$destdir/$destfile"); #Открываем на запись новый файл. binmode FILE; #Устанавливаем бинарный режим. print FILE $upfile; #Записываем в него содержимое загруженного файла. close(FILE); #Закрываем файл.

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