"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 =