Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

Сервер Web своими руками. Язык HTML, приложения CGI и ISAPI, установка серверов Web для Windows

© Александр Фролов, Григорий Фролов
Том 29, М.: Диалог-МИФИ, 1997, 288 стр.

[Назад] [Содеожание] [Дальше]

Примеры программ CGI

В этом разделе мы приведем примеры несложных программ CGI, демонстрирующих динамическое создание документов HTML и обработку данных, введенных при помощи форм.

Программа CGIHELLO

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

Эта программа хороша для проверки возможности запуска программ CGI на вашем сервере WWW или на сервере вашего поставщика услуг Internet. Так как она очень проста, существует немного причин, по которым она могла бы не работать. Это неправильная настройка прав доступа к виртуальному каталогу, содержащему загрузочный модуль программы CGI, а также неправильная ссылка на этот каталог в параметре ACTION оператора <FORM>.

Исходный текст документа, в котором определена форма, представлен в листинге 7.4.

Листинг 7.4. Файл chap7\cgihello\cgihello.htm


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML>
  <HEAD>
    <TITLE>CGI Script Test</TITLE>
  </HEAD>
  <BODY BGCOLOR=#FFFFFF>
    <H1>Запуск программы CGI</H1>
    <FORM METHOD=GET ACTION="http://frolov/frolov-cgi/cgihello.exe">
      <INPUT TYPE=submit VALUE="Send">
    </FORM>
  </BODY>
</HTML>

В этом документе определена форма, содержащая единственную кнопку, созданную оператором <INPUT> и имеющую тип SUBMIT.

В параметре ACTION оператора <FORM> мы указали путь к программе CGI, причем этот путь является виртуальным. Для передачи данных используется метод GET.

Внешний вид формы при ее просмотре навигатором Microsoft Internet Explorer представлен на рис. 7.5.

Рис. 7.5. Форма для запуска программы CGIHELLO

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

Рис. 7.6. Документ HTML, создаваемый динамически программой CGGIHELLO

Рассмотрим исходный текст программы CGIHELLO (листинг 7.5).

Листинг 7.5. Файл chap7\cgihello\cgihello.c


#include <stdio.h>
#include <stdlib.h>

void main(int argc, char *argv[])
{
  printf("Content-type: text/html\n\n");
  printf("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2//EN\">");
  printf("<HTML><HEAD><TITLE>"
     "XYZ Incorporation</TITLE></HEAD><BODY>");

  printf("<H1>Результат работы программы CGI</H1>");
  printf("<P>Эта страница создана динамически в результате"
    " работы программы CGI");
  printf("</BODY></HTML>");
}

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

В первый раз функция printf выводит заголовок HTTP и пустую строку-разделитель. Далее программа CGIHELLO записывает построчно в стандартный поток вывода STDOUT текст документа HTML.

Программа CONTROLS

Более сложная программа CGI называется CONTROLS и выполняет обработку данных, полученных из формы, которая была показана на рис. 7.2. Исходный текст документа HTML с этой формой представлен в листинге 7.1.

Программа CONTROLS отображает в динамически формируемом документе HTML метод, использованный для передачи (POST или GET), размер и тип данных, поступающих от формы. Принятые данные показываются как в исходном виде, так и после перекодировки. Кроме того, в документе HTML располагается список значений всех полей, определенных в форме (рис. 7.7).

Рис. 7.7. Фрагмент документа, сформированного динамически программой CONTROLS

Из рисунка видно, что навигатор прислал серверу WWW 135 байт информации. Так как при этом был использован метод POST, данные были направлены в стандартный поток ввода INPUT. Данные закодированы в кодировке URL, так как содержимое переменой среды CONTENT_TYPE равно application/x-www-form-urlencoded.

Обратите внимание на текстовое поле с именем text1. Все пробелы в соответствующей строке в кодировке URL заменены на символ “+”. Что же касается символов “&” и “,”, то они пришли в виде %26 и %2C. Функция перекодирования возвращает строку в исходный вид - “Sample of text1 &,.”.

Форма, показанная на рис. 7.2, имеет две кнопки, предназначенные для передачи данных серверу WWW. Это обычная кнопка и кнопка в виде графического изображения. Мы нажали графическую кнопку, поэтому от формы пришла информация о координатах курсора мыши в виде переменных с именами x и y.

