Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Программирование скриптов на Perl'e (Выпуск №7)


Служба Рассылок Subscribe.Ru проекта Citycat.Ru

Программирование скриптов на Perl'e (выпуск 7).


Здравствуйте дорогие мои подписчики.


Сегодня в выпуске:




Несколько слов перед началом


Ура!!!   Количество подписчиков на рассылку превысило 5000 человек.   Как мне кажется, непрерывное увеличение количества подписчиков, указывает на то, что эта рассылка все-таки нужна некоторым людям, и я постараюсь не обмануть Ваши надежды, дорогие мои читатели.

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

Итак, начнем, пожалуй.   Сегодняшние темы:

  • Perl для начинающих - Операторы Perl'а
  • CGI-программирование - Библиотека cgi-lib.pl




    Perl для начинающих - Операторы Perl'а


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

    Арифметические операторы

    Арифметические операторы действуют на числовые значения, и их результатом является число.   Если выражение включает строковые операнды, то Perl конвертирует строковые значения в числовые перед тем, как оценить выражение.   Perl выполняет преобразование строк в числа подобно тому, как это делает функция atof() языка С. В настоящее время Perl поддерживает следующие арифметические операторы:
        +  - сложение
        -  - вычитание или изменение знака
        *  - умножение
        /  - деление (только для чисел с плавающей запятой)
        %  - взятие по модулю (только для  целочисленных значений)
    
        Рассмотрим примеры арифметических операций языка Perl:
            $x=2.5;
            $y=3;
            print ($x+2*$y);    # выведет 8.5
            print (7/$y);       # выведет 2.3333333
            print int (7/$y);   # выведет 2
            print (7%$y);       # выведет 1
            print (7.5%$y);     # выведет 1
    
    Примечание:   В Perl'е оператор деления всегда имеет результатом число с плавающей точкой, а результатом взятия одного числа по модулю другого является целое число и, причем предварительно оба операнда преобразуются к целому типу.
        Рассмотрим следующую операцию взятия по модулю:
            print (7.9%3.6);    # выведет  1 то же (7%3)=1
    
        Perl также поддерживает операторы инкремента и декремента:
        --  - декремент в префиксной или постфиксной форме
        ++  - инкремент в префиксной или постфиксной форме
    
    Если оператор стоит перед переменной, то значение переменной изменяется на 1 и полученной значение используется.   Если после переменной, то ее величина изменяется после применения.
        Рассмотрим примеры операций инкремента и декремента:
            $x=4;
            ++$x;
            print $x;       # выведет 5
            $y=$x--;        # уменьшит x после присвоения y значения x
            print "$y $x"   # выведет 5 4
    
    Употребление инкремента к строковым переменным в Perl'е имеет одну особенность.   Каждый символ остается в своем классе (большие, малые, цифры) и учитывается перенос предыдущего символа.   Таким образом, строковые переменные с цифрами работают как числовые переменные.
        Пример:
            print ++($i="09");   # Результат "10"
            print ++($i="a9");   # "b0"
            print ++($i="az");   # "ba"
            print ++($i="aZ");   # "bA"
    
    Наконец, Perl обеспечивает арифметический оператор для возведения в степень (**).   Рассмотрим следующие примеры использования операции возведения в степень:
            $x=2**3;     # результат 8
            $x=2**0.5;   # квадратный корень из 2
            $x=-2**-3;   # 1/(-2 в кубе), результат -1/8 (-0.125)
    

    Побитовые операторы

    Побитовые операторы воздействуют на бинарное представление целых чисел и имеют целочисленный результат.   Если операндом является строка или дробное число, Perl предварительно преобразует его в целое число и затем обрабатывает операнд, используя 32-битное представление.   Все побитовые операторы С представлены в языке Perl:
        |   - побитовое ИЛИ
        &   - побитовое И
        ^   - побитовое исключающее ИЛИ
        ~   - побитовая инверсия
        <<  - сдвиг влево
        >>  - сдвиг вправо
    
        Рассмотрим следующие примеры побитовых операций:
            $x=5;              # 101 в двоичном
            $y=3;              # 011 в двоичном
            print $x|$y;       # 7 (111)
            print $x&$y;       # 1 (001)
            print $x^$y        # 6 (110)
            print $x&~1;       # 4 (100)
            print $x<<2        # 20 (10100)
            print $x>>1        # 2  (10)
    
    Так же как и в С, поведение операторов сдвига вправо зависит от реализации языка в случае, если операнд является отрицательным.

    Операторы сравнения

    Операторы сравнения сравнивают величины двух операндов.   Так же как при работе с арифметическими операторами, Perl преобразует строчные операнды в численные перед тем, как выполнять сравнение.   Для того чтобы позволить скрипту сравнивать строки, которые не являются числами, Perl имеет дополнительные операторы строкового сравнения.   Эти операторы сравнивают строки, используя величины ASCII.   Если численное значение задано как операнд при сравнении строк, оно сначала преобразуется в строку.
        Операторы сравнения:
            Число      Строка     Значение
          -------------------------------------------------------
              ==         eq         равно
              !=         nе         не равно
              >          gt         больше чем
              <          it         меньше чем
              >=         gе         больше или равно
              <=         lе         меньше или равно
              <=>        cmp        не равно (результат со знаком)
    
    Результатом операции сравнения является единица, если сравнение истинно и нуль в противном случае.   Однако последняя операция (<=> или cmp) может возвращать значения -1, 0 или 1 в зависимости от того, является ли значение первого операнда меньше, чем второго, равным ему или большим.

    Примечание:   Оператор cmp языка Perl ведет себя, аналогично функции strcmp() языка С.

        Рассмотрим следующий пример сравнения:
            $x=5;             # x равно 5
            print ($x<4);     # если false, то выведет 0
    

    Логические операторы

    Логические операторы анализируют булевы выражения и возвращают значения <истинно> или <ложно> в качестве результата.   Perl обрабатывает операнды логических операций как булевы величины, т. е. как истинное или ложное значение.

    Логические операторы Perl'а включают следующие:
          &&   - логическое И (если левое выражение возвращает false, правое не выполняется)
          ||   - логическое ИЛИ (если левое выражение возвращает true, правое не выполняется)

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

        !   - логическое отрицание
        ?:  - условная операция
        ,   - последовательное выполнение
    
    Оператор логического отрицания (!) заменяет значение булевой величины на противоположную.   Так же как и в С, в Perl'е условный оператор (?:) использует три операнда.   Выражение, использующее условный оператор, имеет следующую форму:
            Condition ? true-result : false-result
    
    Аналогично, следующее выражение использует условный оператор для того, чтобы предоставить администратору полный доступ, а всем остальным ограниченный:
            $access=($user eq 'root' ? 'Full' : 'Limited');
    
    Оператор последовательного выполнения (,), также известный как оператор "запятая", не является вполне логическим оператором, поскольку он не анализирует истинность своих операндов.   Perl выполняет операнды оператора последовательного выполнения слева направо и возвращает значение самого правого операнда.

    Следующий пример иллюстрирует использование оператора запятая в цикле for:

            for ($i=0, $j=10; $i<10; $i++, $j--)
              {  print $i,' ',$j,"\n";
              }
    
         Результат:
            0 10
            1 9
            2 8
            3 7
            4 6
            5 5
            6 4
            7 3
            8 2
            9 1
    

    Строковые операторы

    Поскольку Perl представляет собой язык для обработки текста, неудивительно, что в него включены дополнительные операторы для работы со строками.   Ниже перечисляются операторы обработки строк:
        .   - конкатенация строк
        х   - репликация
        =~  - сопоставление переменной с образцом
        !~  - то же, что и предыдущее, но с дополненным отрицанием результата
    
        Первые два оператора легко иллюстрируются примером:
            print 'b' . 'an' x 2 . 'a';        # выведет 'banana'
    
    Как показано, это выражение использует конкатенацию строк и оператор репликации для того, чтобы напечатать строку "banana".

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

            $var='banana';
            print ($var=~/ana/) ? TRUE : FALSE;
    
    В этом случае оператор проверки вхождения в строку образца (=~) использовался для проверки того, входит ли образец ana в переменную $var.   В данном случае выражение принимает значение "истинно".

    Операторы присваивания

    Если вы знакомы с языком программирования С, то формы операторов присваивания языка Perl должны быть для вас совершенно знакомыми.   Так же как и в С, эти операторы заставляют Perl выполнить специальные операции со значениями, которые появились с правой стороны оператора, и затем выполнить присваивание:
           =
          +=
          -=
          *=
          /=
          %=
          |=
          &=
          ^=
          ~=
         <<=
         >>=
         **=
          .=
          x=
    
    Когда вы работаете со списками в языке Perl, оператор присваивания не обязательно относится ко всему списку.   Скрипт может присваивать значения отдельным элементам списка, как показано ниже:
            @items[2, 4, 7]=(100, 200, 300);
    
    В этом случае оператор присваивает значение трем элементам списка.   Аналогичным образом следующее выражение распаковывает элементы списка, присваивая значения двух первых элементов двум скалярным переменным, а остаток массива - списочной переменной:
            ($arg1, $arg2, @rest)=@ARGV;
    

    Операуии для работы со списками

    В состав операций для работы со списками входят следующие:
        ,   - конструктор списков
        ..  - оператор области
        х   - оператор репликации
    
    Вы уже использовали конструктор списков для инициализации массивов и создания списка переменных.   Оператор области возвращает в качестве значения последовательность целых чисел, которая начинается от левого операнда и продолжается до правого операнда включительно.   В скриптах часто используют оператор области совместно с конструктором списков для создания списков.   Например, следующее выражение использует оператор области для того, чтобы создать список под именем @digits, который содержит числа от нуля до девяти:
            @digits = 0..9;  # список (1, 2, 3, 4, 5, 6, 7, 8, 9)
    
    Аналогичным образом, это выражение может использовать оператор области для создания области изменений индексов массива.   Предположим, что список @days содержит дни недели (начиная с воскресенья).   В следующем выражении списку @weekdays присваиваются значения, начиная от понедельника до пятницы:
            @weekend = @days[1..5];
    
    Наконец, следующее выражение использует два оператора области для создания списка шестнадцатеричных цифр:
            @hex_digits = (0..9, a..f);
    
    Оператор репликации просто создает копии данного операнда указанное число раз.

    Приоритеты выполнения операторов

    Как и всякий язык программирования, Perl определяет приоритеты выполнения операторов, с помощью которых упорядочивается последовательность их выполнения.   Ниже перечисляются приоритеты операторов, начиная от высшего и следуя к низшему:
            ++  --
            !  ~  \  унарные + и -
            **
            =~  !~
            *  /  %  х
            +  -  .
            <<  >>
            -d  -е  -s  -w (и другие файловые операторы)
            <  >  <=  >=  It  gt  le  ge
            ==  !=  <=>  eq  ne  cmp
            &
            |  ^
            &&
            ||
            ..
            ?:
            =  +=  -=  *= и т. д.
            ,  =>
            not
            and
            or  xor
    
    В своем скрипте вы можете изменять последовательность выполнения операторов с помощью скобок.

    На этом на сегодня все с операторами Perl'а.




    CGI-программирование - Библиотека cgi-lib.pl


    После того как у меня в рассылке появился раздел, посвященный CGI-программированию, я получил письмо, опубликованное в № 5 данной рассылки.   Как Вы помните (если нет - то напомню) в нем мне предлагалось использовать модуль CGI для упрощения работы.

    Сегодня я представляю Вашему вниманию перевод части книги Юджина Эрика Кима (Eugene Eric Kim) "Руководство CGI Разработчика" (CGI Developer's Guide), посвященной библиотеке cgi-lib.pl.



    Руководство по использованию cgi-lib.pl.

    СОДЕРЖАНИЕ

  • Использование cgi-lib.pl
  • Процедуры и Переменные
       
  • &ReadParse
  • &PrintHeader
  • &HtmlTop
  • &HtmlBot
  • &SplitParam
  • &MethGet
  • &MethPost
  • &MyBaseUrl
  • &MyFullUrl
  • &CgiError
  • &CgiDie
  • &PrintVariables
  • &PrintEnv
  • Исходный Код Библиотеки


    Библиотека cgi-lib.pl Стива Бреннера была одним из первых общедоступных и широко используемых библиотек CGI-программирования.   cgi-lib.pl очень упрощает CGI-программирование в Perl'е, обеспечивая процедуры разбора запросов и другие полезные CGI-процедуры.   Эта библиотека написана для Perl 4, хотя будет работать и с Perl 5.

    Первичная функция cgi-lib.pl это разбор ввода формы.   Она разбирает ввод формы и размещает данные в хэше, ассоциирую значения полей с их именами.   Эта библиотека развивалась, начиная с ее первого выпуска и может работать с регулярным декодированным вводом формы (application/x-www-form-urlencoded - данные, которые посланы как аргументы непосредственно в URL) и многоблочным декодированным вводом формы, используемым для недавно предложенной HTML загрузки файла (multipart/form-data - данные, которые посланы как стандартный ввод подобно многоблочным приложениям электронной почты).

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

    Примечание

    Линкольн Стейн написал очень хорошую библиотеку для Perl 5 называемую CGI.pm, которая включает поддержку разбора ввода формы, вывод HTML-форм и внутреннюю отладку.   Если Вы знаете Perl 5 или планируете изучать его, то я рекомендую Вам ее.   Она доступна на http://www-genome.wi.mit.edu/ftp/pub/software/WWW/cgi_docs.html

    Чтобы использовать cgi-lib.pl, Вы должны разместить ее или в том же самом каталоге, что и Ваши Perl-скрипты или в глобальном каталоге Perl-библиотек (обычно это /usr/lib/perl на UNIX машинах).   На UNIX машинах удостоверьтесь, что cgi-lib.pl доступна для чтения всем.

    Использование cgi-lib.pl требует двух шагов: включение библиотеки в скрипт и запроса функций.   Минимальная CGI программа, использующая cgi-lib.pl:

    #!/usr/bin/perl
    
    if (&ReadParse(*input))
      {  print &PrintHeader,&HtmlTop("Form Results");
         print &PrintVariables,&HtmlBot;
      }
    else
      {  print &PrintHeader,&HtmlTop("Entry Form");
         print <<EOM;
    <form method=POST>
      <p>
      Name: <input name="name"><br>
      Age: <input name="age">
      <p>
      <input type=submit>
    </form>
    EOM
         print &HtmlBot;
      }
    
    Эта программа делает следующее:

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

    Главная процедура - &ReadParse, которая берет каждую пару имя/значение формы и помещает в хэш %input.   Массив сортирован по имени, так $input{'name'} эквивалентно 'value'.

    &PrintHeader, &HtmlTop, &PrintVariables и &HtmlBot - все HTML-функции вывода, описанные более подробно дальше.


    В этом разделе я описал и определил функции и переменные, которые доступны в библиотеке cgi-lib.pl.

    &ReadParse разбирает ввод формы MIME типов - application/x-www-form-urlencoded и multipart/form-data.   Передайте ей переменную *varname, и она разместит данные разобранные данные формы в хэше %varname в виде:   $varname{name}=value.

    Если имя имеет больше чем одно ассоциированное значение, то значения отделяются нулевым символом.   Вы можете использовать функцию &SplitParam, чтобы разделить значения $varname{name}.

    Если Вы хотите использовать &ReadParse для сохранения загружаемых файлов, используя HTML загрузку файла, Вы должны изменить значение переменной $cgi-lib'writefiles в cgi-lib.pl с 0 на 1.

    &PrintHeader возвращает следующую строку:   Content-Type: text/html\n\n

    Эта функция вызывается так:   print &PrintHeader;

    &HtmlTop получает строку, которая используется между тэгами <title> и тэгами <h1>.   Она возвращает действительный HTML-заголовок.   Например, код:  
            print &HtmlTop("Hello, World!");
    выведет следующее:
    <html><head>
    <title>Hello, World!</title>
    </head>
    <body>
    <h1>Hello, World!</h1>
    

    &HtmlBot

    &HtmlBot является дополнением &HtmlTop и возвращает строку HTML-окончания:
    </body></html>

    &SplitParam

    &SplitParam разделяет многозначный параметр, возвращенный хэшем из &ReadParse и возвращает список, содержащий каждый отдельный элемент.   Например, если Вы имеете следующую форму:
        <form method=POST>
          Street 1: <input name="street"><br>
          Street 2: <input name="street"><br>
          <input type=submit>
        </form>
    
    и разбираете ее ввод, используя:
            &ReadParse(*input);
    то получаете следующие значения $input{'street'}:
            value1\0value2

    Чтобы разделить эти значения, Вы можете cделать следующее:
            @streets=&SplitParam($input{'street'});

    что возвратило бы список:
            (value1, value2)

    &MethGet возвращает 1, если REQUEST_METHOD - GET, иначе возвращает 0.

    &MethPost

    &MethPost возвращает 1, если REQUEST_METHOD - POST, иначе возвращает 0.

    &MyBaseUrl

    &MyBaseUrl возвращает URL без QUERY_STRING или PATH_INFO.   Например, если URL:
            http://hcs.harvard.edu/cgi-bin/finger?eekim
    то &MyBaseUrl возвратит следующее:
            http://hcs.harvard.edu:80/cgi-bin/finger

    &MyFullUrl

    &MyFullUrl возвращает полный URL, включая любые значения QUERY_STRING или PATH_INFO.   Например, если ваш URL:
            http://hcs.harvard.edu/cgi-bin/counter.cgi/~eekim?file.html
    то &MyFullUrl возвратит следующее:
            http://hcs.harvard.edu:80/cgi-bin/counter.cgi/~eekim?file.html

    &CgiError

    &CgiError принимает список строк и печатает их в форме сообщения ошибки.   Первая строка вставляется между тэгами <title> и <h1>; все последующие строки помещаются между тэгами <p>.   Если никакие строки не переданы, то заголовок по умолчанию и сообщение следующие:
            Error: script $name encountered fatal error
            (Ошибка: скрипт $name столкнулся с фатальной ошибкой)
    где $name - значение &MyFullUrl.   Например:
    &CgiError("Ошибка",
              "Невозможно открытый файл",
              "Пожалуйста, сообщите об этом администратору сервера"
             );
    
    возвращает такое HTML сообщение:
    <html>
    <head>
      <title>Ошибка</title>
    </head>
    
    <body>
      <h1>Ошибка</h1>
      <p>Невозможно открыть файл</p>
      <p>Пожалуйста, сообщите об этом администратору сервера</p>
    </body>
    </html>
    

    &CgiDie

    То же самое, что и &CgiError, но, кроме того, завершает работу скрипта.   &CgiDie печатает сообщение об ошибки на STDERR.

    &PrintVariables

    &PrintVariables возвращает определенный список (<dl>) каждой пары имя/значение.   Например, для пар (name, eugene) и (age, 21), функция &PrintVariables возвращает следующее:
    <dl compact>
    <dt><b>name</b>
    <dd><i>eugene</i>:<br>
    <dt><b>age</b>
    <dd><i>21</i>:<br>
    </dl>
    

    &PrintEnv

    Функция &PrintEnv возвращает определенный список всех переменных окружения.


    Исходный Код Библиотеки

    Этот раздел содержит полный текст библиотеки cgi-lib.pl.
    # Perl Routines to Manipulate CGI input
    # S.E.Brenner@bioc.cam.ac.uk
    # $Id: cgi-lib.pl,v 2.8 1996/03/30 01:36:33 brenner Rel $
    #
    # Copyright (c) 1996 Steven E. Brenner
    # Unpublished work.
    # Permission granted to use and modify this library so long as the
    # copyright above is maintained, modifications are documented, and
    # credit is given for any use of the library.
    #
    # Thanks are due to many people for reporting bugs and suggestions
    # especially Meng Weng Wong, Maki Watanabe, Bo Frese Rasmussen,
    # Andrew Dalke, Mark-Jason Dominus, Dave Dittrich, Jason Mathews
    
    # For more information, see:
    # http://www.bio.cam.ac.uk/cgi-lib/
    
    ($cgi_lib'version = '$Revision: 2.8 $') =~ s/[^.\d]//g;
    
    
    # Parameters affecting cgi-lib behavior
    # User-configurable parameters affecting file upload.
    $cgi_lib'maxdata = 131072; # maximum bytes to accept via POST-2^17
    $cgi_lib'writefiles = 0;   # directory to which to write files, or
                               # 0 if files should not be written
    $cgi_lib'filepre = "cgi-lib"; # Prefix of file names,
                                  # in directory above
    
    # Do not change the following parameters
    # unless you have special reasons
    $cgi_lib'bufsize = 8192; # default buffer size
                             # when reading multipart
    $cgi_lib'maxbound = 100; # maximum boundary length
                             # to be encounterd
    $cgi_lib'headerout = 0;  # indicates whether the header
                             # has been printed
    
    
    # ReadParse
    # Reads in GET or POST data, converts it to unescaped text, and
    # puts key/value pairs in %in, using "\0" to separate multiple
    # selections
    
    # Returns >0 if there was input, 0 if there was no input
    # undef indicates some failure.
    
    # Now that cgi scripts can be put in the normal file space,
    # it is useful to combine both the form and the script in one
    # place.   If no parameters are given (i.e., ReadParse returns
    # FALSE), then a form could be output.
    
    # If a reference to a hash is given, then the data will be stored
    # in that hash, but the data from $in and @in will become
    # inaccessable.
    # If a variable-glob (e.g., *cgi_input) is the first parameter to
    # ReadParse, information is stored there, rather than in $in, @in,
    # and %in.  Second, third, and fourth parameters fill associative
    # arrays analagous to %in with data relevant to file uploads.
    
    # If no method is given, the script will process both command-line
    # arguments of the form: name=value and any text that is in
    # $ENV{'QUERY_STRING'}  This is intended to aid debugging and may
    # be changed in future releases.
    
    sub ReadParse
    {
    local (*in) = shift if @_; # CGI input
    local (*incfn,       # Client's filename (may not be provided)
           *inct,        # Client's content-type (may not be provided)
           *insfn) = @_; # Server's filename (for spooled files)
    local ($len, $type, $meth, $errflag, $cmdflag, $perlwarn);
    
    # Disable warnings as this code deliberately uses local and
    # environment variables which are preset to undef (i.e., not
    # explicitly initialized)
    $perlwarn = $^W;
    $^W = 0;
    
    # Get several useful env variables
    $type = $ENV{'CONTENT_TYPE'};
    $len = $ENV{'CONTENT_LENGTH'};
    $meth = $ENV{'REQUEST_METHOD'};
    
    if ($len > $cgi_lib'maxdata)
      {  &CgiDie("cgi-lib.pl: Request to receive too much data: $len bytes\n");
      }
    
    if (!defined $meth ||
         $meth eq '' ||
         $meth eq 'GET' ||
         $type eq 'application/x-www-form-urlencoded')
      {  local ($key, $val, $i);
              # Read in text
         if (!defined $meth || $meth eq '')
           {  $in = $ENV{'QUERY_STRING'};
              $cmdflag = 1; # also use command-line options
           }
         elsif($meth eq 'GET' || $meth eq 'HEAD')
           {  $in = $ENV{'QUERY_STRING'};
           }
         elsif ($meth eq 'POST')
           {  $errflag = (read(STDIN, $in, $len) != $len);
           }
         else
           {  &CgiDie("cgi-lib.pl: Unknown request method: $meth\n");
           }
         @in = split(/[&;]/,$in);
         push(@in, @ARGV) if $cmdflag; # add command-line parameters
         foreach $i (0 .. $#in)
           {  $in[$i] =~ s/\+/ /g; # Convert plus to space
              ($key, $val) = split(/=/,$in[$i],2); # splits =
              $key =~ s/%([A-Fa-f0-9]{2})/pack("c",hex($1))/ge;
              $val =~ s/%([A-Fa-f0-9]{2})/pack("c",hex($1))/ge;
                  # Associate key and value
                  # \0 is the multiple separator
              $in{$key} .= "\0" if (defined($in{$key}));
              $in{$key} .= $val;
           }
    
      }
    elsif ($ENV{'CONTENT_TYPE'} =~ m#^multipart/form-data#)
      {  # for efficiency, compile multipart code only if needed
         $errflag = !(eval <<'END_MULTIPART');
    
         local ($buf, $boundary, $head, @heads,
                $cd, $ct, $fname, $ctype, $blen);
         local ($bpos, $lpos, $left, $amt, $fn, $ser);
         local ($bufsize, $maxbound, $writefiles) =
           ($cgi_lib'bufsize, $cgi_lib'maxbound, $cgi_lib'writefiles);
    
                # The following lines exist solely to eliminate
                # spurious warning messages
         $buf = '';
    
                # find boundary
         ($boundary) = $type =~ /boundary="([^"]+)"/; #";
         ($boundary) = $type =~ /boundary=(\S+)/ unless $boundary;
         &CgiDie ("Boundary not provided") unless $boundary;
         $boundary = "--" . $boundary;
         $blen = length ($boundary);
    
         if ($ENV{'REQUEST_METHOD'} ne 'POST')
           {  &CgiDie("Invalid request method for multipart/form-data: $meth\n");
           }
    
         if ($writefiles)
           {  local($me);
              stat ($writefiles);
              $writefiles = "/tmp" unless -d _ && -r _ && -w _;
              # ($me) = $0 =~ m#([^/]*)$#;
              $writefiles .= "/$cgi_lib'filepre";
           }
    
    # read in the data and split into parts:
    # put headers in @in and data in %in
    # General algorithm:
    # There are two dividers: the border and the '\r\n\r\n' between
    # header and body. Iterate between searching for these
    # Retain a buffer of size(bufsize+maxbound); the latter part is
    # to ensure that dividers don't get lost by wrapping between two
    # bufs.   Look for a divider in the current batch. If not found,
    # then save all of bufsize, move the maxbound extra buffer to the
    # front of the buffer, and read in a new bufsize bytes. If a
    # divider is found, save everything up to the divider. Then empty
    # the buffer of everything up to the end of the divider. Refill
    # buffer to bufsize+maxbound Note slightly odd organization. Code
    # before BODY: really goes with code following HEAD:, but is put
    # first to 'pre-fill' buffers. BODY: is placed before HEAD:
    # because we first need to discard any 'preface,' which would be
    # analagous to a body without a preceeding head.
    
         $left = $len;
         PART: # find each part of the multi-part while reading data
         while (1)
           {  last PART if $errflag;
              $amt = ($left > $bufsize+$maxbound-length($buf)
                   ? $bufsize+$maxbound-length($buf): $left);
              $errflag = (read(STDIN, $buf, $amt, length($buf)) != $amt);
              $left -= $amt;
    
              $in{$name} .= "\0" if defined $in{$name};
              $in{$name} .= $fn if $fn;
    
              $name=~/([-\w]+)/; # This allows $insfn{$name}
                                 # to be untainted
              if (defined $1)
                {  $insfn{$1} .= "\0" if defined $insfn{$1};
                   $insfn{$1} .= $fn if $fn;
                }
    
              BODY:
    
              while (($bpos = index($buf, $boundary)) == -1)
                {  if ($name) # if no $name, then it's the discard
                     {  if ($fn)
                          {  print FILE substr($buf, 0, $bufsize);
                          }
                        else
                          {  $in{$name} .= substr($buf, 0, $bufsize);
                          }
                     }
                   $buf = substr($buf, $bufsize);
                   $amt = ($left > $bufsize ? $bufsize : $left);
                   $errflag = (read(STDIN, $buf, $amt, $maxbound) != $amt);
                   $left -= $amt;
                }
    
              if (defined $name) # if no $name, then it's the discard
                {  if ($fn)
                     {  print FILE substr($buf, 0, $bpos-2);
                     }
                   else  # kill last \r\n
                     {  $in {$name} .= substr($buf, 0, $bpos-2);
                     }
                }
              close (FILE);
    
              last PART if substr($buf, $bpos + $blen, 4) eq "--\r\n";
              substr($buf, 0, $bpos+$blen+2) = '';
              $amt = ($left > $bufsize+$maxbound-length($buf)
                   ? $bufsize+$maxbound-length($buf) : $left);
              $errflag = (read(STDIN, $buf, $amt, length($buf)) != $amt);
              $left -= $amt;
    
              undef $head; undef $fn;
    
              HEAD:
    
              while (($lpos = index($buf, "\r\n\r\n")) == -1)
                {  $head .= substr($buf, 0, $bufsize);
                   $buf = substr($buf, $bufsize);
                   $amt = ($left > $bufsize ? $bufsize : $left);
                   $errflag = (read(STDIN, $buf, $amt, $maxbound) != $amt);
                   $left -= $amt;
                }
              $head .= substr($buf, 0, $lpos+2);
              push (@in, $head);
              @heads = split("\r\n", $head);
              ($cd) = grep (/^\s*Content-Disposition:/i, @heads);
              ($ct) = grep (/^\s*Content-Type:/i, @heads);
    
              ($name) = $cd =~ /\bname="([^"]+)"/i; #";
              ($name) = $cd =~ /\bname=([^\s:;]+)/i unless defined $name;
    
                  # filename can be null-str
              ($fname) = $cd =~ /\bfilename="([^"]*)"/i; #";
              ($fname) = $cd =~ /\bfilename=([^\s:;]+)/i unless defined $fname;
              $incfn{$name} .= (defined $in{$name} ? "\0" : "") . $fname;
    
              ($ctype) = $ct =~ /^\s*Content-type:\s*"([^"]+)"/i; #";
              ($ctype) = $ct =~ /^\s*Content-Type:\s*([^\s:;]+)/i unless defined $ctype;
    
              $inct{$name} .= (defined $in{$name} ? "\0" : "") . $ctype;
    
              if ($writefiles && defined $fname)
                {  $ser++;
                   $fn = $writefiles . ".$$.$ser";
                   open (FILE, ">$fn") || &CgiDie("Couldn't open $fn\n");
                }
              substr($buf, 0, $lpos+4) = '';
              undef $fname;
              undef $ctype;
           }
    
    1;
    END_MULTIPART
    
         &CgiDie($@) if $errflag;
      }
    else
      {  &CgiDie("cgi-lib.pl: Unknown Content-type: $ENV{'CONTENT_TYPE'}\n");
      }
    
    $^W = $perlwarn;
    return ($errflag ? undef : scalar(@in));
    }
    
    
    
    # PrintHeader
    # Returns the magic line which tells WWW that we're an HTML
    # document
    
    sub PrintHeader
    {  return "Content-type: text/html\n\n";
    }
    
    
    
    # HtmlTop
    # Returns the <head> of a document and the beginning of the body
    # with the title and a body <h1> header as specified by the
    # parameter
    
    sub HtmlTop
    {  local ($title) = @_;
    
      return <<END_OF_TEXT;
    <html>
    <head>
    <title>$title</title>
    </head>
    <body>
    <h1>$title</h1>
    END_OF_TEXT
    }
    
    
    
    # HtmlBot
    # Returns the </body>, </html> codes for the bottom of every HTML
    # page
    
    sub HtmlBot
    {  return "</body>\n</html>\n";
    }
    
    
    
    # SplitParam
    # Splits a multi-valued parameter into a list of the constituent
    # parameters
    
    sub SplitParam
    {  local ($param) = @_;
       local (@params) = split ("\0", $param);
       return (wantarray ? @params : $params[0]);
    }
    
    
    
    # MethGet
    # Return true if this cgi call was using the GET request, false
    # otherwise
    
    sub MethGet
    {  return (defined $ENV{'REQUEST_METHOD'} && $ENV{'REQUEST_METHOD'} eq "GET");
    }
    
    
    
    # MethPost
    # Return true if this cgi call was using the POST request, false
    # otherwise
    
    sub MethPost
    {  return (defined $ENV{'REQUEST_METHOD'} && $ENV{'REQUEST_METHOD'} eq "POST");
    }
    
    
    
    # MyBaseUrl
    # Returns the base URL to the script (i.e., no extra path or query
    # string)
    
    sub MyBaseUrl
    {  local ($ret, $perlwarn);
      $perlwarn = $^W; $^W = 0;
      $ret = 'http://' . $ENV{'SERVER_NAME'} .
      ($ENV{'SERVER_PORT'} != 80 ? ":$ENV{'SERVER_PORT'}"
                                 : '') . $ENV{'SCRIPT_NAME'};
      $^W = $perlwarn;
      return $ret;
    }
    
    
    
    # MyFullUrl
    # Returns the full URL to the script (i.e., with extra path or
    # query string)
    
    sub MyFullUrl
    {  local ($ret, $perlwarn);
      $perlwarn = $^W; $^W = 0;
      $ret='http://'.
           $ENV{'SERVER_NAME'}.
           ($ENV{'SERVER_PORT'} != 80 ? ":$ENV{'SERVER_PORT'}" : '').
           $ENV{'SCRIPT_NAME'}.
           $ENV{'PATH_INFO'}.
           (length ($ENV{'QUERY_STRING'}) ? "?$ENV{'QUERY_STRING'}" : '');
      $^W = $perlwarn;
      return $ret;
    }
    
    
    
    # MyURL
    # Returns the base URL to the script (i.e., no extra path or query
    # string)
    # This is obsolete and will be removed in later versions
    
    sub MyURL
    {  return &MyBaseUrl;
    }
    
    
    
    # CgiError
    # Prints out an error message which which containes appropriate
    # headers, markup, etcetera.
    # Parameters:
    # If no parameters, gives a generic error message
    # Otherwise, the first parameter will be the title and the rest
    # will be given as different paragraphs of the body
    
    sub CgiError
    {  local (@msg) = @_;
       local ($i,$name);
    
       if (!@msg)
         {  $name = &MyFullUrl;
            @msg = ("Error: script $name encountered fatal error\n");
         }
    
       if (!$cgi_lib'headerout)
         {  print &PrintHeader;
            print "<html>\n<head>\n<title>$msg[0]</title>\n</head>\n<body>\n";
         }
       print "<h1>$msg[0]</h1>\n";
       foreach $i (1 .. $#msg)
         {  print "<p>$msg[$i]</p>\n";
         }
    
       $cgi_lib'headerout++;
    }
    
    
    
    
    # CgiDie
    # Identical to CgiError, but also quits with the passed error
    # message.
    
    sub CgiDie
    {  local (@msg) = @_;
       &CgiError (@msg);
       die @msg;
    }
    
    
    
    # PrintVariables
    # Nicely formats variables. Three calling options:
    # A non-null associative array - prints the items in that array
    # A type-glob - prints the items in the associated assoc array
    # nothing - defaults to use %in
    # Typical use: &PrintVariables()
    
    sub PrintVariables
    {  local (*in) = @_ if @_ == 1;
       local (%in) = @_ if @_ > 1;
       local ($out, $key, $output);
    
       $output = "\n<dl compact>\n";
       foreach $key (sort keys(%in))
         {  foreach (split("\0", $in{$key}))
              {  ($out = $_) =~ s/\n/<br>\n/g;
                  $output .= "<dt><b>$key</b>\n <dd>:<i>$out</i>:<br>\n";
              }
         }
       $output .= "</dl>\n";
    
       return $output;
    }
    
    
    
    # PrintEnv
    # Nicely formats all environment variables and returns HTML string
    
    sub PrintEnv
    {  &PrintVariables(*ENV);
    }
    
    
    # The following lines exist only to avoid warning messages
    $cgi_lib'writefiles = $cgi_lib'writefiles;
    $cgi_lib'bufsize = $cgi_lib'bufsize ;
    $cgi_lib'maxbound = $cgi_lib'maxbound;
    $cgi_lib'version = $cgi_lib'version;
    
    1; #return true
    
    Я надеюсь, что это описание поможет Вам в нелегком деле написания CGI-скриптов, хотя я предпочитаю писать код сам.




    Внимание - конкурс


    Сегодня я предлагаю Вашему вниманию конкурс на лучший скрипт счетчика посещений.

    Условия конкурса следующие:

  • скрипт должен быть, по возможности, коротким;
  • скрипт должен быть прост в установке;
  • счетчик должен работать с любым количеством клиентов;
  • скрипт должен вызываться через тэг <img scr="...">.

    Огромная просьба не присылать счетчики, взятые из коллекций готовых скриптов.

    Лучшая работа, с указанием авторства, будет опубликована в рассылке и на ее примере разобрано построение счетчиков посещений.




    Наши друзья


    Дорогие подписчики! Если Вы еще не заглядывали на сайт наших друзей "InfoCity - виртуальный город компьютерной документации" (http://www.infocity.kiev.ua), то Вам обязательно стоит там побывать.

    Очень грамотно организованный сервер компьютерной документации.   Не пугайтесь титульного листа и ныряйте внутрь.   Количество книжек в электронном виде потрясает и радует.

    Здесь Вы можете сразу и подписаться на регулярные новости библиотеки.   Для этого просто вставьте Ваш e-mail в соответствующее поле, ну и конечно же не забудьте нажать на кнопочку "OK", и все. :-))

    Рассылки Subscribe.Ru
    Новости компьютерной библиотеки InfoCity

    Адрес рассылки "Новости компьютерной библиотеки InfoCity" в Каталоге subscribe.ru: http://subscribe.ru/catalog/comp.paper.infocity/

    Архив рассылки "Новости компьютерной библиотеки InfoCity" находится на subscribe.ru по адресу: http://subscribe.ru/archive/comp.paper.infocity/

    InfoCity - виртуальный город технической документации.



    Предлагаю Вашему вниманию еще одного из наших друзей, который имеет непосредственное отношение к тематике этой рассылки, сайт посвященный скриптам:   "WebScript.Ru - каталог скриптов".   Его адрес в Internet'е - http://webscript.ru/ и там Вы найдете множество готовых скриптов как на Perl'e, так и на РНР.

    Кроме того, предлагаю Вашему вниманию рассылку, посвященную новостям этого сайта, новым поступлениям скриптов и темам на форуме, а также новостям из мира Perl'а и PHP - "Новости сайта "Каталог скриптов (Perl & PHP)"".

    Подписаться на нее можно здесь:

    Рассылки Subscribe.Ru
    Новости сайта "Каталог скриптов (Perl & PHP)"

    Адрес рассылки "Новости сайта "Каталог скриптов (Perl & PHP)"" в Каталоге subscribe.ru: http://subscribe.ru/catalog/inet.webbuild.webscript/

    Архив рассылки "Новости сайта "Каталог скриптов (Perl & PHP)"" находится на subscribe.ru по адресу: http://subscribe.ru/archive/inet.webbuild.webscript/

    Заходите - Вам понравится.



    Еще один наш друг - сайт "CGI-REVIEW" (http://cgizone.org), который также посвящен CGI-скриптам.

    Обзор CGI-скриптов (а также сайтов этой тематики).   Поиск, описание, помощь и обсуждение.
    На этом сайте находится каталог cgi-скриптов (34 категории), который пополняется несколько раз в неделю.

    Также предлагаю Вашему вниманию рассылку, посвященную новостям этого сайта и новым поступлениям скриптов - "Обзор CGI-скриптов".

    Подписаться на нее можно здесь:

    Рассылки Subscribe.Ru
    Обзор CGI-скриптов

    Адрес рассылки "Обзор CGI-скриптов" в Каталоге subscribe.ru: http://subscribe.ru/catalog/inet.review.cgi/

    Архив рассылки "Обзор CGI-скриптов" находится на subscribe.ru по адресу: http://subscribe.ru/archive/inet.review.cgi/




    О рассылке


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

    Домашняя страница этой рассылки в Каталоге subscribe.ru: http://subscribe.ru/catalog/comp.soft.prog.perlprog/, здесь же Вы можете и подписаться на данную рассылку.

    Архив этой рассылки находится на subscribe.ru по адресу: http://subscribe.ru/archive/comp.soft.prog.perlprog/

    Данная рассылка распространяется только через subscribe.ru.

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




    Обращение к рекламодателям


    Уважаемые господа!   Если Вы хотите разместить свою рекламу на страницах этой рассылки, то обращайтесь со своими предложениями к ее автору, то есть ко мне, по адресу vitalij@newmail.ru и я сообщу Вам дополнительные сведения.




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


    Виталий Ярошевский vitalij@newmail.ru


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

    1. "CGI Developer's Guide"     Eugene Eric Kim
    2. "Learning Perl"     Randal Schwartz, Tom Christiansen & Larry Wall;   302 pages.   Second Edition, 1997




    http://subscribe.ru/
    E-mail: ask@subscribe.ru

    В избранное