Администрирование

Web — сервер newLISP 10.2.x + cgi + MySQL

Гном в кедах - Администрирование

Web — сервер newLISP 10.2.x + cgi + MySQL

мой опыт создания и использования

NewLISP   — на  мой  взгляд  очень  интересный  язык  программирования. Интерпретатор  newLISP позволяет без напрягов запустить локальный web-сервер, организовав  динамическое  формирование   страниц  с  запросами  к  базе  данных mySQL. Эти  записки  будут  отражением  моего  опыта,  по  формированию  такого программного комплекса. Установка newLISP не должна вызвать проблем, равно как и установка базы данных mySQL, поэтому я пропускаю этот момент. Единственное замечание,  я  использую   Calculate Linux  который  основан  на  базе   Gentoo Linux. Пользователям Windows и MacOS придется немного подправить предложенное.

 

Запуск web-сервера

Запуск    web-сервера    происходит    очень    просто,    от    имени    простого пользователя:

user $ newlisp httpd.conf -http -d 8080 -w /home/user/www &

Используемые параметры запуска:

1. httpd.conf                      -указание использовать конфигурационный файл;

2. -http                               -запуск интерпретатора в режиме web-сервера;

3. -d 8080                           -указывает какой порт нужно слушать;

4.  -w /home/www               -указывает рабочий каталог web-сервера;

5. &                                   -запускать интерпретатор newLISP в фоновом режиме.

 

 

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

 

Создание тестовой базы

 

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

 

 

 

База   содержит   две   таблицы.   Таблица   tb_user   содержит   Ф.И.О.   человека   и необходимые  примечания.  Таблица   tb_phone   содержит   номера  телефонов,   на однозначную  принадлежность  к  конкретному  человеку  которых  указывает  поле user_id, а также содержит краткое описание телефонного номера.

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

 


/**********************************************************************
Создание тестовой базы данных - скрипт phonebook.sql
Автор: kesha
**********************************************************************/
-- Создание базы db_phonebook
CREATE DATABASE IF NOT EXISTS db_phonebook;
-- Создание пользователя базы db_phonebook
GRANT SELECT,INSERT,UPDATE,DELETE ON db_phonebook.*
TO
IDENTIFIED BY 'secret';
-- Переход в базу db_phonebook
USE db_phonebook;
-- Создание таблицы tb_user
CREATE TABLE tb_user (
id INT(10) NOT NULL AUTO_INCREMENT, fio VARCHAR(100) NOT NULL,
example TEXT, PRIMARY KEY(id), UNIQUE(fio));
-- Создание таблицы tb_phone
CREATE TABLE tb_phone (
id INT(10) NOT NULL AUTO_INCREMENT, user_id INT(10) NOT NULL,
phone VARCHAR(20) NOT NULL, example VARCHAR(100), PRIMARY KEY(id), UNIQUE(phone));
/*********************************************************************/

Приведенный скрипт SQL достаточно сохранить в файле phonebook.sql и выполнить, находясь в MySQL мониторе от имени суперпользователя:

user $ mysql -u root -p

Enter password:
mysql> source /home/phonebook.sql; Query OK, 1 row affected (0.00 sec) Query OK, 0 rows affected (0.00 sec) Database changed
Query OK, 0 rows affected (0.06 sec) Query OK, 0 rows affected (0.02 sec) mysql>

Ну и совсем было-бы не плохо добавить немного записей в базу данных. Для этого подойдет скрипт insert_ phonebook.sql

 


/*********************************************************************
Наполнение тестовой базы данных - скрипт insert_phonebook.sql
Автор: kesha
**********************************************************************/
-- Добавление в таблицу tb_user нескольких человек
INSERT INTO tb_user VALUES ( NULL,
'Иванов Иван Иванович',
'Хороший человек');
INSERT INTO tb_user VALUES ( NULL,
'Саяпин Василий Васильевич',
'Нужный человек');
INSERT INTO tb_user VALUES ( NULL,
'Косяков Лука Мудищев',
'На его звонки не отвечать, достанет');
-- Добавление в таблицу tb_phone нескольких телефонов
INSERT INTO tb_phone VALUES ( NULL,
(SELECT id FROM tb_user WHERE fio = 'Иванов Иван Иванович'),
'+7-999-555-55-55',
'Личный телефон');
INSERT INTO tb_phone VALUES ( NULL,
(SELECT id FROM tb_user WHERE fio = 'Иванов Иван Иванович'),
'+7-999-777-77-77',
'Корпоративный телефон');
INSERT INTO tb_phone VALUES ( NULL,
(SELECT id FROM tb_user WHERE fio = 'Саяпин Василий Васильевич'),
'+7-(9999)-999-99-99',
'Домашний телефон');
INSERT INTO tb_phone VALUES ( NULL,
(SELECT id FROM tb_user WHERE fio = 'Косяков Лука Мудищев'),
'+6-(6666)-666-66-66',
'Телефон и факс одновременно');
INSERT INTO tb_phone VALUES ( NULL,
(SELECT id FROM tb_user WHERE fio = 'Косяков Лука Мудищев'),
'+6-666-666-66-66',
'Не понятный телефон, но он иногда с него звонит');
/*********************************************************************/

Добавление записей в базу, и одновременное тестирование, нужно делать так:


user $ mysql db_phonebook -u wwwuser -p
Enter password:
mysql> source /home/insert_phonebook.sql; Query OK, 1 row affected (0.00 sec)
Query OK, 1 row affected (0.00 sec) Query OK, 1 row affected (0.00 sec) Query OK, 1 row affected (0.00 sec) Query OK, 1 row affected (0.00 sec) Query OK, 1 row affected (0.00 sec) Query OK, 1 row affected (0.00 sec) Query OK, 1 row affected (0.00 sec) mysql>
Ошибок нет, значить пора переходить к следующему этапу — доступ к данным с помощью newLISP. newLISP - доступ к базе mySQL