Рассмотрим исходный текст программы CONTROLS (листинг 7.6).

Листинг 7.6. Файл chap7\controls\controls.c


// ===============================================
// Программа CGI controls.c
// Демонстрирует методы получения и обработки
// данных от форм, расположенных в документах HTML
//
// (C) Фролов А.В., 1997
// E-mail: frolov@glas.apc.org
// WWW:    http://www.glasnet.ru/~frolov
//         или
//         http://www.dials.ccas.ru/frolov
// ===============================================

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// Прототипы функций перекодировки
void DecodeStr(char *szString);
char DecodeHex(char *str);

// ------------------------------------------------
// Функция main
// Точка входа программы CGI
// ------------------------------------------------
void main(int argc, char *argv[])
{
  int lSize;
  FILE * fileReceived;
  char * szMethod;
  char * szQueryString;
  char szBuf[8196];
  char szSrcBuf[8196];
  char * szPtr;
  char * szParam;

  // Вывод заголовка HTTP и разделительной строки
  printf("Content-type: text/html\n\n");

  // Вывод начального форагмента документа HTML,
  // формируемого динамически
  printf("<!DOCTYPE HTML PUBLIC"
    " \"-//W3C//DTD HTML 3.2//EN\">");
  printf("<HTML><HEAD><TITLE>XYZ Incorporation"
    "</TITLE></HEAD><BODY BGCOLOR=#FFFFFF>");

  // Определяем метод передачи данных
  szMethod = getenv("REQUEST_METHOD");

  // Обработка метода POST
  if(!strcmp(szMethod, "POST"))
  {
    // Определяем размер данных, полученных от навигатора
    // при передаче данных из полей формы
    lSize = atoi(getenv("CONTENT_LENGTH"));

    // Читаем эти данные в буфер szBuf из
    // стандартного потока ввода STDIN
    fread(szBuf, lSize, 1, stdin);

    // Создаем файл, в который будут записаны
    // принятые данные
    fileReceived = fopen("received.dat", "w");

    // Выполняем запись принятых данных
    fwrite(szBuf, lSize, 1, fileReceived);

    // Закрываем файл принятых данных
    fclose(fileReceived);

    // Отображаем значения некоторых переменных среды
    printf("<H2>Переменные среды</H2>");
    
    // Метод доступа
    printf("REQUEST_METHOD = %s", getenv("REQUEST_METHOD"));

    // Размер полученных данных в байтах
    printf("<BR>CONTENT_LENGTH = %ld", lSize);

    // Тип полученных данных
    printf("<BR>CONTENT_TYPE = %s", getenv("CONTENT_TYPE"));

    // Закрываем буфер данных двоичным нулем, 
    // превращая его таким образом в строку
    szBuf[lSize] = '\0';

    // Делаем копию принятых данных в буфер szSrcBuf
    strcpy(szSrcBuf, szBuf);

    // Отображаем принятые данные без обработки
    printf("<H2>Принятые данные</H2>");
    printf("<P>%s", szSrcBuf);

    // Выполняем перекодировку принятых данных
    DecodeStr(szSrcBuf);

    // Отображаем результат перекодировки
    printf("<H2>Данные после перекодировки</H2>");
    printf("<P>%s", szSrcBuf);

    // Выводим список значений полей формы
    printf("<H2>Список значений полей</H2>");
    
    // Дописываем в конец буфера принятых данных
    // символ "&", который используется в качестве
    // разделителя значений полей
    szBuf[lSize] = '&';
    szBuf[lSize + 1] = '\0';

    // Цикл по полям формы
    for(szParam = szBuf;;)
    {
      // Ищем очередной разделитель
      szPtr = strchr(szParam, '&');
      
      // Если он найден, раскодируем строку параметров
      if(szPtr != NULL)
      {
        *szPtr = '\0';
        DecodeStr(szParam);
      
        // Выводим в документ значение параметра
        printf("%s<BR>", szParam);

        // Переходим к следующему параметру
        szParam = szPtr + 1;

        // Если достигнут конец буфера, завершаем цикл
        if(szParam >= (szBuf + lSize))
          break;
      }
      else
        break;
    }
    
    // Выводим завершающий фрагмент документа HTML
    printf("</BODY></HTML>");
    return;
  }
  
  // Обработка метода GET
  else if(!strcmp(szMethod, "GET"))
  {
    // Получаем данные, полученные от формы.
    // При использовании метода GET эти данные 
    // передаются в переменной среды QUERY_STRING
    szQueryString = getenv("QUERY_STRING");
    
    // Записываем эти данные в выходной файл
    fileReceived = fopen("received.dat", "w");
    fwrite(szQueryString, strlen(szQueryString) + 1, 
       1, fileReceived);
    fclose(fileReceived);
    
    // Выводим в динамически формируемый документ HTML
    // значения некоторых переменных среды
    printf("<H2>Переменные среды</H2>");

    // Метод передачи данных
    printf("REQUEST_METHOD = %s", getenv("REQUEST_METHOD"));

    // Полученные данные
    printf("<BR>QUERY_STRING = %s", szQueryString);

    // Копируем принятые данные в буфер szSrcBuf
    strcpy(szSrcBuf, szQueryString);

    // Отображаем принятые данные
    printf("<H2>Принятые данные</H2>");
    printf("<P>%s", szSrcBuf);
    
    // Перекодируем данные и отображаем результат
    // перекодировки
    DecodeStr(szSrcBuf);

    printf("<H2>Данные после перекодировки</H2>");
    printf("<P>%s", szSrcBuf);

    // Выводим в документ список значений полей формы
    strcpy(szBuf, szQueryString);
    printf("<H2>Список значений полей</H2>");

    szBuf[strlen(szBuf)] = '&';
    szBuf[strlen(szBuf) + 1] = '\0';

    for(szParam = szBuf;;)
    {
      szPtr = strchr(szParam, '&');
      if(szPtr != NULL)
      {
        *szPtr = '\0';
        DecodeStr(szParam);
        printf("%s<BR>", szParam);

        szParam = szPtr + 1;
        if(szParam >= (szBuf + lSize))
          break;
      }
      else
        break;
    }

    printf("</BODY></HTML>");
  }
}

