Уголок линуксоида: программируем библиотеку пользовательских команд
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 редактор
Не пропустите интересное!
Підписывайтесь на наши каналы и читайте анонсы хай-тек новостей, тестов и обзоров в удобном формате!
Обзор Bluetooth-наушников Ugreen Choice H6 Pro: удивительные
Несмотря на бюджетность наушники Ugreen Choice H6 Pro получили активное шумоподавление, пространственное звучание и поддержку кодека LDAC. Расскажем детальнее
Шлем-гарнитура 7Sense SuperBrain 1 позволяет слепым почувствовать окружение, превращая изображение в тактильные сигналы
медицина разработка стартапЭстонский стартап 7Sense представил инновационное устройство под названием SuperBrain 1 — телехаптический шлем, который переводит визуальную информацию в тактильные ощущения
Игру Detroit: Become Human за 6 лет купили 10 миллионов раз
игры статистикаDetroit: Become Human, выпущенная в 2018 году студией Quantic Dream, достигла значительного успеха, разойдясь тиражом в 10 миллионов копий за шесть лет