Программирование Cи / Богатырев_Язык Си в системе Unix
.pdfА. Богатырёв, 1992-96 |
- 318 - |
|
|
|
|
Си в UNIX™ |
||
/* -------- |
Работа со временем ------------------------ */ |
|
|
|||||
void printTime () { |
|
|
|
|
|
|
|
|
time_t |
t; |
/* текущее время */ |
|
|
|
|||
struct |
tm *tm; |
|
|
|
|
|
|
|
extern |
struct tm *localtime (); |
|
|
|
|
|
||
char |
tmbuf[30]; |
|
|
|
|
|
|
|
static |
char *week[7] = { "Вс", |
"Пн", |
"Вт", |
"Ср", |
"Чт", |
"Пт", "Сб" }; |
||
static |
char *month[12] = { "Янв", |
"Фев", |
"Мар", |
"Апр", |
"Май", |
"Июн", |
|
|
|
|
"Июл", "Авг", "Сен", "Окт", |
"Ноя", |
"Дек" |
}; |
|||
time (&t); |
/* узнать текущее время */ |
|
|
|||||
tm = localtime (&t); |
/* разложить его на компоненты */ |
|
||||||
sprintf (tmbuf, "%2s %02d:%02d:%02d %02d-%3s-%d", |
|
|
|
|||||
|
week[tm -> tm_wday], |
/* день недели (0..6) |
*/ |
|
|
|||
|
tm -> tm_hour, |
/* часы |
(0..23) |
|
*/ |
|
|
|
|
tm -> tm_min , |
/* минуты (0..59) |
|
*/ |
|
|
||
|
tm -> tm_sec , |
/* секунды (0..59) |
*/ |
|
|
|||
|
tm -> tm_mday, |
/* число месяца (1..31) */ |
|
|
||||
|
month[tm -> tm_mon], |
/* месяц (0..11) |
|
*/ |
|
|
||
|
tm -> tm_year + 1900 |
/* год |
|
|
*/ |
|
|
|
); |
|
|
|
|
|
|
|
|
gotoXY |
(COLS / 2, TIMELINE); |
|
|
|
|
|
|
|
clearEOL (); |
|
|
|
|
|
|
|
|
gotoXY |
(COLS - strlen (tmbuf) - 1, TIMELINE); |
|
|
|
|
|||
bold (); |
|
|
|
|
|
|
|
|
printf |
("%s", tmbuf); |
|
|
|
|
|
|
|
normal |
(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
8.14. Напишите программу, выдающую файл на экран порциями по 20 строк и ожидающую нажатия клавиши. Усложнения:
a)добавить клавишу для возврата к началу файла.
b)используя библиотеку termcap, очищать экран перед выдачей очередной порции текста.
c)напишите эту программу, используя библиотеку curses.
d)используя curses, напишите программу параллельного просмотра 2-х файлов в 2-х неперекрывающихся окнах.
e)то же в перекрывающихся окнах.
8.15.Напишите функции включения и выключения режима эхо-отображения набираемых на клавиатуре символов (ECHO).
8.16.То же про "режим немедленного ввода" (CBREAK). В обычном режиме строка, набранная на клавиатуре, сначала попадает в некоторый буфер в драйвере терминала†.
"Сырая" |
|
"Каноническая" |
|
|
клавиатура--->ОчередьВвода-- |
*-- |
>ОчередьВвода-- |
>read |
|
|
| |
|
файл-устройство |
|
драйвер терминала |
V эхо |
/dev/tty?? |
||
|
| |
|
|
|
экран<---ОчередьВывода---<-- |
*-- |
<----------- |
<--- |
write |
Этот буфер используется для предчтения - вы можете набирать текст на клавиатуре еще до того, как программа запросит его read-ом: этот набранный текст сохранится в буфере и при поступлении запроса будет выдан из буфера. Также, в каноническом режиме ICANON, буфер ввода используется для редактирования
† Такие буфера носят название "character lists" - clist. Существуют "сырой" (raw) clist, в который попадают ВСЕ символы, вводимые с клавиатуры; и "канонический" clist, в котором хранится отредактированная строка - обработаны забой, отмена строки. Сами специальные символы (редактирования и генерации сигналов) в каноническую очередь не попадают (в режиме ICANON).
А. Богатырёв, 1992-96 |
- 319 - |
Си в UNIX™ |
введенной строки: забой отменяет последний набранный символ, CTRL/U отменяет всю набранную строку; а также он используется для выполнения некоторых преобразований символов на вводе и выводе‡.
Введенная строка попадает в программу (которая запросила данные с клавиатуры при помощи read, gets, putchar) только после того, как вы нажмете кнопку <ENTER> с кодом '\n'. До этого вводимые символы накапливаются в буфере, но в программу не передаются - программа тем временем "спит" в вызове read. Как только будет нажат символ '\n', он сам поступит в буфер, а программа будет разбужена и сможет наконец прочесть из буфера ввода набранный текст.
Для меню, редакторов и других "экранных" программ этот режим неудобен: пришлось бы слишком часто нажимать <ENTER>. В режиме CBREAK нажатая буква немедленно попадает в вашу программу (без ожидания нажатия '\n'). В данном случае буфер драйвера используется только для предчтения, но не для редактирования вводимого текста. Редактирование возлагается на вас - предусмотрите его в своей программе сами!
Заметьте, что код кнопки <ENTER> ("конец ввода") - '\n' - не только "проталкивает" текст в программу, но и сам попадает в буфер драйвера, а затем в вашу программу. Не забывайте его как-то обрабатывать.
В MS DOS функция чтения кнопки в режиме ~ECHO+CBREAK называется getch(). В UNIX аналогично ей будет работать обычный getchar(), если перед его использованием установить нужные режимы драйвера tty вызовом ioctl. По окончании программы режим драйвера надо восстановить (за вас это никто не сделает). Также следует восстанавливать режим драйвера при аварийном завершении программы (по любому сигналу††).
Очереди ввода и вывода используются также для синхронизации скорости работы программы (скажем, скорости наполнения буфера вывода символами, поступающими из программы через вызовы write) и скорости работы устройства (с которой драйвер выбирает символы с другого конца очереди и выдает их на экран); а также для преобразований символов на вводе и выводе. Пример управления всеми режимами есть в приложении.
8.17. Функциональные клавиши большинства дисплеев посылают в линию не один, а несколько символов. Например на терминалах, работающих в системе команд стандарта ANSI, кнопки со стрелками посылают такие последовательности:
стрелка вверх "\033[A" кнопка Home "\033[H" стрелка вниз "\033[B" кнопка End "\033[F" стрелка вправо "\033[C" кнопка PgUp "\033[I" стрелка влево "\033[D" кнопка PgDn "\033[G"
(поскольку первым символом управляющих последовательностей обычно является символ '\033' (escape), то их называют еще escape-последовательностями). Нам же в программе удобно воспринимать такую последовательность как единственный код с целым значением большим 0xFF. Склейка последовательностей символов, поступающих от функциональных клавиш, в такой внутренний код - также задача экранной библиотеки (учет системы команд дисплея на вводе).
Самым интересным является то, что одиночный символ '\033' тоже может прийти с клавиатуры - его посылает клавиша Esc. Поэтому если мы строим распознаватель клавиш, который при поступлении кода 033 начинает ожидать составную последовательность - мы должны выставлять таймаут, например alarm(1); и если по его истечении больше никаких символов не поступило - выдавать код 033 как код клавиши Esc.
Напишите распознаватель кодов, поступающих с клавиатуры. Коды обычных букв выдавать как есть (0..0377), коды функциональных клавиш выдавать как числа >= 0400. Учтите, что разные типы дисплеев посылают разные последовательности от одних и тех же функциональных клавиш: предусмотрите настройку на систему команд ДАННОГО дисплея при помощи библиотеки termcap. Распознаватель удобно строить при помощи сравнения поступающих символов с ветвями дерева (спускаясь по нужной ветви дерева при поступлении очередного символа. Как только достигли листа дерева - возвращаем код, приписанный этому листу):
‡ Режимы преобразований, символы редактирования, и.т.п. управляются системным вызовом ioctl. Большой пример на эту тему есть в приложении.
†† Если ваша программа завершилась аварийно и моды терминала остались в "странном" состоянии, то привести терминал в чувство можно командой stty sane
А. Богатырёв, 1992-96 |
|
|
|
- 320 - |
Си в UNIX™ |
---> '\033' --- |
> '[' |
--- |
> 'A' -- |
> выдать 0400 |
|
| |
|
\ |
--> 'B' -- |
> |
0401 |
| |
|
\ |
--> 'C' -- |
> |
0402 |
| |
|
\ |
--> 'D' -- |
> |
0403 |
\-- |
> 'X' |
----------- |
|
> |
0404 |
|
|
|
... |
|
|
Нужное дерево стройте при настройке на систему команд данного дисплея.
Библиотека curses уже имеет такой встроенный распознаватель. Чтобы составные последовательности склеивались в специальные коды, вы должны установить режим keypad:
int c; WINDOW *window;
...
keypad(window, TRUE);
...
c = wgetch(window);
Без этого wgetch() считывает все символы поодиночке. Символические названия кодов для функциональных клавиш перечислены в <curses.h> и имеют вид KEY_LEFT, KEY_RIGHT и.т.п. Если вы работаете с единственным окном размером с весь экран, то в качестве параметра window вы должны использовать стандартное окно stdscr (это имя предопределено в include-файле curses.h).
# ======================================== Makefile для getch getch: getch.o
cc getch.o -o getch -ltermlib
getch.o: getch.c getch.h
cc -g -DUSG -c getch.c
/* Разбор составных последовательностей клавиш с клавиатуры. */ /* ================================================== getch.h */
#define |
FALSE |
0 |
#define |
TRUE |
1 |
#define BOOLEAN unsigned char
#define INPUT_CHANNEL |
0 |
#define OUTPUT_CHANNEL |
1 |
#define KEY_DOWN |
0400 |
#define KEY_UP |
0401 |
#define KEY_LEFT |
0402 |
#define KEY_RIGHT |
0403 |
#define KEY_PGDN |
0404 |
#define KEY_PGUP |
0405 |
#define KEY_HOME |
0406 |
#define KEY_END |
0407 |
#define KEY_BACKSPACE |
0410 |
#define KEY_BACKTAB |
0411 |
#define KEY_DC |
0412 |
#define KEY_IC |
0413 |
#define KEY_DL |
0414 |
#define KEY_IL |
0415 |
#define KEY_F(n) |
(0416+n) |
#define ESC |
' 33' |