// ------------------------------------------------
// Функция DecodeStr
// Раскодирование строки из кодировки URL
// ------------------------------------------------
void DecodeStr(char *szString)
{
  int src;
  int dst;
  char ch;

  // Цикл по строке
  for(src=0, dst=0; szString[src]; src++, dst++)
  {
    // Получаем очередной символ перекодируемой строки
    ch = szString[src];

    // Заменяем символ "+" на пробел
    ch = (ch == '+') ? ' ' : ch;
    
    // Сохраняем результат
    szString[dst] = ch;
    
    // Обработка шестнадцатеричных кодов вида "%xx"
    if(ch == '%')
    {
      // Выполняем преобразование строки "%xx"
      // в код символа
      szString[dst] = DecodeHex(&szString[src + 1]);
      src += 2;
    }
  }
  
  // Закрываем строку двоичным нулем
  szString[dst] = '\0';
}

// ------------------------------------------------
// Функция DecodeHex
// Раскодирование строки "%xx"
// ------------------------------------------------
char DecodeHex(char *str)
{
  char ch;

  // Обрабатываем старший разряд
  if(str[0] >= 'A')
    ch = ((str[0] & 0xdf) - 'A') + 10;
  else
    ch = str[0] - '0';

  // Сдвигаем его влево на 4 бита
  ch <<= 4;

  // Обрабатываем младший разряд и складываем
  // его со старшим
  if(str[1] >= 'A')
    ch += ((str[1] & 0xdf) - 'A') + 10;
  else
    ch += str[1] - '0';

  // Возвращаем результат перекодировки
  return ch;
}

Функция main программы CONTROLS вначале выводит в стандартный поток вывода STDOUT заголовок HTTP и начальные строки динамически формируемого документа HTML. Для вывода мы использовали функцию printf.

Далее функция main определяет использованный метод передачи данных, анализируя содержимое переменной среды REQUEST_METHOD. Это необходимо, так как при разных методах передачи необходимо использовать различные методы получения входных данных. Значение переменной среды программа получает при помощи функции getenv.

