4.1 WWW (World Wide Web) и средства интерактивного взаимодействия
4.2Спецификация CGI 4.2.1Переменные окружения 4.2.2Стандартный вывод 4.2.3Стандартный входной поток 4.2.4Аргументы командной строки
4.3Последовательность действий для обработки входных данных cgi-модуля для разных методов запроса GET и POST 4.3.1Для метода GET 4.3.2Для метода POST
4.4Примеры cgi-модулей
4.1 WWW (World Wide Web) и средства интерактивного взаимодействия
Цель данной главы познакомить пользователя с той частью WWW-технологий которая связана с созданием интерактивных интерфейсов и предполагается что пользователь знаком с основами WWW, HTML и С/С++.
В общем случае, интерактивный интерфейс пользователя представляет собой систему, обеспечивающую взаимодействие пользователя и программы. Для WWW, интерактивный интерфейс можно определить как последовательность HTML-документов, реализующих интерфейс пользователя. Можно также условно классифицировать принципы построения интерфейса по типу формирования HTML-документа:
статический
динамический
В первом случае источником интерфейса является HTML-документ, созданный в каком-либо текстовом или HTML-ориентированном редакторе. Следовательно, данный документ остается неизменным в течение использования. Во втором случае источником интерфейса является HTML-документ сгенерированный cgi-модулем. Следовательно, появляется некоторая гибкость в видоизменении интерфейса во время использования.
Таким образом, можно ввести понятие интерактивного интерфейса для WWW.
Интерактивный интерфейс для WWW представляет собой последовательность статических или динамически формируемых HTML-документов, реализующих интерфейс пользователя.
Практически любая задача, решающая проблему получения данных от клиента, связана с построением интерфейса. Наиболее интересным является построение интерфейсов к различным базам данных, доступ к SQL-серверу, получение информации от периферийных устройств, создание клиентских рабочих мест. Все это возможно посредством CGI(Common Gateway Interface).
Common Gateway Interface (CGI) является стандартом интерфейса внешней прикладной программы с WWW сервером.
Задача построения вышеназванных интерфейсов делится на две части:
Клиентская часть
Серверная часть
Рисунок 4-1. Две части интерактивного интерфейса.
Клиентская часть
Для создания клиентской части необходимо создать HTML-документ, в котором реализован интерфейс с пользователем. В языке HTML это возможно посредством форм.
Конструкции языка HTML, используемые при реализации форм, даны в приложении 1 к гл. 4.
Серверная часть
Серверная часть состоит из исполняемого модуля, решающего основные задачи обработки данных поступающих от клиентской части, формирования ответа в формате HTML, и т.д. Такой модуль называется cgi-модулем
.
Методы HTTP запроса
Для реализации взаимодействия "клиент-сервер" важно, какой метод HTTP запроса использует клиентская часть при обращении к WWW серверу. В общем случае, запрос - это сообщение, посылаемое клиентом серверу. Первая строка HTTP запроса(см. гл.3) включает в себя метод, который должен быть применен к запрашиваемому ресурсу, идентификатор ресурса(URI-Uniform Resource Identifier), и используемую версию HTTP-протокола. В рассматриваемом нами случае, клиентская часть применяет методы запроса POST
и GET.
Метод POST используется для запроса серверу, чтобы тот принял информацию, включенную в запрос, как относящуюся к ресурсу, указанному идентификатором ресурса. Метод GET используется для получения любой информации, идентифицированной идентификатором ресурса в HTTP запросе.
Для WWW-сервера стандарта NCSA прикладные программы или CGI-модули, обрабатывающие поток данных от клиента или (и) формирующие обратный поток данных могут быть написаны на таких языках программирования как:
C/C++;
Любой UNIX shell;
Fortran;
Perl;
Visual Basic;
TCL;
AppleScript;
4.2 Спецификация CGI
CGI определяет 4 информационных потока.
Переменные окружения
Стандартный входной поток
Стандартный выходной поток
Командная строка
Рисунок 4-2. CGI-интерфейс.
4.2.1 Переменные окружения
Переменные окружения условно делятся на два типа:
общие для всех типов запросов (устанавливаются для всех типов)
зависящие от метода запроса
К переменным первого типа относятся следующие переменные:
SERVER_SOFTWARE
содержит информацию о WWW сервере (название/версия)
SERVER_NAME
содержит информацию об имени машины, на которой запущен WWW сервер, символическое имя или IP адрес соответствующие URL.
GATEWAY_INERFACE
содержит информацию о версии CGI(CGI/версия)
Следующие переменные являются специфичными для разных типов запросов и значения этим переменным присваиваются перед вызовом cgi-модуля.
CONTENT_LENGTH
значение этой переменной соответствует длине стандартного входного потока в символах.
CONTENT_TYPE
эта переменная специфицирована для запросов содержащих дополнительную информацию, таких как HTTP POST и PUT, и содержит тип данных этой информации.
SERVER_PROTOCOL
эта переменная содержит информацию об имени и версии информационного протокола (протокол/версия).
SERVER_PORT
значение переменной содержит номер порта, на который был послан запрос.
REQUEST_METHOD
метод запроса, который был использован "POST","GET","HEAD" и т.д.
PATH_INFO
значение переменной содержит полученный от клиента виртуальный путь до cgi-модуля
PATH_TRANSLATED
значение переменной содержит физический путь до cgi-модуля, преобразованный из значения PATH_INFO.
SCRIPT_NAME
виртуальный путь к исполняемому модулю, используемый для получения URL.
QUERY_STRING
значение этой переменной соответствует строке символов следующей за знаком "?" в URL соответствующему данному запросу. Эта информация не декодируется сервером.
REMOTE_HOST
содержит символическое имя удаленной машины, с которой был произведен запрос. В случае отсутствия данной информации сервер присваивает пустое значение и устанавливает переменную REMOTE_ADDRESS.
REMOTE_ADDRESS
содержит IP адрес клиента
AUTH_TYPE
если WWW-сервер поддерживает аутентификацию (подтверждение подлинности) пользователей и cgi-модуль является защищенным от постороннего доступа то, значение переменной специфицирует метод аутотентификации.
REMOTE_USER
содержит имя пользователя в случае аутотентификации.
REMOTE_IDENT
содержит имя пользователя, полученное от сервера (если сервер поддерживает аутентификацию согласно RFC 931)
HTTP_ACCEPT
список типов MIME известных клиенту. Каждый тип в списке должен быть отделен запятой согласно спецификации HTTP (тип/подтип,тип/подтип и т.д.)
HTTP_USER_AGENT
название программы просмотра которую использует клиент при посылке запроса.
4.2.2 Стандартный вывод
СGI
- модуль выводит информацию в стандартный выходной поток. Этот вывод может представлять собой или документ, сгенерированный cgi-модулем, или инструкцию серверу, где получить необходимый документ. Обычно cgi
-модуль производит свой вывод. Преимущество такого подхода в том, что cgi
-модуль не должен формировать полный HTTP заголовок на каждый запрос.
Заголовок выходного потока
В некоторых случаях необходимо избегать обработки сервером вывода cgi-модуля, и посылать клиенту данные без изменений. Для отличия таких cgi-модулей, CGI требует, чтобы их имена начинались на nph-. В этом случае формирование синтаксически правильного ответа клиенту cgi-модуль берет на себя.
Заголовки с синтаксическим разбором
Вывод cgi-модуля должен начинаться с заголовка содержащего определенные строки и завершаться двумя символами CR(0x10).
Любые строки не являющиеся директивами сервера, посылаются непосредственно клиенту. На данный момент, CGI спецификация определяет три директивы сервера:
Content-type
MIME или тип возвращаемого документа
Например: Content-type
: text/html <CR><CR> сообщает серверу, что следующие за этим сообщением данные - есть документ в формате HTML
Location
указывает серверу, что возвращается не сам документ, а ссылка на него
Если аргументом является URL, то сервер передаст указание клиенту на перенаправление запроса. Если аргумент представляет собой виртуальный путь, сервер вернет клиенту заданный этим путем документ, как если бы клиент запрашивал этот документ непосредственно.
Например: Location:
http://host/file.txt приведет к тому, что WWW сервер выдаст file.txt, как если бы он был затребован клиентом. Если cgi-модуль возвращает ссылки на gopher сервер, например на gopher://gopher.ncsa.uiuc.edu/. Вывод будет следующий:
Location
: gopher://gopher.ncsa.uiuc.edu/
*Status
задает серверу HTTP/1.0 строку-статус, которая будет послана клиенту в формате: nnn xxxxx
где: nnn - 3-х цифровой код статуса
ххххх - строка причины
Например: HTTP/1.0 200 OK
Server: NCSA/1.0a6
Content-type: text/plain
<динамически генерируемый текст сообщения>
В данном случае, клиенту будет сообщено об успешном выполнении запроса.
4.2.3 Стандартный входной поток
В случае метода запроса POST
данные передаются как содержимое HTTP запроса. И будут посланы в стандартный входной поток.
Данные передаются cgi-модулю в следующей форме:
name=value&name1=value1&...&nameN=valueN где name - имя переменной, value - значение переменной, N - количество переменных
На файловый дескриптор стандартного потока ввода посылается CONTENT_LENGTH байт. Так же сервер передает cgi-модулю CONTENT_TYPE (тип данных). Сервер не посылает символ конца файла после передачи CONTENT_LENGTH байт данных или после того, как cgi-модуль их прочитает. Переменные окружения CONTENT_LENGTH и CONTENT_TYPE устанавливаются в тот момент, когда сервер выполняет cgi-модуль. Таким образом, если в результате исполнения формы с аргументом тега FORM - METHOD="POST" сформирована строка данных firm=МММ&price=100023, то сервер установит значение CONTENT_LENGTH равным 21 и CONTENT_TYPE в application/x-www-form-urlencoded, а в стандартный поток ввода посылается блок данных.
В случае метода GET
, строка данных передается как часть URL. Т.е. например http://host/cgi-bin/script?name1=value1&name2=value2
В этом случае переменная окружения QUERY_STRING принимает значение name1=value1&name2=value2
4.2.4 Аргументы командной строки
СGI
-модуль в командной строке от сервера получает:
остаток URL после имени cgi-модуля в качестве первого параметра (первый параметр будет пуст, если присутствовало только имя cgi-модуля), и
список ключевых слов в качестве остатка командной строки для скрипта поиска, или
чередующиеся имена полей формы с добавленным знаком равенства и соответствующих значений переменных.
Ключевые слова, имена и значения полей формы передаются декодированными (из HTTP URL формата кодирования) и перекодированными в соответствии с правилами кодирования Bourne shell так, что cgi-модуль в командной строке получит информацию без необходимости осуществлять дополнительные преобразования.
4.3 Последовательность действий для обработки входных данных cgi-модуля для разных методов запроса GET и POST
Исходя из разницы методов запросов GET и POST, можно определить последовательность действий для обработки входных данных cgi-модуля для разных типов запросов.
4.3.1 Для метода GET
Получить значение переменной QUERY_STRING
Декодировать имена и их значения (учитывая, что все пробелы при декодировании сервером были заменены символом "+" и все символы с десятичным кодом больше 128 преобразованы в символ "%" и следующим за ним шестнадцатеричным кодом символа.)
Сформировать структуру соответствия "имя - значение" для дальнейшего использования в cgi-модуле
4.3.2 Для метода POST
Получить из стандартного входного потока CONTENT_LENGTH символов
Декодировать имена и их значения (учитывая, что все пробелы при декодировании сервером были заменены символом "+
" и все символы с десятичным кодом больше 128 преобразованы в символ "%
" и следующим за ним шестнадцатеричным кодом символа.)
Сформировать структуру соответствия "имя - значение" для дальнейшего использования в cgi-модуле
Очевидно, что отличие только в источнике данных. Поэтому, в принципе, возможно создание единого модуля для методов POST и GET. Необходимо только добавить в начало проверку значения переменной REQUEST_METHOD для определения метода запроса. После формирования структуры "имя-значение" можно приступить к решению задач, ради которых, собственно, создавался cgi-м
Следующим важным моментом является динамическое формирование cgi-модулем HTML-документа (оформление результата работы модуля). Например, таблицы выборки из базы данных.
Для этого cgi-модуль должен выдать в стандартный выходной поток заголовок состоящий из строки: Content-type: text/html
и пустой строки (двух символов CR
)
После этого заголовка можно давать любой текст в формате HTML.
4.4 Примеры cgi-модулей
В качестве примера рассмотрим работу тестовых программ поставляющихся вместе с программным обеспечением сервера НТТРD стандарта NCSA.
Для тестирования работы форм поставляются программы : post-query
- для тестирования работы форм с методом запроса POST query
- для тестирования работы форм с методом запроса GET util.c -
описание функций для обработки входного потока (используется query и post-query).
Рассмотрим простой пример формы на языке HTML использующую программу query.
<HTML> <HEAD> <TITLE>Пример использования CGI</TITLE> </HEAD> <BODY> <FORM ACTION="http://iceman.cnit.nsu.ru/cgi-bin/post-query" METHOD="POST"> <B>Введите свое имя<I>(Фамилию Имя Отчество)</I>:</B> <INPUT name=RealName type=text size=40 maxlength=60 value="Петров Иван Сидорович"><P> Пол: <INPUT name=Sex type=Radio value="Мужской" CHECKED>- мужской <INPUT name=Sex type=Radio value="Женский">-женский<P> <INPUT name=Submit type=submit value="Послать запрос"><BR> <INPUT name=Reset type=reset value="Сброс"> </FORM> </BODY> </HTML>
После инициации формы путем нажатия кнопки "Послать запрос" WWW сервер обрабатывает поток данных от формы (заменяет все пробелы в именах и значениях на символ "+", заменяет все символы с десятичным кодом большим 128 на символ "%" и следующим за ним шестнадцатеричным кодом символа (например "И" в %С8)). Выходной поток примет следующий вид:
RealName=%CF%E5%F2%F0%EE%E2+%C8%E2%E0%ED+%D1%E8%E4%EE%F0 %EE%E2%E8%F7&Sex=%CC%F3%E6%F1%EA%EE%E9&Submit=%CF%EE%F1 %EB%E0%F2%FC+%E7%E0%EF%F0%EE%F1
В момент передачи управления модулю post-query сервер присваивает значения переменным окружения и аргументам командной строки:
argc = 0. argv = SERVER_SOFTWARE = NCSA/1.5.1 SERVER_NAME = iceman.cnit.nsu.ru GATEWAY_INTERFACE = CGI/1.1 SERVER_PROTOCOL = HTTP/1.0 SERVER_PORT = 80 REQUEST_METHOD = POST HTTP_ACCEPT = image/gif, image/x-xbitmap, image/jpeg, image/pjpeg,*/* PATH_INFO = PATH_TRANSLATED = SCRIPT_NAME = /cgi-bin/test-cgi QUERY_STRING = REMOTE_HOST = fwa.cnit.nsu.ru REMOTE_ADDR = 193.124.209.74 REMOTE_USER = AUTH_TYPE = CONTENT_TYPE = application/x-www-form-urlencoded CONTENT_LENGTH = 142
Результат работы post-query: <H1>Query Results</H1>You submitted the following name/value pairs:<p> <ul> <li> <code>RealName = Петров Иван Сидорович</code> <li> <code>Sex = Мужской</code> <li> <code>Submit = Послать запрос </code> </ul>
И на экране браузера Query Results You submitted the following name/value pairs: RealName = Петров Иван Сидорович Sex = Мужской Submit = Послать запрос
Ниже приведен исходный текст программы post-query.
#include <stdio.h>#ifndef NO_STDLIB_H#include <stdlib.h>#elsechar *getenv();#endif#define MAX_ENTRIES 10000typedef struct {char *name;char *val;} entry;char *makeword(char *line, char stop);char *fmakeword(FILE *f, char stop, int *len);char x2c(char *what);void unescape_url(char *url);void plustospace(char *str); main(int argc, char *argv[]) {entry entries[MAX_ENTRIES];register int x,m=0;int cl;printf("Content-type: text/html%c%c",10,10);if(strcmp(getenv("REQUEST_METHOD"),"POST")) { printf("This script should be referenced with a METHOD of POST.n");printf("If you don't understand this, see this "); printf("<A HREF="http://www.ncsa.uiuc.edu/SDG/Software/Mosaic/Docs/fill-out-forms/overview.html"> forms overview</A>.%c",10);exit(1);} if(strcmp(getenv("CONTENT_TYPE"),"application/x-www-form-urlencoded")) {printf("This script can only be used to decode form results. n");exit(1);}cl = atoi(getenv("CONTENT_LENGTH"));for(x=0;cl && (!feof(stdin));x++) {m=x;entries[x].val = fmakeword(stdin,'&',&cl); plustospace(entries[x].val);unescape_url(entries[x].val);entries[x].name = makeword(entries[x].val,'=');}printf("<H1>Query Results</H1>");printf("You submitted the following name/value pairs:<p>%c",10);printf("<ul>%c",10);for(x=0; x <= m; x++)printf("<li> <code>%s = %s</code>%c",entries[x].name, entries[x].val,10);printf("</ul>%c",10);}
Надо отметить, что post-query не обрабатывает имена, поэтому в примере они даны на английском языке. Если Вы используете русские названия имен, то вы должны обработать имена также как и значения, т.е. заменить все символы "+" на пробелы и преобразовать шестнадцатеричные коды кириллических символов в сам символ. Приведем также исходный текст функций используемых post-query
char *makeword
(char *line, char stop) {/* Предназначена для выделения части строки, ограниченной "стоп-символами"*/int x = 0,y;char *word = (char *) malloc(sizeof(char) * (strlen(line) + 1));for(x=0;((line[x]) && (line[x] != stop));x++)word[x] = line[x];word[x] = '0';if(line[x]) ++x;y=0;while(line[y++] = line[x++]);return word;}char *fmakeword
(FILE *f, char stop, int *cl) {/* Предназначена для выделения строки, ограниченной "стоп-символом" stop,
из потока f
длиной cl.
*/int wsize;char *word;int ll;wsize = 102400;ll=0;word = (char *) malloc(sizeof(char) * (wsize + 1));while(1) {word[ll] = (char)fgetc(f);if(ll==wsize) {word[ll+1] = '0';wsize+=102400;word = (char *)realloc(word,sizeof(char)*(wsize+1));}--(*cl);if((word[ll] == stop) || (feof(f)) || (!(*cl))) {if(word[ll] != stop) ll++;word[ll] = '0';return word;}++ll;}}char x2c
(char *what) {/* Предназначена для преобразования шестнадцатиричного кода символа в код символа */register char digit;digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A')+10 : (what[0] - '0'));digit *= 16;digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : (what[1] - '0'));return(digit);}void unescape_url
(char *url) {register int x,y;for(x=0,y=0;url[y];++x,++y) {if((url[x] = url[y]) == '%') {url[x] = x2c(&url[y+1]);y+=2;}}url[x] = '0';}void plustospace
(char *str) {/*замена символов "+" на символ "пробел"*/ register int x;for(x=0;str[x];x++) if(str[x] == '+') str[x] = ' ';}
Для демонстрации реализации формы с методом запроса GET воспользуемся той же самой формой, что и для метода POST и программой query. Для этого изменим значение атрибутов ACTION и METHOD в теге FORM.
<FORM action="http://iceman.cnit.nsu.ru/cgi-bin/query" METHOD=GET>
После инициации формы сервер установит следующие значения для переменных окружения и аргументов командной строки:
argc = 0. argv is = SERVER_SOFTWARE = NCSA/1.5.1 SERVER_NAME = iceman.cnit.nsu.ru GATEWAY_INTERFACE = CGI/1.1 SERVER_PROTOCOL = HTTP/1.0 SERVER_PORT = 80 REQUEST_METHOD = GET HTTP_ACCEPT = image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */* PATH_INFO = PATH_TRANSLATED = SCRIPT_NAME = /cgi-bin/test-cgi QUERY_STRING = RealName=%CF%E5%F2%F0%EE%E2+%C8%E2%E0%ED+%D1%E8 %E4%EE%F0%EE%E2%E8%F7&Sex=%CC%F3%E6%F1%EA%EE%E9&Submit=%CF%EE %F1%EB%E0%F2%FC+%E7%E0%EF%F0%EE%F1 REMOTE_HOST = fwa.cnit.nsu.ru REMOTE_ADDR = 193.124.209.74 REMOTE_USER = AUTH_TYPE = CONTENT_TYPE = CONTENT_LENGTH =
Как мы видим, выходной поток от формы появился в значении переменной QUERY_STRING.
Результат работы query полностью совпадает с результатом работы post-query.
Приложение 1 к гл.4 Конструкции языка HTML для построения форм
<FORM атрибуты>...</FORM>
использование: предназначен для получения информации от клиента и определяет начало и конец формы.
атрибуты:
Обязательные ACTION
- определяет URI (Universal Resource Identifier-адрес или место расположения документа) CGI-скрипта METHOD
- определяет метод передачи информации скрипту. Возможные значения GET или POST.
Необязательные [ENCTYPE]
- определяет тип MIME декодирования информации (значение этого атрибута по умолчанию - "application/x-www-form-urlencoded"). [SCRIPT]
- используется для передачи URI скрипта. Язык скрипта и интерфейс пользователя при этом не являются частью спецификации HTML 3.0
Важно:
Формы не могут быть вложенными!
Для реализации формы используются следующие теги.
<INPUT>
использование: предназначен для создания различных по своей функциональности полей ввода.
атрибуты:
Обязательные:
TYPE
- определяет тип поля формы.
Допустимые значения: TEXT
- позволяет символьный ввод. PASSWORD
- предназначено для "скрытого" ввода символов (вводимые символы не отображаются). CHECKBOX
- поле, позволяющее два состояния ("есть", "нет"). Должен применяться с атрибутами NAME и VALUE RADIO
- поле, позволяющее выбор "один из всех" SUBMIT
- кнопка инициирующая передачу информации из формы обрабатывающему скрипту, определенному в ACTION в соответствии с методом, определенным атрибутом METHOD.RESET
- кнопка, сбрасывающая все введенные ранее значения. IMAGE
- поле позволяющее воспроизвести событие SUBMIT при помощи вашего изображения, при этом возвращается два значения: name.x = координата Х и name.y = координата Y, где Х и Y координаты положения курсора мыши на изображении в момент щелчка. HIDDEN
- поле создающее неотображаемое значение. RANGE
- определяет поле позволяющее ввести цифровое значение с определенными допустимыми верхним и нижним пределами. Используется вместе с атрибутами MAX и MIN определяющими область допустимых значений (например: TYPE=RANGE MIN=1 MAX=10).
NAME
- значение этого атрибута определяет идентификатор поля.VALUE
- значение этого атрибута определяет что будет передано в качестве значения по умолчанию для данного поля при инициации формы. SRC
- определяет URI файла изображения. Используется только с типом поля IMAGE. [CHECKED]
- позволяет установить начальное значение поля типа CHECKBOX. SIZE
- определяет размер поля. [MAXLENGTH]
- определяет максимальное количество символов, допустимое для ввода в поле.[ALIGN]
- позволяет позиционирование
Допустимые значения:
по вертикали TOP
- выравнивание по верху. MIDDLE
- выравнивание по середине. BOTTOM
выравнивание по низу. Эти значения используются только с TYPE=IMAGE.
по горизонтали [LEFT]
- выравнивание слева [RIGHT]
- выравнивание справа
[DISABLED]
- определяет поле как "read only" - только для чтения. Значение в поле не может быть изменено пользователем.[ERROR]
- определяет сообщение об ошибке, объясняющее, почему введенное значение в поле не верно.
<TEXTAREA
атрибуты>...</TEXTAREA>
использование: предназначен для определения области ввода текста. Размер поля определяется атрибутами.
атрибуты:
NAME
- значение этого атрибута определяет идентификатор поля. Возвращается при инициации формы. ROWS
- определяет количество строк в текстовой области. COLS
- определяет количество столбцов в текстовой области.[VALUE]
- задает значение по умолчанию. [DISABLED]
- определяет поле как "read only" - только для чтения. Значение в поле не может быть изменено пользователем. [ERROR]
- определяет сообщение об ошибке, объясняющее, почему введенное значение в поле не верно.
<SELECT атрибуты>
<OPTION >
значение1 ...
<OPTION >
значениеN </SELECT>
использование: предназначен для определения области выбора из нескольких значений (меню).
Атрибуты:
NAME
- значение этого атрибута определяет идентификатор поля. Возвращается при инициации формы.[SIZE]
- определяет количество видимых возможных значений. [MULTIPLE]
- определяет возможность множественного выбора. [DISABLED]
- определяет меню как "read only" - только для чтения. Значения в меню не может быть выбрано пользователем и показывается серым цветом.
<OPTION атрибуты>
значение
использование: используется только с <SELECT> для определения пунктов меню.
атрибуты:
SELECTED
- определяет значение по умолчанию VALUE
- определяет возвращаемое значение
Примечание: в [ ] даны необязательные атрибуты