Введение в программирование скриптов на C
Информация
Язык программирования C — это традиционный инструмент разработки программного обеспечения, используемый на протяжении последних 25 лет (с момента появления Unix). С учетом того, что Unix в настоящее время является основной серверной средой, умение программировать CGI-скрипты на C является одним из необходимых условий успешной работы Web-инженера.
Вникнув повнимательнее в спецификацию CGI, любой программист поймет, что спецификация создавалась с расчетом на Unix и С. Работа с переменными окружения и потоками стандартного ввода/вывода построена с учетом особенностей среды и средств программирования. При адаптации спецификации CGI в других средах, например, MS-Windows, программирование многих механизмов обмена приходится модифицировать.
Большинство задач при разработке скриптов можно решить средствами Perl, но есть ряд задач, которые требуют использования C-программ. Наиболее распространенным применением C являются скрипты-интерфейсы к базам данных. В качестве примера такого интерфейса воспользуемся библиотекой PostgreSQL.
В данном разделе речь пойдет о программировании CGI-скриптов на классическом C без объектно-ориентрованных особенностей C++.
Общая структура C-скрипта
Скрипт на языке C ничем не отличается от обычной C-программы. Собственно, это набор процедур, среди которых есть главная процедура. Этой главной процедуре передается управление при загрузке программы в оперативную память. Главная процедура контролирует вызов других процедур и весь ход выполнения программы. Простой скрипт — это одна главная процедура.
При разработке С-скрипта следует всегда помнить, что в отличие от скрипта на Bash и Perl, С-скрипт, прежде чем выполнить, нужно еще и откомпилировать, т.е. превратить в исполняемый компьютером код. Если в системе нет компилятора для C, то программировать С-скрипты будет довольно сложно.
Синтаксически главная процедура выглядит следующим образом:
#include <stdlib.h> #include <stdio.h> void main(argc,argv,env) int *argc; char *argv[]; char *env[]; { /* тело программы */ }
В этом фрагменте представлены все основные элементы программирования CGI-скриптов на С. Строки в начале программы ( #include... ) позволяют включить в текст программы декларации (описатели) стандартных функций. Строка " void main..." — это объявление главной процедуры. В качестве параметров в данную процедуру (функцию) передаются:
- число аргументов командной строки — argc ;
- указатель на массив аргументов командной строки — argv ;
- указатель на массив переменных окружения — env.
Само тело программы помещается между символами фигурных скобок "{...}". Фраза "тело программы" размещена между парой "/* ... */". Это комментарий. Сама программа на С состоит из операторов. Операторы могут быть простые и составные. Простые операторы – это, например, оператор присваивания. Составной оператор — это блок. Блок представляет собой последовательность операторов, заключенную в фигурные скобки "{...}". В конце простого оператора должен стоять символ ";". В нашем примере объявление (декларирование) переменных перед блоком тела программы — это последовательность простых операторов.
Про С часто говорят, что в языке только пять операторов, а все остальное — это библиотека стандартных функций. Операторов в нем, конечно, больше, но такая характеристика в целом верна.
Стандартный поток вывода
Стандартный поток вывода в С ассоциируется с дескриптором STDOUT. Самым распространенным способом записи данных в этот поток является функция форматного вывода printf. Если скрипт должен что-то передать браузеру пользователя, то первое, что нужно сделать — это применить printf для формирования HTTP-заголовка:
main() { printf("Content-type: text/html\n\n"); printf("<H1>C и CGI</H1>"); }
Первый вызов printf формирует заголовок — определяет тип тела HTTP-отклика, а второй вызов формирует заглавие первого уровня в HTML-документе. В общем случае у функции printf три аргумента: printf(FILE,"format",VARS_LIST); FILE — дескриптор файла, "format" — формат вывода данных, VARS_LIST — список переменных, чьи значения подлежат выводу. Если дескриптор файла опущен, то вывод направляется в поток стандартного вывода. Список переменных указывается в том случае, если в формате вывода есть шаблоны вывода для переменных из этого списка.
Для каждого типа данных в С существует свой шаблон вывода. Перечислим только некоторые из них:
%d — вывод целого числа; %s — вывод массива символов (строки); %f — вывод вещественного числа; %x — вывод целого числа в шестнадцатеричном виде.
Для того, чтобы распечатать аргументы командной строки, можно применить следующий формат:
int i; ... for(i=0;i<argc;i++) { printf("arg[%d]=%s\n",i,argv[i]); }
В данном случае переменная цикла i — это целая константа, поэтому в квадратных скобках указано [%d]. Второй аргумент списка переменных — указатель на массив символов (строка, содержащая значение аргумента командной строки ), поэтому после знака равенства ("=") применен шаблон вывода массива символов %s.
Переменные окружения
Третий аргумент главной процедуры — указатель на массив переменных окружения, каждый элемент которого представляет собой строковую константу вида "имя-значение". Неудобство работы с этим массивом заключается в том, что заранее не известно, сколько элементов он содержит. Список переменных окружения кончается в тот момент, когда при его переборе встречается указатель NULL. Пример такого перебора представлен ниже:
#include <stdlib.h> #include <stdio.h> void main(argc,argv,env) int argc; char *argv[]; char *env[]; { int i; i=0; while(env[i]) { printf("%d:%s\n",i,env[i]); i++; } }
В данном случае перебор идет до тех пор, пока значение указателя больше 0. При этом переменные окружения выдаются в виде "имя:значение". Для того, чтобы воспользоваться этими значениями, придется разбирать каждую строку, выделяя имя и значение переменной при помощи манипуляций с адресами внутри строки. Любые адресные операции — это потенциальные ошибки (хотя они и позволяют писать быстрые программы).
К счастью, в С есть функция getenv(). В качестве аргумента этой функции достаточно указать имя переменной окружения, и система вернет указатель на его значение. Например, необходимо знать, сколько символов нужно считать со стандартного ввода скрипта:
int i,n; char *query; ... n = atoi(getenv("CONTENT_LENGTH")); query = (char *) malloc(n+1); for(i=0;i<n;i++) { query[i] = getc(); } ... free(query);
В этом примере при помощи последовательного применения getenv и atoi (ascii to integer) из переменной окружения в переменную целого типа помещается значение переменной окружения — CONTENT_LENGTH. Последовательное применение функций здесь необходимо, т.к. значение переменной окружения — строка, а мы хотим использовать число, следовательно, строку символов следует не только получить, но еще и преобразовать в число.
Для преобразования строк в числа часто используют функцию форматного ввода sscanf. В нашем случае это выглядело примерно следующим образом:
char *length; int n; ... length = getenv("CONTENT_LENGTH"); sscanf(length,"%d",&n); ...
Следует заметить, что sscanf — это довольно сложная функция. Она предназначена для ввода любой информации и ее преобразования. Как показывает практика, иногда sscanf работает не так, как предполагает программист. Поэтому незачем стрелять из пушки по воробьям — применяйте лучше специализированные функции преобразования, например, atoi, если это возможно.