Если данные передаются методом POST, программа будет считывать их из стандартного потока ввода STDIN. Размер данных находится в переменной среды CONTENT_LENGTH. Соответствующая текстовая строка получается функцией getenv и преобразуется в численное значение функцией atoi.

Чтение данных из входного потока выполняется за один вызов функции fread:


fread(szBuf, lSize, 1, stdin);

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

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


fileReceived = fopen("received.dat", "w");
fwrite(szBuf, lSize, 1, fileReceived);
fclose(fileReceived);

Текущим каталогом при запуске этой программы в среде сервера Microsoft Information Server будет каталог с загрузочным модулем программы CONTROLS. Заметим, что для того чтобы программа CGI могла создать файл в каталоге, необходимо соответствующим образом настроить права доступа. В последней главе нашей книги вы найдете информацию о том, как это можно сделать для сервера Microsoft Information Server.

После сохранения принятых данных в файле программа CONTROLS выводит в стандартный поток вывода содержимое некоторых переменных среды: REQUEST_METHOD, CONTENT_LENGTH и CONTENT_TYPE.

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

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

Перекодировка выполняется функцией DecodeStr,, определенной в нашем приложении. Эту функцию мы рассмотрим позже.

После перекодирования результат будет находиться в буфере szSrcBuf, откуда он и берется для отображения.

На завершающем этапе обработки данных, полученных от формы, программа CONTROLS записывает в выходной документ HTML значения отдельных полей. Напомним, что эти значения имеют формат &<Имя_поля>=<Значение>, при этом символ “&” используется как разделитель.

Наша программа закрывает исходный буфер с принятыми данными дополнительным символом “&” (для простоты сканирования), после чего запускает цикл по полям формы.

В этом цикле во входной строке с помощью функции strchr ищется символ разделитель. Если этот символ найден, он заменяется на двоичный нуль, после чего полученная текстовая строка значения параметра перекодируется функцией DecodeStr и выводится в выходной поток STDOUT.

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

В конце программа CONTROLS закрывает документ HTML, записывая в него команды </BODY> и </HTML>.

Если данные передаются в программу CONTROLS методом GET, входные данные находятся в переменной среды QUERY_STRING, которую мы получаем следующим образом:


szQueryString = getenv("QUERY_STRING");

Обработка принятых данных выполняется аналогично тому, как это делается при методе POST. Разница заключается лишь в том, что в документе мы отображаем содержимое только переменных REQUEST_METHOD и QUERY_STRING.

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

Эта функция сканирует входную строку, заменяя символы “+” на пробелы. Если в перекодируемой строке встречается комбинация символов вида "%xx", она заменяются на однобайтовый код соответствующего символа с помощью функции DecodeHex.

Функция DecodeHex комбинирует значение кода символа из старшего и младшего разряда преобразуемой комбинации символов.

Программа AREF

До сих пор в наших примерах мы использовали программы CGI только для обработки данных из полей форм, указывая адрес URL загрузочного файла программы в параметре ACTION оператора <FORM>. Однако есть и другая возможность вызова программ CGI: вы можете указать их адрес в параметре HREF оператора ссылки <A>. В этом случае вы можете передать программе CGI параметры, указав их после имени файла загрузочного модуля через разделительный символ “?”. Программа получит строку параметров методом GET и сможет извлечь ее из переменной среды с именем QUERY_STRING.

Пример документа HTML, в котором демонстрируется вызов программы CGI указанным выше способом, приведен в листинге 7.7.

Листинг 7.7. Файл chap7\aref\aref.htm


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML>
  <HEAD>
    <TITLE>Ссылки на документы HTML</TITLE>
  </HEAD>
  <BODY BGCOLOR=#FFFFFF>

    <A HREF="http://frolov/frolov-cgi/aref.exe?page1">Домашняя страница</A><BR>
    <A HREF="http://frolov/frolov-cgi/aref.exe?page2">Книги</A><BR>
    <A HREF="http://frolov/frolov-cgi/aref.exe?page3">Статьи</A><BR>

  </BODY>
</HTML>

В этом документе есть три ссылки на программу CGI с именем aref.exe, причем каждый раз ей передаются разные параметры. Внешне документ выглядит так, как это показано на рис. 7.8.

Рис. 7.8. Документ HTML с ссылками на программу CGI

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

