Уголок линуксоида: программируем библиотеку пользовательских команд
29.08.08Мой Компьютер, №8, 14.02.2008
Благодаря рассматриваемой библиотеке, можно в некоторой мере автоматизировать рутинную работу, связанную с вводом команд пользователя. Но обработка ввода — это далеко не главная и совсем не единственная особенность библиотеки. Она позволяет гибко настраивать программу, беря на себя практически всю заботу о сборе данных от пользователя. Программисту остается только получить эти данные с помощью средств библиотеки и соответствующим образом изменить поведение программы. Да, и не лишним будет знать просто о том, что есть такая библиотека для программирования в консоли — хотя бы чтоб поддержать светскую беседу :-). Сразу отмечу, что дополнительную информацию про эту библиотеку можно получить с помощью man readline, info readline, да и Гугл никто не отменял :-), так что в статье я буду использовать только основные сведения. Если все же останутся вопросы, мыльте, чем смогу — помогу.
Итак, предлагаю заняться разработкой (громко звучит :-)) консольного приложения. Для начала сделаем «заготовку» программы — никакой полезной работы она выполнять не будет, но все недостающее легко будет добавить самостоятельно. Кстати, писать будем на С/С++ (при использовании библиотеки разница не принципиальна, отличаться будет только компилятор: g++ — для «плюсов» и gcc — для С, но все-таки предпочтение отдаю плюсам, тем более что я уже привык к потоковому вводу/выводу).
(Примечание автора: хотел бы извиниться за столь подробное изложение казалось бы простых моментов, но статья учитывает возможности начинающих пользователей — «паверъюзеры» наверняка знакомы с этой библиотекой, а если и не знакомы, то разобраться в ней самостоятельно не составит особых усилий.)
С самого начала предлагаю установить библиотеку readline для разработки (те, у кого она установлена, естественно, могут пропустить этот шаг).
Для моего Кubuntu 7.04 (feisty) хватило следующих команд:
apt-get install libncurses5-dev libreadline5-dev
или
sudo apt-get install libncurses5-dev libreadline5-dev
(если вы не суперпользователь).
Скачивается, если я не ошибаюсь, порядка 1 Мб, что сегодня не требует сверхусилий, на мой взгляд.
Итак, что же такое readline, и с чем его едят. Для начала спросим официальную справку (man readline). Оттуда узнаем, что для того, чтобы пользоваться библиотекой, необходимо подключить заголовочный файл:
#include <readline/readline.h>
(Указанный следом #include <readline/history.h> — для поддержки истории в вашей программе. Тут все совсем просто, смотрите справку. Но если останутся вопросы — пишите.)
Оттуда же узнаем и об основных недостатках библиотеки: в разделе «BUGS» читаем, что «It’s too big and too slow» :-).
Как видим дальше, возможностей довольно много, но сегодня нас будет интересовать лишь одноименная функция, прототип которой описывается следующим образом:
char *readline(const char *prompt)
Что же, собственно, делает эта функция? Она всего-навсего возвращает указатель на строку, введенную пользователем с stdin (входной поток, в большинстве случаев — клавиатура; но ничего не помешает вам подать на вход вашей программы файл с командами — достаточно использовать перенаправление вывода, но это я уже отвлекся :-)) после приглашения prompt. Следует отметить, что выделение памяти происходит внутри функции, но ее освобождение после завершения работы — на совести программиста.
Но вернемся к функции. На первый взгляд, все слишком просто, но давайте не будем забывать об ознакомительном характере нашей программы. Если хотите взглянуть на документацию (пропишите сами в поисковике что-то вроде «GNU Readline Library»), то там есть пример. Немного упрощенную его версию я сейчас и приведу в качестве демонстрации работы этой библиотеки, а затем прокомментирую основные моменты.
(Для набора исходного текста программы я лично использую joe, но, как говорится, «на вкус и цвет…»).
#include <iostream>
using namespace std;
#include <readline/readline.h> //подключаем нашу библиотеку
//здесь — наши функции, вызываемые по определенной команде
int action() { cout<<«Some action.»<<endl; }
int help() { cout<<«[HELP] Commands: action, help, about, quit»<<endl; }
int about() { cout<<«[ABOUT] Readline library example»<<endl; }
int quit() { exit(0); }
//описание структуры «команда»: имя команды, указатель на функцию-обработчик и дополнительное поле (его можно использовать для справочной информации о функции, например; в нашей программе не используется)
typedef struct
{
char *name;
rl_hook_func_t *func;
char *doc;
} COMMAND;
//здесь объявляем массив команд — собственно, множество команд, распознаваемых программой
COMMAND commands[]={
{ «action», action, «action description» },
{ «help», help, «help description» },
{ «about», about, «about description» },
{ «quit», quit, «just quit» },
{ (char*)NULL, (rl_hook_func_t*)NULL, (char*)NULL }
};
//прототип функции, отвечающей за распознавание и обработку введенной команды
COMMAND *getcommand(char*);
//выполнение программы начинается с main
int main(int argc, char **argv)
{
char *line;
//указатель на массив символов — буфер для информации, введенной пользователем
cout<<«Readline library example by SM-pro»<<endl;
while (1) //работаем, пока не получим команду «выход»
{
line = readline(«[readline example]$ «);
//получаем информацию от пользователя
COMMAND *command;
command = getcommand(line); //распознаем команду
if ( command )
command->func(); //вызываем соответствующий обработчик…
else
cout<<«[ERROR] No such command!»<<endl; //…или сообщаем, что команда не распознана — ошибка ввода
free(line); //освобождение памяти, выделенной под буфер для входных данных от пользователя
}
return 0;
}
//здесь — распознавание и обработка команды
COMMAND *getcommand(char* name)
{
//проверяем все обрабатываемые команды на предмет совпадения имени команды с введенной пользователем строкой
for (int i=0; commands[i].name; i++)
//если находим совпадение — возвращаем указатель на элемент массива команд
if ( !strcmp(name, commands[i].name) )
return ( &commands[i] );
//если совпадение не обнаружено — возвращаем нулевой указатель
return ( (COMMAND*)NULL );
}
Итак, что же собственно делает эта нехитрая программа и как? В начале указываем препроцессору, что надо включить заголовочный файл readline.h, который лежит в каталоге readline. Потом мы объявляем некоторый набор функций — это обработчики команд. На этих строках, правда, они еще просто глобальные функции, но все еще впереди. Дальше мы определяем структуру — тип нашей команды. Она состоит из трех полей: первое поле — собственно имя команды, при обработке которой будет вызываться функция, адрес которой задан во втором поле. И третье поле — некоторое описание команды (в нашей программе нигде не используется). А про второе поле следует сказать еще пару слов. Дело в том, что в используемой библиотеке (точнее, в подключенном заголовочном файле) определен некоторый набор типов — указателей на функции, и мы из этого набора используем только тип rl_hook_func_t, что соответствует int (*)(). Подробнее о наборе этих типов можно узнать из документации (ну, к примеру, rl_command_func_t соответствует int (*)(int, int) и т.д.).
На этом и заканчивается объявление набора команд, обрабатываемых нашей программой. Далее идет прототип функции, описание которой находится в конце листинга. В функции getcommand мы ищем совпадение между введенной строкой и именами наших команд (именно команд, а не функций-обработчиков, просто в данном примере они совпадают), и если находим, то возвращаем указатель на введенную команду (указатель на структуру с описанием данной команды). И если не находим — возвращаем ноль (для этого случая и нужен последний элемент из массива структур commands). Потом освобождаем память, выделенную под указатель line. Дальше идет функция main, где собственно и происходит все это безобразие :-). Здесь мы спрашиваем у пользователя команду до тех пор, пока не встретим команду выхода, а если введенная команда не найдена, сообщаем об этом. Не стоит также забывать про освобождение памяти, которая выделяется для хранения строки — это, как уже упоминалось, следует делать самостоятельно.
Теперь сохраняем это в файл, например, read.h и компилируем следующим образом:
g++ read.cpp -l readline -o read
Где -l readline и есть подключение нужной нам библиотеки. Теперь запускаем:
./read
И любуемся результатом.
Вот, собственно, и все, о чем я хотел рассказать. Кому-то может показаться, что это все слишком просто…
Так и есть :-), но напомню, что это лишь демонстрация того, что можно делать в консоли. Как видите, здесь тоже есть готовые решения — не нужно каждый раз изобретать велосипед.
Удачи!
Сергей «sm-pro» МЕЛЬНИЧУК
Web-droid редактор
Не пропустите интересное!
Підписывайтесь на наши каналы и читайте анонсы хай-тек новостей, тестов и обзоров в удобном формате!
Обзор ноутбука ASUS Vivobook S 15 с процессором Qualcomm: перспективный?
Шасси ноутбуков ASUS Vivobook получилось настолько удачным что с ним можно встретить модели самого разного уровня. Неудивительно что Вивобук стал и платформой для обкатки процессоров от производителя мобильных чипов – компании Qualcomm
Технология Audio Cu от Fasetto для передачи звука по электросети получит сертификацию Dolby Atmos
аудиоТехнология Audio Cu от Fasetto поддерживает конфигурации аудиосистем от 2.0 до 9.1, включая Dolby Atmos до 7.1.2, что позволяет добиться полноценного объемного звучания
Возврат посылок Нова пошта станет бесплатным
сервис события в УкраинеИзменения особенно выгодны для клиентов, которые занимаются коммерческими отправками, так как теперь их расходы на возврат товаров, которые не были забраны, снизятся