В стандартной  поставке  newLISP  идут  несколько  модулей,  сейчас  нужен модуль под названием mysql.lsp, он как раз предназначен для легкого доступа к базе данных  mysql.  Свои  эксперименты  я  буду  проводить   используя  поставляемую вместе с newLISP среду разработки, которая вызывается командой newlisp-edit. В прочем тестировать получаемый скрипт лучше напрямую, из консоли.

#! /usr/bin/newlisp

;; Отработка навыков использования модуля mysql.lsp

;; Скрипт - mysql-test.lsp Автор: kesha; Подключение модуля mysql.lsp
(module "mysql.lsp"); Инициализация
(MySQL:init); Подключение к базе данных
(MySQL:connect "127.0.0.1" "wwwuser" "secret" "db_phonebook"); Стандартный SQL запрос к базе данных
(MySQL:query "select * from tb_user;"); Вывод на экран результатов SQL запроса
(println (MySQL:fetch-all)); Завершение соединения с базой данных
(MySQL:close-db); Конец скрипта
(exit)

Теперь стоит сохранить получившийся скрип в домашней папке под именем  mysql- test.lsp и запускать консоль. Главное не забыть сделать скрипт исполняемым.


user $ chmod +x mysql-test.lsp
user $ ./mysql-test.lsp true
(("1" "Иванов Иван Иванович" "Хороший человек")
("2" "Саяпин Василий Васильевич" "Нужный человек")
("3" "Косяков Лука Мудищев" "На его звонки не отвечать, достанет"))
user $

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

WEB — морда

Если  web-сервер  newLISP  запущен  на  стандартный  http  порт  (80),  то  при обращении к серверу из броузера порт можно не указывать. Если же используется не стандартный порт (например 8080), то обращаться к серверу из броузера нужно так http://localhost:8080. По умолчанию сервер ищет web-страничку index.html и если ее не находит, то возвращает ERR:404. Значить в первую очередь необходимо создать эту страничку. Моя страничка будет состоять из двух фреймов. В верхнем фрейме я размещу какой нибудь баннер, ну или типа того, в нижнем  фрейме собственно и будет происходить взаимодействие с базой данных. Вот файл index.html:

<HTML>

<HEAD>

