Операционная система UNIX. Руководство программиста

Программа editor


Здесь подпрограммы curses используются для создания редактора текстов. Для простоты программа editor хранит буфер в stdscr; ясно, что в настоящем редакторе следовало бы иметь отдельную структуру для буфера. В этой программе есть и другие упрощения: не предусматривается редактирование файлов, содержащих больше строк, чем помещается на экране, либо со строками, длина которых больше длины строки на экране; считается, что файл не содержит управляющих символов.

Об этой программе стоит сказать следующее. Во-первых, она использует подпрограммы move(), mvaddstr(), flash(), wnoutrefresh(), clrtoeol(). Эти подпрограммы рассматривались в разделе Использование подпрограмм пакета curses.

Во-вторых, в ней применяются некоторые подпрограммы curses, о которых мы ранее не упоминали. Например, функция, осуществляющая запись файла, обращается к подпрограмме mvinch(), которая возвращает символ, находящийся в указанной позиции окна. Структура данных, используемая для записи файла, не содержит информации о длине строк и их количестве в файле, поэтому при записи уничтожаются хвостовые пробелы. Программа использует также подпрограммы insch(), delch(), insertln(), deleteln(), которые вставляют или удаляют символ или строку. Информацию об этих подпрограммах см. в curses(3X).

В-третьих, этот редактор воспринимает в качестве команд как специальные клавиши, так и символы ASCII. С одной стороны, для новичков более удобен в освоении редактор, использующий специальные клавиши. Им легче использовать стрелки для перемещения курсора, чем запомнить, что букве h соответствует движение влево, j - вниз, k - вверх, а l - вправо. С другой стороны, опытные пользователи обычно предпочитают использовать символы ASCII, так как в этом случае не нужно переносить пальцы в ту часть клавиатуры, где находятся специальные клавиши.

Примечание

Ваша curses-программа сможет работать с более широким набором терминалов, если в ней с каждой специальной клавишей будет связан символ ASCII, поскольку не на каждом терминале клавиатура включает, например, стрелки.


В-четвертых, команда CTRL+ L выполняет функцию, которую должны иметь большинство curses-программ. Случается так, что другие программы выводят что-либо на экран (например, при передаче сообщений), либо экран портится в результате сбоев оборудования. В этом случае пользователь может ввести CTRL+L, в результате чего экран очищается и заполняется вновь посредством

wrefresh (curscr)

Наконец, еще одной важной особенностью является то, что вводимые команды заканчиваются нажатием CTRL+D, а не ESC. Было бы соблазнительно использовать ESC, так как эта клавиша является одной из немногих, присутствующих на любой клавиатуре (наряду с RETURN и BREAK). Однако, это может привести к возникновению двусмысленности. Большинство терминалов использует последовательности символов, начинающиеся с ESC для управления терминалом и имеют клавиши, при нажатии на которые передаются такие последовательности. Если программа получает с клавиатуры ESC, она не может различить, нажал ли пользователь ESC или другую функциональную клавишу.

Editor и другие использующие curses программы решают эту проблему, устанавливая таймер. Если за некоторый интервал времени приходит еще символ и он может быть началом последовательности, которую генерирует функциональная клавиша, программа пытается читать последующие символы до тех пор, пока не будет считана вся последовательность для функциональной клавиши, либо символ, который не может быть частью такой последовательности, либо пока не истечет определенное время. Эта стратегия хороша, но не абсолютно надежна. Пользователь может нажать ESC и сразу за ней еще клавишу, и тогда curses-программа решит, что была нажата функциональная клавиша. Кроме того, ESC не сразу передается обрабатывающей программе, в результате чего последняя медленнее реагирует на нажатие этой клавиши.

Многие существующие ныне программы используют ESC в качестве одной из основных своих команд, и это положение нельзя изменить, не вызвав недовольства большого количества пользователей. Чтобы использовать функциональную клавиатуру, эти программы должны решать описанную выше проблему двусмысленности - в лучшем случае путем использования таймера. Мораль всего этого ясна: избегайте использования клавиши ESC в Ваших программах.





/* Программа editor - экранный редактор. Пользовательский интерфейс подобен подмножеству vi. Для простоты буфер хранится в stdscr */

#include <curses.h>

#define CTRL(c) ((c) & 037)

main (argc, argv) int argc; char **argv; { extern void perror(), exit(); int i, n, l; int c; int line = 0; FILE *fd;

if (argc != 2) { fprintf (stderr, "usage: %s file\n", argv [0]); exit (1); }

fd = fopen (argv [1], "r"); if (fd == NULL) { perror (argv [1]); exit (2); }

initscr (); cbreak (); nonl (); noecho (); idlok (stdscr, TRUE); keypad (stdscr, TRUE);

/* Читаем файл */ while ((c = getc(fd)) != EOF) { if (c == '\n') line++; if (line > LINES - 2) break; addch(c); } fclose (fd);

move (0, 0); refresh (); edit ();

/* Записываем файл */ fd = fopen (argv [1], "w"); for (l = 0; l < LINES - 1; l++) { n = len(l); for (i = 0; i < n; i++) putc (mvinch (l, i) & A_CHARTEXT, fd); putc('\n', fd); } fclose(fd);

endwin (); exit (0); }

len (lineno) int lineno; { int linelen = COLS - 1;

while (linelen >= 0 && mvinch (lineno, linelen) == ' ') linelen--; return linelen + 1; }

/* Глобальное значение текущего положения курсора */ int row, col;

edit () { int c;

for (;;) { move (row, col); refresh (); c = getch ();

/* Команды редактора */ switch (c) {

/* hjkl и стрелки: перемещают курсор в указанном направлении */ case 'h': case KEY_LEFT: if (col > 0) col--; else flash (); break;

case 'j': case KEY_DOWN: if (row < LINES - 1) row++; else flash (); break;

case 'k': case KEY_UP: if (row > 0) row--; else flash (); break;

case 'l': case KEY_RIGHT: if (col < COLS - 1) col++; else flash (); break;

/* i: переход в режим ввода */ case KEY_IC: case 'i': input (); break;

/* x: удалить текущий символ */ case KEY_DC: case 'x': delch (); break;

/* o: вставить строку и перейти в режим ввода */ case KEY_IL: case 'o': move (++row, col = 0); insertln (); input (); break;

/* d: удалить текущую строку */ case KEY_DL: case 'd': deleteln (); break;



/* CTRL+L: перерисовать экран */ case KEY_CLEAR: case CTRL('L'): wrefresh (curscr); break;

/* w: записать и закончить работу */ case 'w': return;

/* q: закончить работу без записи файла */ case 'q': endwin (); exit (2);

default: flash (); break; } } }

/* Режим ввода: принимает и вставляет символы Выход: CTRL+D или EIC */ input () { int c;

standout (); mvaddstr (LINES - 1, COLS - 20, "Режим ввода"); standend (); move (row, col); refresh (); for (;;) { c = getch (); if (c == CTRL('D') c == KEY_EIC) break; insch (c); move (row, ++col); refresh (); } move (LINES - 1, COLS - 20); clrtoeol (); move (row, col); refresh (); }




Содержание раздела