"C++" |-,
обладающим средствами для объектно-ориентированного программирования и родовых
____________________
|- C++ как и C разработан в AT&T; произносится "Си плас-плас"
А. Богатырев, 1992-95 - 395 - Си в UNIX
классов. Существуют также расширения стандартного "C" объектно-ориентированными воз-
можностями ("Objective-C"). Большой простор предоставляет также сборка программ из
частей, написанных на разных языках программирования (например, "C", "Pascal", "Pro-
log").
____________________
|=|=|= Автор благодарит авторов программ и книг по Си и UNIX, по которым некогда
учился я сам; коллег из ИПК Минавтопрома/Демоса; программистов из сетей Usenet и Rel-
com, давших материалы для задач и рассуждений; слушателей курсов по Си за многочис-
ленный материал для книги.
А.Богатырев.
Ученью не один мы посвятили год,
Потом других учить пришел и нам черед.
Какие ж выводы из этой всей науки?
Из праха мы пришли, нас ветер унесет.
Омар Хайям
Оглавление.
0. Напутствие в качестве вступления. .......................................... 1
1. Простые программы и алгоритмы. Сюрпризы, советы. ........................... 3
2. Массивы, строки, указатели. ................................................ 81
3. Мобильность и машинная зависимость программ. Проблемы с русскими буквами.
........................................................................... 122
4. Работа с файлами. .......................................................... 137
5. Структуры данных. .......................................................... 172
6. Системные вызовы и взаимодействие с UNIX. .................................. 186
6.1. Файлы и каталоги. ........................................................ 189
6.2. Время в UNIX. ............................................................ 198
6.3. Свободное место на диске. ................................................ 207
6.4. Сигналы. ................................................................. 212
6.5. Жизнь процессов. ......................................................... 219
6.6. Трубы и FIFO-файлы. ...................................................... 230
6.7. Нелокальный переход. ..................................................... 233
6.8. Хозяин файла, процесса, и проверка привелегий. ........................... 235
6.9. Блокировка доступа к файлам. ............................................. 240
6.10. Файлы устройств. ........................................................ 244
6.11. Мультиплексирование ввода-вывода ......................................... 259
6.12. Простой интерпретатор команд. ........................................... 271
7. Текстовая обработка. ....................................................... 283
8. Экранные библиотеки и работа с видеопамятью. ............................... 348
9. Приложения. ................................................................ 391
9.1. Таблица приоритетов операций языка C++ .................................... 391
9.2. Правила преобразований типов. ............................................ 391
9.3. Таблица шестнадцатеричных чисел (HEX). ................................... 392
9.4. Таблица степеней двойки. ................................................. 393
9.5. Двоичный код: внутреннее представление целых чисел. ...................... 393
10. Примеры. .................................................................. 394
Пример 1. Размен монет. ......................................................
Пример 2. Подсчет букв в файле. ..............................................
Пример 3. Центрирование строк. ...............................................
Пример 4. Разметка текста для nroff. .........................................
Пример 5. Инвертирование порядка слов в строках. .............................
Пример 6. Пузырьковая сортировка. ............................................
Пример 7. Хэш-таблица. .......................................................
Пример 8. Простая база данных. ...............................................
Пример 9. Вставка/удаление строк в файл. .....................................
Пример 10. Безопасный free, позволяющий обращения к автоматическим перемен-
ным. .....................................................................
Пример 11. Поимка ошибок при работе с динамической памятью. ..................
Пример 12. Копирование/перемещение файла. ....................................
Пример 13. Обход поддерева каталогов в MS DOS при помощи chdir. ..............
Пример 14. Работа с сигналами. ...............................................
Пример 15. Управление скоростью обмена через линию. ..........................
Пример 16. Просмотр файлов в окнах. ..........................................
Пример 17. Работа с иерархией окон в curses. Часть проекта uxcom. ............
Пример 18. Поддержка содержимого каталога. Часть проекта uxcom. ..............
Пример 19. Роллируемое меню. Часть проекта uxcom. ............................
Пример 20. Выбор в строке-меню. Часть проекта uxcom. .........................
Пример 21. Редактор строки. Часть проекта uxcom. .............................
Пример 22. Выбор в прямоугольной таблице. Часть проекта uxcom. ...............
Пример 23. UNIX commander - простой визуальный Шелл. Головной модуль проекта
uxcom. ...................................................................
Пример 24. Общение двух процессов через "трубу". .............................
Пример 25. Общение процессов через FIFO-файл. ................................
Пример 26. Общение процессов через общую память и семафоры. ..................
Пример 27. Протоколирование работы программы при помощи псевдотерминала и
процессов. ...............................................................
Пример 28. Оценка фрагментированности файловой системы. ......................
Пример 29. Восстановление удаленного файла в BSD-2.9. ........................
Пример 30. Копирование файлов из MS DOS в UNIX. ..............................
Пример 31. Программа, печатающая свой собственный текст. .....................
Пример 32. Форматирование текста Си-программы. ...............................
1.11. Треугольник из звездочек. ............................................... 6
1.34. Простые числа. .......................................................... 10
1.36. Целочисленный квадратный корень. ........................................ 12
1.39. Вычисление интеграла по Симпсону. ....................................... 14
1.49. Сортировка Шелла. ....................................................... 20
1.50. Быстрая сортировка. ..................................................... 21
1.67. Функция чтения строки. .................................................. 28
1.88. Перестановки элементов. ................................................. 38
1.117. Схема Горнера. ......................................................... 58
1.137. Системная функция qsort - формат вызова. ............................... 67
1.146. Процесс компиляции программ. ........................................... 76
2.58. Функция bcopy. .......................................................... 108
2.59. Функция strdup. ......................................................... 111
2.61. Упрощенный аналог функции printf. ....................................... 112
3.9. _ctype[] .................................................................. 126
3.12. Программа транслитерации: tr. ........................................... 129
3.16. Функция записи трассировки (отладочных выдач) в файл. ................... 132
3.18. Условная компиляция: #ifdef .............................................. 132
4.39. Быстрый доступ к строкам файла. ......................................... 161
4.45. Эмуляция основ библиотеки STDIO, по мотивам 4.2 BSD. .................... 165
5.12. Отсортированный список слов. ............................................ 180
5.16. Структуры с полями переменного размера. ................................. 183
5.17. Список со "старением". .................................................. 184
6.1.1. Определение типа файла. ................................................ 189
6.1.3. Выдача неотсортированного содержимого каталога (ls). ................... 191
6.1.5. Рекурсивный обход каталогов и подкаталогов. ............................ 192
6.2.9. Функция задержки в микросекундах. ...................................... 201
6.4.3. Функция sleep. ......................................................... 217
6.10.1. Определение текущего каталога: функция getwd. ......................... 252
6.10.2. Канонизация полного имени файла. ...................................... 257
6.11.1. Мультиплексирование ввода из нескольких файлов. ....................... 259
6.11.2. Программа script. ..................................................... 261
7.12. Программа uniq. ......................................................... 285
7.14. Расширение табуляций в пробелы, функция untab. .......................... 285
7.15. Функция tabify. ......................................................... 285
7.25. Поиск методом половинного деления. ...................................... 288
7.31. Программа печати в две полосы. .......................................... 292
7.33. Инвертирование порядка строк в файле. ................................... 296
7.34. Перенос неразбиваемых блоков текста. .................................... 298
7.36. Двоичная сортировка строк при помощи дерева. ............................ 300
7.41. Функция match. .......................................................... 309
7.43. Функция контекстной замены по регулярному выражению. .................... 313
7.44. Алгоритм быстрого поиска подстроки в строке. ............................ 316
7.52. Коррекция правописания. ................................................. 321
7.67. Калькулятор-1. .......................................................... 330
7.68. Калькулятор-2. .......................................................... 336
8.1. Осыпающиеся буквы. ....................................................... 350
8.13. Использование библиотеки termcap. ....................................... 359
8.17. Разбор ESC-последовательностей с клавиатуры. ............................ 371
11. Список литературы.
1) Б.Керниган, Д.Ритчи, А.Фьюер. Язык программирования Си. Задачи по языку Си. -
М.: Финансы и статистика, 1985.
2) М.Уэйт, С.Прата, Д.Мартин. Язык Си. Руководство для начинающих. - М.: Мир,
1988.
3) М.Болски. Язык программирования Си. Справочник. - М.: Радио и связь, 1988.
4) Л.Хэнкок, М.Кригер. Введение в программирование на языке Си. - М.: Радио и
связь, 1986.
5) М.Дансмур, Г.Дейвис. ОС UNIX и программирование на языке Си. - М.: Радио и
связь, 1989.
6) Р.Берри, Б.Микинз. Язык Си. Введение для программистов. - М.: Финансы и ста-
тистика, 1988.
7) М.Беляков, А.Ливеровский, В.Семик, В.Шяудкулис. Инструментальная мобильная опе-
рационная система ИНМОС. - М.: Финансы и статистика, 1985.
8) К.Кристиан. Введение в операционную систему UNIX. - М.: Финансы и статистика,
1985.
9) Р.Готье. Руководство по операционной системе UNIX. - М.: Финансы и статистика,
1986.
10) М.Банахан, Э.Раттер. Введение в операционную систему UNIX. - М.: Радио и
связь, 1986.
11) С.Баурн. Операционная система UNIX. - М.: Мир, 1986.
12) П.Браун. Введение в операционную систему UNIX. - М.: Мир, 1987.
13) M.Bach. The design of the UNIX operating system. - Prentice Hall, Englewood
Cliffs, N.J., 1986.
14) S.Dewhurst, K.Stark. Programming in C++. - Prentice Hall, 1989.
15) M.Ellis, B.Stroustrup. The annotated C++ Reference Manual. - Addison-Wesley,
1990.
/* Пример 1 */
/* Задача о размене монеты:
* Поиск всех возможных коэффициентов a0 .. an разложения числа S
* в виде
* S = a0 * c0 + a1 * c1 + ... + an * cn
* где веса c0 .. cn заданы заранее и упорядочены.
* Веса и коэффициенты неотрицательны (ai >= 0, ci >= 0).
*/
#include <stdio.h>
/* Достоинства разменных монет (веса ci) */
int cost[] = {
1, 2, 3, 5, 10, 15, 20, 50, 100, 300, 500 /* копеек */
};
#define N (sizeof cost / sizeof(int))
int count[ N ]; /* число монет данного типа (коэффициенты ai) */
long nvar; /* число вариантов */
main( ac, av ) char *av[];
{
int coin;
if( ac == 1 ){
fprintf( stderr, "Укажите, какую монету разменивать: %s число\n",
av[0] );
exit(1);
}
coin = atoi( av[1] );
printf( " Таблица разменов монеты %d коп.\n", coin );
printf( " Каждый столбец содержит количество монет указанного достоинства.\n" );
printf( "-------------------------------------------------------------------\n" );
printf( "| 5р. | 3р. | 1р. | 50к.| 20к.| 15к.| 10к.| 5к.| 3к.| 2к.| 1к.|\n" );
printf( "-------------------------------------------------------------------\n" );
change( N-1, coin );
printf( "-------------------------------------------------------------------\n" );
printf( "Всего %ld вариантов\n", nvar );
}
/* рекурсивный размен */
change( maxcoin, sum )
int sum; /* монета, которую меняем */
int maxcoin; /* индекс по массиву cost[] монеты максимального
* достоинства, допустимой в данном размене.
*/
{
register i;
if( sum == 0 ){ /* вся сумма разменяна */
/* распечатать очередной вариант */
putchar( '|' );
for( i = N-1 ; i >= 0 ; i-- )
if( count[i] )
printf(" %3d |", count[ i ] );
else
printf(" |" );
putchar( '\n' );
nvar++;
return;
}
if( sum >= cost [ maxcoin ] ){
/* если можно выдать монету достоинством cost[maxcoin] ,
* то выдать ее:
*/
count[ maxcoin ] ++; /* посчитали выданную монету */
/* размениваем остаток суммы :
* Первый аргумент - может быть можно дать еще одну такую монету ?
* Второй аргумент - общая сумма убавилась на одну монету cost[maxcoin].
*/
change( maxcoin, sum - cost[maxcoin] );
count[ maxcoin ] --; /* ... Теперь попробуем иной вариант ... */
}
/* попробовать размен более мелкими монетами */
if( maxcoin )
change( maxcoin-1, sum );
}
/* Пример 2 */
/* Подсчет количества вхождений каждой из букв алфавита в файл.
* Выдача таблицы.
* Подсчет частоты использования битов в байтах файла.
*/
#include <stdio.h>
#include <ctype.h>
long bcnt[8];
char masks[8] = { /* маски битов */
1, 2, 4, 8, 16, 32, 64, 128 };
long cnt[256]; /* счетчики для каждой из 256 букв */
/* распечатка букв в стиле языка СИ */
char *pr( c ){
static char buf[ 20 ];
switch( c ){
case '\n': return " \\n " ;
case '\r': return " \\r " ;
case '\t': return " \\t " ;
case '\b': return " \\b " ;
case '\f': return " \\f " ;
case '\033': return " ESC" ;
case '\0': return " \\0 " ;
case 0177: return " ^? " ;
}
if( c < ' ' ){
sprintf( buf, " ^%c ", c + 'A' - 1 );
}else if( isspace(c)){
sprintf( buf, " '%c'", c );
}else if( ! isprint( c ))
sprintf( buf, "\\%3o", c );
else sprintf( buf, " %c ", c );
return buf;
}
main( argc, argv ) char **argv; {
FILE *fp;
if( argc == 1 ) process( stdin );
else{ argv++; argc--;
while( *argv ){
printf( "----- FILE %s -----\n", *argv );
if((fp = fopen( *argv, "r" )) == NULL ){
printf( "Can not open\n" );
}else{ process( fp ); fclose( fp ); }
argv++; argc--;
}
}
exit(0);
}
/* обработать файл с поинтером fp */
process( fp ) FILE *fp;
{ register i; int c; int n;
/* зачистка счетчиков */
for( i=0; i < 256; i++ ) cnt[i] = 0L;
for( i=0; i < 8 ; i++ ) bcnt[i] = 0;
while( ( c=getc(fp)) != EOF ){
c &= 0377;
/* подсчитать букву */
cnt[ c ] ++;
/* подсчет битов */
for( i=0; i < 8; i++ )
if( c & masks[i] )
bcnt[ i ] ++;
}
/* выдача результатов в COL колонок */
#define COL 4
printf( "\tASCII map\n" );
for( n=i=0; i < 256; i++ ){
/* if( cnt[i] == 0l ) continue; */
printf( "%s %5ld |", pr(i), cnt[i] );
if( ++n == COL ){ n = 0; putchar('\n'); }
/* или if((i % COL) == (COL-1)) putchar('\n'); */
}
printf( "\n\tBITS map\n" );
for( i=7; i >=0 ; i-- ) printf( "%6d ", i );
putchar( '\n' );
for( i=7; i >=0 ; i-- )
printf( "%6ld ", bcnt[i] );
putchar( '\n' ); putchar( '\n' );
}
/* Пример 3 */
/* Центрирование строк текста. Пример на работу с указателями. */
/* Входные строки не должны содержать табуляций */
/* Вызов: a.out < входной_файл */
#include <stdio.h>
extern char *gets();
#define WIDTH 60 /* ширина листа */
main(){
char rd[81]; register char *s;
char *head, /* начало текста */
*tail; /* конец текста */
register int len, i;
int shift; /* отступ */
/* Читать со стандартного ввода в rd по одной строке,
* пока файл не кончится. При вводе с клавиатуры конец файла
* обозначается нажатием клавиш CTRL+D
*/
while( gets( rd ) != NULL ){
if( !*rd ){
/* Строка пуста */
putchar( '\n' ); continue;
}
/* пропуск пробелов в начале строки */
for( s = rd; *s == ' ' ; s++ );
if( ! *s ){
/* Строка состоит только из пробелов */
putchar( '\n' ); continue;
}
head = s;
/* встать на конец строки */
while( *s ) s++;
/* искать последний непробел */
s--;
while( *s == ' ' && s != rd ) s--;
tail = s;
/* Длина текста */ len = (tail-head) + 1;
/* разность указателей - целое */
shift = (WIDTH - len)/2;
if(shift < 0 ){
fprintf(stderr, "Строка длиннее чем %d\n", WIDTH );
shift = 0;
}
/* Печать результата */
for( i=0; i < shift; i++ ) putchar( ' ' );
while( head <= tail ) putchar( *head++ );
putchar( '\n' );
}
}
/* Пример 4 */
/* Предварительная разметка текста для nroff */
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h> /* прототип strchr() */
#include <locale.h>
FILE *fout = stdout; /* канал вывода */
/* Состояния вывода */
#define SPACE 0 /* пробелы */
#define TEXT 1 /* текст */
#define PUNCT 2 /* знаки препинания */
#define UC(c) ((unsigned char)(c))
/* Вывод строки текста из буфера */
void putstr (FILE *fp, unsigned char *s) {
/* Punct - знаки препинания, требующие приклеивания к
* концу предыдущего слова.
* PunctS - знаки, всегда требующие после себя пробела.
* PunctN - знаки, которые могут следовать за знаком
* препинания без пробела.
*/
static char Punct [] = ",:;!?.)" ;
static char PunctS[] = ",:;" ;
static char PunctN[] = " \t\"'" ;
#define is(c, set) (strchr(set, UC(c)) != NULL)
int c, state = TEXT, cprev = 'X';
while ((c = *s) != '\0') {
/* Пробелы */
if(isspace(c)) state = SPACE;
/* Знаки препинания. Пробелы перед ними игнорируются.
*/ else if(is(c, Punct)){
switch(state){
case SPACE: if(is(cprev, Punct ) && cprev==c && c != ')')
putc(' ', fp);
/* а просто пробелы - игнорировать */ break;
case PUNCT: if(is(cprev, PunctS)) putc(' ', fp); break;
}
putc(cprev = c, fp); /* выводим сам знак */
state = PUNCT;
} else {
/* Несколько пробелов сворачиваем в один */
switch(state){
case SPACE: putc(' ', fp); break;
case PUNCT: if(!is(c, PunctN)) putc(' ', fp); break;
}
putc(cprev = c, fp); /* сама буква */
state = TEXT;
if(c == '\\') putc('e', fp);
}
s++;
} /* пробелы в конце строки просто игнорируются */
putc ('\n', fp);
}
/* Обработать файл с именем name */
void proceed (char *name) {
FILE *fp;
static unsigned char inp[2048];
/* достаточно большой буфер ввода */
if (strcmp(name, "-") == 0 ) fp = stdin;
else if ((fp = fopen (name, "r")) == NULL) {
fprintf (stderr, "Cannot read %s\n", name);
return;
}
while (fgets (inp, sizeof inp, fp) != NULL) {
register unsigned char *s, *p;
int len = strlen (inp);
if (len && inp[len - 1] == '\n')
inp[--len] = '\0';
if (!*inp) {
/* .sp N - пропуск N пустых строк */
space: fprintf (fout, ".sp 1\n");
continue;
}
/* обрезать концевые пробелы */
for(p = NULL, s = inp; *s; ++s){
if (!isspace (*s)) p = s;
}
if(p) p[1] = '\0';
else goto space;
/* p указывает на последний непробел */
/* Удалить переносы слов в конце строки: перенос - это
минус, прижатый к концу слова */
if (*p == '-' && p != inp /* не в начале строки */
&& isalnum(UC(p[-1])) /* после буквы */
){ int c; *p = '\0'; /* затереть перенос */
/* Читаем продолжение слова из начала следующей строки */
while (isspace (c = getc (fp)));
ungetc (c, fp);
while ((c = getc (fp)) != '\n' && !isspace (c))
*p++ = c;
*p = '\0';
if (c != '\n' ){ /* прочли пробел */
/* вычитываем ВСЕ пробелы */
while (isspace(c = getc (fp)));
if(c != '\n') ungetc (c, fp);
}
}
/* .pp - директива начала абзаца. */
if (isspace (*inp)) {
fprintf (fout, ".pp\n");
for (s = inp; isspace (*s); s++);
putstr (fout, s);
}
else {
if (*inp == '.' || *inp == '\'')
fprintf (fout, "\\&");
putstr (fout, inp);
}
}
if( fp != stdin ) fclose (fp);
}
int main (int argc, char *argv[]) {
int i;
setlocale(LC_ALL, "");
for (i = 1; i < argc; i++)
proceed (argv[i]);
return 0; /* exit code */
}
/* Пример 5 */
/* Программа, распечатывающая слова в строках файла в обратном порядке */
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <locale.h>
#define MAXL 255 /* макс. длина строки */
/* Если бы мы не включили ctype.h, то мы должны были бы определить
* #define isspace(c) ((c) == ' ' || (c) == '\t' || (c) == '\f')
*/
main ( argc, argv ) char **argv;{
setlocale(LC_ALL, "");
if( argc == 1 ){
/* программа вызвана без аргументов */
munch( "" );
}else{
/* аргументы программы - имена файлов */
while( argv[ 1 ] ){
munch( argv[1] );
argv++;
argc--;
}
}
total(); exit(0);
}
/* обработать файл с именем name */
munch( name ) char *name;
{
char l[MAXL]; /* буфер для очередной строки */
int len; /* длина этой строки */
char *words[50]; /* таблица полей строки */
char **s; /* служебная */
int nwords; /* число слов в строке */
FILE *fp;
if( name == NULL || !*name )
fp = stdin; /* стандартный ввод */
else
if( (fp = fopen( name, "r" )) == NULL ){
fprintf( stderr, "Не могу открыть файл %s\n",
name );
return;
}
printf( "----------------------------%s----\n", name );
while( fgets( l, MAXL, fp ) != NULL ){
len = strlen( l );
if( len && l[len-1] == '\n' )
l[--len] = '\0' ;
if( nwords = parse( l, words)){
/* распечатка слов в обратном порядке */
for( --nwords; nwords >= 0; nwords-- ){
printf( "%s ", words[ nwords] );
add( words[ nwords ] );
}
}
putchar ('\n');
}
if( fp != stdin ) fclose( fp );
}
/* разобрать строку на слова */
parse( s, tabl )
register unsigned char *s;
unsigned char *tabl[];
{
char eol = 0;
int nwords = 0;
while ( !eol ){
/* пропустить пробелы и табуляции */
while(isspace(*s)) s++;
if( !*s ) /* строка кончилась */
break;
*tabl++ = s; nwords++;
/* начало очередного слова */
/* пока не пробел и не конец строки */
while( *s && !isspace(*s))s++;
/* указатель стоит на символе, следующем за словом */
if( ! *s ) eol ++;
*s = '\0';
/* закрыли Слово, начинаем Дело */
s++;
}
*tabl = NULL;
return nwords;
}
/* построение таблицы слов, встречающихся в файле */
#define MAXWORDS 1024
struct W{
int ctr; /* число вхождений слова */
char *wrd; /* слово */
}w [MAXWORDS]; /* таблица */
int busy = 0 ; /* занято в таблице */
extern char *malloc();
/* Добавить слово в таблицу */
add( word ) char *word;
{
register i;
static alert = 1;
/* нет ли уже слова в таблице ? */
/* если есть - просто увеличить счетчик */
for( i = 0; i < busy ; i++ ){
if( !strcmp( word, w[i].wrd )){
w[i].ctr++;
return;
}
}
if( busy >= MAXWORDS ){
if( alert ){
fprintf( stderr, "Переполнение таблицы слов\7\n");
alert = 0;
}
return;
}
/* нет, слова нет. Заносим: */
w[busy].wrd = malloc( strlen( word ) + 1 );
/* 1 байт под символ \0 */
if( w[busy].wrd == NULL ){
fprintf( stderr, "Мало памяти\n");
busy = MAXWORDS+1; /* якобы переполнение */
return;
}
w[busy].ctr = 1;
strcpy( w[busy].wrd, word );
busy++;
}
compare( a, b ) struct W *a, *b;
{
return strcoll( a-> wrd, b-> wrd );
/* strcoll сравнивает слова в алфавитном порядке */
}
/* выдача всех слов, встреченных в тексте, и числа их вхождений */
total(){
register i;
/* сортируем слова по алфавиту */
qsort( w, busy, sizeof(struct W), compare );
printf( "-----|-----------ИТОГ---------------\n");
for( i=0; i < busy; i++ )
printf( "%4d | %s\n",
w[i].ctr,
w[i].wrd
);
}
/* Пример 6 */
/* Сортировка букв в строке методом "пузырька" (bubble sort) */
#define YES 1
#define NO 0
bsort(s) char *s;
{
register i; /* индекс сравниваемой буквы */
register need = YES; /* надо ли продолжать сортировку ? */
while( need ){
need = NO; /* не надо */
for(i=0; s[i+1]; i++ )
/* условие цикла: мы сравниваем i-ую и i+1-ую буквы,
* поэтому и проверяем наличие i+1ой буквы
*/
if( s[i] > s[i+1] ){ /* в неверном порядке */
swap( &s[i], &s[i+1] ); /* переставить */
need = YES; /* что-то изменилось: надо будет
* повторить просмотр массива букв */
}
}
}
/* А вот вариант сортировки, написанный с указателями */
bpsort(s) char *s;
{
register char *p; register need = YES;
while( need ){
need = NO;
for( p = s; p[1] != '\0' ; p++ )
if( *p > *(p+1) ){
swap( p, p+1 ); need = YES;
}
}
}
/* обмен двух букв, находящихся по адресам s1 и s2 */
swap( s1, s2 ) register char *s1, *s2;
{
char tmp; /* temporary */
tmp = *s1; *s1 = *s2; *s2 = tmp;
}
char sample1[] = "Homo homini lupus est - ergo bibamus!";
char sample2[ sizeof sample1 ]; /* массив такого же размера */
main(){
strcpy( sample2, sample1 ); /* скопировать */
bsort ( sample1 ); printf( "%s\n", sample1 );
bpsort( sample2 ); printf( "%s\n", sample2 );
}
/* Пример 7 */
/* Работа с хэш-таблицей. Часть функций написана так, чтобы
* быть независимой от типов ключа и значения и легко
* подвергаться модификации.
*/
#include <stdio.h>
#include <string.h> /* prototype for strchr() */
extern void *malloc(unsigned size);
/* типы ключа и значения: в нашем случае это строки */
typedef unsigned char uchar;
typedef uchar *VAL; typedef uchar *KEY;
/* Для использования следует реализовать операции
int HASHFUNC(KEY); int EQKEY(KEY, KEY);
void FREEVAL(VAL); void SETVAL(VAL, VAL);
void FREEKEY(KEY); void SETKEY(KEY, KEY);
*/
#define HASHSIZE 21 /* размер таблицы: очень хорошо 2**n */
uchar *strudup(const uchar *s){ /* создание копии строки в "куче" */
uchar *p = (uchar *) malloc(strlen(s)+1); strcpy(p, s); return p;
}
/* одна из возможных хэш-функций */
unsigned int hash; /* последнее вычисленное значение хэш-функции */
int HASHFUNC(KEY key){
unsigned int i = 0; uchar *keysrc = key;
while(*key){
i = (i << 1)|(i >> 15); /* ROL */
i ^= *key++;
}
hash = i % HASHSIZE;
printf( "hash(%s)=%d\n", keysrc, hash); /* отладка */
return hash;
}
#define EQKEY(s1, s2) (strcmp(s1, s2) == 0)
#define FREEKEY(s) free(s)
#define FREEVAL(s) free(s)
#define SETVAL(at,s) at = strudup(s)
#define SETKEY(at,s) at = strudup(s)
#define KEYFMT "%s"
#define VALFMT "%s"
/* ================== типо-независимая часть ================= */
struct cell {
struct cell *next; /* ссылка на очередной элемент */
KEY key; /* ключ */
VAL val; /* значение */
} *hashtable[ HASHSIZE ]; /* хэш-таблица */
/* получение значения по ключу */
struct cell *get(KEY key){
struct cell *p;
for(p = hashtable[HASHFUNC(key)]; p; p = p->next)
if(EQKEY(p->key, key))
return p;
return NULL; /* отсутствует */
}
/* занести пару ключ:значение в таблицу */
void set(KEY key, VAL val){
struct cell *p;
/* проверить - не было ли звена с таким ключом */
if((p = get(key)) == NULL){ /* не было */
if(!(p = (struct cell *) malloc(sizeof(*p)))) return;
SETKEY(p->key, key);
p->next = hashtable[hash]; /* hash вычислено в get() */
hashtable[hash] = p;
} else /* уже было: изменить значение */
FREEVAL(p->val);
SETVAL(p->val, val);
}
/* удаление по ключу */
int del(KEY key){
int indx = HASHFUNC(key);
struct cell *p, *prev = NULL;
if((p = hashtable[indx]) == NULL) return 0;
for( ;p ;prev = p, p=p->next)
if(EQKEY(p->key, key)){
FREEVAL(p->val); FREEKEY(p->key);
if( p == hashtable[indx] ) /* голова списка */
hashtable[indx] = p->next;
else prev->next = p->next;
free((void *) p ); return 1; /* удален */
}
return 0; /* не было такого */
}
/* распечатать пару ключ:значение */
void printcell(struct cell *ptr){
putchar('(');
printf( KEYFMT, ptr->key ); putchar(',');
printf( VALFMT, ptr->val ); putchar(')');
}
/* распечатка таблицы (для отладки) */
void printtable(){
register i; struct cell *p;
printf("----TABLE CONTENTS----\n");
for(i=0; i < HASHSIZE; i++)
if((p = hashtable[i]) != NULL){
printf( "%d: ", i);
for(; p; p=p->next)
printcell(p), putchar(' ');
putchar('\n');
}
}
/* итератор */
struct celliter {
int index; struct cell *ptr;
};
/* выдать очередное значение */
struct cell *nextpair(struct celliter *ci){
struct cell *result;
while((result = ci->ptr) == NULL){
if( ++(ci->index) >= HASHSIZE )
return NULL; /* больше нет */
ci->ptr = hashtable[ci->index];
}
ci->ptr = result->next; return result;
}
/* инициализация итератора */
struct cell *resetiter(struct celliter *ci){
ci->index = (-1); ci->ptr = NULL;
return nextpair(ci); /* первое значение */
}
/* =========================================================== */
void main(){ /* таблица из имен и размеров файлов текущего каталога */
struct celliter ci; struct cell *cl;
char key[40], value[40]; struct cell *val;
extern FILE *popen(); FILE *fp; char *s ;
/* popen() читает вывод команды, заданной в 1-ом аргументе */
fp = popen( "ls -s", "r" );
while( fscanf( fp, "%s%s", value, key) == 2 )
set(key, value);
pclose(fp); /* popen() надо закрывать pclose(); */
for(;;){
printf( "-> " ); /* приглашение */
if( !gets( key )) break; /* EOF */
if( *key == '-' ){ /* -КЛЮЧ :удалить */
printf( del( key+1 ) ? "OK\n" : "нет такого\n");
continue;
}
if( !*key || !strcmp(key, "=")){ /* = :распечатать таблицу*/
printtable(); continue;
}
if(s = strchr(key, '=')){ /* КЛЮЧ=ЗНАЧЕНИЕ :добавить */
*s++ = '\0';
set(key, s); continue;
}
if((val = get( key )) == NULL) /* КЛЮЧ :найти значение */
printf( "нет такого ключа\n");
else{ printf( "значение "); printf(VALFMT, val->val);
putchar('\n');
}
}
/* распечатка таблицы при помощи итератора */
for( cl = resetiter(&ci) ; cl ; cl = nextpair(&ci))
printcell(cl), putchar('\n');
}
/* Пример 8 */
/* Пример маленькой базы данных.
* Данные хранятся БЕЗ дубликатов.
* Надо заметить, что используется плохой (неэффективный)
* алгоритм доступа - линейный поиск.
*/
#include <stdio.h>
/* Все записи в базе имеют фиксированный размер */
#define VLEN 20
#define KEY_FREE (-13) /* ключ свободного места. Он выбран
произвольно, но не должен встречаться в качестве входных данных */
struct data{
short b_key; /* ключ */
char b_val[VLEN]; /* строка-значение */
};
char BASEF[] = ".base" ; /* имя файла базы */
FILE *fbase; /* pointer на базу */
struct data tmp; /* вспомогательная переменная */
void
initBase (void){
/* fopen: r read (чтение)
* w write (запись), файл пересоздается.
* (создается, если не было, если был - опустошается).
* r+ чтение и запись (файл уже существует).
* w+ чтение и запись (создается пустой файл).
* a append (запись в конец файла), создать если нет:
* имеется в виду, что КАЖДАЯ операция записи сначала
* ставит указатель записи на конец файла.
* В MS DOS нетекстовый файл НЕОБХОДИМО открывать как
* rb wb rb+ wb+ ab+ иначе ничего не будет работать.
*/
if(( fbase = fopen( BASEF, "r+" )) == NULL ){
if(( fbase = fopen( BASEF, "w+" )) == NULL ){
fprintf( stderr, "Не могу открыть базу данных %s\n",
BASEF );
exit(1);
}
fprintf( stderr, "База создана\n" );
}
}
void
closeBase (void){
fclose( fbase );
}
/* Учтите, что если вы записываете в файл структуры, то в файле
не будет разделения на строки - файл НЕТЕКСТОВЫЙ! Поэтому и
читать такой файл можно только структурами: read(), fread()
(но не scanf-ом и не fgets-ом)
*/
/* Поиск по ключу .
Выдать (-1), если записи с данным ключом нет,
иначе - номер слота, где содержится запись с данным ключом.
*/
int
bget (int key)
{
int n;
/* последовательно просмотреть весь файл */
rewind( fbase );
/* в начало файла. Равно fseek(fbase, 0L, 0); */
n = 0 ;
/* int сколько_элементов_массива_действительно_считано =
* fread( адрес_массива_куда_считывать,
* размер_одного_элемента_массива,
* сколько_элементов_считывать_в_массив, канал );
* Заметьте, что количество данных задается НЕ в байтах,
* а в 'штуках'
*/
while( fread( &tmp, sizeof( tmp ), 1, fbase ) == 1 ){
if( tmp.b_key == key )
return n;
n++;
}
return (-1); /* не найдено */
}
/* модифицировать запись с индексом ind */
void
bmod (
int ind,
int key, /* новый ключ */
char *val /* новое значение */
)
{
struct data new;
fseek( fbase, (long) sizeof( struct data ) * ind, 0 );
new.b_key = key;
strncpy( new.b_val, val, VLEN );
/* int сколько_элементов_массива_действительно_записано =
* fwrite( адрес_массива_который_записывать,
* размер_одного_элемента_массива,
* сколько_элементов_массива_записывать, канал );
*/
if( fwrite( &new, sizeof new , 1, fbase ) != 1 )
fprintf( stderr, "Ошибка записи.\n" );
}
/* удаление записи по ключу */
int
bdel (int key){
int ind = bget( key );
if( ind == -1 )
return (-1); /* записи с таким ключом нет */
bmod( ind, KEY_FREE, "" ); /* записать признак свободного места */
return 0;
}
/* Служебная процедура дописи к концу файла */
void
bappend (int key, char *val)
{
struct data new;
/* встать на конец файла */
fseek( fbase, 0L, 2 );
/* и записать новую структуру в конец */
new.b_key = key;
strncpy( new.b_val, val, VLEN );
fwrite( &new, sizeof( struct data ) , 1, fbase );
}
/* добавление новой записи. Если запись с таким ключом уже есть -
выдать ошибку
*/
int
bput (int key, char *val)
{
int i = bget( key );
if( i != -1 )
return (-1); /* запись уже есть */
/* найти свободное место */
i = bget( KEY_FREE );
if( i == -1 ) { /* нет свободных мест */
bappend( key, val );
return 0;
}
/* иначе свободное место найдено.
* Заменяем дырку на полезную информацию */
bmod( i, key, val );
}
/* распечатать всю базу данных подряд */
void
bprint (void){
int n;
int here = 0;
rewind( fbase );
n = 0;
printf( "-номер--ключ-------значение-----------------\n" );
while( fread( &tmp, sizeof tmp, 1, fbase ) == 1 ){
if( tmp.b_key == KEY_FREE ){
n++;
continue;
}
printf( "#%-2d| %6d\t| %s\n", n, tmp.b_key, tmp.b_val );
here ++; n++;
}
printf( "--------------------------------------------\n" );
printf( "Длина базы:%d Занято:%d\n\n", n, here );
}
/* замена поля val у записи с ключом key */
int
bchange (int key, char *val)
{
int ind;
ind = bget( key );
if( ind == -1 ){
/* запись с таким ключом не существует */
/* Добавить как новую запись */
bput( key, val );
return 0;
}
bmod( ind, key, val );
return 1;
}
/* Аналогичная функция, но использующая другой способ.
* Кроме того, если такой ключ отсутствует - ничего не делается
*/
int
bchg (int key, char *val)
{
struct data d;
rewind( fbase ); /* в начало файла */
while( fread( &d, sizeof d, 1, fbase ) == 1 ){
/* поиск ключа */
if( d.b_key == key ){
/* вернуться назад от текущей позиции */
fseek( fbase, - (long) sizeof d, 1 );
/* не годится (long)-sizeof d !!! */
d.b_key = key;
strncpy( d.b_val, val, VLEN );
fwrite( &d, sizeof d, 1, fbase );
/* между fread и fwrite должен быть
* хоть один fseek. (магическое заклинание!)
*/
fseek( fbase, 0L, 1); /* никуда не сдвигаться */
return 0; /* сделано */
}
}
return (-1); /* такого ключа не было */
}
/* Пример */
void
main (void){
int i;
initBase();
bprint();
bdel( 8 );
printf( "Создаем базу данных\n" );
bput( 1, "строка 1" );
bput( 2, "строка 2" );
bput( 3, "строка 3" );
bput( 4, "строка 4" );
bprint();
printf( "Удаляем записи с ключами 1 и 3\n" );
bdel( 1 );
bdel( 3 );
bprint();
printf( "Добавляем записи 5, 6 и 7\n" );
bput( 5, "строка 5" );
bput( 6, "строка 6" );
bput( 7, "строка 7" );
bprint();
printf( "Заменяем строку в записи с ключом 2\n" );
bchange( 2, "новая строка 2" );
bprint();
printf( "Заменяем строку в записи с ключом 4\n" );
bchg( 4, "новая строка 4" );
bprint();
printf( "Заменяем строку в записи с ключом 6 и ключ 6 на 8\n" );
i = bget( 6 );
printf( "Сейчас запись с ключом 6 содержит \"%s\"\n",
tmp.b_val );
bmod( i, 8, "Новая строка 6/8" );
bprint();
closeBase();
}
/* Пример 9 */
/* Вставка/удаление строк в файл */
#include <stdio.h>
#define INSERT_BEFORE 1 /* Вставить строку перед указанной */
#define INSERT_AFTER 2 /* Вставить строку после указанной */
#define DELETE 3 /* Удалить строку */
#define REPLACE 4 /* Заменить строку */
/* К каждой строке linenum должно относиться не более 1 операции !!! */
struct lineop {
char op; /* Операция */
long linenum; /* Номер строки в файле (с 0) */
char *str; /* Строка (или NULL для DELETE) */
};
long lineno; /* номер текущей строки */
int fileChange (char *name, /* имя файла */
struct lineop ops[], /* задание */
int nops /* число элементов в массиве ops[] */
){
FILE *fin, *fout;
static char TMPNAME[] = " ? ";
char buffer[BUFSIZ];
register i;
struct lineop tmpop;
if ((fin = fopen (name, "r")) == NULL)
return (-1);
if ((fout = fopen (TMPNAME, "w")) == NULL) {
fclose (fin); return (-1);
}
lineno = 0L;
while (fgets (buffer, BUFSIZ, fin) != NULL) {
if( nops ) for (i = 0; i < nops; i++)
if (lineno == ops[i].linenum) {
switch (ops[i].op) {
case DELETE: /* удалить */
break;
case INSERT_BEFORE: /* вставить перед */
fprintf (fout, "%s\n", ops[i].str);
fputs (buffer, fout);
break;
case INSERT_AFTER: /* вставить после */
fputs (buffer, fout);
fprintf (fout, "%s\n", ops[i].str);
break;
case REPLACE: /* заменить */
fprintf (fout, "%s\n", ops[i].str);
break;
}
/* переставить выполненную операцию в конец массива и забыть */
tmpop = ops[nops-1]; ops[nops-1] = ops[i]; ops[i] = tmpop;
nops--; goto next;
}
/* иначе строка не числится в массиве ops[] : скопировать */
fputs (buffer, fout);
next:
lineno++;
}
fclose (fin); fclose (fout); rename (TMPNAME, name);
return nops; /* число несделанных операций (0 - все сделано) */
}
struct lineop myops[] = {
{ DELETE, 2L, NULL },
{ INSERT_BEFORE, 0L, "inserted before 0" },
{ INSERT_BEFORE, 10L, "inserted before 10" },
{ INSERT_AFTER, 5L, "inserted after 5" },
{ DELETE, 6L, NULL },
{ INSERT_AFTER, 8L, "inserted after 8" },
{ INSERT_AFTER, 12L, "inserted after 12" },
{ REPLACE, 3L, "3 replaced" }
};
void main( void ){
int n;
n = fileChange( "aFile", myops, sizeof(myops)/sizeof(struct lineop));
printf( "Строк в файле: %ld; осталось операций: %d\n", lineno, n);
}
/*
исходный файл получившийся файл
line 0 inserted before 0
line 1 line 0
line 2 line 1
line 3 3 replaced
line 4 line 4
line 5 line 5
line 6 inserted after 5
line 7 line 7
line 8 line 8
line 9 inserted after 8
line 10 line 9
inserted before 10
line 10
Строк в файле: 11; осталось операций: 1
*/
/* Пример 10 */
/* Проблема: позволить делать вызов free(ptr)
* на данные, не отводившиеся malloc()-ом.
* Решение: вести список всех данных,
* отведенных malloc()ом.
* Возможно также отслеживание диапазона адресов,
* но последнее является машинно-зависимым решением.
*
* При большом количестве файлов эта программа - неплохой тест
* производительности машины!
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct _cell {
void *addr;
struct _cell *next;
} Cell;
typedef struct _entry {
int length;
int used;
Cell *list;
} Entry;
/* Хэшированная таблица */
#define NENTRIES 64
Entry aTab[NENTRIES];
/* Хэш-функция от адреса */
int aHash(void *addr){
unsigned long x = (unsigned long) addr;
x >>= 3; /* деление на 8, так как адреса из malloc()
обычно четные,
поскольку выровнены на границу double */
return(x % NENTRIES);
/* Тут к месту напомнить, что вычисление остатка от деления на степень двойки
* можно соптимизировать:
* x % (2**N) = x & 0b0001.....1 (N двоичных единиц)
* К примеру, x % 64 = x & 0x3F; (6-ая степень двойки)
*/
}
/* Выделить память, записать адрес в таблицу */
void *aCalloc(int n, int m){
void *ptr = calloc(n, m);
Entry *ep = &aTab[ aHash(ptr) ];
Cell *p;
for(p=ep->list; p; p=p->next)
if(p->addr == NULL){
/* Свободная ячейка: переиспользовать */
p->addr = ptr;
ep->used++;
return ptr;
}
/* Нет свободных, завести новую */
p = (Cell *) calloc(1, sizeof(Cell));
p->addr = ptr;
p->next = ep->list;
ep->list = p;
ep->length++;
ep->used++;
return ptr;
}
/* Освободить память */
int aFree(void *ptr){
Entry *ep = &aTab[ aHash(ptr) ];
Cell *p;
for(p=ep->list; p; p=p->next)
if(p->addr == ptr){
free(ptr);
p->addr = NULL;
/* Ячейка не удаляется, но метится как свободная */
ep->used--;
return 1;
}
/* Нет, такой указатель не отводился.
* Не делать free()
*/
return 0;
}
/* Выдать статистику об использовании хэша */
void aStat(){
int i;
int len_all;
int used_all;
for(i=len_all=used_all=0; i < NENTRIES; i++){
len_all += aTab[i].length;
used_all += aTab[i].used;
printf("%d/%d%s", aTab[i].used, aTab[i].length,
i==NENTRIES-1 ? "\n":" ");
}
printf("%d/%d=%g%%\n",
used_all, len_all,
(double)used_all * 100 / len_all);
}
/* ТЕСТ =================================================================*/
Cell *text;
/* Прочитать файл в память */
void fileIn(char *name){
char buf[10000];
FILE *fp;
if((fp = fopen(name, "r")) == NULL){
printf("Cannot read %s\n", name);
return;
}
while(fgets(buf, sizeof buf, fp) != NULL){
char *s;
Cell *p;
s = (char *) aCalloc(1, strlen(buf)+1);
strcpy(s, buf);
p = (Cell *) aCalloc(sizeof(Cell), 1);
p->addr = s;
p->next = text;
text = p;
}
fclose(fp);
}
/* Уничтожить текст в памяти */
void killAll(){
Cell *ptr, *nxtp;
ptr = text;
while(ptr){
nxtp = ptr->next;
if(!aFree(ptr->addr)) printf("No free(1)\n");
if(!aFree(ptr)) printf("No free(2)\n");
ptr = nxtp;
}
}
/* Удалить из текста строки, начинающиеся с определенной буквы */
void randomKill(int *deleted){
unsigned char c = rand() % 256;
Cell *ptr, *prevp;
unsigned char *s;
retry:
prevp = NULL; ptr = text;
while(ptr){
s = (unsigned char *) ptr->addr;
if(*s == c){ /* нашел */
if(!aFree(s)) printf("No free(3)\n");
/* исключить из списка */
if(prevp) prevp->next = ptr->next;
else text = ptr->next;
if(!aFree(ptr)) printf("No free(4)\n");
/* Заведомо неправильный free
if(!aFree(ptr+1)) printf("No free(5)\n");
*/
(*deleted)++;
goto retry;
}
prevp = ptr;
ptr = ptr->next;
}
}
int main(int ac, char *av[]){
int i, r, d;
char buffer[4098];
srand(time(NULL));
for(i=1; i < ac; i++){
printf("File: %s\n", av[i]);
fileIn(av[i]);
aStat();
d = 0;
for(r=0; r < 128; r++) randomKill(&d);
printf("%d lines deleted\n", d);
aStat();
}
killAll();
aStat();
if(!aFree(buffer))
printf("buffer[] - не динамическая переменная.\n");
return 0;
}
/* Пример 11 */
/* Пакет для ловли наездов областей выделенной памяти
* друг на друга,
* а также просто повреждений динамически отведенной памяти.
*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h> /* O_RDWR */
#include <sys/types.h>
#include <ctype.h>
#include <locale.h>
#define CHECKALL
/*
----------------- <--------- ptr
| red_zone | головная "пограничная зона"
-----------------
| byte[0] |
| ... |
| byte[size-1] |
| placeholder |
----------------- выровнено на границу RedZoneType
| red_zone | хвостовая "пограничная зона"
-----------------
Основные идеи состоят в следующем:
1) Перед и после области данных строится зона,
заполненная заранее известным "узором".
Если ее содержимое изменилось, испорчено -
значит мы где-то разрушили нашу память.
2) Ведется таблица всех отведенных malloc()-ом сегментов памяти;
для экономии места эта таблица вынесена в файл (но зато это
очень медленно).
3) Мы не можем пользоваться библиотекой STDIO для обменов с файлом,
потому что эта библиотека сама использует malloc() и буфера
могут быть разрушены.
*/
typedef char *RedZoneType; /* выравнивание на границу указателя */
/* Можно выравнивать на границу double:
typedef double RedZoneType;
*/
/* Сегмент, выделяемый в оперативной памяти */
typedef struct _allocFrame {
RedZoneType red_zone; /* головная "пограничная зона" */
RedZoneType stuff[1]; /* место для данных */
/* хвостовая "пограничная зона" безымянна */
} AllocFrame;
const int RedZoneTypeSize = sizeof(RedZoneType);
/* Запись, помещаемая в таблицу всех выделенных malloc()ом
* областей памяти.
*/
typedef struct _memFileRecord {
AllocFrame *ptr; /* адрес */
size_t size, adjsize; /* размер выделенной области */
/* (0,0) - означает "сегмент освобожден" */
int serial;
} MemFileRecord;
char red_table[] = {
0x01, 0x03, 0x02, 0x04,
0x11, 0x13, 0x12, 0x14,
0x21, 0x23, 0x22, 0x24,
0x31, 0x33, 0x32, 0x34
};
char free_table[] = {
'F', 'r', 'e', 'e', 'p', 't', 'r', '\0',
'F', 'r', 'e', 'e', 'p', 't', 'r', '\0'
};
/* Файл для хранения таблицы указателей */
static int mem_fd = (-1);
#define PTABLE "PointerTable.bin"
#define NRECORDS 256
MemFileRecord memrecords[NRECORDS];
/* ============================================================= */
void MEMputTableRecord(AllocFrame *newptr, AllocFrame *oldptr,
size_t size, size_t adjsize);
void MEMputTableRecordKilled(AllocFrame *ptr);
void MEMerasePreviousRecords(AllocFrame *ptr);
int MEMcheckRecord(MemFileRecord *rec);
int MEMcheck_consistency(AllocFrame *ptr);
void MEMmakeRedZones(char *cptr, size_t size, size_t adjsize);
void MEMopenFd();
/* ============================================================= */
/* Этим следует пользоваться вместо стандартных функций */
void *MEMmalloc (size_t size);
void *MEMrealloc(void *ptr, size_t size);
void *MEMcalloc (size_t n, size_t size);
void MEMfree (void *ptr);
void MEMcheckAll(); /* это можно вызывать в середине программы */
/* ============================================================= */
void MEMopenFd(){
if(mem_fd < 0){
close(creat(PTABLE, 0644)); /* создать файл */
mem_fd = open(PTABLE, O_RDWR); /* чтение+запись */
unlink(PTABLE); /* только для M_UNIX */
atexit(MEMcheckAll);
setlocale(LC_ALL, "");
}
}
/* Поместить запись в таблицу всех указателей на
* выделенные области памяти.
*/
void MEMputTableRecord(AllocFrame *newptr, /* для записи */
AllocFrame *oldptr, /* для стирания */
size_t size, /* размер данных */
size_t adjsize /* размер всей записи с зонами */
){
MemFileRecord memrecord;
static int serial = 0;
memrecord.ptr = newptr;
memrecord.size = size;
memrecord.adjsize = adjsize;
memrecord.serial = serial++;
MEMopenFd();
#ifdef CHECKALL
/* стереть прежние записи про этот адрес */
MEMerasePreviousRecords(oldptr);
#endif
lseek(mem_fd, 0L, SEEK_END); /* в конец */
write(mem_fd, &memrecord, sizeof memrecord); /* добавить */
}
/* Сделать запись об уничтожении области памяти */
void MEMputTableRecordKilled(AllocFrame *ptr){
/* Пометить как size=0, adjsize=0 */
MEMputTableRecord(ptr, ptr, 0, 0);
}
/* Коды ответа функции проверки */
#define OK 0 /* все хорошо */
#define DAMAGED 1 /* повреждена "погранзона" */
#define FREED 2 /* эта память уже освобождена */
#define NOTHERE (-1) /* нет в таблице */
/* Проверить сохранность "пограничных зон" */
int MEMcheckRecord(MemFileRecord *rec){
int code = OK;
char *cptr;
register i;
AllocFrame *ptr = rec->ptr;
size_t size = rec->size;
size_t adjsize = rec->adjsize;
if(size == 0 && adjsize == 0){
printf("%p [%p] -- сегмент уже освобожден, "
"record=#%d.\n",
&ptr->stuff[0], ptr,
rec->serial
);
return FREED;
}
cptr = (char *) ptr;
for(i=0; i < adjsize; i++){
if(i < RedZoneTypeSize || i >= RedZoneTypeSize + size ){
/* головная погранзона ИЛИ хвостовая погранзона */
if( cptr[i] != red_table[ i % RedZoneTypeSize ] ){
printf("%p [%p] -- испорчен байт %4d [%4d]"
"= 0x%02X '%c' record=#%d size=%lu.\n",
&ptr->stuff[0], ptr,
i - RedZoneTypeSize, i,
cptr[i] & 0xFF,
isprint(cptr[i] & 0xFF) ? cptr[i] & 0xFF : '?',
rec->serial, size
);
code = DAMAGED;
}
}
}
for(i=0; i < RedZoneTypeSize; i++)
if(cptr[i] == free_table[i]){
printf("%p -- уже освобождено?\n", ptr);
code = FREED;
}
if(code != OK) putchar('\n');
return code;
}
/* Проверить сохранность памяти по указателю ptr. */
int MEMcheck_consistency(AllocFrame *ptr){
MemFileRecord mr_found;
int nrecords, i, found = 0;
size_t size;
MEMopenFd();
/* Ищем запись в таблице указателей */
lseek(mem_fd, 0L, SEEK_SET); /* перемотать в начало */
for(;;){
size = read(mem_fd, memrecords, sizeof memrecords);
nrecords = size / sizeof(memrecords[0]);
if(nrecords <= 0) break;
for(i=0; i < nrecords; i++)
if(memrecords[i].ptr == ptr){
/* Мы ищем последнюю запись про память
* с таким адресом, поэтому
* вынуждены прочитать ВЕСЬ файл.
*/
mr_found = memrecords[i];
found++;
}
}
if(found) {
return MEMcheckRecord(&mr_found);
} else {
printf("%p -- запись в таблице отсутствует.\n", ptr);
return NOTHERE;
}
}
/* Уничтожить все прежние записи про ptr, прописывая их adjsize=0 */
void MEMerasePreviousRecords(AllocFrame *ptr){
int nrecords, i, found;
size_t size;
MEMopenFd();
lseek(mem_fd, 0L, SEEK_SET); /* перемотать в начало */
for(;;){
found = 0;
size = read(mem_fd, memrecords, sizeof memrecords);
nrecords = size / sizeof(memrecords[0]);
if(nrecords <= 0) break;
for(i=0; i < nrecords; i++)
if(memrecords[i].ptr == ptr){
memrecords[i].adjsize = 0;
/* memrecords[i].size = 0; */
found++;
}
if(found){
lseek(mem_fd, -size, SEEK_CUR); /* шаг назад */
write(mem_fd, memrecords, size); /* перезаписать */
}
}
}
void MEMcheckAll(){
#ifdef CHECKALL
int nrecords, i;
size_t size;
printf("Проверка всех указателей -------------\n");
MEMopenFd();
lseek(mem_fd, 0L, SEEK_SET); /* перемотать в начало */
for(;;){
size = read(mem_fd, memrecords, sizeof memrecords);
nrecords = size / sizeof(memrecords[0]);
if(nrecords <= 0) break;
for(i=0; i < nrecords; i++)
if(memrecords[i].adjsize != 0)
MEMcheckRecord(&memrecords[i]);
}
printf("Проверка всех указателей завершена ---\n");
#endif
}
/* ============================================================= */
/* Заполнение пограничных зон образцом - "следовой дорожкой" */
void MEMmakeRedZones(char *cptr, size_t size, size_t adjsize){
register i;
for(i=0; i < adjsize; i++){
if(i < RedZoneTypeSize || i >= RedZoneTypeSize + size ){
/* головная погранзона ИЛИ
* хвостовая погранзона + дополнение
* до целого числа RedZoneType-ов
*/
cptr[i] = red_table[ i % RedZoneTypeSize ];
}
}
}
/* ============================================================= */
/* Функция выделения памяти */
void *MEMmalloc(size_t size){
AllocFrame *retptr;
int fullRedZoneTypes =
(size + RedZoneTypeSize - 1) / RedZoneTypeSize;
size_t adjustedSize =
sizeof(retptr->red_zone) * 2 + /* две погранзоны */
fullRedZoneTypes * RedZoneTypeSize;
retptr = (AllocFrame *) malloc(adjustedSize);
if(retptr == NULL) return NULL;
MEMmakeRedZones ((char *) retptr, size, adjustedSize);
MEMputTableRecord(retptr, retptr, size, adjustedSize);
return &retptr->stuff[0];
/* вернуть указатель на зону данных */
}
void *MEMrealloc(void *ptr, size_t size){
AllocFrame *retptr;
char *cptr = (char *)ptr - RedZoneTypeSize; /* прежний AllocFrame */
AllocFrame *oldptr = (AllocFrame *) cptr;
int fullRedZoneTypes =
(size + RedZoneTypeSize - 1) / RedZoneTypeSize;
size_t adjustedSize =
sizeof(retptr->red_zone) * 2 +
fullRedZoneTypes * RedZoneTypeSize;
/* Проверить сохранность того, что мы сейчас будем realloc-ить */
MEMcheck_consistency(oldptr);
retptr = (AllocFrame *) realloc((void *)oldptr, adjustedSize);
if(retptr == NULL) return NULL;
MEMmakeRedZones ((char *) retptr, size, adjustedSize);
MEMputTableRecord(retptr, oldptr, size, adjustedSize);
return &retptr->stuff[0];
}
void *MEMcalloc(size_t n, size_t size){
size_t newsize = n * size;
void *ptr = MEMmalloc(newsize);
memset(ptr, '\0', newsize);
return ptr;
}
/* Очистка отведенной памяти.
* ptr - это указатель не на AllocFrame,
* а на данные - то есть на stuff[0].
*/
void MEMfree(void *ptr){
char *cptr = (char *)ptr - RedZoneTypeSize;
int i, code;
code = MEMcheck_consistency((AllocFrame *) cptr);
for(i=0; i < RedZoneTypeSize; i++)
cptr[i] = free_table[i];
if(code != FREED) free((void *) cptr);
MEMputTableRecordKilled((AllocFrame *) cptr);
}
/* ============================================================= */
/* Тестовый пример */
/* ============================================================= */
#define MAXPTRS 512
char *testtable[MAXPTRS];
/* Сгенерировать строку случайной длины со случайным содержимым */
char *wildstring(int c){
#define N 1024
char teststring[N + 1];
int len, i;
char *ptr;
len = rand() % N;
for(i=0; i < len; i++)
teststring[i] = c;
teststring[len] = '\0';
ptr = (char *) MEMmalloc(len + 1);
if(ptr){
strcpy(ptr, teststring);
} else printf("NULL wildstring()\n");
return ptr;
}
int main(int ac, char *av[]){
int ilen, len, n, i;
srand(time(NULL));
for(n=0; n < MAXPTRS; n++)
testtable[n] = wildstring('A');
#define DAMAGE (MAXPTRS/3*2-1)
#ifdef DAMAGE
/* Навести порчу */
len = strlen(testtable[DAMAGE]);
testtable[DAMAGE][len+1] = 'x';
testtable[DAMAGE][-2] = 'y';
printf("ptr=%p len=%d\n", testtable[DAMAGE], len);
#endif
for(n=0; n < MAXPTRS/2; n++){
char *p = wildstring('B');
int length = strlen(p);
char *ptr;
i = rand() % MAXPTRS;
/* Не забыть присвоить возвращенное realloc() значение
* обратно в testtable[i] !!!
*/
testtable[i] = ptr =
(char *) MEMrealloc(testtable[i], length + 1);
if(ptr == NULL) printf("Не могу сделать realloc()\n");
else strcpy(ptr, p);
#ifdef DAMAGE
/* Порча */
if(n == MAXPTRS/3){
ptr[length+2] = 'z';
}
#endif
MEMfree(p);
}
for(n=0; n < MAXPTRS; n++){
if(testtable[n]) MEMfree(testtable[n]);
}
#ifdef DAMAGE
MEMfree(testtable[DAMAGE]);
#endif
return 0;
}
/* Пример 12 */
/* Программа, совмещающая команды mv и cp. Иллюстрация работы с файлами.
* Пример того, как программа может выбирать род работы
* по своему названию.
* Компиляция:
* cc cpmv.c -o copy ; ln copy move
* По мотивам книги М.Дансмура и Г.Дейвиса.
*/
#include <stdio.h> /* буферизованный ввод/вывод */
#include <sys/types.h> /* системные типы данных */
#include <sys/stat.h> /* struct stat */
#include <fcntl.h> /* O_RDONLY */
#include <errno.h> /* системные коды ошибок */
/* #define strrchr rindex /* для версии ДЕМОС (BSD) */
extern char *strrchr(char *, char); /* из библиотеки libc.a */
extern int errno;
char MV[] = "move"; char CP[] = "copy";
#define OK 1 /* success - успех */
#define FAILED 0 /* failure - неудача */
#define YES OK
#define NO 0
/* Выделить базовое имя файла:
* ../wawa/xxx --> xxx
* zzz --> zzz
* / --> /
*/
char *basename( char *name ){
char *s = strrchr( name , '/' );
return (s == NULL) ? name : /* нет слэшей */
(s[1] == '\0') ? name : /* корневой каталог */
s + 1;
}
#define ECANTSTAT (-1) /* файл не существует */
struct ftype {
unsigned type; /* тип файла */
dev_t dev; /* код устройства, содержащего файл */
ino_t ino; /* индексный узел файла на этом устройстве */
};
/* Получение типа файла */
struct ftype filetype( char *name /* имя файла */ )
{
struct stat st; struct ftype f;
if( stat( name, &st ) < 0 ){
f.type = ECANTSTAT; f.dev = f.ino = 0;
} else { f.type = st.st_mode & S_IFMT;
f.dev = st.st_dev; f.ino = st.st_ino;
}
return f;
}
/* Удаляет файлы, кроме устройств */
int unlinkd( char *name, unsigned type )
{
if( type == S_IFBLK || type == S_IFCHR || type == S_IFDIR)
return 0;
return unlink( name );
}
/* Функция нижнего уровня: копирование информации большими порциями */
int copyfile( int from, int to )
/* from - дескриптор откуда */
/* to - дескриптор куда */
{
char buffer[ BUFSIZ ];
int n; /* число прочитанных байт */
while(( n = read( from, buffer, BUFSIZ )) > 0 )
/* read возвращает число прочитанных байт,
* 0 в конце файла
*/
if( write( to, buffer, n ) != n ){
printf( "Write error.\n" );
return FAILED;
}
return OK;
}
/* Копирование файла */
int docopy(char *src, char *dst, unsigned typefrom, unsigned typeto)
{ int retc; int fdin, fdout;
printf( "copy %s --> %s\n", src, dst );
if((fdin = open( src, O_RDONLY )) < 0 ){
printf( "Сan't read %s\n", src );
return FAILED;
}
if((fdout = creat( dst, 0644 )) < 0 ){ /* rw-r--r-- */
printf( "Can't create %s\n", dst );
return FAILED;
}
retc = copyfile( fdin, fdout );
close( fdin ); close( fdout );
return retc;
}
/* Переименование файла. Вернуть OK, если удачно, FAILED - неудачно */
int mlink(char *src, char *dst, unsigned typefrom, unsigned typeto)
{
switch( typefrom ){
case S_IFDIR: /* переименование каталога */
printf( "rename directory %s --> %s\n", src, dst );
if( access( dst, 0 ) == 0 ){
/* 0 - проверить существование файла */
printf( "%s exists already\n", dst );
/* файл уже существует */
return FAILED;
}
if( link( src, dst ) < 0 ){
printf( "Can't link to directory %s\n", dst );
perror( "link" );
/* Возможно, что для выполнения link() для каталогов,
* программа должна обладать правами суперпользователя.
*/
return FAILED;
}
unlink( src );
return OK;
default: /* dst - не существует или обычный файл */
printf( "move %s --> %s\n", src, dst );
unlinkd( dst, typeto );
/* зачищаем место, т.к. link()
* отказывается выполняться, если
* файл dst уже существует (errno==EEXIST).
*/
if( link( src, dst ) < 0 ) return FAILED;
unlinkd( src, typefrom ); /* удаляем старый файл */
return OK;
}
}
/* Если не получилось связать файл при помощи link() - следует
* скопировать файл в указанное место, а затем уничтожить старый файл.
*/
int mcopy(char *src, char *dst, unsigned typefrom, unsigned typeto)
{
if( typefrom == S_IFDIR )
return FAILED;
/* каталог не копируем, поскольку непосредственная запись
* в каталог (как целевой файл) разрешена только ядру ОС.
*/
return docopy( src, dst, typefrom, typeto );
}
/* Переименование файла */
int domove(char *src, char *dst, unsigned typefrom, unsigned typeto)
{
switch( typefrom ){
default:
if( ! mlink( src, dst, typefrom, typeto)){
if( ! mcopy( src, dst, typefrom, typeto)){
printf( "Can't move %s\n", src );
return FAILED;
} else unlinkd( src, typefrom ); /* стереть старый */
}
break;
case S_IFDIR: /* каталог переименовываем в каталог */
if( ! strcmp( ".", basename(src))){
printf( "impossible to move directory \".\"\n" );
return FAILED;
}
if( ! mlink( src, dst, typefrom, typeto )){
if( errno == EXDEV )
printf( "No cross device directory links\n" );
return FAILED;
}
break;
case ECANTSTAT:
printf( "%s does not exist\n", src );
return FAILED;
}
return OK; /* okay */
}
int docpmv( char *src, /* файл-источник */
char *dst, /* файл-получатель */
struct ftype typeto, /* тип файла-получателя */
int cp, /* 0 - переименование, 1 - копирование */
int *confirm /* запрашивать подтверждение на перезапись ? */
){
struct ftype typefrom; /* тип источника */
char namebuf[BUFSIZ]; /* новое имя получателя (если надо) */
typefrom = filetype(src);
if(typefrom.type == ECANTSTAT){ /* не существует */
printf("%s does not exist.\n", src);
return FAILED;
}
if( typefrom.type != S_IFDIR && typeto.type == S_IFDIR ){
/* файл в каталоге dst */
sprintf(namebuf, "%s/%s", dst, basename(src));
typeto = filetype(dst = namebuf);
}
if(typefrom.dev == typeto.dev && typefrom.ino == typeto.ino){
/* Нельзя копировать файл сам в себя */
printf("%s and %s are identical.\n", src, dst);
return OK; /* так как файл уже есть - считаем это удачей */
}
/* если получатель уже существует, то
* запросить подтверждение на перезапись */
if(*confirm && typeto.type == S_IFREG){
char answer[40];
printf("%s already exists. Overwrite (y/n/all) ? ", dst);
fflush(stdout);
switch( *gets(answer)){
case 'n': default: return OK; /* ничего не делать */
case 'y': break;
case 'a': *confirm = NO; /* дальше - без запросов */
break;
}
}
return cp ? docopy(src, dst, typefrom.type, typeto.type) :
domove(src, dst, typefrom.type, typeto.type) ;
}
void main(int argc, char *argv[]) {
char *cmd; int cp, i, err, confirm = YES;
struct ftype typeto; /* тип файла-получателя */
if( argc < 3 ) {
printf( "Usage: %s source... destination\n", argv[0] );
exit(1);
/* ненулевой код возврата сигнализирует об ошибке */
}
/* выделяем базовое имя программы. */
cmd = basename( argv[0] );
if ( !strcmp( cmd, CP )) cp = 1;
else if( !strcmp( cmd, MV )) cp = 0;
else{
printf( "%s - wrong program name.\n", cmd );
exit(2);
}
typeto = filetype( argv[argc-1] );
if(cp && typeto.type != S_IFDIR && typeto.type != S_IFBLK
&& typeto.type != S_IFCHR && argc > 3){
printf("Group of files can be copied "
"to the directory or device only.\n"); exit(3);
}
if(!cp && typeto.type != S_IFDIR && argc > 3){
printf("Group of files can be moved "
"to the directory only.\n"); exit(4);
}
for(err=0, i=1; i < argc-1; i++)
err += ! docpmv(argv[i], argv[argc-1], typeto,
cp, &confirm);
exit(err); /* 0, если не было ошибок */
}
/* Пример 13 */
/* Обход дерева каталогов в MS DOS при помощи смены текущего каталога.
* Аналог ls -R в UNIX. По аналогичному алгоритму работает программа
* find . -print (напишите команду find, используя match())
*/
#define STYLE2
#include <stdio.h>
#include <stdlib.h>
#include <dir.h>
#include <dos.h>
#include <alloc.h> /* для malloc() */
#include <string.h> /* strchr(), strrchr(), strcpy(), ... */
/* прототипы */
char *strend(char *s); char *strdup(const char *s);
void action(int, char **); void main(int, char **);
int listdir(char *); void printdir(int n);
#ifdef STYLE2
void lookdir(char *s, int ac, char **av, register int level);
#else
void lookdir(char *s, int ac, char **av);
#endif
char root[256]; /* имя стартового каталога */
char cwd[256]; /* полное имя текущего каталога */
char *strend(register char *s){ while(*s)s++; return s; }
char *strdup(const char *s){ /* прототип malloc в <stdlib.h> */
char *p = (char *) malloc(strlen(s) + 1);
if(p) strcpy(p, s); return p;
}
stop(){ /* Реакция на control/break */
chdir( root );
/* Это необходимо потому, что MS DOS имеет (в отличие от UNIX)
понятие "текущий каталог" как глобальное для всей системы.
Если мы прервем программу, то окажемся не в том каталоге,
откуда начинали. */
printf( "\nInterrupted by ctrl-break\n");
return 0; /* exit */
}
void main(int argc, char **argv){
/* получить имя текущего каталога */
(void) getcwd(root, sizeof root);
ctrlbrk( stop ); /* установить реакцию на ctrl/break */
#ifndef STYLE2
lookdir( "." /* корень дерева */, argc, argv );
#else
/* для примера: дерево от "\\" а не от "." */
lookdir( "\\", argc, argv, 0 /* начальный уровень */ );
#endif /*STYLE2*/
chdir(root); /* вернуться в исх. каталог */
}
# ifndef STYLE2
void lookdir(char *s, int ac, char **av){
static int level = 0; /* уровень рекурсии */
# else
void lookdir(char *s, int ac, char **av, register int level){
# endif /*STYLE2*/
struct ffblk dblk, *psd = &dblk;
register done;
if( chdir(s) < 0 ){ /* войти в каталог */
printf( "Cannot cd %s\n", s ); return;
} else if (level == 0){ /* верхний уровень */
(void) getcwd(cwd, sizeof cwd);
/* получить полное имя корня поддерева */
}
action(ac, av);
/* искать имена каталогов, удовлетворяющие шаблону "*" */
/* (не в алфавитном порядке !) */
done = findfirst("*.", psd, FA_DIREC);
while( !done ){
if((psd->ff_attrib & FA_DIREC) && psd->ff_name[0] != '.' ){
/* Видим каталог: войти в него! */
char *tail = strend(cwd); char *addplace;
if( tail[-1] == '\\' ){
addplace = tail;
}else{
*tail = '\\'; addplace = tail+1;
}
strcpy(addplace, psd->ff_name);
#ifndef STYLE2
level++; lookdir( psd->ff_name, ac, av ); level--;
#else
lookdir( psd->ff_name, ac, av, level+1 );
#endif
*tail = '\0';
}
/* Искать следующее имя. Информация о точке, где был
* прерван поиск, хранится в dblk */
done = findnext(psd);
}
if( level ) chdir( ".." ); /* выйти вверх */
}
/* Выполнить действия в каталоге */
void action(int ac, char **av){
extern int busy;
busy = 0;
if( ac == 1 ) listdir( "*.*" );
else{
av++;
while( *av ) listdir( *av++ );
}
printdir( busy );
}
#define MAXF 400
struct fst{
char *name; long size; short attr;
} files[MAXF];
int busy; /* сколько имен собрано */
/* Собрать имена, удовлетворяющие шаблону. */
int listdir( char *picture ){
int done, n; struct ffblk dentry;
for(n=0, done=findfirst(picture, &dentry,0xFF /* все типы */);
busy < MAXF && !done ;
done = findnext( &dentry )){
files[busy].name = strdup(dentry.ff_name);
files[busy].size = dentry.ff_fsize;
files[busy].attr = dentry.ff_attrib;
n++; busy++;
}
return n;
}
/* int cmp(struct fst *a, struct fst *b) */
/* новые веяния в Си требуют такого прототипа: */
int cmp(const void *a, const void *b){
return strcmp(((struct fst *) a) -> name,
((struct fst *) b) -> name );
}
/* отсортировать и напечатать */
void printdir(int n){
register i;
struct fst *f;
qsort( files, n, sizeof files[0], cmp );
printf( "Directory %s\n", cwd );
for( i=0, f = files; i < n; i++, f++ )
printf("\t%-16s\t%10ld\t%c%c%c%c%c%c\n",
f->name, f->size,
f->attr & FA_DIREC ? 'd':'-', /* directory */
f->attr & FA_RDONLY ? 'r':'-', /* read only */
f->attr & FA_HIDDEN ? 'h':'-', /* hidden */
f->attr & FA_SYSTEM ? 's':'-', /* system */
f->attr & FA_LABEL ? 'l':'-', /* volume label */
f->attr & FA_ARCH ? 'a':'-' /* archive */
), free(f->name);
putchar('\n');
}
/* Пример 14 */
/* Демонстрация работы с longjmp/setjmp и сигналами */
/* По мотивам книги М.Дансмура и Г.Дейвиса. */
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <setjmp.h>
/*#define IGN*/ /* потом откомментируйте эту строку */
jmp_buf cs_stack; /* control point */
int in_cs; /* флаг, что мы в критической секции */
int sig_recd; /* флаг signal received */
/* активная задержка */
Delay(){
int i; for( i=0; i < 10000; i++ ){ i += 200; i -= 200; }
}
interrupt( code ){
fprintf( stderr, "\n\n***\n" );
fprintf( stderr, "*** Обрабатываем сигнал (%s)\n",
code == 1 ? "разрешенный" : "отложенный" );
fprintf( stderr, "***\n\n" );
}
/* аргумент реакции на сигнал - номер сигнала (подставляется системой) */
void mexit( nsig ){
fprintf( stderr, "\nУбили сигналом #%d...\n\n", nsig ); exit(0);
}
void main(){
extern void sig_vec(); int code; int killable = 1;
signal( SIGINT, mexit );
signal( SIGQUIT, mexit );
fprintf( stderr, "Данная программа перезапускается по сигналу INTR\n" );
fprintf( stderr, "Выход из программы по сигналу QUIT\n\n\n" );
fprintf( stderr, "Сейчас вы еще можете успеть убить эту программу...\n\n" );
Delay(); Delay(); Delay();
for(;;){
if( code = setjmp( cs_stack )){
/* Возвращает не 0, если возврат в эту точку произошел
* по longjmp( cs_stack, code ); где code != 0
*/
interrupt( code ); /* пришло прерывание */
} /* else setjmp() возвращает 0,
* если это УСТАНОВКА контрольной точки (то есть
* сохранение регистров SP, PC и других в буфер cs_stack),
* а не прыжок на нее.
*/
signal( SIGINT, sig_vec ); /* вызывать по прерыванию */
if( killable ){
killable = 0;
fprintf( stderr,
"\7Теперь сигналы INTR обрабатываются особым образом\n\n\n" );
}
body(); /* основная программа */
}
}
body(){
static int n = 0; int i;
fprintf( stderr, "\tВошли в тело %d-ый раз\n", ++n );
ecs();
for( i=0; i < 10 ; i++ ){
fprintf( stderr, "- %d\n",i); Delay();
}
lcs();
for( i=0; i < 10 ; i++ ){
fprintf( stderr, "+ %d\n",i); Delay();
}
}
/* запоминание полученных сигналов */
void sig_vec(nsig){
if( in_cs ){ /* we're in critical section */
#ifdef IGN
signal( SIGINT, SIG_IGN ); /* игнорировать */
fprintf( stderr, "Дальнейшие прерывания будут игнорироваться\n" );
#else
signal( SIGINT, sig_vec );
fprintf( stderr, "Дальнейшие прерывания будут подсчитываться\n" );
#endif
fprintf( stderr, "Получен сигнал и отложен\n" );
sig_recd++ ; /* signal received */
/* пометить, что сигнал пришел */
}else{
signal( SIGINT, sig_vec );
fprintf( stderr, "Получен разрешенный сигнал: прыгаем на рестарт\n" );
longjmp( cs_stack, 1);
}
}
ecs(){ /* enter critical section */
fprintf( stderr, "Откладываем прерывания\n" );
sig_recd = 0; in_cs = 1;
}
lcs(){ /* leave critical section */
fprintf( stderr, "Разрешаем прерывания\n" );
in_cs = 0;
if( sig_recd ){
fprintf( stderr,
"Прыгаем на рестарт, т.к. есть отложенный сигнал (%d раз)\n",
sig_recd );
sig_recd = 0;
signal( SIGINT, sig_vec );
longjmp( cs_stack, 2);
}
}
/* Пример 15 */
/* Команда для изменения скорости обмена в линии (baud).*/
/* Пример вызова в XENIX: baud /dev/tty1a 9600 */
/* /dev/tty1a - это коммуникационный последов. порт #1 */
/* Про управление модами терминала смотри man termio */
#include <fcntl.h>
#include <termio.h>
struct termio old, new; int fd = 2; /* stderr */
struct baudrate{ int speed; char *name;} br[] = {
{ B0, "HANGUP" }, { B1200, "1200" }, { B9600, "9600" },
{ B600, "600" }, { B2400, "2400" }, { EXTA, "19200" },
};
#define RATES (sizeof br/sizeof br[0])
main(ac, av) char *av[];
{ register i; char *newbaud;
if( ac == 3 ){
if((fd = open(av[1], O_RDWR)) < 0 ){
printf("Не могу открыть %s\n", av[1]); exit(1);
} newbaud = av[2];
} else newbaud = av[1];
if( ioctl(fd, TCGETA, &old) < 0 ){
printf("Попытка управлять не терминалом и не портом.\n");
exit(2);
}
if(newbaud == (char*)0) newbaud = "<не задано>";
new=old;
for(i=0; i < RATES; i++)
if((old.c_cflag & CBAUD) == br[i].speed) goto ok;
printf("Неизвестная скорость\n"); exit(3);
ok: printf("Было %s бод\n", br[i].name);
for(i=0; i < RATES; i++)
if( !strcmp(newbaud, br[i].name)){
new.c_cflag &= ~CBAUD; /* побитное "или" всех масок B... */
new.c_cflag |= br[i].speed;
if( ioctl(fd, TCSETA, &new) < 0) perror("ioctl");
/* Скорость обмена может не измениться, если терминал
* не открыт ни одним процессом (драйвер не инициализирован).
*/ exit(0);
}
printf("Неверная скорость %s\n", newbaud); exit(4);
}
/* Пример 16 */
/*#!/bin/cc -DUSG wins.c -o wins -lncurses -lx
Просмотр двух файлов в перекрывающихся окнах.
Редактирование содержимого окон.
*/
/* _______________________ файл wcur.h __________________________ */
#include "curses.h"
/* Макросы, зависимые от реализации curses */
/* число колонок и строк в окне: */
# define wcols(w) ((w)-> _maxx+1 )
# define wlines(w) ((w)-> _maxy+1 )
/* верхний левый угол окна: */
# define wbegx(w) ((w)-> _begx )
# define wbegy(w) ((w)-> _begy )
/* координаты курсора в окне: */
# define wcurx(w) ((w)-> _curx )
# define wcury(w) ((w)-> _cury )
/* доступ к памяти строк окна: */
# define wtext(w) ((w)-> _line) /* chtype **_line; */
/* в других реализациях: ((w)-> _y) */
/* Псевдографика: Для curses Для IBM PC MS DOS */
#define HOR_LINE '\200' /* 196 */
#define VER_LINE '\201' /* 179 */
#define UPPER_LEFT '\210' /* 218 */
#define LOWER_LEFT '\202' /* 192 */
#define UPPER_RIGHT '\212' /* 191 */
#define LOWER_RIGHT '\204' /* 217 */
#define LEFT_JOIN '\205' /* 195 */
#define RIGHT_JOIN '\207' /* 180 */
#define TOP_JOIN '\211' /* 194 */
#define BOTTOM_JOIN '\203' /* 193 */
#define MIDDLE_CROSS '\206' /* 197 */
#define BOX '\272' /* 219 */
#define BOX_HATCHED '\273' /* 177 */
#define LABEL '\274' /* 3 */
#define RIGHT_TRIANG '\234' /* 16 */
#define LEFT_TRIANG '\235' /* 17 */
#define YES 1
#define NO 0
#define MIN(a,b) (((a) < (b)) ? (a):(b))
#define MAX(a,b) (((a) > (b)) ? (a):(b))
#define A_ITALICS A_ALTCHARSET /* в этой версии curses-а - курсив */
#ifndef ESC
# define ESC '\033' /* escape */
#endif
#define ctrl(c) (c & 037)
/* перерисовка экрана */
#define RedrawScreen() { vidattr(curscr->_attrs = A_NORMAL); \
wrefresh(curscr); }
/* curscr - служебное окно - копия текущего состояния экрана дисплея
* для сравнения со сформированным НОВЫМ образом экрана - newscr.
* Поле _attrs в структуре окна содержит текущие атрибуты окна,
* именно это поле изменяется wattrset(), wattron(), wattroff();
*/
/* _______________________ файл wins.c __________________________ */
#include "wcur.h"
#include <signal.h>
WINDOW *wbase1, *wbase2; /* окна рамки (фоновые окна) */
WINDOW *w1, *w2; /* окна для текста */
/* Размеры и расположение окон */
/* COLS - предопределенная переменная: число колонок */
/* LINES - // - : число строк на экране */
#define W1ysize (LINES/2) /* высота */
#define W1xsize (COLS/3*2) /* ширина */
#define W1y 5 /* y верхнего левого угла на экране */
#define W1x 20 /* x верхнего левого угла на экране */
#define W2ysize (LINES/2)
#define W2xsize (COLS/3*2)
#define W2y 10
#define W2x 5
FILE *fp1, *fp2; /* просматриваемые файлы */
/* Завершить работу */
void die(sig){ /* аргумент - номер сигнала */
/* Восстановление режимов терминала */
echo(); /* эхо-отображение вводимых букв */
nocbreak(); /* ввод с системным редактированием строки */
mvcur( -1, -1, LINES-1, 0 ); /* курсор в нижн. левый угол */
endwin(); /* окончание работы с curses-ом */
putchar('\n');
exit(sig); /* завершение работы с кодом sig. 0 - успешно */
}
int run;
void stop(nsig){ signal(SIGINT, SIG_IGN); run = 0; beep(); }
char label[3][5] = { /* Демонстрация псевдографики */
{ UPPER_LEFT, TOP_JOIN, UPPER_RIGHT, HOR_LINE, '\0' },
{ LEFT_JOIN, MIDDLE_CROSS, RIGHT_JOIN, VER_LINE, '\0' },
{ LOWER_LEFT, BOTTOM_JOIN, LOWER_RIGHT, BOX, '\0' }
};
/* Нарисовать рамку, название и фон окна */
wborder( w, name ) WINDOW *w; char *name;
{ register i, j;
for(i=1; i < wlines(w)-1; i++ ){
/* поставить курсор и выдать символ */
mvwaddch(w, i, 0, VER_LINE );
/* mvwaddch(w,y,x,c) = wmove(w,y,x); waddch(w,c); */
/* wmove(w,y,x) - логич. курсор в позицию (y,x) */
/* waddch(w,c) - выдать символ в позиции курсора,
продвинуть курсор. Аналог putchar */
mvwaddch(w, i, wcols(w)-1, VER_LINE );
}
for(j=1; j < wcols(w)-1; j++ ){
mvwaddch(w, 0, j, HOR_LINE );
mvwaddch(w, wlines(w)-1, j, HOR_LINE );
} /* Углы */
mvwaddch(w, 0, 0, UPPER_LEFT);
mvwaddch(w, wlines(w)-1, 0, LOWER_LEFT);
mvwaddch(w, wlines(w)-1, wcols(w)-1, LOWER_RIGHT);
mvwaddch(w, 0, wcols(w)-1, UPPER_RIGHT);
/* Рисуем заголовки вверху и внизу на рамке.
* Заголовки выдаем в центре рамки.
*/
if( (j = (wcols(w) - strlen(name))/2 ) > 0 ){
/* логический курсор - в 0 строку, позицию j */
wmove(w, 0, j);
/* задать режимы выделений */
wattrset( w, A_BOLD | A_BLINK | A_REVERSE );
waddstr( w, name ); /* выдать строку в окно */
wmove( w, wlines(w)-1, j);
wattrset( w, A_ITALICS | A_STANDOUT );
waddstr ( w, name );
wattrset( w, A_NORMAL ); /* нормальные атрибуты */
}
}
/* режим редактирования текста в окнах */
int mode = 0; /* 0 - замена, 1 - вставка */
main( ac, av ) char **av;
{
char buffer[512];
int need1, need2;
int c; void (*save)();
WINDOW *w; /* активное окно */
if( ac < 3 ){
fprintf( stderr, "Вызов: %s file1 file2\n", av[0] );
exit( 1 );
}
if((fp1 = fopen( av[1], "r" )) == NULL ){
fprintf( stderr, "Не могу читать %s\n", av[1] );
exit( 2 );
}
if((fp2 = fopen( av[2], "r" )) == NULL ){
fprintf( stderr, "Не могу читать %s\n", av[2] );
exit( 2 );
}
/* Инициализировать curses */
initscr();
signal( SIGINT, die ); /* по ctrl/C - умереть */
signal( SIGQUIT,die );
/* Создать окна */
/* высота ширина Y и X верх.левого угла */
wbase1 = newwin( W1ysize, W1xsize, W1y, W1x);
if( wbase1 == NULL ){
fprintf( stderr, "Не могу создать wbase1\n" );
goto bad;
}
wbase2 = newwin( W2ysize, W2xsize, W2y, W2x);
if( wbase2 == NULL ){
fprintf( stderr, "Не могу создать wbase2\n" );
goto bad;
}
/* Создать подокна для текста */
/* база высота ширина Y угла X угла */
w1 = subwin( wbase1, W1ysize - 2, W1xsize - 2, W1y+1, W1x+1);
w2 = subwin( wbase2, W2ysize - 2, W2xsize - 2, W2y+1, W2x+1);
scrollok( w1, TRUE ); /* разрешить роллирование окон */
scrollok( w2, TRUE );
wattrset( w2, A_REVERSE ); /*установить атрибуты текста в окнах*/
wattrset( stdscr, A_STANDOUT );
wborder( wbase1, av[1] );
wborder( wbase2, av[2] ); /* рамки */
werase( w1 ); werase( w2 ); /* очистить окна */
/* фон экрана */
werase( stdscr );
/* функции без буквы w... работают с окном stdscr (весь экран) */
for(c=0; c < 3; c++)
mvwaddstr(stdscr, c, COLS-5, &label[c][0]);
move( 1, 10 ); addstr( "F1 - переключить окна" );
mvaddstr( 2, 10, "F5 - переключить режим вставки/замены" );
move( 3, 10 ); printw( "F%d - удалить строку, F%c - вставить строку",
7, '8' );
mvwprintw(stdscr, 4,10, "ESC - выход, CTRL/C - прервать просмотр");
/* wprintw(w, fmt, ...) - аналог printf для окон */
/* В нижний правый угол экрана ничего не выводить:
* на некоторых терминалах это роллирует экран и тем самым
* портит нам картинку.
*/
wattrset( stdscr, A_NORMAL );
wmove( stdscr, LINES-1, COLS-1 );
waddch( stdscr, ' ' );
wnoutrefresh( stdscr );
/* виртуальное проявление окна. */
run = need1 = need2 = 1; /* оба файла не достигли конца */
/* прерывать просмотр по CTRL/C */
save = signal(SIGINT, stop);
while( run && (need1 || need2)){
if( need1 ){
/* прочесть строку из первого файла */
if( fgets( buffer, sizeof buffer, fp1 ) == NULL )
need1 = 0; /* конец файла */
else{
/* выдать строку в окно */
waddstr( w1, buffer );
}
}
if( need2 ){
/* прочесть строку из второго файла */
if( fgets( buffer, sizeof buffer, fp2 ) == NULL )
need2 = 0; /* конец файла */
else{
waddstr( w2, buffer );
/* wnoutrefresh( w2 ); */
}
}
/* Проявить w1 поверх w2 */
touchwin( wbase2 ); wnoutrefresh( wbase2 );
touchwin( w2 ); wnoutrefresh( w2 );
touchwin( wbase1 ); wnoutrefresh( wbase1 );
touchwin( w1 ); wnoutrefresh( w1 );
/* touchwin - пометить окно как целиком измененное.
* wnoutrefresh - переписать изменения в новый образ
* экрана в памяти. */
/* Проявить изображение на экране терминала
* (вывести новый образ экрана). При этом выводятся
* лишь ОТЛИЧИЯ от текущего содержимого экрана
* (с целью оптимизации).
*/
doupdate();
}
fclose(fp1); fclose(fp2);
/* восстановить спасенную реакцию на сигнал */
signal(SIGINT, save);
/* Редактирование в окнах */
noecho(); /* выкл. эхо-отображение */
cbreak(); /* немедленный ввод набранных клавиш
* (без нажатия кнопки \n) */
keypad( w1, TRUE ); /* распознавать функц. кнопки */
keypad( w2, TRUE );
scrollok( w1, FALSE ); /* запретить роллирование окна */
w = w1; /* текущее активное окно */
for( ;; ){
int y, x; /* координаты курсора в окне */
wrefresh( w ); /* обновить окно. Примерно соответствует
* wnoutrefresh(w);doupdate(); */
c = wgetch( w ); /* ввести символ с клавиатуры */
/* заметим, что в режиме noecho() символ не
* отобразится в окне без нашей помощи !
*/
getyx( w, y, x ); /* узнать координаты курсора в окне */
/* не надо &y &x, т.к. это макрос, превращающийся в пару присваиваний */
switch( c ){
case KEY_LEFT: /* шаг влево */
waddch( w, '\b' );
break;
case KEY_RIGHT: /* шаг вправо */
wmove( w, y, x+1 );
break;
case KEY_UP: /* шаг вверх */
wmove( w, y-1, x );
break;
case KEY_DOWN: /* шаг вниз */
wmove( w, y+1, x );
break;
case KEY_HOME: /* в начало строки */
case KEY_LL: /* KEY_END в конец строки */
{ int xbeg, xend;
wbegend(w, &xbeg, &xend);
wmove(w, y, c==KEY_HOME ? xbeg : xend);
break;
}
case '\t': /* табуляция */
x += 8 - (x % 8);
if( x >= wcols( w ))
x = wcols(w)-1;
wmove(w, y, x);
break;
case KEY_BACKTAB: /* обратная табуляция */
x -= 8 - (x % 8);
if( x < 0 ) x = 0;
wmove( w, y, x );
break;
case '\b': /* забой */
case KEY_BACKSPACE:
case '\177':
if( !x ) break; /* ничего */
wmove( w, y, x-1 );
/* and fall to ... (и провалиться в) */
case KEY_DC: /* удаление над курсором */
wdelch( w );
break;
case KEY_IC: /* вставка пробела над курсором */
winsch( w, ' ' );
break;
case KEY_IL:
case KEY_F(8): /* вставка строки */
winsertln( w );
break;
case KEY_DL: /* удаление строки */
case KEY_F(7):
wdeleteln( w );
break;
case ESC: /* ESC - выход */
goto out;
case KEY_F(1): /* переключение активного окна */
if( w == w1 ){
touchwin( wbase2 ); wnoutrefresh( wbase2 );
touchwin( w2 ); wnoutrefresh( w2 );
w = w2;
} else {
touchwin( wbase1 ); wnoutrefresh( wbase1 );
touchwin( w1 ); wnoutrefresh( w1 );
w = w1;
}
break;
case KEY_F(5): /* переключение режима редактирования */
mode = ! mode;
break;
case ctrl('A'): /* перерисовка экрана */
RedrawScreen(); break;
case '\n': case '\r':
waddch( w, '\n' );
break;
default: /* добавление символа в окно */
if( c >= 0400 ){
beep(); /* гудок */
break; /* функц. кнопка - не буква */
}
if( mode ){
winsch( w, ' ' ); /* раздвинь строку */
}
waddch( w, c ); /* выдать символ в окно */
break;
}
}
out:
wrefresh( w ); wsave(w);
bad:
die(0); /* вызов без возврата */
}
/* Сохранить содержимое окна в файл, обрезая концевые пробелы */
wsave(w) WINDOW *w;
{
FILE *fp = fopen("win.out", "w");
register int x,y, lastnospace; int xs, ys;
getyx(w, ys, xs);
for( y=0; y < wlines(w); y++ ){
/* поиск последнего непробела */
for( lastnospace = (-1), x=0; x < wcols(w); x++ )
/* читаем символ из координат (x,y) окна */
if((mvwinch(w,y,x) & A_CHARTEXT) != ' ' )
lastnospace = x;
/* запись в файл */
for( x=0 ; x <= lastnospace; x++ ){
wmove(w,y,x);
putc( winch(w) & A_CHARTEXT, fp );
}
putc( '\n', fp );
}
fclose(fp);
wmove(w, ys, xs ); /* вернуть курсор на прежнее место */
}
/* На самом деле
* winch(w) = wtext(w)[ wcury(w) ][ wcurx(w) ];
* Предложим еще один, более быстрый способ чтения памяти окна
* (для ЗАПИСИ в окно он непригоден, т.к. curses еще
* специальным образом помечает ИЗМЕНЕННЫЕ области окон).
*/
/* Найти начало и конец строки */
int wbegend(w, xbeg, xend) WINDOW *w; int *xbeg, *xend;
{
/* Тип chtype: 0xFF - код символа; 0xFF00 - атрибуты */
chtype ch, *thisline = wtext(w)[ wcury(w) ];
register x, notset = TRUE;
*xbeg = *xend = 0;
for(x=0; x < wcols(w); x++)
/* & A_CHARTEXT игнорирует атрибуты символа */
if(((ch=thisline[x]) & A_CHARTEXT) != ' '){
if((*xend = x+1) >= wcols(w))
*xend = wcols(w) - 1;
if(notset){ notset = FALSE; *xbeg=x; }
}
return (*xend - *xbeg);
}
/* Пример 17 */
/* Window management: "стопка" окон
* cc -DTEST -DUSG w.c -lncurses -lx
*
*____ Файл w.h для Пример 17, Пример 19, Пример 21, Пример 23 _____ */
#include "wcur.h" /* Тот же, что в Пример 16 */
extern int botw, topw;
extern struct WindowList { /* Элемент списка окон */
WINDOW *w; /* окно */
int next; /* следующее окно в списке */
char busy; /* 0:слот свободен, 1:окно видимо, -1:окно спрятано */
} wins[]; /* значения поля busy: */
#define W_VISIBLE 1 /* окно видимо */
#define W_FREE 0 /* слот таблицы свободен */
#define W_HIDDEN (-1) /* окно спрятано */
#define EOW (-1)
#define WIN(n) wins[n].w
/* если совсем нет видимых окон... */
#define TOPW (topw != EOW ? WIN(topw) : stdscr)
#define BOTW (botw == EOW ? stdscr : WIN(botw))
#define MAXW 15
#define iswindow(n) wins[n].busy
int RaiseWin (WINDOW *w); void PopWin ();
void DestroyWin(WINDOW *w, int destroy);
int HideWin (WINDOW *w);
#define KillWin(w) DestroyWin(w, TRUE)
#define DropWin(w) DestroyWin(w, FALSE)
#define PushWin(w) RaiseWin(w)
#define BAR_HOR 01 /* окно имеет горизонтальный scroll bar */
#define BAR_VER 02 /* окно имеет вертикальный scroll bar */
#define DX 2 /* отступ от краев окна */
#define BARWIDTH 2 /* ширина scroll bar-а */
#define BARHEIGHT 1 /* высота */
/* Вычисление координат строки выбора в окне */
#define WY(title, y) ((y) + (title ? 3 : 1))
#define WX(x) ((x) + 1 + DX)
#define XEND(w,scrollok) (wcols(w)-((scrollok & BAR_VER) ? BARWIDTH+2 : 1))
void whorline (WINDOW *w, int y, int x1, int x2);
void wverline (WINDOW *w, int x, int y1, int y2);
void wbox (WINDOW *w, int x1, int y1, int x2, int y2);
void wborder (WINDOW *w);
void wboxerase (WINDOW *w, int x1, int y1, int x2, int y2);
void WinBorder (WINDOW *w, int bgattrib, int titleattrib, char *title,
int scrollok, int clear);
void WinScrollBar(WINDOW *w, int whichbar, int n, int among,
char *title, int bgattrib);
/* Спасение/восстановление позиции курсора */
typedef struct { int x, y; } Point;
#define SetPoint(p, yy, xx) { (p).x = (xx); (p).y = (yy);}
#define GetBack(p, w) wmove((w), (p).y, (p).x)
/* _______________________ файл w.c _____________________________ */
/* УПРАВЛЕНИЕ ПОРЯДКОМ ОКОН НА ЭКРАНЕ */
/* ______________________________________________________________ */
#include "w.h"
int botw = EOW, topw = EOW; /* нижнее и верхнее окна */
struct WindowList wins[MAXW]; /* список управляемых окон */
/* Прочесть символ из окна, проявив окно (если оно не спрятано) */
int WinGetch (WINDOW *win) { register n, dorefr = YES;
if(botw != EOW) for(n=botw; n != EOW; n=wins[n].next)
if(wins[n].w == win){
if(wins[n].busy == W_HIDDEN) dorefr = NO; /* спрятано */
break;
}
if( dorefr ) wrefresh (win); /* проявка */
else doupdate ();
for(;;){ n = wgetch (win); /* собственно чтение */
if( n == ctrl('A')){ RedrawScreen(); continue; }
return n;
}
}
/* Вычислить новое верхнее окно */
static void ComputeTopWin(){ register n;
if(botw == EOW) topw = EOW; /* список стал пуст */
else{ /* ищем самое верхнее видимое окно */
for(topw = EOW, n=botw; n != EOW; n=wins[n].next)
/* спрятанное окно не может быть верхним */
if( wins[n].busy == W_VISIBLE) topw = n;
/* Может совсем не оказаться видимых окон; тогда
* topw == EOW, хотя botw != EOW. Макрос TOPW предложит
* в качестве верхнего окна окно stdscr */
}
}
/* Виртуально перерисовать окна в списке в порядке снизу вверх */
static void WinRefresh(){ register nw;
/* чистый фон экрана */
touchwin(stdscr); wnoutrefresh(stdscr);
if(botw != EOW) for(nw=botw; nw != EOW; nw=wins[nw].next)
if(wins[nw].busy == W_VISIBLE){
touchwin(wins[nw].w); wnoutrefresh(wins[nw].w);
}
}
/* Исключить окно из списка не уничтожая ячейку */
static int WinDelList(WINDOW *w){ register nw, prev;
if(botw == EOW) return EOW; /* список пуст */
for(prev=EOW, nw=botw; nw != EOW; prev=nw, nw=wins[nw].next)
if(wins[nw].w == w){
if(prev == EOW) botw = wins[nw].next; /* было дно стопки */
else wins[prev].next = wins[nw].next;
return nw; /* номер ячейки в таблице окон */
}
return EOW; /* окна не было в списке */
}
/* Сделать окно верхним, если его еще не было в таблице - занести */
int RaiseWin(WINDOW *w){ int nw, n;
if((nw = WinDelList(w)) == EOW){ /* не было в списке */
for(nw=0; nw < MAXW; nw++) /* занести в таблицу */
if( !iswindow(nw)){ wins[nw].w = w; break; }
if(nw == MAXW){ beep(); return EOW; } /* слишком много окон */
}
/* поместить окно nw на вершину списка */
if(botw == EOW) botw = nw;
else{ for(n = botw; wins[n].next != EOW; n=wins[n].next);
wins[n].next = nw;
}
wins[nw].busy = W_VISIBLE; /* окно видимо, слот занят */
wins[topw = nw].next = EOW; WinRefresh(); return nw;
}
/* Удалить окно из списка и (возможно) уничтожить */
/* Окно при этом исчезнет с экрана */
void DestroyWin(WINDOW *w, int destroy){ int nw;
if((nw = WinDelList(w)) != EOW){ /* окно было в списке */
ComputeTopWin();
wins[nw].busy = W_FREE; /* ячейка свободна */
wins[nw].w = NULL;
}
if(destroy) delwin(w); /* уничтожить curses-ное окно */
WinRefresh();
}
void PopWin(){ KillWin(TOPW); }
/* Спрятать окно, и при этом сделать его самым нижним. */
int HideWin(WINDOW *w){ register nw, prev;
if(botw == EOW) return EOW; /* список пуст */
for(prev = EOW, nw = botw; nw != EOW; prev = nw, nw = wins[nw].next )
if(wins[nw].w == w){
wnoutrefresh(w); /* вместо untouchwin(w); */
wins[nw].busy = W_HIDDEN; /* спрятано */
if( nw != botw ){
wins[prev].next = wins[nw].next; /* удалить из списка */
wins[nw].next = botw; botw = nw; /* на дно стопки */
}
WinRefresh();
ComputeTopWin();
return nw;
}
return EOW; /* нет в списке */
}
/* _______________ ОФОРМИТЕЛЬСКИЕ РАБОТЫ _____________________ */
/* Нарисовать горизонтальную линию */
void whorline(WINDOW *w, int y, int x1, int x2){
for( ; x1 <= x2; x1++) mvwaddch(w, y, x1, HOR_LINE);
}
/* Нарисовать вертикальную линию */
void wverline(WINDOW *w, int x, int y1, int y2){
for( ; y1 <= y2; y1++) mvwaddch(w, y1, x, VER_LINE);
}
/* Нарисовать прямоугольную рамку */
void wbox(WINDOW *w, int x1, int y1, int x2, int y2){
whorline(w, y1, x1+1, x2-1);
whorline(w, y2, x1+1, x2-1);
wverline(w, x1, y1+1, y2-1);
wverline(w, x2, y1+1, y2-1);
/* Углы */
mvwaddch (w, y1, x1, UPPER_LEFT);
mvwaddch (w, y1, x2, UPPER_RIGHT);
mvwaddch (w, y2, x1, LOWER_LEFT);
/* Нижний правый угол нельзя занимать ! */
if(! (wbegx(w) + x2 == COLS-1 && wbegy(w) + y2 == LINES-1))
mvwaddch (w, y2, x2, LOWER_RIGHT);
}
/* Нарисовать рамку вокруг окна */
void wborder(WINDOW *w){ wbox(w, 0, 0, wcols(w)-1, wlines(w)-1); }
/* Очистить прямоугольную область в окне */
void wboxerase(WINDOW *w, int x1, int y1, int x2, int y2){
int x, y; register i, j; getyx(w, y, x);
for(i=y1; i <= y2; ++i) for(j=x1; j <= x2; j++)
mvwaddch(w, i, j, ' ');
wmove(w, y, x);
}
/* Нарисовать рамку и заголовок у окна */
void WinBorder (WINDOW *w, int bgattrib, int titleattrib, char *title,
int scrollok, int clear){
register x, y;
wattrset (w, bgattrib); /* задать цвет окна */
if(clear) werase(w); /* заполнить окно цветными пробелами */
wborder (w); /* нарисовать рамку вокруг окна */
if (title) { /* если есть заголовок ... */
for (x = 1; x < wcols (w) - 1; x++){
wattrset(w, bgattrib); mvwaddch (w, 2, x, HOR_LINE);
/* очистка поля заголовка */
wattrset(w, titleattrib); mvwaddch (w, 1, x, ' ');
}
wattrset(w, bgattrib);
mvwaddch (w, 2, 0, LEFT_JOIN);
mvwaddch (w, 2, wcols (w) - 1, RIGHT_JOIN);
wattrset (w, A_BOLD | titleattrib);
mvwaddstr(w, 1, (wcols(w)-strlen(title))/2, title);
wattrset (w, bgattrib);
}
if (scrollok & BAR_VER) { /* выделить столбец под scroll bar. */
int ystart = WY(title, 0), xend = XEND(w, scrollok);
for (y = ystart; y < wlines (w) - 1; y++)
mvwaddch (w, y, xend, VER_LINE);
mvwaddch (w, wlines (w)-1, xend, BOTTOM_JOIN);
mvwaddch (w, ystart-1, xend, TOP_JOIN);
}
/* затычка */
if(wcols(w)==COLS && wlines(w)==LINES){ wattrset(w, A_NORMAL);
mvwaddch(w, LINES-1, COLS-1, ' ');
}
wattrset (w, bgattrib);
}
/* Нарисовать вертикальный scroll bar (горизонтальный не сделан) */
/* Написано не очень аккуратно */
void WinScrollBar(WINDOW *w, int whichbar, int n, int among,
char *title, int bgattrib){
register y, i;
int starty = WY(title, 0);
int endy = wlines (w) - 1;
int x = XEND(w, whichbar) + 1;
int height = endy - starty ;
if(whichbar & BAR_VER){ /* вертикальный */
wattrset (w, A_NORMAL);
for (y = starty; y < endy; y++)
for (i = 0; i < BARWIDTH; i++)
mvwaddch (w, y, x + i, ' ');
y = starty;
if(among > 1) y += ((long) (height - BARHEIGHT) * n / (among - 1));
wattron(w, A_BOLD);
for (i = 0; i < BARWIDTH; i++)
mvwaddch (w, y, x + i, BOX);
wattrset(w, bgattrib | A_BOLD );
if( wcols(w) >= 10 )
mvwprintw(w, 0, wcols(w)-9, "%03d/%03d", n+1, among);
}
wattrset (w, bgattrib);
}
#ifdef TEST
main(){ WINDOW *w[5]; register i, y;
initscr(); /* запустить curses */
w[0] = newwin(16, 20, 4, 43); /* создать 5 окон */
w[1] = newwin(12, 20, 7, 34);
w[2] = newwin(6, 30, 3, 40);
w[3] = newwin(7, 35, 12, 38);
w[4] = newwin(6, 20, 11, 54);
for(i=0; i < 5; i++){
keypad (w[i], TRUE);
wattrset(w[i], A_REVERSE); werase(w[i]);
wborder (w[i]); mvwprintw(w[i], 1, 2, "Window %d", i);
RaiseWin(w[i]); /* сделать верхним окном */
}
noecho(); cbreak(); /* прозрачный ввод */
for(;botw != EOW;){ int c;
/* нарисовать порядок окон */
for(i=botw, y=0; y < 5; y++, i=(i==EOW ? EOW : wins[i].next))
mvprintw(8 - y, 5, i==EOW ? "~": "%d%c", i,
wins[i].busy == W_HIDDEN ? 'h':' ');
mvprintw(9, 5, "topw=%3d botw=%3d", topw, botw);
wnoutrefresh(stdscr); /* вирт. проявка этих цифр */
c = WinGetch(TOPW);
/* здесь происходит doupdate();
* и только в этот момент картинка проявляется */
switch(c){
case KEY_DC: PopWin(); break;
case KEY_IC: KillWin(BOTW); break;
case '0': case '1': case '2': case '3': case '4': case '5':
c -= '0'; if( !iswindow(c)){ beep(); break; }
RaiseWin(WIN(c)); break;
case 'D': KillWin(w[2]); break;
case 'h': HideWin(BOTW); break;
case 'H': HideWin(TOPW); break;
case ESC: goto out;
default: waddch(TOPW, c & 0377); break;
}
}
mvaddstr(LINES-2, 0, "Больше нет окон"); refresh();
out: echo(); nocbreak(); endwin();
}
#endif
/* Пример 18 */
/* _______________________ файл glob.h ___________________________*/
/* ПОДДЕРЖКА СПИСКА ИМЕН ФАЙЛОВ ЗАДАННОГО КАТАЛОГА */
/* ______________________________________________________________ */
#define FILF
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
# define DIR_SIZE 14
extern char *malloc(unsigned); char *strdup(const char *str);
extern char *getenv();
extern char *strchr(char *, char), *strrchr(char *, char);
#define ISDIR(mode) ((mode & S_IFMT) == S_IFDIR)
#define ISDEV(mode) ((mode & S_IFMT) & (S_IFCHR|S_IFBLK))
#define ISREG(mode) ((mode & S_IFMT) == S_IFREG)
#define ISEXE(mode) ((mode & S_IFMT) == S_IFREG && (mode & 0111))
#define isdir(st) ISDIR(st.st_mode)
#define isdev(st) ISDEV(st.st_mode)
#define isreg(st) ISREG(st.st_mode)
#define isexe(st) ISEXE(st.st_mode)
#define YES 1
#define NO 0
#define I_DIR 0x01 /* это имя каталога */
#define I_EXE 0x02 /* это выполняемый файл */
#define I_NOSEL 0x04 /* строку нельзя выбрать */
#define I_SYS (I_DIR | I_EXE | I_NOSEL)
/* Скопировано из treemk.c
* Лучше просто написать #include "glob.h" в файле treemk.c
*/
#define FAILURE (-1) /* код неудачи */
#define SUCCESS 1 /* код успеха */
#define WARNING 0 /* нефатальная ошибка */
typedef struct _info { /* структура элемента каталога */
char *s; /* имя файла */
short fl; /* флаг */
union _any{
int (*act)(); /* возможно связанное действие */
char *note; /* или комментарий */
unsigned i; /* или еще какой-то параметр */
struct _info *inf;
} any; /* вспомогательное поле */
#ifdef FILF
/* дополнительные необязательные параметры, получаемые из stat(); */
long size;
int uid, gid;
unsigned short mode;
#endif
} Info;
typedef union _any Any;
extern Info NullInfo;
#define MAX_ARGV 256 /* Максимальное число имен в каталоге */
typedef struct { /* Содержимое каталога name */
time_t lastRead; /* время последнего чтения каталога */
Info *files; /* содержимое каталога */
char *name; /* имя каталога */
ino_t ino; dev_t dev; /* I-узел и устройство */
char valid; /* существует ли этот каталог вообще */
short readErrors; /* != 0, если каталог не читается */
} DirContents;
/* Виды сортировки имен в каталоге */
typedef enum { SORT_ASC, SORT_DESC, SORT_SUFX,
SORT_NOSORT, SORT_SIZE } Sort;
extern Sort sorttype; extern int in_the_root;
int gcmps (const void *p1, const void *p2);
Info *blkcpy(Info *v); void blkfree(Info *v);
Info *glob(char **patvec, char *dirname);
Info *glb(char *pattern, char *dirname);
int ReadDir(char *dirname, DirContents *d);
struct savech{ char *s, c; };
#define SAVE(sv, str) (sv).s = (str); (sv).c = *(str)
#define RESTORE(sv) if((sv).s) *(sv).s =