http-equiv="content-type" content="text/html; charset=UTF-8"></strong>Тестирование web-сервера newLISP<strong>
</HEAD><FRAMESET rows="15%,85%">
<FRAME frameborder="0" src="/head.html" name="head"><FRAME frameborder="0" src="/body.cgi?method=new" name="body">
</FRAMESET></strong> <br><strong><BODY></strong>Ваш браузер пора уже обновить!!!<strong></BODY></strong><strong><</strong><strong>/NOFRAMES></strong> <br><strong><</strong><strong>/HTML></strong></pre> <p>Ниже  дается  пример  баннера,  странички  head.html,  которая  будет  загружаться  в верхний фрейм. Конечно в реальности ее можно украсить и получше:</p> <pre><br><strong><</strong><strong>HTML></strong><strong><</strong><strong>HEAD></strong> <br><strong><META </strong>http-equiv="content-type" content="text/html; charset=UTF-8"<strong>></strong><strong><</strong><strong>/HEAD></strong> <br><strong><BODY></strong><strong><H1 </strong>alight="left">Моя телефонная книга<strong></H1><BP></strong> <br><strong><HR </strong>alight="center" width="96%" size="5" color="silver"><strong><</strong><strong>/BODY></strong> <br><strong><</strong><strong>/HTML></strong><br>Наступает волнующий момент, пора занятся скриптами cgi, а конкретно скриптом body.cgi. Этот скрипт и будет отвечать за содержимое нижнего фрейма страницы index.html, а также, фактически будет нести всю нагрузку на телефонную книгу. </pre> <p><strong>Тестирование CGI</strong></p> <p>Про модули, которые идут в   стандартной поставке newLISP, я уже в курсе. Теперь  мне нужен  модуль  cgi.lsp.  Модуль  простой  как  три  рубля.  Он  позволяет получать и обрабатывать данные, переданные  серверу  в запросе как методом GET так  и  методом  POST.  Позволяет  передавать  клиенту  web-страничку,  в  которую внедрен  код  newLISP  обрамленный  тегами  <strong><%...%></strong>,  обрабатывая  этот  код.  В качестве тестового примера создам страничку body.html:</p> <pre><br><strong><</strong><strong>HTML></strong><strong><</strong><strong>HEAD></strong> <br><strong><META </strong>http-equiv="content-type" content="text/html; charset=UTF-8"<strong>></strong><strong><</strong><strong>/HEAD></strong> <br><strong><BODY></strong><strong><%</strong>(print html-body)<strong>%></strong> <br><strong><</strong>/BODY><strong> </strong><br><strong><</strong><strong>/HTML></strong>В теле странички код newLISP печатает переменную html-body, осталось только подготовить эту переменную. Для первого опыта подойдет вот такой скрипт: </pre> <p>#! /usr/bin/newlisp</p> <p><strong></strong></p> <pre>;; cgi - скрипт web-сервера: body.cgi <br>;; Автор: kesha; Подключаю модуль cgi.lsp <br>(<strong>m</strong><strong>o</strong><strong>dule </strong>"cgi.lsp"); Формирование тела странички <br>(<strong>s</strong><strong>et </strong>'html-body "<strong><H1>Тест скрипта body.cgi</H1></strong>"); Обработка метода new <br>(<strong>w</strong><strong>h</strong><strong>en </strong>(<strong>= </strong>(<strong>CGI:get </strong>"method") "new") (<strong>CGI:put-page </strong>"body.html") (<strong>exit</strong>)); Завершение скрипта <br>(<strong>exit</strong>) Здесь все просто. Формирую переменную html-body, проверяю параметры запроса. Если параметры запроса удовлетворяют заданным — отправляю клиенту страничку body.html. Пора протестировать, да нужно не забыть сделать скрипт body.cgi исполняемым. </pre> <p><strong></strong><strong>user $</strong> chmod +x body.cgi</p> <pre><br>Копирую файлы index.html, head.html, body.html и body.cgi в папку, с которой работает newLISP web-сервер (/home/user/www/): </pre> <p><strong></strong><strong>user $</strong> cp ./index.html ./head.html ./body.html ./body.cgi /home/user/www/ Cтартую web-сервер:</p> <p><strong></strong><strong>user $</strong> newlisp -http -d 8080 -w /home/user/www &</p> <p>Вот теперь можно в браузере набирать http://localhost:8080</p> <p> </p> <p style="text-align: center;"></p> <p><strong>Ч</strong><strong>т</strong><strong>о хочу получить?</strong></p> <p>Для удобства пользования «Телефонной книгой», нужно организовать поиск по  базе  как  имен,  так  и  телефонов.  В  самом  простом  случае  на  веб-страничке должно присуствовать:</p> <p>1. Средство определения поиска (поиск по Ф.И.О., или по телефону);</p> <p>2.  Поле для ввода данных запроса;</p> <p>3.  Средство, например кнопка, посылки запроса в базу данных.</p> <p>Результат  поиска  должен  выдаваться  в  табличном  виде,  и  там-же  должна присутствовать кнопка возврата к начальной странице.</p> <p>Поиск хочу получить достаточно универсальный, то-есть по буквам и (или) цифрам   стоящим   в   любой   позиции   искомого  слова   (словосочетания).   Для обозначения любых символов (в любом количестве) буду использовать звездочку - *:</p> <p>1.  *                           - все, что есть в базе;</p> <p>2. *вич                     - все слова оканчивающиеся на вич;</p> <p>3. лу*                       - все слова начинающиеся на лу;</p> <p>4. *ви*                     - все слова содержащие слог ви;</p> <p>5. л*а                        - все слова начинающиеся на л, и оканчивающиеся на а.</p> <p>Код скрипта body.cgi получится перемешанным из кода html, sql и собственно кода newlisp, по этому я буду код выделять цветом:</p> <pre><br><strong><span style="font-size: small;">1. <span style="color: #0000ff;">вот так</span> — буду выделять примечания; </span></strong><br><strong><span style="font-size: small;">2. <span style="color: #c0c0c0;">вот так</span> — код html; </span></strong><br><strong><span style="font-size: small;">3. <span style="color: #00ff00;">вот так</span> — код sql; </span></strong><br><strong><span style="font-size: small;">4. вот так — код newlisp; </span></strong><br><strong><span style="font-size: small;">5. <span style="color: #ff0000;">вот так</span> — вставки с необходимыми замечаниями.</span></strong> </pre> <p><strong>Полное погружение.</strong></p> <p>Возьму уже существующий скрипт body.cgi и буду постепенно наполнять его функционалом. Вот заготовка скрипта:</p> <pre><br><strong>#! /usr/bin/newlisp</strong> <br><span style="color: #0000ff;">;; cgi - скрипт web-сервера: body.cgi </span><br><span style="color: #0000ff;">;; Автор: kesha </span><br><span style="color: #0000ff;">; Подключаю модули</span> <br>(<strong>m</strong><strong>o</strong><strong>dule </strong>"cgi.lsp") <br>(<strong>m</strong><strong>o</strong><strong>dule </strong>"mysql.lsp") <br>; Инициализация и соединение с базой данных <br>(<strong>MySQL:init</strong>) <br>(<strong>MySQL:connect </strong>"127.0.0.1" "wwwuser" "secret" "db_phonebook") <br><span style="color: #0000ff;">; Корректное завершение скрипта</span> <br>(<strong>define </strong>(exit-body) (<strong>MySQL:close-db</strong>) (<strong>exit</strong>)) <br><span style="color: #ff0000;">============== Здесь будет весь остальной текст программы ==============</span> <br><span style="color: #0000ff;">; Завершение скрипта</span> <br>(exit-body) </pre> <p>Тут все просто. Подключаю требуемые модули, получаю дескриптор базы mySQL (инициализирую базу), подключаюсь к базе и... Здесь мне приходится позаботится о корректном  завершении  скрипта.  У  алгоритма   может   быть  несколько  ветвей, приводящих  к   завершению   программы.  Правила   newLISP  требуют   завершать программу  вызовом  функции  exit,  а  ведь   перед  этим  нужно  еще  завершить соединение  с  базой  данных,  поэтому  я  определяю  функцию  exit-body.  Функция корректно закрывает соединение с базой данных, и завершает работу программы.</p> <p>Теперь модернизирую обработчик метода new:</p> <pre><br><span style="color: #0000ff;">; Обработка метода new и нажатия кнопки Restore</span> <br>(<strong>w</strong><strong>h</strong><strong>en </strong>(<strong>or </strong>(= (<strong>CGI:get </strong>"method") "new") (<strong>CGI:get </strong>"Restore"))(<strong>s</strong><strong>et </strong>'html-body <strong>[text]</strong> <br><span style="color: #999999;"><div align="center"></span><span style="color: #999999;"><form name="StartForm" method="post" action="body.cgi"> </span><br><span style="color: #999999;"><select name="TypeSearch"> <OPTION>Человек </span><br><span style="color: #999999;"><OPTION>Телефон </span><br><span style="color: #999999;"></select> </span><br><span style="color: #999999;"><input type="text" name="StrSearch" size="100"> </span><br><span style="color: #999999;"><input type="submit" name="NewSearch" value="Искать"> </span><br><span style="color: #999999;"></form> </span><br><span style="color: #999999;"></div></span> <br><strong>[</strong><strong>/</strong><strong>t</strong><strong>ext]</strong>) <br>(<strong>CGI:put-page </strong>"body.html") (exit-body)) </pre> <p>Кнопка Restore — это будет кнопка возврата из просмотра результатов поиска по базе данных. Сначала я проверяю, вызывается метод new или нажата кнопка Restore. Дальше присваиваю переменной html-body сроку, представляющую собой код html.</p> <p>Тут   необходимы   пояснения.  В   языке   newLISP   существует   три   способа обозначить строковую константу, это — двойные кавычки, фигурные скобки и теги [text]...[/text]. Здесь я использую теги.</p> <p>Наконец отправляю клиенту страничку body.html (она ни как не изменилась), и</p> <p>завершаю работу программы. Вот что получилось:</p> <p> </p> <p style="text-align: center;"></p> <p>Следующий шаг, обработка поискового запроса:</p> <pre><br><span style="color: #0000ff;">;Обработка нажатия кнопки NewSearch</span> <br>(<strong>w</strong><strong>h</strong><strong>en </strong>(<strong>CGI:get </strong>"NewSearch") (<strong>set </strong>'html-body (<strong>append</strong> <br>(test-value (<strong>CGI:get </strong>"TypeSearch") (<strong>replace </strong>{*}(<strong>CGI:get </strong>"StrSearch"){%})) <br><strong>[t</strong><strong>e</strong><strong>xt]</strong> <br><span style="color: #999999;"><div align="center"> </span><br><span style="color: #999999;"><form name="RestoreForm" method="post" action="body.cgi"> </span><br><span style="color: #999999;"><input type="submit" name="Restore" value="На главную страницу"> </span><br><span style="color: #999999;"></form> </span><br><span style="color: #999999;"></div></span> <br><strong>[</strong><strong>/</strong><strong>t</strong><strong>ext]</strong>)) <br>(<strong>CGI:put-page </strong>"body.html") (exit-body)) </pre> <p>В форме поиска кнопка «Искать» имеет имя NewSearch, вот здесь я как раз и обрабатываю  нажатие  этой  кнопки.  Опять  идет  присвоение  строкового  значения переменной  html-body,  но  это  значение  уже  формируется   функцией  append,  в качестве параметров которой передается строка возвращаемая функцией test-value (кстати эту функцию еще предстоит написать), ну и соответственно код html. В свою очередь в функцию test-value в виде параметров передается значение выпадающего списка TypeSearch, из формы поиска, и обработанная функцией  replace строка для поиска. Функция replace заменяет в строке, переданной ей вторым параметром, все символы  переданные ей первым параметром, на символы переданные ей третьим параметром.  Тоесть  *  на  %,  это  нужно  для  организации  поиска  в  базе  данных, средствами самой базы данных.</p> <p>Дальше  опять  посылаю  клиенту  страничку  body.html  и  завершаю  работу программы.</p> <p>Пора проверить параметры на корректность:</p> <pre><br><span style="color: #0000ff;">; Проверка корректности параметров для поиска в базе </span><br><span style="color: #0000ff;">; и формирование требуемого подзапроса</span> <br>(<strong>define </strong>(test-value table-search text-search) (<strong>if </strong>(<strong>empty? </strong>text-search) <br>(<strong>append</strong> <br>{<span style="color: #999999;"><div align="center"><H1>Пустая строка запроса...</H1><BR> </div></span>})<br> (<strong>case </strong>table-search ("Человек" (query-base (<strong>append</strong> <br>"<span style="color: #00ff00;">(SELECT id FROM tb_user WHERE fio LIKE '</span>" text-search "<span style="color: #00ff00;">')</span>"))) ("Телефон" (query-base (<strong>append</strong> <br>"<span style="color: #00ff00;">(SELECT user_id FROM tb_phone WHERE phone LIKE '</span>" text-search "'<span style="color: #00ff00;">)</span>")))<br> (<strong>t</strong><strong>rue </strong>(<strong>append</strong> <br>{<span style="color: #999999;"><div align="center"></span>} <br>{<span style="color: #999999;"><H1>Не корректные параметры запроса...</H1><BR> </div></span>}))))) </pre> <p>В этой функции я в первую очередь проверяю пустая строка для поиска? Если пустая — сообщаю об этом клиенту и завершаю работу.</p> <p style="text-align: center;"></p> <p>Если  со  строкой  для  поиска  все  нормально,  то  используя  функцию  case, формирую два sql  подзапроса.  Один для поиска по Ф.И.О., второй для поиска по телефонному номеру. Возможно по какой либо причине не удастся определить какой подзапрос  нужно  формировать,  тогда  отправляю клиенту   сообщение  об  этой трагедии,  и  завершаю  работу   программы.  Подзапросы  формируются  функцией append, которая объединяет текст sql запроса и строку, предназначенную для поиска. Полученный подзапрос передаю в функцию  query-base:</p> <pre><br><span style="color: #0000ff;">; Поиск в базе данных</span> <br>(<strong>define </strong>(query-base text-query) (<strong>MySQL:query </strong>(<strong>append [text]</strong> <br><span style="color: #00ff00;">SELECT tb_user.fio,tb_user.example,tb_phone.phone,tb_phone.example </span><br><span style="color: #00ff00;">FROM tb_user,tb_phone </span><br><span style="color: #00ff00;">WHERE tb_user.id = tb_phone.user_id </span><br><span style="color: #00ff00;">AND tb_user.id IN</span> <br><strong>[</strong><strong>/</strong><strong>t</strong><strong>ext]</strong> <br>text-query <br>" <span style="color: #00ff00;">LIMIT 0,10;</span>")) <br>(<strong>i</strong><strong>f </strong>(= (<strong>MySQL:num-rows</strong>) 0) (<strong>append</strong> <br>{<div align="center"> <br><H1>По Вашему запросу результатов нет...</H1><BR> <br></div>}) (format-result))) </pre> <p>Функцией append формирую полный sql запрос к базе данных, ну и отправляю его в базу. Проверяю количество найденных строк, если ноль — оповещаю об этом клиента  и  завершаю  работу,  еже-ли  что-то  нашли,  тогда  перехожу  к  функции формирования результатов поиска.</p> <p> </p> <p style="text-align: center;"></p> <p>Форматирование результатов поиска осуществляет функция  format-result:</p> <pre><br><span style="color: #0000ff;">; Форматирование результата поиска в базе данных</span> (<strong>define </strong>(format-result)(<strong>append</strong> <br>{<table border="1" width="96%" align="center">} (<strong>join </strong>(<strong>map </strong>(<strong>fn </strong>(y)(<strong>append </strong>"<tr>" <br>(<strong>j</strong><strong>oin </strong>(<strong>m</strong><strong>a</strong><strong>p </strong>(<strong>f</strong><strong>n </strong>(x)(<strong>append </strong>"<td>"x"</td>")) y)) <br>"</tr>")) (<strong>MySQL</strong>:fetch-all))) <br>"</table><BR>")) </pre> <p>Результаты  поиска  в  базе  данных  возвращаются  <strong>MySQL</strong>:fetch-all  в  виде двумерного массива строк:</p> <p>(("1" "Иванов Иван Иванович" "Хороший человек")</p> <pre><br>("2" "Саяпин Василий Васильевич" "Нужный человек") <br>("3" "Косяков Лука Мудищев" "На его звонки не отвечать, достанет")) </pre> <p>Функция  map  позволяет  пройтись  по  одному  измерению  массива  строк,  и применить   к   каждому   элементу   массива   действия,   определяемые   анонимной функцией fn (или канонически —  lambda). Этим действием будет вторая функция map,  которая  посредством  append,  завернет  каждую  строчку  в  теги  <td>...</td>. Результат я передаю функции join, которая объединяет строки в одну. В результате я получаю одномерный   массив,  каждую   строку   которого  заворачиваю   в   теги</p> <p><tr>...</tr>, и склеиваю результат в одну большую строку еще одной функцией join.</p> <p> </p> <p style="text-align: center;"></p> <p><strong>Выводы</strong></p> <p>NewLISP     —    очень    мощный     скриптовый    язык    программирования, распространяемый по лицензии GPL, автора Lutz Mueller (от меня ему респект и уважуха). Маленький размер интерпретатора, большой функционал и использование только стандартных библиотек — вот несомненные достоинства newLISP</p> <p> </p> <p style="text-align: center;"></p> <p>Библиотеки используемые newLISP:</p> <pre><br><strong>user $ </strong>ldd /usr/bin/newlisp <br>linux-vdso.so.1 => (0x00007fffde771000) libm.so.6 => /lib/libm.so.6 (0x00000032a2800000) libdl.so.2 => /lib/libdl.so.2 (0x00000032a2400000) <br>libreadline.so.6 => /lib/libreadline.so.6 (0x00000032a3800000) <br>libc.so.6 => /lib/libc.so.6 (0x00000032a2000000) <br>/lib64/ld-linux-x86-64.so.2 (0x00000032a0a00000) <br>libncurses.so.5 => /lib/libncurses.so.5 (0x00000032a0e00000) И для примера библиотеки используемые bash: </pre> <pre><br><strong>user $ </strong>ldd /bin/bash <br>linux-vdso.so.1 => (0x00007fffbeb1f000) <br>libncurses.so.5 => /lib/libncurses.so.5 (0x00000032a0e00000) <br>libdl.so.2 => /lib/libdl.so.2 (0x00000032a2400000) <br>libc.so.6 => /lib/libc.so.6 (0x00000032a2000000) <br>/lib64/ld-linux-x86-64.so.2 (0x00000032a0a00000) </pre> <p><strong>Заключение</strong></p> <p>Данный  материал  можно  использовать  как  вам  заблагорассудится,  только пожалуйста автора не забывайте ;-)</p> <p>P.S. Полный текст скрипта body.cgi</p> <pre><br><strong>#! /usr/bin/newlisp</strong> <br><span style="color: #0000ff;">;; cgi - скрипт web-сервера: body.cgi </span><br><span style="color: #0000ff;">;; Автор: kesha </span><br><span style="color: #0000ff;">; Подключаю модули</span><br><span style="color: #000000;"> (<strong>m</strong><strong>o</strong><strong>dule </strong>"cgi.lsp") <br></span><br><span style="color: #000000;">(<strong>module </strong>"mysql.lsp")</span> <br><span style="color: #0000ff;">; Инициализация и соединение с базой данных</span> <br>(<strong>MySQL:init</strong>) <br>(<strong>MySQL:connect </strong>"127.0.0.1" "wwwuser" "secret" "db_phonebook") <br><span style="color: #0000ff;">; Корректное завершение скрипта</span> <br>(<strong>define </strong>(exit-body) (<strong>MySQL:close-db</strong>) (<strong>exit</strong>)) <br><span style="color: #0000ff;">; Форматирование результата поиска в базе данных</span> <br>(<strong>define </strong>(format-result)(<strong>append</strong> <br>{<span style="color: #999999;"><table border="1" width="96%" align="center"></span>}<br> (<strong>join </strong>(<strong>map </strong>(<strong>fn </strong>(y)(<strong>append </strong>"<span style="color: #999999;"><tr></span>" <br>(<strong>j</strong><strong>oin </strong>(<strong>m</strong><strong>a</strong><strong>p </strong>(<strong>f</strong><strong>n </strong>(x)(<strong>append </strong>"<span style="color: #999999;"><td></span>"x"<span style="color: #999999;"></td></span>")) y)) <br>"<span style="color: #999999;"></tr></span>")) (<strong>MySQL</strong>:fetch-all))) <br>"<span style="color: #999999;"></table><BR></span>")) <br><span style="color: #0000ff;">; Поиск в базе данных</span> <br>(<strong>define </strong>(query-base text-query) (<strong>MySQL:query </strong>(<strong>append [text]</strong> <br><span style="color: #00ff00;">SELECT tb_user.fio,tb_user.example,tb_phone.phone,tb_phone.example </span><br><span style="color: #00ff00;">FROM tb_user,tb_phone </span><br><span style="color: #00ff00;">WHERE tb_user.id = tb_phone.user_id </span><br><span style="color: #00ff00;">AND tb_user.id IN</span> <br><strong>[</strong><strong>/</strong><strong>t</strong><strong>ext]</strong> <br>text-query <br>" <span style="color: #00ff00;">LIMIT 0,10;</span>")) <br>(<strong>i</strong><strong>f </strong>(= (<strong>MySQL:num-rows</strong>) 0) (<strong>append</strong> <br>{<span style="color: #999999;"><div align="center"> </span><br><span style="color: #999999;"><H1>По Вашему запросу результатов нет...</H1><BR> </span><br><span style="color: #999999;"></div></span>}) (format-result))) <br><span style="color: #0000ff;">; Проверка корректности параметров для поиска в базе </span><br><span style="color: #0000ff;">; и формирование требуемого подзапроса</span> <br>(<strong>define </strong>(test-value table-search text-search) (<strong>if </strong>(<strong>empty? </strong>text-search) <br>(<strong>append</strong> <br>{<span style="color: #999999;"><div align="center"><H1>Пустая строка запроса...</H1><BR> </div></span>}) (<strong>case </strong>table-search <br>("Человек" (query-base (<strong>append</strong> <br>"<span style="color: #00ff00;">(SELECT id FROM tb_user WHERE fio LIKE '</span>" text-search "<span style="color: #00ff00;">')</span>"))) ("Телефон" (query-base (<strong>append</strong> <br>"<span style="color: #00ff00;">(SELECT user_id FROM tb_phone WHERE phone LIKE '</span>" text-search "<span style="color: #00ff00;">'</span><span style="color: #00ff00;">)</span>"))) <br>(<strong>t</strong><strong>rue </strong>(<strong>append</strong> <br>{<span style="color: #999999;"><div align="center"></span>} <br>{<span style="color: #999999;"><H1>Не корректные параметры запроса...</H1><BR> </div></span>}))))) <br><span style="color: #0000ff;">; Обработка нажатия кнопки NewSearch</span> <br>(<strong>w</strong><strong>h</strong><strong>en </strong>(<strong>CGI:get </strong>"NewSearch") (<strong>set </strong>'html-body (<strong>append</strong> <br>(test-value (<strong>CGI:get </strong>"TypeSearch") (<strong>replace </strong>{*}(<strong>CGI:get </strong>"StrSearch"){%})) <br><strong>[t</strong><strong>e</strong><strong>xt]</strong> <br><span style="color: #999999;"><div align="center"> </span><br><span style="color: #999999;"><form name="RestoreForm" method="post" action="body.cgi"> </span><br><span style="color: #999999;"><input type="submit" name="Restore" value="На главную страницу"> </span><br><span style="color: #999999;"></form> </span><br><span style="color: #999999;"></div></span> <br><strong>[</strong><strong>/</strong><strong>t</strong><strong>ext]</strong>)) <br>(<strong>CGI:put-page </strong>"body.html") (exit-body)) <br><span style="color: #0000ff;">; Обработка метода new и нажатия кнопки Restore</span> <br>(<strong>w</strong><strong>h</strong><strong>en </strong>(<strong>or </strong>(= (<strong>CGI:get </strong>"method") "new") (<strong>CGI:get </strong>"Restore")) <br>(<strong>s</strong><strong>et </strong>'html-body <strong>[text]</strong> <br><span style="color: #999999;"><div align="center"> </span><br><span style="color: #999999;"><form name="StartForm" method="post" action="body.cgi"> </span><br><span style="color: #999999;"><select name="TypeSearch"> </span><br><span style="color: #999999;"><OPTION>Человек </span><br><span style="color: #999999;"><OPTION>Телефон </span><br><span style="color: #999999;"></select> </span><br><span style="color: #999999;"><input type="text" name="StrSearch" size="100"> </span><br><span style="color: #999999;"><input type="submit" name="NewSearch" value="Искать"> </span><br><span style="color: #999999;"></form> </span><br><span style="color: #999999;"></div></span> <br><strong>[</strong><strong>/</strong><strong>t</strong><strong>ext]</strong>) <br>(<strong>CGI:put-page </strong>"body.html") (exit-body)) <br><span style="color: #0000ff;">; Завершение скрипта</span> <br>(exit-body)</pre> <p></p> <table align="center" class="pagenav"> <tr> <th class="pagenav_next"> <a href="/programs-for-linux/gnome-kde/administration/53-iftop-lan-monitor.html">Следующая ></a> </th> </tr> </table> <div class="buttons">Поделится с другими </div> <script type="text/javascript"> <!-- var jcomments=new JComments(56, 'com_content','/index.php?option=com_jcomments&tmpl=component'); jcomments.setList('comments-list'); //--> </script> <div id="jc"> <div id="comments"> <h4>Комментарии<a class="rss" href="/component/jcomments/feed/com_content/56.html" title="RSS лента комментариев этой записи" target="_blank"> </a><a class="refresh" href="#" title="Обновить список комментариев" onclick="jcomments.showPage(56,'com_content',0);return false;"> </a> </h4> <div id="comments-list" class="comments-list"> <div class="even" id="comment-item-75"> <div class="rbox"> <div class="rbox_tr"> <div class="rbox_tl"> <div class="rbox_t">   </div> </div> </div> <div class="rbox_m"> <div class="comment-box usertype-guest"> <span class="comments-vote"><span id="comment-vote-holder-75"><a href="#" class="vote-good" title="Хороший комментарий!" onclick="jcomments.voteComment(75, 1);return false;"></a><a href="#" class="vote-poor" title="Плохой комментарий!" onclick="jcomments.voteComment(75, -1);return false;"></a> <span class="vote-none">0</span></span></span> <a class="comment-anchor" href="/programs-for-linux/gnome-kde/administration/56-web-newlisp-102x-cgi-mysql.html#comment-75" id="comment-75">#1</a> <span class="comment-author">guest</span> <span class="comment-date">27.08.2011 13:50</span> <div class="comment-body" id="comment-body-75">Интересно!<br> Побольше бы таких статей о newlisp. </div> <span class="comments-buttons"><a href="#" onclick="jcomments.quoteComment(75); return false;">Цитировать</a></span> </div> <div class="clear"></div> </div> <div class="rbox_br"> <div class="rbox_bl"> <div class="rbox_b">   </div> </div> </div> </div> </div> </div> <div id="comments-list-footer"> <a class="refresh" href="#" title="Обновить список комментариев" onclick="jcomments.showPage(56,'com_content',0);return false;">Обновить список комментариев</a><br> <a class="rss" href="/component/jcomments/feed/com_content/56.html" target="_blank">RSS лента комментариев этой записи</a> </div> </div> <h4>Добавить комментарий</h4> <a id="addcomments" href="#addcomments"></a> <form id="comments-form" name="comments-form" action="javascript:void(null);"> <p><input id="comments-form-name" type="text" name="name" value="" maxlength="20" size="22" tabindex="1"> <label for="comments-form-name">Имя (обязательное)</label></p> <p><input id="comments-form-email" type="text" name="email" value="" size="22" tabindex="2"> <label for="comments-form-email">E-Mail</label></p> <p><input id="comments-form-homepage" type="text" name="homepage" value="" size="22" tabindex="3"> <label for="comments-form-homepage">Сайт</label></p> <p> <textarea id="comments-form-comment" name="comment" cols="65" rows="8" tabindex="5"></textarea></p> <p><input class="checkbox" id="comments-form-subscribe" type="checkbox" name="subscribe" value="1" tabindex="5"> <label for="comments-form-subscribe">Подписаться на уведомления о новых комментариях</label><br></p> <p><br> <span class="captcha" onclick="jcomments.clear('captcha');">Обновить</span><br> <input class="captcha" id="comments-form-captcha" type="text" name="captcha-refid" value="" size="5" tabindex="6"><br></p> <div id="comments-form-buttons"> <div class="btn" id="comments-form-send"> <div> <a href="#" tabindex="7" onclick="jcomments.saveComment();return false;" title="Отправить (Ctrl+Enter)">Отправить</a> </div> </div> <div class="btn" id="comments-form-cancel" style="display:none;"> <div> <a href="#" tabindex="8" onclick="return false;" title="Отменить">Отменить</a> </div> </div> <div style="clear:both;"></div> </div> <input type="hidden" name="object_id" value="56"> <input type="hidden" name="object_group" value="com_content"> </form> <script type="text/javascript"> <!-- function JCommentsInitializeForm() { var jcEditor = new JCommentsEditor('comments-form-comment', true); jcEditor.initSmiles('https://studylinux.ru/components/com_jcomments/images/smiles'); jcEditor.addSmile(':D','laugh.gif'); jcEditor.addSmile(':lol:','lol.gif'); jcEditor.addSmile(':-)','smile.gif'); jcEditor.addSmile(';-)','wink.gif'); jcEditor.addSmile('8)','cool.gif'); jcEditor.addSmile(':-|','normal.gif'); jcEditor.addSmile(':-*','whistling.gif'); jcEditor.addSmile(':oops:','redface.gif'); jcEditor.addSmile(':sad:','sad.gif'); jcEditor.addSmile(':cry:','cry.gif'); jcEditor.addSmile(':o','surprised.gif'); jcEditor.addSmile(':-?','confused.gif'); jcEditor.addSmile(':-x','sick.gif'); jcEditor.addSmile(':eek:','shocked.gif'); jcEditor.addSmile(':zzz','sleeping.gif'); jcEditor.addSmile(':P','tongue.gif'); jcEditor.addSmile(':roll:','rolleyes.gif'); jcEditor.addSmile(':sigh:','unsure.gif'); jcEditor.addCounter(1000, 'Осталось:', ' символов', 'counter'); jcomments.setForm(new JCommentsForm('comments-form', jcEditor)); } if (window.addEventListener) {window.addEventListener('load',JCommentsInitializeForm,false);} else if (document.addEventListener){document.addEventListener('load',JCommentsInitializeForm,false);} else if (window.attachEvent){window.attachEvent('onload',JCommentsInitializeForm);} else {if (typeof window.onload=='function'){var oldload=window.onload;window.onload=function(){oldload();JCommentsInitializeForm();}} else window.onload=JCommentsInitializeForm;} //--> </script> <div id="comments-footer" align="center"> JComments </div> <script type="text/javascript"> <!-- jcomments.setAntiCache(1,0,0); //--> </script> </div> <div class="article-ratings"> <form method="post" action="https://studylinux.ru/programs-for-linux/gnome-kde/administration/56-web-newlisp-102x-cgi-mysql.html"> <span class="content_rating">Рейтинг пользователей: / 1</span><br> <span class="content_vote">Худший<input type="radio" alt="vote 1 star" name="user_rating" value="1"><input type="radio" alt="vote 2 star" name="user_rating" value="2"><input type="radio" alt="vote 3 star" name="user_rating" value="3"><input type="radio" alt="vote 4 star" name="user_rating" value="4"><input type="radio" alt="vote 5 star" name="user_rating" value="5" checked>Лучший <input class="button" type="submit" name="submit_vote" value="Рейтинг"><input type="hidden" name="task" value="vote"><input type="hidden" name="option" value="com_content"><input type="hidden" name="cid" value="56"><input type="hidden" name="url" value="https://studylinux.ru/programs-for-linux/gnome-kde/administration/56-web-newlisp-102x-cgi-mysql.html"></span> </form> </div> </div> <div class="module-bm"> <div class="module-bl"></div> <div class="module-br"></div> </div> </div> </div> </div> </div> <div class="clr"></div> </div> </div> </div> </div> <div class="col2"> <div id="leftcol"> <div class="sidecol-tm"> <div class="sidecol-tl"></div> <div class="sidecol-tr"></div> </div> <div class="sidecol-m"> <div class="sidecol-l"> <div class="sidecol-r"> <div class=""> <div class="moduletable"> <div class="module-inner"> <form action="/programs-for-linux/gnome-kde/administration.html" method="post" name="login" id="form-login"> <p id="form-login-username"><label for="modlgn_username">Логин</label><br> <input placeholder="Логин" id="modlgn_username" type="text" name="username" class="inputbox" alt="username" size="18"></p> <p id="form-login-password"><label for="modlgn_passwd">Пароль</label><br> <input placeholder="Пароль" id="modlgn_passwd" type="password" name="passwd" class="inputbox" size="18" alt="password"></p> <p id="form-login-remember"><label for="modlgn_remember">Remember Me</label><input id="modlgn_remember" type="checkbox" name="remember" value="yes" alt="Remember Me"></p> <input type="submit" name="Submit" class="button" value="Login"> <ul> <li> <a href="/component/user/reset.html">Forgot your password?</a> </li> <li> <a href="/component/user/remind.html">Forgot your username?</a> </li> <li> <a href="/component/user/register.html">Create an account</a> </li> </ul> <input type="hidden" name="task" value="login"><input type="hidden" name="option" value="com_user"><input type="hidden" name="silent" value="true"><input type="hidden" name="return" value="aHR0cDovL3N0dWR5bGludXgucnUvcHJvZ3JhbXMtZm9yLWxpbnV4L2dub21lLWtkZS9hZG1pbmlzdHJhdGlvbi81Ni13ZWItbmV3bGlzcC0xMDJ4LWNnaS1teXNxbC5odG1s"><input type="hidden" name="92873d6dfa77521940131d4381139a69" value="1"> </form> </div> </div> </div> <div class=""> <div class="moduletable"> <div class="side-style-h3"> <h3 class="module-title">Кто онлайн?</h3> </div> <div class="module-inner"> <div> <h4></h4> <ul></ul> </div> </div> </div> </div> <div class=""> <div class="moduletable"> <div class="side-style-h3"> <h3 class="module-title">Donate</h3> </div> <div class="module-inner"> Помочь проекту <a href="/component/content/article/3-about/43-donate.html" target="_blank"><img src="http://img.smskopilka.ru/common/digits/target2/58/58008-101.gif" align="center" title="Сделать пожертвование" alt="Сделать пожертвование"></a> </div> </div> </div> <div class=""> <div class="moduletable"> <div class="side-style-h3"> <h3 class="module-title">Реклама</h3> </div> <div class="module-inner">  <a href="/component/content/article/37-linux-other/61-reklama.html" target="_blank"></a> </div> </div> </div> <div class=""> <div class="moduletable"> <div class="side-style-h3"> <h3 class="module-title">Мы в twitter</h3> </div> <div class="module-inner"> <div style="text-align: center;"> </div> </div> </div> </div> <div class=""> <div class="moduletable"> <div class="side-style-h3"> <h3 class="module-title">Последние комментарии</h3> </div> <div class="module-inner"> <ul class="jclist"> <li> <span class="jcl_comment">Мало! Продолжить вплоть до KDE</span><br> By alexx</li> <li> <span class="jcl_comment">средствами proftpd как понимаю нельзя. Однако сто...</span><br> By анонимус</li> <li> <span class="jcl_comment">Я тоже решал эту задачу. Столкнулся с некоторыми п...</span><br> By Петр</li> <li> <span class="jcl_comment">ага....ну я уже разобрался) сейчас задача стоит к...</span><br> By io</li> <li> <span class="jcl_comment">Сори, написано в конце статьи. А вы точно DNS пров...</span><br> By Петр</li> <li> <span class="jcl_comment">В том, что в этой статье ничего не описано про нас...</span><br> By Петр</li> <li> <span class="jcl_comment">Можно ли привязать FTP к определённому интерфейсу,...</span><br> By remalex</li> <li> <span class="jcl_comment">Блин, сделал как в статье. Но не джобит( инет я по...</span><br> By IO</li> </ul> </div> </div> </div> <div class=""> <div class="moduletable"> <div class="module-inner"><img src="http://4.bp.blogspot.com/-tDDGbeVPiMo/TWIUxtXcqvI/AAAAAAAACrs/1UuFQ7mBH_A/s1600/xn--h1aebjvk2d.xn--p1ai_6.jpg" title="ЛинуксыРФ" alt="линуксы.рф"></div> </div> </div> <div class=""> <div class="moduletable"> <div class="side-style-h3"> <h3 class="module-title">RamblerTop100</h3> </div> <div class="module-inner">  </div> </div> </div> </div> </div> </div> <div class="sidecol-bm"> <div class="sidecol-bl"></div> <div class="sidecol-br"></div> </div> </div> </div> </div> </div> </div> </div> </div> </div> </div> </div> </div> <div class="main-bm"> <div class="main-bl"></div> <div class="main-br"></div> </div> </div> <div id="footer-bg"> <div class="wrapper"> <div id="footer"> <div class="copyright-block"> <a href="/" title="StudyLinux.ru - Linux для начинающих"></a> <div id="copyright"> <a href="/" title="StudyLinux.ru - Linux для начинающих"></a> </div> </div> </div> <div id="footer-bg2"></div> <div id="footer-bg3"></div> </div> </div> </body> </html>