Рис. 7.9. Документ, который отображается программой CGI при выборе строки “Статьи”

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

Листинг 7.8. Файл chap7\aref\aref.c


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void main(int argc, char *argv[])
{
  char * szQueryString;

  szQueryString = getenv("QUERY_STRING");

  if(!strcmp(szQueryString, "page1"))
    printf("Location: home.htm\n\n");
  
  else if(!strcmp(szQueryString, "page2"))
    printf("Location: books.htm\n\n");
  
  else if(!strcmp(szQueryString, "page3"))
    printf("Location: capital.htm\n\n");

  else
    printf("Location: error.htm\n\n");
}

Программа получает значение переменной среды QUERY_STRING, пользуясь для этого функцией getenv. Далее она сравнивает значение параметра со строками “page1”, “page2” и “page3”. При совпадении программа возвращает навигатору адрес URL соответствующего документа HTML, формируя заголовок HTTP специального вида:


Location: <Адрес URL документа HTML или графического изображения>\n\n

Когда навигатор получает от сервера WWW такой заголовок, он отображает в своем окне документ или файл графического изображения, адрес URL которого указан в заголовке.

Таким образом, программа CGI может анализировать параметры, поступающие от навигатора через ссылку или поля формы, а затем не только динамически формировать документ HTML для отображения в окне навигатора, но и возвращать ссылки на уже существующие документы в виде их адресов URL.

Эта возможность может пригодиться вам для организации ссылок на документы HTML через списки, создаваемые оператором <SELECT>, расположенном в форме. Программа CGI может определить, какая строка была выбрана в списке в момент посылки заполненной формы серверу WWW, и в зависимости от этого либо возвратить ссылку на тот или иной существующий документ HTML, либо сформировать новый документ HTML динамически.

Программа COUNTER

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

Существуют различные методы создания счетчиков, доступность которых во многом определяется программным обеспечением и настройкой сервера WWW, а также доброй волей поставщика услуг Internet (если ваш сервер WWW виртуальный и физически расположен у поставщика). В нашей книге мы расскажем о том, как сделать счетчик посещений с помощью программы CGI и расширения ISAPI (в следующей главе).

Самый простой способ создания счетчика заключается в следующем. Те документы HTML, на которых необходимо разместить счетчик, преобразуется в файл шаблона. В этом файле в том месте, где должно располагаться текстовое значение счетчика, необходимо поместить последовательность заранее определенных символов, например, символы “ххххх” или “~~~~~”.

Пример такого файла шаблона вы можете найти в листинге 7.9.

Листинг 7.9. Файл chap7\counter\home.tm


<HTML>
  <BODY BGCOLOR="#FFFFFF">
    <H1>Главная страница фирмы XYZ Inc.</H1>
    <P>Добро пожаловать на нашу главную страницу!
    <HR>
    <P>Вы посетитель номер <B>~~~~~</B> с 1 января 1913 года
  </BODY>
</HTML>

Мы назвали файл home.tm, хотя вы можете выбирать для файлов шаблона любые имена.

Далее вам нужно сделать ссылку на документ, содержащий счетчик. Такая ссылка указывает на программу CGI, выполняющую подсчет посещений. В листинге 7.10 ссылка на программу счетчика выполнена с использованием оператора <A>.

Листинг 7.10. Файл chap7\counter\default.htm


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML>
  <HEAD>
    <TITLE>Счетчик посещений</TITLE>
  </HEAD>
  <BODY BGCOLOR=#FFFFFF>
    <H1>Посетите нашу главную страницу</H1>
    <A HREF="http://frolov/frolov-cgi/counter.exe?">Главная страница</A><BR>
  </BODY>
</HTML>

Внешний вид этого документа показан на рис. 7.10.

Рис. 7.10. Документ, который содержит ссылку на программу CGI, отображающую главную страницу

Обратите внимание, что вслед за именем программы мы разместили разделительный символ “?”, после которого вы можете указывать дополнительные параметры, например, номер счетчика. Это может пригодиться, если ваша программа CGI должна выполнять отдельный подсчет посещений для различных документов HTML.

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

А как подсчитывать посещения?

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

После того как текущее значение счетчика посещений будет записано в соответствующее поле шаблона, программа счетчика может вывести шаблон в стандартный поток вывода STDOUT.

Документ HTML, полученный таким образом из нашего шаблона, показан на рис. 7.11.

Рис. 7.11. Документ HTML со счетчиком, созданный динамически на базе файла шаблона

Исходный текст программы COUNTER, работающей с использованием описанного выше алгоритма, приведен в листинге 7.11.

Листинг 7.11. Файл chap7\counter\counter.c


// ===============================================
// Программа CGI counter.c
// Реализация счетчика посещений
//
// (C) Фролов А.В., 1997
// E-mail: frolov@glas.apc.org
// WWW:    http://www.glasnet.ru/~frolov
//         или
//         http://www.dials.ccas.ru/frolov
// ===============================================

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <io.h>
#include <fcntl.h>

void main(int argc, char *argv[])
{
  // Идентификатор файла шаблона главной страницы
  HFILE hSrcFile;

  // Идентификатор файла счетчика
  FILE *CounterFile;

  // Размер файла шаблона
  DWORD dwFileSize;

  // Адрес шаблона главной страницы
  LPSTR szTemplate;

  // Временный буфер для работы со счетчиком
  CHAR  szBuf[10];

  // Текущее значение счетчика
  INT   nCounter;

  // Указатель на поле счетчика в шаблоне
  LPSTR szCounterPtr;

  // -----------------------------------------------
  // Считываем файл шаблона в оперативную память
  // -----------------------------------------------
  
  // Открываем файл шаблона
  hSrcFile = _lopen("HOME.TM", OF_READ);
  
  // Определяем размер файла в байтах
  dwFileSize = _llseek(hSrcFile, 0, 2);

  // Устанавливаем указатель текущей позиции
  // на начало файла шаблона
  _llseek(hSrcFile, 0, 0);

  // Получаем память для буфера шаблона
  szTemplate = malloc(dwFileSize);

  // Загружаем шаблон в буфер
  _hread(hSrcFile, szTemplate, dwFileSize);
    
  // -----------------------------------------------
  // Увеличиваем значение счетчика в файле
  // -----------------------------------------------
  
  // Открываем файл счетчика для чтения
  CounterFile = fopen("CNTDAT.DAT", "r");

  // Читаем из файла строку значения счетчика
  fgets(szBuf, 7, CounterFile);

  // Закрываем файл счетчика
  fclose(CounterFile);
  
  // Преобразуем значение счетчика из текстовой
  // строки в численную величину
  sscanf(szBuf, "%d", &nCounter);
  
  // Увеличиваем значение счетчика
  nCounter++;
  
  // Записываем в буфер szBuf пять цифр нового
  // значения счетчика
  sprintf(szBuf, "%05.5ld", nCounter);

  // Сохраняем новое значение счетчика в файле
  CounterFile = fopen("CNTDAT.DAT", "w");
  fprintf(CounterFile, "%d", nCounter);
  fclose(CounterFile);

  // -----------------------------------------------
  // Заменяем 5 цифр значения счетчика на новые
  // в буфере шаблона
  // -----------------------------------------------

  // Ищем маркер поля счетчика
  szCounterPtr = strstr(szTemplate, "~~~~~");
  
  // Копируем в это поле новое значение счетчика
  if(szCounterPtr != NULL)
    strncpy(szCounterPtr, szBuf, 5);
  
  // Выводим заголовок HTTP и разделительную строку
  printf("Content-type: text/html\n\n");

  // Выводим шаблон с измененным значением
  // поля счетчика
  fwrite(szTemplate, dwFileSize, 1, stdout);

  // Освобождаем буфер шаблона    
  free(szTemplate);
}

Получив управление, программа COUNTER считывает файл шаблона в оперативную память. Для упрощения исходного текста программы мы применили для работы с файлом функции _lopen, _llseek и _hread, которые являются специфичными для операционной системы Microsoft Windows. Вы можете использовать здесь любые другие функции, предназначенные для работы с файлами.

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

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

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

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

После обновления шаблона в стандартный поток вывода записывается обычный заголовок HTTP (функцией printf) и шаблон (функцией fwrite). На последнем этапе освобождается буфер памяти, заказанный для буфера шаблона.

[Назад] [Содеожание] [Дальше]