О КОПИРАЙТАХ |
Вся предоставленная на этом сервере информация собрана нами из разных источников. Если Вам кажется, что публикация каких-то документов нарушает чьи-либо авторские права, сообщите нам об этом. |
|
|
|
|
/* Пример 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 = (sv).c
/* _______________________ файл glob.c __________________________ */
#include "glob.h"
int in_the_root = NO; /* читаем корневой каталог ? */
Sort sorttype = SORT_SUFX; /* сортировка имен по суффиксу */
Info NullInfo = { NULL, 0 }; /* и прочие поля = 0 (если есть) */
char *strdup(const char *s){
char *p = malloc(strlen(s)+1); if(p)strcpy(p, s); return p; }
/* Содержится ли любой из символов в строке ? */
int any(register char *s, register char *p){
while( *s ){ if( strchr(p, *s)) return YES; s++; }
return NO;
}
/* Найти последнюю точку в имени */
static char *lastpoint (char *s)
{ register char *last; static char no[] = "";
if((last = strchr(s, '.')) == NULL) return no;
/* если имя начинается с точки - не считать ее */
return( last == s ? no : last );
}
/* Сравнение строк с учетом их суффиксов */
int strsfxcmp (register char *s1, register char *s2){
char *p1, *p2, c1, c2; int code;
p1 = lastpoint (s1); p2 = lastpoint (s2);
if (code = strcmp (p1, p2)) return code; /* суффиксы разные */
/* иначе: суффиксы равны. Сортируем по головам */
c1 = *p1; c2 = *p2; *p1 = '\0'; *p2 = '\0'; /* временно */
code = strcmp (s1, s2);
*p1 = c1; *p2 = c2; return code;
}
/* Функция сортировки */
int gcmps(const void *p1, const void *p2){
Info *s1 = (Info *) p1, *s2 = (Info *) p2;
switch( sorttype ){
default:
case SORT_ASC: return strcmp(s1->s, s2->s);
case SORT_DESC: return -strcmp(s1->s, s2->s);
case SORT_SUFX: return strsfxcmp(s1->s, s2->s);
case SORT_NOSORT: return (-1);
#ifdef FILF
case SORT_SIZE: return (s1->size < s2->size ? -1 :
s1->size == s2->size ? 0 : 1 );
#endif
}
}
/* Копирование блока */
Info *blkcpy(Info *v){
register i, len;
Info *vect = (Info *) malloc(((len=blklen(v)) + 1) * sizeof(Info));
for(i=0; i < len; i++ ) vect[i] = v[i];
vect[len] = NullInfo; return vect;
}
/* Измерение длины блока */
int blklen(Info *v){
int i = 0;
while( v->s ) i++, v++;
return i;
}
/* Очистка блока (уничтожение) */
void blkfree(Info *v){
Info *all = v;
while( v->s )
free((char *) v->s ), v++;
free((char *) all );
}
/* Сравнение двух блоков */
int blkcmp( register Info *p, register Info *q ){
while( p->s && q->s && !strcmp(p->s, q->s) &&
(p->fl & I_SYS) == (q->fl & I_SYS)){ p++; q++; }
if( p->s == NULL && q->s == NULL )
return 0; /* совпадают */
return 1; /* различаются */
}
char globchars [] = "*?[";
Info gargv[MAX_ARGV]; int gargc;
static short readErrors;
void greset() { gargc = 0; readErrors = 0; }
/* Расширить шаблон имен файлов в сами имена */
static void globone(char *pattern, char dirname[]){
extern char *strdup(); struct stat st;
DIR *dirf; struct dirent *d;
if( any(pattern, globchars) == NO ){ /* no glob */
gargv[gargc] = NullInfo;
gargv[gargc].s = strdup(pattern);
gargc++;
gargv[gargc] = NullInfo;
return;
}
if((dirf = opendir(dirname)) == NULL){ readErrors++; goto out; }
while(d = readdir(dirf)){
if(match(d->d_name, pattern)){
char fullname[512];
if( sorttype != SORT_NOSORT && !strcmp(d->d_name, "."))
continue;
/* В корневом каталоге имя ".." следует пропускать */
if( in_the_root && !strcmp(d->d_name, "..")) continue;
/* Проверка на переполнение */
if( gargc == MAX_ARGV - 1){
free(gargv[gargc-1].s);
gargv[gargc-1].s = strdup(" Слишком много файлов!!!");
gargv[gargc-1].fl = I_SYS;
break;
}
gargv[gargc] = NullInfo;
gargv[gargc].s = strdup(d->d_name);
sprintf(fullname, "%s/%s", dirname, d->d_name);
if(stat(fullname, &st) < 0) gargv[gargc].fl |= I_NOSEL;
else if(isdir(st)) gargv[gargc].fl |= I_DIR;
else if(isexe(st)) gargv[gargc].fl |= I_EXE;
#ifdef FILF
gargv[gargc].size = st.st_size;
gargv[gargc].uid = st.st_uid;
gargv[gargc].gid = st.st_gid;
gargv[gargc].mode = st.st_mode;
#endif
gargc++;
}
}
closedir(dirf);
out: gargv[ gargc ] = NullInfo;
}
/* Расширить несколько шаблонов */
Info *glob(char **patvec, char *dirname){
greset();
while(*patvec){ globone(*patvec, dirname); patvec++; }
qsort(gargv, gargc, sizeof(Info), gcmps);
return blkcpy(gargv);
}
Info *glb(char *pattern, char *dirname){ char *pv[2];
pv[0] = pattern; pv[1] = NULL; return glob(pv, dirname);
}
/* Прочесть содержимое каталога, если оно изменилось:
* Вернуть: 0 - каталог не менялся;
* 1 - изменился;
* 1000 - изменился рабочий каталог (chdir);
* -1 - каталог не существует;
*/
int ReadDir(char *dirname, DirContents *d){
struct stat st; Info *newFiles;
int save = YES; /* сохранять метки у файлов ? */
int dirchanged = NO; /* сделан chdir() ? */
/* каталог мог быть удален, а мы об этом не извещены */
if( stat(dirname, &st) < 0 ){
d->valid = NO; d->lastRead = 0L;
if(d->files) blkfree(d->files);
d->files = blkcpy( &NullInfo );
return (-1); /* не существует */
} else d->valid = YES;
/* не изменился ли адрес каталога, хранимого в *d ? */
if(d->ino != st.st_ino || d->dev != st.st_dev){ /* изменился */
d->ino = st.st_ino; d->dev = st.st_dev;
save = NO; d->lastRead = 0L; dirchanged = YES;
}
/* не изменилось ли имя каталога ? */
if( !d->name || strcmp(d->name, dirname)){
if(d->name) free(d->name); d->name = strdup(dirname);
/* save=NO; d->lastRead = 0; */
}
/* проверим, был ли модифицирован каталог ? */
if( save==YES && d->files && st.st_mtime == d->lastRead )
return 0; /* содержимое каталога не менялось */
d->lastRead = st.st_mtime;
newFiles = glb("*", d->name); /* прочесть содержимое каталога */
if(save == YES && d->files){
register Info *p, *q;
if( !blkcmp(newFiles, d->files)){
blkfree(newFiles); return 0; /* не изменилось */
} /* иначе сохранить пометки */
for(p= d->files; p->s; p++)
for(q= newFiles; q->s; ++q)
if( !strcmp(p->s, q->s)){
q->fl |= p->fl & ~I_SYS; break;
}
}
if(d->files) blkfree(d->files);
d->files = newFiles; d->readErrors = readErrors;
return 1 + (dirchanged ? 999:0);
/* каталог изменился */
}
.
/* Пример 19 */
/* ________________________файл menu.h __________________________ */
/* РОЛЛИРУЕМОЕ МЕНЮ */
/* _______________________________________________________________*/
#include <ctype.h>
#include <sys/param.h>
#define M_HOT '\\' /* горячий ключ */
#define M_CTRL '\1' /* признак горизонтальной черты */
#define MXEND(m) XEND((m)->win,(m)->scrollok)
#define NOKEY (-33) /* горячего ключа нет */
#define MAXLEN MAXPATHLEN /* макс. длина имен файлов */
typedef enum { /* Коды, возвращаемые handler-ом (HandlerReply *reply) */
HANDLER_OUT = 0, /* выйти из функции выбора */
HANDLER_CONTINUE = 1, /* читать очередную букву */
HANDLER_NEWCHAR = 2, /* пойти на анализ кода handler-ом. */
HANDLER_SWITCH = 3, /* пойти на switch() */
HANDLER_AGAIN = 4 /* перезапустить всю функцию выбора */
} HandlerReply;
typedef struct _Menu { /* паспорт меню */
int nitems; /* число элементов меню */
Info *items; /* сам массив элементов */
int *hotkeys; /* "горячие" клавиши */
int key; /* клавиша, завершившая выбор */
int current; /* текущая строка списка */
int shift; /* сдвиг окна от начала меню */
int scrollok; /* окно роллируемое ? */
WINDOW *win; /* окно для меню */
int left, top, height, width; /* координаты меню на экране и
размер окна win */
int textwidth, textheight; /* размер подокна выбора */
int bg_attrib; /* атрибут фона окна */
int sel_attrib; /* атрибут выбранной строки */
char *title; /* заголовок меню */
Point savep;
void (*showMe) (struct _Menu *m);
void (*scrollBar) (struct _Menu *m, int n, int among);
int *hitkeys; /* клавиши, обрабатываемые особо */
int (*handler) (struct _Menu *m, int c, HandlerReply *reply);
} Menu;
/* Структура окна с меню:
*--------------* +0
| ЗАГОЛОВОК | +1
*-----------*--* +2
|+ стр1ааа | | +3
| стр2ббб |##| <- scroll bar шириной BARWIDTH
| стр3ввв | |
*___________|__*
|DX| len |DX|BS|
*/
/* Метки у элементов меню */
#define M_BOLD I_DIR /* яркая строка */
#define M_HATCH 0x08 /* строка тусклая */
#define M_LFT 0x10 /* для использования в pulldown menu */
#define M_RGT 0x20 /* для использования в pulldown menu */
#define M_LABEL 0x40 /* строка имеет метку */
#define M_LEFT (-111)
#define M_RIGHT (-112)
#define TOTAL_NOSEL (-I_NOSEL)
#define M_SET(m, i, flg) (((m)->items)[i]). fl |= (flg)
#define M_CLR(m, i, flg) (((m)->items)[i]). fl &= ~(flg)
#define M_TST(m, i, flg) ((((m)->items)[i]).fl & (flg))
#define M_ITEM(m, i) ((((m)->items)[i]).s)
/* Прототипы */
int MnuInit (Menu *m); void MnuDeinit (Menu *m);
void MnuDrawItem (Menu * m, int y, int reverse, int selection);
int MnuNext (Menu *m); int MnuPrev (Menu *m);
int MnuFirst(Menu *m); int MnuLast (Menu *m);
int MnuPgUp (Menu *m); int MnuPgDn (Menu *m);
int MnuThis (Menu *m); int MnuHot (Menu *m, unsigned c);
int MnuName (Menu *m, char *name);
void MnuDraw (Menu *m); void MnuHide(Menu *m);
void MnuPointAt (Menu *m, int y);
void MnuPoint (Menu *m, int line, int eraseOld);
int MnuUsualSelect (Menu *m, int block);
int is_in(register int c, register int s[]);
char *MnuConvert (char *s, int *pos);
#define M_REFUSED(m) ((m)->key < 0 || (m)->key == ESC )
#define MNU_DY 1
/* _______________________ файл menu.c __________________________ */
#include "w.h"
#include "glob.h"
#include "menu.h"
#include <signal.h>
/* ---------------- implementation module ------------------------- */
/* Не входит ли символ в специальный набор? Массив завершается (-1) */
int is_in(register int c, register int s[]){
while (*s >= 0) {
if(*s == c) return YES;
s++;
}
return NO;
}
char STRING_BUFFER[ MAXLEN ]; /* временный буфер */
/* Снять пометку с "горячей" клавиши. */
char *MnuConvert (char *s, int *pos){
int i = 0;
*pos = (-1);
while (*s) {
if (*s == M_HOT) { *pos = i; s++; }
else STRING_BUFFER[i++] = *s++;
}
STRING_BUFFER[i] = '\0'; return STRING_BUFFER;
}
/* Рамка вокруг окна с меню */
static void MnuWin (Menu *m) {
WinBorder(m->win, m->bg_attrib, m->sel_attrib,
m->title, m->scrollok, YES);
}
/* Нарисовать scroll bar в нужной позиции */
static void MnuWinBar (Menu *m) {
WINDOW *w = m -> win; /* окно */
WinScrollBar(m->win, m->scrollok, m->current, m->nitems,
m->title, m->bg_attrib);
if(m->scrollBar) /* может быть еще какие-то наши действия */
m->scrollBar(m, m->current, m->nitems);
}
/* Роллирование меню */
/*
+---+----->+-МАССИВ--+<-----+
| n|всего |;;;;;;;;;| | shift сдвиг до окна
cur| | |;;;;;;;;;| |
текущий| | =ОКНО============<---------|
элемент| | I ;;;;;;;;; I | y строка окна
0..n-1 | | I ;;;;;;;;; I | |
+------>I###:::::::::###I<--+ |h высота окна
| I ;;;;;;;;; I |
| =================<---------+
| |;;;;;;;;;|
+----->|_________|
*/
static void MnuRoll (Menu *ptr,
int aid, /* какой новый элемент выбрать (0..n-1) */
int *cur, int *shift,
int h, /* высота окна (строк) */
int n, /* высота items[] (элементов) */
void (*go) (Menu *p, int y, int eraseOld),
void (*draw) (Menu *p),
int DY
) {
int y = *cur - *shift; /* текущая строка окна */
int newshift; /* новый сдвиг */
int AID_UP, AID_DN;
if (aid < 0 || aid >= n) return; /* incorrect */
if (y < 0 || y >= h) return; /* incorrect */
AID_UP = MIN (DY, n);
AID_DN = MAX (0, MIN (n, h - 1 - DY));
if (aid < *cur && y <= AID_UP && *shift > 0)
goto scroll; /* down */
if (aid > *cur && y >= AID_DN && *shift + h < n)
goto scroll; /* up */
if (*shift <= aid && aid < *shift + h) {
/* роллировать не надо, а просто пойти в нужную строку окна */
(*go) (ptr, aid - *shift, YES);
*cur = aid; /* это надо изменять ПОСЛЕ (*go)() !!! */
return;
}
scroll:
if (aid > *cur) newshift = aid - AID_DN; /* вверх up */
else if (aid < *cur) newshift = aid - AID_UP; /* вниз down */
else newshift = *shift;
if (newshift + h > n) newshift = n - h;
if (newshift < 0) newshift = 0;
*shift = newshift; *cur = aid;
(*draw) (ptr); /* перерисовать окно */
(*go) (ptr, aid - newshift, NO); /* встать в нужную строку окна */
}
/* Инициализация и разметка меню. На входе:
m->items Массив строк.
m->title Заголовок меню.
m->top Верхняя строка окна (y).
m->left Левый край (x).
m->handler Обработчик нажатия клавиш или NULL.
m->hitkeys Специальные клавиши [] или NULL.
m->bg_attrib Цвет фона окна.
m->sel_attrib Цвет селекции.
*/
int MnuInit (Menu *m) {
int len, pos; char *s; register i;
m -> current = m -> shift = 0;
m -> scrollok = m -> key = 0;
if (m -> hotkeys) { /* уничтожить старые "горячие" ключи */
free ((char *) m -> hotkeys); m -> hotkeys = (int *) NULL;
}
/* подсчет элементов меню */
for (i = 0; M_ITEM (m, i) != (char *) NULL; i++);
m -> nitems = i;
/* отвести массив для "горячих" клавиш */
if (m -> hotkeys = (int *) malloc (sizeof (int) * m -> nitems)) {
for (i = 0; i < m -> nitems; i++)
m -> hotkeys[i] = NOKEY;
}
/* подсчитать ширину текста */
len = m -> title ? strlen (m -> title) : 0;
for (i = 0; i < m -> nitems; i++) {
if (*(s = M_ITEM (m, i)) == M_CTRL) continue;
s = MnuConvert (s, &pos);
if (m -> hotkeys && pos >= 0)
m -> hotkeys[i] =
isupper (s[pos]) ? tolower (s[pos]) : s[pos];
if ((pos = strlen (s)) > len)
len = pos;
}
/* сформировать окно */
#define BORDERS_HEIGHT (2 + (m -> title ? 2 : 0))
#define BORDERS_WIDTH (2 + 2*DX + (m -> scrollok ? BARWIDTH + 1 : 0))
m -> height = m->nitems + BORDERS_HEIGHT;
if (m -> height > LINES * 2 / 3) { /* слишком высокое меню */
m -> scrollok = BAR_VER; /* будет роллироваться */
m -> height = LINES * 2 / 3;
}
if((m -> width = len + BORDERS_WIDTH) > COLS ) m->width = COLS;
m -> textheight = m->height - BORDERS_HEIGHT;
m -> textwidth = m->width - BORDERS_WIDTH;
/* окно должно лежать в пределах экрана */
if( m->top + m->height > LINES ) m->top = LINES - m->height;
if( m->left + m->width > COLS ) m->left = COLS - m->width;
if( m->top < 0 ) m->top = 0;
if( m->left < 0 ) m->left = 0;
if( m->win ){ /* уничтожить старое окно */
KillWin( m->win ); m->win = NULL; }
if( m->win == NULL ){ /* создать окно и нарисовать основу */
if((m->win = newwin(m->height, m->width, m->top, m->left))
== NULL) return 0;
keypad(m->win, TRUE); MnuWin(m); MnuDraw(m);
/* но окно пока не вставлено в список активных окон */
}
return ( m->win != NULL );
}
/* Деинициализировать меню */
void MnuDeinit (Menu *m) {
if( m->win ){ KillWin (m->win); m->win = NULL; }
if( m->hotkeys ){
free ((char *) m -> hotkeys); m -> hotkeys = (int *) NULL;
}
}
/* Спрятать меню */
void MnuHide (Menu *m){ if( m->win ) HideWin(m->win); }
/* Зачистить место для line-той строки окна меню */
static void MnuBox (Menu *m, int line, int attr) {
register WINDOW *w = m -> win;
register i, xend = MXEND(m);
wattrset (w, attr);
for (i = 1; i < xend; i++)
mvwaddch (w, line, i, ' ');
/* ликвидировать последствия M_CTRL-линии */
wattrset (w, m->bg_attrib);
mvwaddch (w, line, 0, VER_LINE);
mvwaddch (w, line, xend, VER_LINE);
wattrset (w, m->bg_attrib);
}
/* Нарисовать строку меню в y-ой строке окна выбора */
void MnuDrawItem (Menu *m, int y, int reverse, int selection) {
register WINDOW *w = m -> win;
int pos, l, attr;
int ay = WY (m->title, y), ax = WX (0);
char *s, c;
int hatch, bold, label, cont = NO, under;
if (y + m -> shift >= 0 && y + m -> shift < m -> nitems) {
s = M_ITEM (m, y + m -> shift);
hatch = M_TST (m, y + m -> shift, I_NOSEL) ||
M_TST (m, y + m -> shift, M_HATCH);
bold = M_TST (m, y + m -> shift, M_BOLD);
label = M_TST (m, y + m -> shift, M_LABEL);
under = M_TST (m, y + m -> shift, I_EXE);
}
else { /* строка вне допустимого диапазона */
s = "~"; label = hatch = bold = NO;
}
if (*s == M_CTRL) { /* нарисовать горизонтальную черту */
int x, xend = MXEND(m);
wattrset(w, m->bg_attrib);
for(x=1; x < xend; x++)
mvwaddch(w, ay, x, HOR_LINE);
mvwaddch (w, ay, 0, LEFT_JOIN);
mvwaddch (w, ay, xend, RIGHT_JOIN);
wattrset (w, m->bg_attrib);
return;
}
l = strlen(s = MnuConvert (s, &pos));
c = '\0';
if (l > m -> textwidth) { /* слишком длинная строка */
c = s[m -> textwidth];
s[m -> textwidth] = '\0'; cont = YES;
if (pos > m -> textwidth) pos = (-1);
}
if (selection)
MnuBox (m, ay, reverse ? m->sel_attrib : m->bg_attrib);
wattrset (w, attr = (bold ? A_BOLD : 0) |
(hatch ? A_ITALICS : 0) |
(under ? A_UNDERLINE : 0) |
(reverse ? m->sel_attrib : m->bg_attrib));
mvwaddstr (w, ay, ax, s);
if( cont ) mvwaddch(w, ay, ax+m->textwidth, RIGHT_TRIANG);
/* Hot key letter */
if (pos >= 0) {
wattron (w, bold ? A_ITALICS : A_BOLD);
mvwaddch (w, ay, WX(pos), s[pos]);
}
if (label){ /* строка помечена */
wattrset (w, attr | A_BOLD);
mvwaddch (w, ay, 1, LABEL);
}
if (under){
wattrset (w, A_BOLD);
mvwaddch (w, ay, ax-1, BOX_HATCHED);
}
if (c) s[m->textwidth] = c;
wattrset (w, m->bg_attrib);
SetPoint (m->savep, ay, ax-1); /* курсор поставить перед словом */
}
/* Выбор в меню подходящего элемента */
int MnuNext (Menu *m) {
char *s; register y = m -> current;
for (++y; y < m -> nitems; y++)
if ((s = M_ITEM (m, y)) && *s != M_CTRL && !M_TST (m, y, I_NOSEL))
return y;
return (-1);
}
int MnuPrev (Menu *m) {
char *s; register y = m -> current;
for (--y; y >= 0; --y)
if ((s = M_ITEM (m, y)) && *s != M_CTRL && !M_TST (m, y, I_NOSEL))
return y;
return (-1);
}
int MnuPgUp (Menu *m) {
char *s; register n, y = m -> current;
for (--y, n = 0; y >= 0; --y) {
if ((s = M_ITEM (m, y)) && *s != M_CTRL && !M_TST (m, y, I_NOSEL))
n++;
if (n == m -> textheight) return y;
}
return MnuFirst (m);
}
int MnuPgDn (Menu *m) {
char *s; register n, y = m -> current;
for (++y, n = 0; y < m -> nitems; y++) {
if ((s = M_ITEM (m, y)) && *s != M_CTRL && !M_TST (m, y, I_NOSEL))
n++;
if (n == m -> textheight) return y;
}
return MnuLast (m);
}
int MnuFirst (Menu *m) {
char *s; register y;
for (y = 0; y < m -> nitems; y++)
if ((s = M_ITEM (m, y)) && *s != M_CTRL && !M_TST (m, y, I_NOSEL))
return y;
return (-1);
}
int MnuLast (Menu *m) {
char *s; register y;
for (y = m -> nitems - 1; y >= 0; --y)
if ((s = M_ITEM (m, y)) && *s != M_CTRL && !M_TST (m, y, I_NOSEL))
return y;
return (-1);
}
int MnuThis (Menu *m) {
char *s;
if (m -> current < 0 || m -> current >= m -> nitems)
return (-1); /* error */
if ((s = M_ITEM (m, m -> current)) &&
*s != M_CTRL && !M_TST (m, m -> current, I_NOSEL))
return m -> current;
return (-1);
}
int MnuName (Menu *m, char *name) {
char *s; register y; int pos;
for(y = 0; y < m -> nitems; ++y)
if ((s = M_ITEM (m, y)) && *s != M_CTRL && !M_TST (m, y, I_NOSEL) &&
strcmp(name, MnuConvert(s, &pos)) == 0 ) return y;
return (-1);
}
int MnuHot (Menu *m, unsigned c) {
register y; char *s;
if (m -> hotkeys == (int *) NULL)
return (-1);
if (c < 0400 && isupper (c))
c = tolower (c);
for (y = 0; y < m -> nitems; y++)
if (c == m -> hotkeys[y] &&
(s = M_ITEM (m, y)) && *s != M_CTRL && !M_TST (m, y, I_NOSEL))
return y;
return (-1);
}
/* Нарисовать содержимое меню для выбора */
void MnuDraw (Menu *m) {
register i, j;
for (i = 0; i < m -> textheight; i++)
MnuDrawItem (m, i, NO, m -> scrollok ? YES : NO);
}
/* Поставить курсор в line-тую строку окна. */
void MnuPoint(Menu *m, int line,
int eraseOld /* стирать старую селекцию? */){
int curline = m->current - m->shift; /* текущая строка окна */
if (line < 0 || line >= m -> textheight) return; /* ошибка */
if (eraseOld && curline != line) /* стереть старый выбор */
MnuDrawItem (m, curline, NO, YES);
MnuDrawItem (m, line, YES, YES); /* подсветить новую строку */
}
/* Перейти к y-той строке массива элементов, изменить картинку */
void MnuPointAt (Menu *m, int y) { char *s;
if (y < 0 || y >= m->nitems) return; /* ошибка! */
if ((s = M_ITEM (m, y)) == NULL || *s == M_CTRL) return;
MnuRoll (m, y, &m -> current, &m -> shift,
m -> textheight, m -> nitems,
MnuPoint, MnuDraw, MNU_DY);
if (m -> scrollok) MnuWinBar(m); /* сдвинуть scroll bar */
GetBack(m->savep, m->win); /* вернуть курсор в начало строки селекции,
* откуда он был сбит MnuWinBar-ом */
}
/* Выбор в меню без участия "мыши". */
int MnuUsualSelect (Menu *m, int block) {
int sel, snew, c, done = 0;
m -> key = (-1);
if( ! m->win ) return TOTAL_NOSEL;
if((sel = MnuThis (m)) < 0)
if((sel = MnuFirst (m)) < 0)
return TOTAL_NOSEL; /* в меню нельзя ничего выбрать */
RaiseWin (m->win); /* сделать окно верхним */
MnuPointAt (m, sel); /* проявить */
if(m->showMe) m->showMe(m); /* может быть изменить позицию ? */
for (;;) {
c = WinGetch (m->win);
INP:
if (m -> hitkeys && m -> handler) {
HandlerReply reply;
if (is_in (c, m -> hitkeys)) {
c = (*m -> handler) (m, c, &reply);
/* восстановить scroll bar */
MnuPointAt (m, m -> current);
switch (reply) {
case HANDLER_CONTINUE: continue;
case HANDLER_NEWCHAR: goto INP;
case HANDLER_OUT: goto out;
case HANDLER_SWITCH: default:
break; /* goto switch(c) */
}
}
}
switch (c) {
case KEY_UP:
if ((snew = MnuPrev (m)) < 0) break;
goto mv;
case KEY_DOWN:
next:
if ((snew = MnuNext (m)) < 0) break;
goto mv;
case KEY_HOME:
if ((snew = MnuFirst (m)) < 0) break;
goto mv;
case KEY_END:
if ((snew = MnuLast (m)) < 0) break;
goto mv;
case KEY_NPAGE:
if ((snew = MnuPgDn (m)) < 0) break;
goto mv;
case KEY_PPAGE:
if ((snew = MnuPgUp (m)) < 0) break;
goto mv;
case KEY_IC: /* поставить/снять пометку */
if (M_TST (m, sel, M_LABEL)) M_CLR (m, sel, M_LABEL);
else M_SET (m, sel, M_LABEL);
MnuPointAt (m, sel);
/* Если вы вычеркнете goto next;
* и оставите просто break;
* то вставьте в это место
* MnuPoint( m, m->current - m->shift, NO ); */
goto next;
case KEY_DC:
if (M_TST (m, sel, M_HATCH)) M_CLR (m, sel, M_HATCH);
else M_SET (m, sel, M_HATCH);
MnuPointAt (m, sel); goto next;
case KEY_LEFT:
if (block & M_LFT) {
sel = M_LEFT; goto out;
} break;
case KEY_RIGHT:
if (block & M_RGT) {
sel = M_RIGHT; goto out;
} break;
case 0: break;
default:
if (c == '\n' || c == '\r' || c == ESC)
goto out;
if ((snew = MnuHot (m, c)) < 0) {
beep(); break;
}
/* иначе найден HOT KEY (горячая клавиша) */
done++; goto mv;
}
continue;
mv:
MnuPointAt (m, sel = snew);
if(done){ wrefresh(m->win); /* проявить новую позицию */ break; }
}
out: wnoutrefresh(m->win);
return((m->key = c) == ESC ? -1 : sel);
/* Меню автоматически НЕ ИСЧЕЗАЕТ: если надо -
* явно делайте MnuHide(m); после MnuUsualSelect(); */
}
.
/* Пример 20 */
/* ______________________________________________________________ */
/* PULL_DOWN меню (меню-строка) */
/* _______________________ файл pull.h __________________________ */
typedef struct {
Info info; /* строка в меню */
Menu *menu; /* связанное с ней вертикальное меню */
char *note; /* подсказка */
} PullInfo;
typedef struct _Pull { /* Паспорт меню */
int nitems; /* количество элементов в меню */
PullInfo *items;/* элементы меню */
int *hotkeys; /* горячие ключи */
int key; /* клавиша, завершившая выбор */
int current; /* выбранный элемент */
int space; /* интервал между элементами меню */
int bg_attrib; /* цвет фона строки */
int sel_attrib; /* цвет выбранного элемента */
Point savep;
void (*scrollBar) (struct _Pull *m, int n, int among);
} PullMenu;
#define PYBEG 0 /* строка, в которой размещается меню */
#define PM_BOLD I_DIR
#define PM_NOSEL I_NOSEL
#define PM_LFT M_LFT
#define PM_RGT M_RGT
#define PM_SET(m, i, flg) (m)->items[i].info.fl |= (flg)
#define PM_CLR(m, i, flg) (m)->items[i].info.fl &= ~(flg)
#define PM_TST(m, i, flg) ((m)->items[i].info.fl & (flg))
#define PM_ITEM(m, i) ((m)->items[i].info.s)
#define PM_MENU(m, i) ((m)->items[i].menu)
#define PM_NOTE(m, i) ((m)->items[i].note)
#define COORD(m, i) ((m)->space * (i+1) + PullSum(m, i))
int PullInit(PullMenu *m);
int PullSum(PullMenu *m, int n);
void PullDraw(PullMenu *m);
int PullShow(PullMenu *m);
void PullHide(PullMenu *m);
void PullDrawItem(PullMenu *m, int i, int reverse, int selection);
void PullPointAt(PullMenu *m, int y);
int PullHot(PullMenu *m, unsigned c);
int PullPrev(PullMenu *m);
int PullNext(PullMenu *m);
int PullFirst(PullMenu *m);
int PullThis(PullMenu *m);
int PullUsualSelect(PullMenu *m);
#define PullWin stdscr
#define PM_REFUSED(m) ((m)->key < 0 || (m)->key == ESC )
/* _______________________ файл pull.c __________________________ */
#include "glob.h"
#include "w.h"
#include "menu.h"
#include "pull.h"
int PullSum(PullMenu *m, int n){
register i, total; int pos;
for(i=0, total = 0; i < n; i++ )
total += strlen( MnuConvert(PM_ITEM(m, i), &pos ));
return total;
}
/* Разметка меню. На входе:
p->items массив элементов с M_HOT-метками и связанных меню.
p->bg_attrib цвет фона строки.
p->sel_attrib цвет выбранного элемента.
Меню всегда размещается в окне stdscr (PullWin).
*/
int PullInit(PullMenu *m){
/* подменю не должны быть инициализированы,
* т.к. все равно будут сдвинуты в другое место */
int total, pos; char *s; register i;
m->key = m->current = 0;
if(m->hotkeys){
free((char *) m->hotkeys); m->hotkeys = (int *) NULL;
}
/* подсчитать элементы меню */
m->nitems = 0;
for( i=0, total = 0; PM_ITEM(m, i) != NULL; i++ ){
total += strlen(s = MnuConvert(PM_ITEM(m, i), &pos));
m->nitems++;
}
if( total > wcols(PullWin)){ /* меню слишком широкое */
err: beep(); return 0;
}
m->space = (wcols(PullWin) - total - 2) / (m->nitems + 1);
if( m->space <= 0 ) goto err;
/* разметить горячие клавиши */
if( m-> hotkeys = (int *) malloc( sizeof(int) * m->nitems )){
for(i=0; i < m->nitems; i++ )
m->hotkeys[i] = NOKEY;
}
for( i=0; i < m->nitems; i++ ){
if( PM_MENU(m,i)){
PM_MENU(m,i)->left = COORD(m, i) - 1;
PM_MENU(m,i)->top = PYBEG + 1;
PM_MENU(m,i)->bg_attrib = m-> bg_attrib;
PM_MENU(m,i)->sel_attrib = m-> sel_attrib;
if( PM_MENU(m,i)->win )
MnuDeinit( PM_MENU(m,i));
MnuInit( PM_MENU(m,i));
}
if( m->hotkeys ){
s = MnuConvert(PM_ITEM(m, i), &pos);
if( pos >= 0 )
m->hotkeys[i] =
isupper(s[pos]) ? tolower(s[pos]) : s[pos];
}
}
keypad(PullWin, TRUE); return 1;
}
/* Проявить pull-down меню */
int PullShow(PullMenu *m){
register i; int first, last;
first = last = (-1);
for(i=0; i < m->nitems; i++ ){
PM_SET(m, i, PM_LFT | PM_RGT );
if( !PM_TST(m, i, PM_NOSEL)){
if( first < 0 ) first = i;
last = i;
}
}
if( first < 0 ) return (TOTAL_NOSEL);
if(first == last ){
PM_CLR(m, first, PM_LFT | PM_RGT );
}else{
PM_CLR(m, first, PM_LFT);
PM_CLR(m, last, PM_RGT);
}
wmove(PullWin, PYBEG, 0);
wattrset(PullWin, m->bg_attrib);
wclrtoeol(PullWin);
PullDraw(m); return 1;
}
void PullDraw(PullMenu *m){ register i;
for(i=0; i < m->nitems; i++ )
PullDrawItem(m, i, NO, NO);
}
/* Спрятать pull-down меню. Сама строка остается, подменю исчезают */
void PullHide(PullMenu *m){
register i;
for(i=0; i < m->nitems; i++ )
if( PM_MENU(m, i)) MnuHide( PM_MENU(m, i));
PullDraw(m);
}
/* Нарисовать элемент меню */
void PullDrawItem(PullMenu *m, int i, int reverse, int selection){
int x, pos, hatch = PM_TST(m, i, PM_NOSEL );
char *s;
x = COORD(m, i); s = MnuConvert( PM_ITEM(m, i), &pos );
wattrset(PullWin,
(reverse ? m->sel_attrib : m->bg_attrib) |
(hatch ? A_ITALICS : 0 ));
/*mvwaddch(PullWin, PYBEG, x-1, reverse ? LEFT_TRIANG : ' ');*/
mvwaddstr(PullWin, PYBEG, x, s);
/*waddch(PullWin, reverse ? RIGHT_TRIANG : ' ');*/
if( pos >= 0 ){ /* Hot key letter */
wattron(PullWin, A_BOLD);
mvwaddch(PullWin, PYBEG, x + pos, s[pos]);
}
wmove (PullWin, PYBEG, x-1); SetPoint(m->savep, PYBEG, x-1);
wattrset(PullWin, m->bg_attrib);
}
int PullPrev(PullMenu *m){
register y;
for( y = m->current - 1; y >= 0; y-- )
if( !PM_TST(m, y, PM_NOSEL )) return y;
return (-1);
}
int PullNext(PullMenu *m){
register y;
for( y = m->current+1; y < m->nitems; y++ )
if( !PM_TST(m, y, PM_NOSEL)) return y;
return (-1);
}
int PullFirst(PullMenu *m){
register y;
for( y = 0; y < m->nitems; y++ )
if( !PM_TST(m, y, PM_NOSEL)) return y;
return (-1);
}
int PullThis(PullMenu *m){
register y;
if( m->current < 0 || m->current >= m->nitems )
return (-1);
if( PM_TST(m, m->current, PM_NOSEL))
return (-1);
return m->current;
}
int PullHot(PullMenu *m, unsigned c){
register y;
if( m-> hotkeys == (int *) NULL )
return (-1);
if( c < 0400 && isupper(c))
c = tolower(c);
for( y=0; y < m->nitems; y++ )
if( c == m->hotkeys[y] && !PM_TST(m, y, PM_NOSEL))
return y;
return (-1);
}
/* Указать на элемент n */
void PullPointAt( PullMenu *m, int n){
if( n < 0 || n >= m->nitems ) return ; /* error */
if( n != m->current ){
if( PM_MENU(m, m->current))
MnuHide( PM_MENU(m, m->current));
PullDrawItem( m, m->current, NO, YES );
}
m -> current = n;
PullDrawItem( m, n, YES, YES );
if( m->scrollBar ){
m->scrollBar( m, n, m->nitems );
GetBack(m->savep, PullWin);
}
}
/* Выбор в меню */
int PullUsualSelect(PullMenu *m){
int autogo = NO, c, code, done = 0, snew, sel, reply = (-1);
m->key = (-1);
if((sel = PullThis(m)) < 0 )
if((sel = PullFirst(m)) < 0 ) return TOTAL_NOSEL;
if( PullShow(m) < 0 ) return TOTAL_NOSEL;
PullPointAt(m, sel); /* начальная позиция */
for(;;){
if( autogo ){ /* Автоматическая проявка подменю */
if( PM_MENU(m, m->current) == NULL)
goto ask;
code = MnuUsualSelect(PM_MENU(m, m->current),
PM_TST(m, m->current, PM_LFT) |
PM_TST(m, m->current, PM_RGT));
MnuHide(PM_MENU(m, m->current));
c = PM_MENU(m, m->current)->key;
if(code == (-1)){
reply = (-1); goto out;
}
/* в подменю ничего нельзя выбрать */
if( code == TOTAL_NOSEL) goto ask;
/* MnuUsualSelect выдает специальные коды для
* сдвигов влево и вправо */
if( code == M_LEFT ) goto left;
if( code == M_RIGHT ) goto right;
reply = code; goto out;
} else
ask: c = WinGetch(PullWin);
switch(c){
case KEY_LEFT:
left: if((snew = PullPrev(m)) < 0 ) goto ask;
goto mv;
case KEY_RIGHT:
right: if((snew = PullNext(m)) < 0 ) goto ask;
goto mv;
case ESC:
reply = (-1); goto out;
case '\r': case '\n':
if( PM_MENU(m, m->current) == NULL){ reply = 0; goto out; }
autogo = YES; break;
default:
if((snew = PullHot(m, c)) < 0 ) break;
if( PM_MENU(m, snew) == NULL){ reply=0; done++; }
autogo = YES; goto mv;
}
continue;
mv: PullPointAt(m, sel = snew);
if( done ) break;
}
out: wnoutrefresh(PullWin); PullHide(m); m->key = c;
wattrset(PullWin, A_NORMAL); /* NOT bg_attrib */
return reply; /* номер элемента, выбранного в меню
PM_MENU(m, m->current) */
}
.
/* Пример 21 */
/* РЕДАКТОР СТРОКИ И ИСТОРИЯ РЕДАКТИРУЕМЫХ СТРОК */
/* _______________________ файл hist.h __________________________ */
/* ИСТОРИЯ. ЗАПОМИНАНИЕ СТРОК И ВЫДАЧА ИХ НАЗАД ПО ТРЕБОВАНИЮ. */
/* ______________________________________________________________ */
typedef struct { /* Паспорт истории */
Info *list; /* запомненные строки */
int sz; /* размер истории (макс.) */
int len; /* текущее число строк */
Menu mnu; /* меню для выборки из истории */
} Hist;
void HistInit(Hist *h, int n);
void HistAdd (Hist *h, char *s, int fl);
Info *HistSelect(Hist *h, int x, int y);
/* _______________________ файл hist.c __________________________ */
#include "w.h"
#include "glob.h"
#include "menu.h"
#include "hist.h"
/* Проинициализировать новую "историю" емкостью n строк */
void HistInit(Hist *h, int n){
register i;
if( h->list ){ blkfree( h->list ); h->list = NULL; }
h->len = 0;
h->mnu.title = "History";
h->mnu.bg_attrib = A_NORMAL;
h->mnu.sel_attrib = A_REVERSE;
h->list = (Info *) malloc( (n+1) * sizeof(Info));
if( ! h->list ){
h->sz = 0; return;
}else h->sz = n;
for( i=0; i < n+1 ; i++ )
h->list[i] = NullInfo;
}
/* Добавить строку s с меткой fl в историю */
void HistAdd (Hist *h, char *s, int fl){
register i, j; Info tmp;
if( h->sz == 0 ) return;
/* А нет ли уже такой строки ? */
for( i=0; i < h->len; i++ )
if( !strcmp(s, h->list[i].s )){ /* есть ! */
if( i == 0 ) return; /* первая */
/* сделать ее первой строкой */
tmp = h->list[i];
for( j=i-1; j >= 0; --j )
h->list[j+1] = h->list[j];
h->list[0] = tmp;
return;
}
if( h->len < h->sz ){
for( i=h->len-1; i>= 0; i-- )
h->list[i+1] = h->list[i];
h->len ++ ;
}else{
/* выкинуть самую старую строку из истории */
free( h->list[ h->sz - 1 ].s );
for( i=h->sz - 2; i >= 0; i-- )
h->list[i+1] = h->list[i];
}
(h->list)[0].s = strdup(s); (h->list)[0].fl = fl;
}
/* Выборка строки из истории */
Info *HistSelect(Hist *h, int x, int y){
if( h->len == 0 ) return (Info *) NULL;
h->mnu.top = y;
h->mnu.left = x;
h->mnu.items = h->list;
MnuInit( & h->mnu );
if( h->mnu.hotkeys ){
register i;
for(i=0 ; i < h->mnu.nitems; i++ )
h->mnu.hotkeys[i] = h->list[i].s[0] & 0377;
}
MnuUsualSelect( & h->mnu, 0 );
MnuDeinit ( & h->mnu );
if( M_REFUSED ( & h->mnu ))
return (Info *) NULL;
return & h->list[ h->mnu.current ];
}
/* _______________________ файл line.h __________________________ */
/* РЕДАКТОР ДЛИННЫХ СТРОК (ВОЗМОЖНО ШИРЕ ЭКРАНА) */
/* ______________________________________________________________ */
typedef struct _LineEdit { /* Паспорт редактора строки */
WINDOW *win; /* окно для редактирования */
int width; /* ширина поля редактирования */
int left, top; /* координаты поля редактирования в окне */
int pos; /* позиция в строке */
int shift; /* число символов скрытых левее поля */
char *line; /* строка которая редактируется */
int maxlen; /* максимальная длина строки */
int len; /* текущая длина строки */
int insert; /* 1 - режим вставки; 0 - замены */
int nc; /* 1 - стирать строку по первому нажатию */
int cursorOn; /* курсор включен (для графики) */
int bg_attrib; /* цвет текста */
int fr_attrib; /* цвет пустого места в поле */
int wl_attrib; /* цвет краев строки */
int sel_attrib; /* цвет символа под курсором */
Hist *histIn; /* история для выборки строк */
Hist *histOut; /* история для запоминания строк */
int key; /* кнопка, завершившая редактирование */
Point savep;
/* функции проявки и убирания окна (если надо) */
int (*showMe)(struct _LineEdit *le); /* 1 при успехе */
void (*hideMe)(struct _LineEdit *le);
void (*posMe) (struct _LineEdit *le); /* установка позиции */
/* Функция рисования scroll bar-а (если надо) */
void (*scrollBar)(struct _LineEdit *le, int whichbar, int n, int among);
/* Специальная обработка клавиш (если надо) */
int *hitkeys;
int (*handler)(struct _LineEdit *le, int c, HandlerReply *reply);
} LineEdit;
void LePutChar( LineEdit *le, int at);
void LeCursorHide( LineEdit *le );
void LeCursorShow( LineEdit *le );
void LePointAt( LineEdit *le, int at );
void LePoint( LineEdit *le, int x, int eraseOld );
void LeDraw( LineEdit *le );
void LeReport( LineEdit *le );
void LeDelCh ( LineEdit *le );
void LeInsCh ( LineEdit *le, int c );
void LeRepCh ( LineEdit *le, int c );
int LeInsStr( LineEdit *le, char *s);
int LeWerase( LineEdit *le, char *to );
int LeEdit( LineEdit *le );
#define LINE_DX 1
#define LE_REFUSED(m) ((m)->key < 0 || (m)->key == ESC )
/* _______________________ файл line.c __________________________ */
/* Редактор строки. Эта версия была изначально написана *
* для графики, поэтому здесь не совсем CURSES-ные алгоритмы */
#include "w.h"
#include "glob.h"
#include "menu.h"
#include "hist.h"
#include "line.h"
/* Удалить букву из строки */
static char cdelete(register char *s, int at) { char c;
s += at; if((c = *s) == '\0') return c;
while( s[0] = s[1] ) s++; return c;
}
/* Вставить букву в строку */
static void insert(char *s, int at, int c){
register char *p;
s += at; p = s;
while(*p) p++; /* найти конец строки */
p[1] = '\0'; /* закрыть строку */
for( ; p != s; p-- )
p[0] = p[-1];
*s = c;
}
/* Нарисовать видимую часть строки с позиции from */
static void LeDrawLine( LineEdit *le, int from ){
LeCursorHide( le );
for( ; from < le->width; from++ )
LePutChar(le, from);
/* курсор остается спрятанным */
}
/* Выдать символ строки в позиции at */
void LePutChar( LineEdit *le, int at){
int off = le->shift + at;
int bgcolor = le->bg_attrib, wall;
wall = /* символ на краю поля и строка выходит за этот край ? */
( at == 0 && le->shift ||
( at >= le->width - 1 && le->shift + le->width < le->len ));
bgcolor =
( off < le->len ) ? le->bg_attrib :
( at >= le->width || off >= le->maxlen ) ? (le->bg_attrib | A_ITALICS):
/* чистое место в поле */ le->fr_attrib ;
wattrset( le->win, wall? le->wl_attrib|A_BOLD|A_ITALICS: bgcolor);
mvwaddch( le->win, le->top, le->left + at,
off < le->len ? le->line[off] : ' ' );
wattrset( le->win, le->bg_attrib);
}
/* Спрятать курсор. x в интервале 0..le->width */
void LeCursorHide( LineEdit *le ){
int x = le->pos - le->shift;
if( x < 0 || x > le->width || le->cursorOn == NO )
return;
LePutChar( le, x ); le->cursorOn = NO;
}
/* Проявить курсор */
void LeCursorShow( LineEdit *le ){
int x = le->pos - le->shift, saveattr = le->bg_attrib;
if( x < 0 || x > le->width || le->cursorOn == YES ) return;
le->bg_attrib = le->sel_attrib | (le->insert==NO ? A_BOLD : 0);
LePutChar(le, x); le->bg_attrib = saveattr;
wmove(le->win, le->top, le->left + x); le->cursorOn = YES;
SetPoint(le->savep, le->top, le->left+x);
}
/* Функция прокрутки длинной строки через окошко */
static void LeRoll( LineEdit *ptr,
int aid, int *cur, int *shift,
int width, /* ширина окна */
int len, int maxlen,
void (*go) (LineEdit *p, int x, int eraseOld),
void (*draw)(LineEdit *p), /* перерисовщик поля */
int LDX
){
int x = *cur - *shift, oldshift = *shift, newshift = oldshift;
int AID_LFT, AID_RGT, drawn = NO;
if( aid < 0 || aid > len ) return; /* incorrect */
if( x < 0 || x > width ) return; /* incorrect */
AID_LFT = MIN(LDX, maxlen);
AID_RGT = MAX(0, MIN(maxlen, width-1 - LDX));
if( aid < *cur && x <= AID_LFT && oldshift > 0 )
goto Scroll;
else if( aid > *cur && x >= AID_RGT && oldshift + width < maxlen )
goto Scroll;
if( oldshift <= aid && aid < oldshift + width )
/* прокрутка не нужна - символ уже видим */
goto Position;
Scroll:
if( aid >= *cur )
newshift = aid - AID_RGT;
else newshift = aid - AID_LFT;
if( newshift + width > maxlen || (len == maxlen && aid == len))
newshift = maxlen - width;
if( newshift < 0 )
newshift = 0;
if( newshift != oldshift ){
*shift = newshift; (*draw)(ptr); drawn = YES;
}
Position:
if((x = aid - newshift) >= width && len != maxlen )
beep(); /* ERROR */
(*go)(ptr, x, !drawn ); *cur = aid;
}
/* Поставить курсор на at-тый символ строки */
void LePointAt( LineEdit *le, int at ){
/* at == len допустимо */
if( at < 0 || at > le->len ) return;
if( le->pos == at ) return; /* уже на месте */
LeCursorHide( le );
LeRoll( le, at, & le->pos, & le->shift,
le->width, le->len, le->maxlen,
LePoint, LeDraw,
LINE_DX);
le->pos = at;
LeCursorShow( le );
}
/* Нарисовать подходящий scroll bar */
void LePoint( LineEdit *le, int x, int eraseOld ){
if(le->scrollBar)
(*le->scrollBar)(le, BAR_HOR, x + le->shift, le->maxlen+1 );
GetBack( le->savep, le->win);
}
/* Нарисовать подходящий scroll bar */
/* Вызывай это каждый раз, когда len изменится */
void LeReport( LineEdit *le ){
if(le->scrollBar)
le->scrollBar (le, BAR_VER, le->len, le->maxlen+1 );
GetBack( le->savep, le->win);
}
/* Нарисовать видимую часть строки */
void LeDraw( LineEdit *le ){
LeDrawLine( le, 0);
}
/* Удаление буквы из строки */
void LeDelCh( LineEdit *le ){
if( le->len <= 0 || le->pos < 0 || le->pos >= le->len ) return;
LeCursorHide( le );
(void) cdelete( le->line, le->pos );
le->len --;
LeDrawLine( le, le->pos - le->shift );
LeReport( le );
}
/* Вставка буквы в строку */
void LeInsCh( LineEdit *le, int c ){
if( le->len < 0 || le->pos < 0 || le->pos > le->len ) return;
LeCursorHide( le );
insert( le->line, le->pos, c );
le->len++;
LeDrawLine( le, le->pos - le->shift );
LeReport( le );
}
/* Замена буквы в строке */
void LeRepCh( LineEdit *le, int c ){
if( le->len <= 0 || le->pos < 0 || le->pos >= le->len ) return;
LeCursorHide( le );
le->line[ le->pos ] = c;
LePutChar( le, le->pos - le-> shift );
}
/* Вставка подстроки в строку редактирования */
int LeInsStr( LineEdit *le, char *s){
int len = le->len, slen = strlen(s);
register i;
if( len + slen > le->maxlen )
slen = le->maxlen - len;
if( ! slen ) return 0;
for( i=0; i < slen ; i ++ )
insert( le->line, le->pos+i, s[i] );
le->len += slen;
LeCursorHide( le );
LeDrawLine( le, le->pos - le->shift );
LePointAt( le, le->pos + slen );
LeReport( le );
return slen ;
}
/* Стирание слова */
int LeWerase( LineEdit *le, char *to ){
register i;
register char *s = le->line;
char c;
if( to ) *to = '\0';
i = le->pos;
if( s[i] == ' ' || s[i] == '\0' ){
/* найти конец слова */
for( --i; i >= 0 ; i-- )
if( s[i] != ' ' ) break;
if( i < 0 || le->len == 0 ){
beep(); return NO; }
}
/* найти начало слова */
for( ; i >= 0 && s[i] != ' ' ; i-- );
i++; /* i < 0 || s[i] == ' ' */
LeCursorHide( le ); LePointAt( le, i );
while( s[i] != ' ' && s[i] != '\0' ){
c = cdelete( s, i );
if( to ) *to++ = c;
le->len --;
}
/* удалить пробелы после слова */
while( s[i] == ' ' ){
c = cdelete( s, i );
le->len --;
}
if( to ) *to = '\0';
LeDrawLine( le, i - le->shift );
LeReport( le );
return YES;
}
/* Редактор строки
le->line что редактировать.
le->maxlen макс. длина строки.
le->win окно, содержащее поле редактирования.
le->width ширина поля редактирования.
le->top коорд-ты поля редактирования
le->left в окне win.
le->insert = YES режим вставки.
le->nc = YES стирать строку при первом нажатии.
le->histIn входная история или NULL.
le->histOut выходная история или NULL.
le->showMe функция проявки окна или NULL.
le->hideMe функция спрятывания окна или NULL.
le->hitkeys специальные клавиши или NULL.
le->handler обработчик специальных клавиш или NULL.
le->scrollBar рисовалка scroll bar-ов или NULL.
le->posMe установка позиции в строке при входе.
le->bg_attrib цвет поля.
le->fr_attrib цвет незаполненной части поля.
le->wl_attrib цвет краев поля при продолжении.
le->sel_attrib цвет символа под курсором.
*/
int LeEdit( LineEdit *le ){
int c;
int nchar = 0; /* счетчик нажатых клавиш */
Info *inf;
/* проявить окно */
if( le->showMe )
if( (*le->showMe) (le) <= 0 )
return (-1);
if( !le->win ) return (le->key = -1);
Again:
le -> pos = 0;
le -> len = strlen( le->line );
le -> shift = 0;
le -> cursorOn = NO;
le->key = (-1);
LeDraw( le );
if(le->posMe) (*le->posMe)(le);
LePointAt(le, le->pos );
LePoint( le, le->pos - le->shift, NO );
LeReport( le );
for (;;) {
LeCursorShow( le );
c = WinGetch(le->win); /* прочесть символ с клавиатуры */
nchar++; /* число нажатых клавиш */
INP:
if( le->hitkeys && le->handler ){
HandlerReply reply;
if( is_in(c, le->hitkeys)){ /* спецсимвол ? */
c = (*le->handler)(le, c, &reply);
/* Восстановить scroll bars */
LePoint( le, le->pos - le->shift, NO );
LeReport( le );
switch( reply ){
case HANDLER_CONTINUE: continue;
case HANDLER_NEWCHAR: goto INP;
case HANDLER_OUT: goto out;
case HANDLER_AGAIN: /* reset */
LeCursorHide(le); goto Again;
case HANDLER_SWITCH:
default: break; /* goto switch(c) */
}
}
}
sw:
switch (c) {
case KEY_RIGHT: /* курсор вправо */
if (le->pos != le->len && le->len > 0)
LePointAt( le, le->pos + 1);
break;
case KEY_LEFT: /* курсор влево */
if (le->pos > 0)
LePointAt(le, le->pos - 1);
break;
case '\t': /* табуляция вправо */
if (le->pos + 8 > le->len)
LePointAt(le, le->len);
else
LePointAt(le, le->pos + 8);
break;
case KEY_BACKTAB: /* табуляция влево */
case ctrl('X'):
if( le->pos - 8 < 0 )
LePointAt(le, 0);
else LePointAt(le, le->pos - 8 );
break;
case KEY_HOME: /* в начало строки */
LePointAt(le, 0); break;
case KEY_END: /* в конец строки KEY_LL */
if( le->len > 0 )
LePointAt(le, le->len);
break;
case 0177: /* стереть символ перед курсором */
case KEY_BACKSPACE:
case '\b':
if (le->pos == 0) break;
LePointAt(le, le->pos - 1); /* налево */
/* и провалиться в DC ... */
case KEY_F (6): /* стереть символ над курсором */
case KEY_DC:
if (! le->len || le->pos == le->len)
break;
LeDelCh(le);
break;
case KEY_UP: /* вызвать историю */
case KEY_DOWN:
case KEY_NPAGE:
case KEY_PPAGE:
case KEY_F(4):
if( ! le->histIn ) break;
/* иначе позвать историю */
inf = HistSelect( le->histIn,
wbegx(le->win) + le->pos - le->shift + 2, le->top + 1);
if( inf == (Info *) NULL )
break;
LeCursorHide( le );
strncpy( le->line, inf->s, le->maxlen );
goto Again;
out: case '\r': case '\n': case ESC:
/* ввод завершен - выйти */
LeCursorHide( le );
if( c != ESC && le->histOut && *le->line )
/* запомнить строку в историю */
HistAdd( le->histOut, le->line, 0);
if( le->hideMe ) /* спрятать окно */
(*le->hideMe)(le);
return (le->key = c);
case KEY_F (8): /* стереть всю строку */
case ctrl('U'):
le->line[0] = '\0';
le->len = le->pos = le->shift = 0;
LeCursorHide( le );
LeReport( le );
goto REWRITE;
case KEY_F(0): /* F10: стереть до конца строки */
if( le->pos == le->len ) break;
le->line[ le->pos ] = '\0';
le->len = strlen( le->line );
LeCursorHide( le );
LeDrawLine( le, le->pos - le->shift );
LeReport( le );
break;
case ctrl('W'): /* стереть слово */
LeWerase( le, NULL );
break;
case ctrl('A'): /* перерисовка */
LeCursorHide(le); /* RedrawScreen(); */
REWRITE: LeDraw(le);
break;
case KEY_F(7): /* переключить режим вставки/замены */
le->insert = ! le->insert;
LeCursorHide( le );
break;
#ifndef M_UNIX
case ctrl('V'): /* ввод заэкранированного символа */
nchar--;
c = WinGetch(le->win);
nchar++;
if( c >= 0400 ) goto sw;
goto Input;
#endif
case 0: break;
default: /* ввод обычного символа */
if (c >= 0400 || ! isprint(c)) break;
Input: if( le->nc && nchar == 1 && le->insert &&
/*le->pos == 0 &&*/ le->len != 0 ){
/* если это первая нажатая кнопка, то
/* удалить все содержимое строки
/* и заменить ее нажатой буквой */
le->shift = 0;
le->len = le->pos = 1;
le->line[0] = c;
le->line[1] = '\0';
LeCursorHide( le );
LeReport( le );
goto REWRITE;
}
if (!le->insert) {
/* REPLACE - режим замены */
if (le->pos == le->len)
goto AddChar; /* временный INSERT */
LeRepCh( le, c );
LePointAt( le, le->pos + 1 );
} else {
/* INSERT - режим вставки */
AddChar:
if( le->len >= le->maxlen ){
beep(); /* строка переполнена */
break;
}
LeInsCh( le, c );
LePointAt( le, le->pos + 1 );
} /* endif */
} /* endswitch */
} /* endfor */
} /* endfunc */
.
/* Пример 22 */
/* ______________________________________________________________ */
/* ПРЯМОУГОЛЬНАЯ ТАБЛИЦА-МЕНЮ */
/* _______________________ файл table.h _________________________ */
typedef struct _Table { /* Паспорт таблицы */
int nitems; /* количество элементов в таблице */
Info *items; /* массив элементов */
char *fmt; /* формат вывода */
int key; /* кнопка, завершившая выбор в таблице */
int current; /* номер выбранного элемента */
int shift; /* число элементов перед окном */
WINDOW *win; /* окно в котором размещена таблица */
int left, top, height, width; /* размеры и расположение
таблицы в окне */
int space; /* интервал между колонками */
int elen; /* макс. ширина колонки */
int tcols; /* число колонок в таблице */
int cols; /* число колонок в видимой части таблицы */
int cutpos; /* позиция для обрубания слишком длинных строк */
int scrollok; /* роллируется ? */
int exposed; /* нарисована ? */
int elems; /* текущее число эл-тов в подокне */
int maxelems; /* максимальное число эл-тов в подокне */
int maxshift; /* максимальный сдвиг */
int bg_attrib, sel_attrib; /* цвет фона и выбранного
элемента */
Point savep;
/* Функции проявки/спрятывания окна */
int (*showMe)(struct _Table *tbl);
void (*hideMe)(struct _Table *tbl);
void (*scrollBar)(struct _Table *tbl, int whichbar, int n, int among);
/* Обработчик специальных клавиш */
int *hitkeys;
int (*handler)(struct _Table *tbl, int c, HandlerReply *reply);
} Table;
#define T_BOLD M_BOLD
#define T_NOSEL I_NOSEL
#define T_HATCH M_HATCH
#define T_LABEL M_LABEL
#define T_SET(m, i, flg) (((m)->items)[i]).fl |= (flg)
#define T_CLR(m, i, flg) (((m)->items)[i]).fl &= ~(flg)
#define T_TST(m, i, flg) ((((m)->items)[i]).fl & (flg))
#define T_ITEM(m, i) ((((m)->items)[i]).s)
/* Формат 'd' ниже вставлен лишь для текущего состояния использования
* форматов в нашем проекте: */
#define T_ITEMF(m, i, cut) \
((m)->fmt && *(m)->fmt != 'd' ? \
TblConvert(T_ITEM((m), i), (m)->fmt, (cut)) : T_ITEM((m), i))
#define T_VISIBLE(tbl, new) ((tbl)->exposed == YES && \
(new) >= (tbl)->shift && (new) < (tbl)->shift + (tbl)->elems)
#define TLABSIZE 2 /* ширина поля меток */
#define T_REFUSED(t) ((t)->key < 0 || (t)->key == ESC )
int TblCount( Table *tbl );
void TblInit( Table *tbl, int forcedOneColumn );
void TblChkCur ( Table *tbl );
int TblChkShift( Table *tbl );
void TblChk ( Table *tbl );
char *TblConvert( char *s, char *fmt, int cutpos );
void TblPointAt ( Table *tbl, int snew );
void TblPoint ( Table *tbl, int snew, int eraseOld );
void TblDraw ( Table *tbl );
void TblDrawItem( Table *tbl, int at, int reverse, int selection);
void TblBox ( Table *tbl, int at, int reverse, int hatched,
int width, int axl, int axi, int ay);
int TblClear( Table *tbl );
int TblPlaceByName( Table *tbl, char *p );
void TblReport( Table *tbl );
void TblTag ( Table *tbl, int at, int flag);
void TblUntag( Table *tbl, int at, int flag);
void TblRetag( Table *tbl, int at, int flag);
void TblTagAll( Table *tbl, char *pattern, int flag );
void TblUntagAll( Table *tbl, char *pattern, int flag );
int TblUsualSelect( Table *tbl );
/* _______________________ файл table.c _________________________ */
#include "w.h"
#include "glob.h"
#include "menu.h"
#include "table.h"
extern char STRING_BUFFER[MAXLEN]; /* imported from menu.c */
/* надо указать размер, чтоб работал sizeof(STRING_BUFFER) */
/* Переформатировать строку по формату fmt для выдачи в таблицу.
* Пока предложена простейшая интерпретация. */
char *TblConvert( char *s, char *fmt, int cutpos ){
if( fmt && *fmt == 'd'){
register i, j, len; char *p = strrchr(s, '.');
if((len = strlen(s)) < DIR_SIZE && *s != '.' && p ){
int sufxlen = strlen(p);
for(i=0; i < len - sufxlen ; ++i) STRING_BUFFER[i] = s[i];
for(; i < DIR_SIZE - sufxlen; ++i) STRING_BUFFER[i] = ' ';
for(j=0; i < DIR_SIZE; j++, ++i) STRING_BUFFER[i] = p[j];
STRING_BUFFER[i] = '\0';
} else strcpy(STRING_BUFFER, s);
if(cutpos > 0 && cutpos < sizeof(STRING_BUFFER))
STRING_BUFFER[cutpos] = '\0';
} else { /* без формата, только обрубание */
if( cutpos <= 0 ) cutpos = 32000; /* Обрубание выключено */
strncpy(STRING_BUFFER, s, MIN(sizeof(STRING_BUFFER) - 1, cutpos));
}
return STRING_BUFFER;
}
/* Обрубить s до длины cutpos букв */
char *TblCut( char *s, int cutpos ){
if( cutpos <= 0 ) return s;
strncpy(STRING_BUFFER, s, MIN(sizeof(STRING_BUFFER) - 1, cutpos));
return STRING_BUFFER;
}
/* Подсчет элементов таблицы и ширины столбца */
int TblCount( Table *tbl ){
register i, L, LL; char *s;
L = i = 0;
if( tbl->items)
while((s = T_ITEM(tbl, i)) != NULL ){
if( tbl->fmt )
s = TblConvert(s, tbl->fmt, 0);
LL = strlen(s);
if( LL > L ) L = LL;
i++;
}
tbl->nitems = i; return L;
}
/* Разметка таблицы. На входе:
t->items Массив данных, показываемый в меню.
t->exposed = NO Таблица уже нарисована ?
t->fmt Формат строк, выводимых в таблицу.
t->win Окно для размещения таблицы.
t->showMe Функция проявки окна.
t->hideMe Функция упрятывания окна.
t->hitkeys Специальные клавиши []. Конец -1.
t->handler Обработчик или NULL.
t->width Ширина поля таблицы.
t->height Высота поля таблицы.
t->left Левый край таблицы в окне.
t->top Верхний край таблицы в окне.
t->scrollBar Функция рисования scroll-bar-а или NULL.
t->bg_attrib Цвет фона (== цвету фона окна).
t->sel_attrib Цвет выбранного элемента.
forcedOneColumn == YES делает таблицу в 1 колонку.
*/
void TblInit( Table *tbl, int forcedOneColumn ){
int mlen = TblCount( tbl ); /* самый широкий элемент таблицы */
/* усечь до ширины таблицы */
if( mlen > tbl->width || forcedOneColumn )
mlen = tbl->width; /* слишком широко */
/* ширина столбца таблицы = ширина элемента + поле меток + разделитель */
tbl->elen = mlen + TLABSIZE + 1;
/* #####строка_элемент| */
/* метки элемент 1 */
/* число столбцов во всей таблице */
tbl->tcols = (tbl->nitems + tbl->height - 1) / tbl->height;
/* число столбцов, видимых через окно (+1 для ошибок округления) */
tbl->cols = tbl->width / (tbl->elen + 1);
if( tbl->cols == 0 ){ /* слишком широкая таблица */
tbl->cols = 1; /* таблица в одну колонку */
tbl->elen = tbl->width - 2;
mlen = tbl->elen - (TLABSIZE + 1);
tbl->cutpos = mlen; /* и придется обрубать строки */
} else tbl->cutpos = 0; /* без обрубания */
tbl->cols = MIN(tbl->cols, tbl->tcols);
/* интервал между колонками */
tbl->space = (tbl->width - tbl->cols * tbl->elen)/(tbl->cols+1);
if( tbl->space < 0 ){ beep(); tbl->space = 0; }
/* сколько элементов умещается в окно */
tbl->maxelems = tbl-> cols * tbl->height;
tbl->maxshift = (tbl->tcols * tbl->height) - tbl->maxelems;
if( tbl->maxshift < 0 ) tbl->maxshift = 0;
/* требуется ли роллирование таблицы через окно */
tbl->scrollok = (tbl->nitems > tbl->maxelems);
tbl->elems = tbl->shift = tbl->current = 0; /* пока */
tbl->exposed = NO; /* таблица еще не нарисована */
tbl->key = (-1);
}
/* Проверить корректность текущей позиции */
void TblChkCur( Table *tbl ){
if( tbl->current >= tbl->nitems )
tbl->current = tbl->nitems - 1;
if( tbl->current < 0 )
tbl->current = 0;
}
/* Проверить корректность сдвига (числа элементов ПЕРЕД окном) */
int TblChkShift( Table *tbl ){
register int oldshift = tbl->shift;
/* в колонке должно быть видно достаточно много элементов */
if( tbl->cols == 1 && /* таблица в 1 колонку */
tbl->tcols > 1 && /* но всего в ней не одна колонка */
tbl->nitems - tbl->shift < tbl->height / 2 + 1
) tbl->shift = tbl->nitems - (tbl->height/2 + 1);
if( tbl->shift > tbl->maxshift )
tbl->shift = tbl->maxshift;
if( tbl->shift < 0 )
tbl->shift = 0;
return tbl->shift != oldshift; /* скорректировано ? */
}
/* Проверить корректность параметров таблицы */
void TblChk( Table *tbl ){
again:
TblChkCur( tbl ); TblChkShift( tbl );
if( tbl -> maxelems ){
if( tbl -> current >= tbl->shift + tbl->maxelems ){
tbl->shift = tbl->current - (tbl->maxelems - 1);
goto again;
}
if( tbl->current < tbl->shift ){
tbl->shift = tbl->current; goto again;
}
}
}
/* Указать на snew-тый элемент списка, перерисовать картинку */
void TblPointAt( Table *tbl, int snew ){
int curCol; /* текущий столбец всей таблицы (для current) */
int newCol; /* нужный столбец таблицы (для snew) */
int colw; /* нужный столбец ОКНА (для snew) */
int gap; /* зазор */
int newshift = tbl->shift; /* новый сдвиг окна от начала массива */
int drawn = NO; /* таблица целиком перерисована ? */
/* ПРоверить корректность номера желаемого элемента */
if( snew < 0 ) snew = 0;
if( snew >= tbl->nitems ) snew = tbl->nitems - 1;
if( tbl->current == snew && tbl->exposed == YES)
return; /* уже стоим на требуемом элементе */
#define WANTINC 1
#define WANTDEC (tbl->cols-1-WANTINC)
gap = (tbl->height - (tbl->shift % tbl->height)) % tbl->height;
/* gap - это смещение, которое превращает строгую
постолбцовую структуру
--0-- --3--
--1-- --4--
--2-- --5--
в сдвинутую структуру
____ |------
gap=2___/ пусто g0 --1-- g3 | --4-- g6 ....
\____пусто g1 --2-- g4 | --5-- g7
--0-- g2 --3-- g5 | --6-- g8
|------ shift=4
*/
/* операция прокрутки данных через таблицу: TblRoll() _________________*/
/* Элемент уже виден в текущем окне ? */
/* Параметр elems вычисляется в TblDraw() */
if( T_VISIBLE(tbl, snew))
goto ThisWindow;
/* smooth scrolling (гладкое роллирование) */
if( snew == tbl->shift + tbl->elems &&
/* элемент непосредственно следующий ЗА окном */
tbl->current == tbl->shift + tbl->elems - 1
/* курсор стоит в нижнем правом углу окна */
){
newshift++; gap--;
if ( gap < 0 ) gap = tbl->height - 1 ;
goto do_this;
}
if( snew == tbl->shift - 1 &&
/* элемент непосредственно стоящий ПЕРЕД окном */
tbl->current == tbl->shift
/* и курсор стоит в верхнем левом углу окна таблицы */
){
newshift --; gap = (gap + 1) % tbl->height;
goto do_this;
}
/* jump scrolling (прокрутка скачком) */
curCol = (tbl->current+gap) / tbl->height;
newCol = (snew +gap) / tbl->height;
if( tbl->cols > 1 ){
if( newCol > curCol ) colw = WANTINC;
else colw = WANTDEC;
} else colw = 0;
newshift = (newCol - colw) * tbl->height - gap ;
do_this:
if( tbl->shift != newshift || tbl->exposed == NO){
tbl->shift = newshift;
TblChkShift( tbl ); /* >= 0 && <= max */
TblDraw( tbl ); /* перерисовать все окно с нового места */
drawn = YES; /* перерисовано целиком */
}
ThisWindow: /* поставить курсор в текущем окне без перерисовки окна */
TblPoint( tbl, snew, !drawn );
/* tbl->current = snew; сделается в TblPoint() */
}
/* Поставить курсор на элемент в текущем окне */
void TblPoint ( Table *tbl, int snew, int eraseOld ){
if( ! T_VISIBLE(tbl, snew)){
beep(); /* ERROR !!! */ return;
}
if( eraseOld && tbl->current != snew )
TblDrawItem( tbl, tbl->current, NO, YES );
TblDrawItem( tbl, snew, YES, YES );
tbl->current = snew;
TblReport( tbl );
}
/* Нарисовать scroll bar в нужной позиции. Кроме того,
* в эту функцию можно включить и другие действия, например
* выдачу имени T_ITEM(tbl, tbl->current) на рамке окна. */
void TblReport( Table *tbl ){
if ( tbl->scrollBar )
(*tbl->scrollBar)( tbl, BAR_VER|BAR_HOR,
tbl->current, tbl->nitems);
GetBack( tbl->savep, tbl->win ); /* курсор на место ! */
}
/* Перерисовать все окно таблицы */
void TblDraw( Table *tbl ){
register next;
/* число элементов в таблице (может остаться незанятое
* место в правой нижней части окна */
tbl->elems = MIN(tbl->nitems - tbl->shift, tbl->maxelems );
for( next = 0; next < tbl->maxelems; next++ )
TblDrawItem(tbl, next + tbl->shift, NO, tbl->scrollok ? YES : NO);
tbl->exposed = YES; /* окно изображено */
}
/* Нарисовать элемент таблицы */
void TblDrawItem( Table *tbl, int at, int reverse, int selection){
register WINDOW *w = tbl->win;
int pos; char *s; int hatch, bold, label, under;
int ax, axl, ay, column;
if( at >= 0 && at < tbl->nitems ){
s = T_ITEM( tbl, at );
if( tbl->fmt )
s = TblConvert(s, tbl->fmt, tbl->cutpos);
else if( tbl->cutpos > 0 )
s = TblCut(s, tbl->cutpos);
/* выделения */
hatch = T_TST( tbl, at, T_HATCH );
bold = T_TST( tbl, at, T_BOLD );
label = T_TST( tbl, at, T_LABEL );
under = T_TST( tbl, at, I_EXE );
} else { s = "~"; label = hatch = bold = under = NO; }
at -= tbl->shift; /* координату в списке перевести в коорд. окна */
ay = tbl->top + at % tbl->height;
column = at / tbl->height;
/* начало поля меток */
axl = tbl->left + tbl->space + column * (tbl->space + tbl->elen);
/* начало строки-элемента */
ax = axl + TLABSIZE;
if(selection)
TblBox( tbl, at, reverse, reverse && hatch, strlen(s), axl, ax, ay );
wattrset (w, reverse ? tbl->sel_attrib : tbl->bg_attrib);
if( hatch ) wattron(w, A_ITALICS);
if( bold ) wattron(w, A_BOLD);
if( under ) wattron(w, A_UNDERLINE);
mvwaddstr(w, ay, ax, s);
wattrset(w, tbl->bg_attrib | (bold ? A_BOLD:0));
if( label ) mvwaddch(w, ay, axl, LABEL);
if( under ){ wattron(w, A_BOLD); mvwaddch(w, ay, axl+1, BOX_HATCHED);}
wattrset(w, tbl->bg_attrib);
if( column != tbl->cols-1 ) /* не последний столбец */
mvwaddch(w, ay, axl+tbl->elen-1 + (tbl->space+1)/2, VER_LINE);
wmove(w, ay, ax-1); /* курсор перед началом строки */
SetPoint(tbl->savep, ay, ax-1); /* запомнить координаты курсора */
}
/* Зачистить область окна для рисования элемента таблицы */
void TblBox(Table *tbl, int at, int reverse, int hatched,
int width, int axl, int axi, int ay){
register WINDOW *w = tbl->win;
int len = tbl->elen;
wattrset (w, tbl->bg_attrib);
wboxerase(w, axl, ay, axl+len-1, ay);
wattrset (w, reverse ? tbl->sel_attrib : tbl->bg_attrib);
/* если ниже задать axl+len+1, то подсвеченный
* прямоугольник будет фиксированного размера */
wboxerase(w, axi, ay, axl+width-1, ay);
wattrset (w, tbl->bg_attrib);
}
/* Зачистить прямоугольную рабочую область окна tbl->win,
* в которой будет изображаться таблица.
* Эта функция нигде не вызывается ЯВНО, поэтому ВЫ должны
* вызывать ее сами после каждого TblInit() -
* для этого удобно поместить ее в демон (*showMe)();
*/
int TblClear( Table *tbl ){
tbl->exposed = NO;
tbl->elems = 0; /* Это всегда происходит при exposed:= NO */
wboxerase( tbl->win,
tbl->left, tbl->top,
tbl->left + tbl->width - 1,
tbl->top + tbl->height - 1);
return 1;
}
/* Пометить элемент в таблице */
void TblTag( Table *tbl, int at, int flag){
if( T_TST(tbl, at, flag)) return;
T_SET(tbl, at, flag);
if( T_VISIBLE(tbl, at))
TblDrawItem(tbl, at, tbl->current == at ? YES:NO, YES );
}
/* Снять пометку с элемента таблицы */
void TblUntag( Table *tbl, int at, int flag){
if( ! T_TST(tbl, at, flag)) return;
T_CLR(tbl, at, flag);
if( T_VISIBLE(tbl, at))
TblDrawItem(tbl, at, tbl->current == at ? YES:NO, YES );
}
/* Изменить пометку элемента таблицы */
void TblRetag( Table *tbl, int at, int flag){
if( T_TST(tbl, at, flag)) T_CLR(tbl, at, flag);
else T_SET(tbl, at, flag);
if( T_VISIBLE(tbl, at))
TblDrawItem(tbl, at, tbl->current == at ? YES:NO, YES );
}
/* Используется в match() для выдачи сообщения об ошибке */
void TblMatchErr(){}
/* Пометить элементы, чьи имена удовлетворяют шаблону */
void TblTagAll( Table *tbl, char *pattern, int flag ){
register i;
for(i=0; i < tbl->nitems; i++)
if( !T_TST(tbl, i, I_DIR) && match( T_ITEMF(tbl, i, 0), pattern))
TblTag( tbl, i, flag );
}
/* Снять пометки с элементов по шаблону имени */
void TblUntagAll( Table *tbl, char *pattern, int flag ){
register i;
for(i=0; i < tbl->nitems; i++)
if( match( T_ITEMF(tbl, i, 0), pattern))
TblUntag( tbl, i, flag );
}
/* Указать на элемент по шаблону его имени */
int TblPlaceByName( Table *tbl, char *p ){
register i; char *s;
for( i=0; i < tbl->nitems; i++ ){
s = T_ITEMF(tbl, i, 0);
if( match( s, p )){
if( tbl->exposed == NO ){
/* Задать некорректный shift,
* чтобы окно полностью перерисовалось */
tbl->shift = tbl->nitems+1; tbl->elems = 0;
}
TblPointAt( tbl, i );
return i;
}
} return (-1);
}
/* Перемещение по таблице набором первых букв названия элемента */
static int TblTrack( Table *tbl, int c){
char *s; register i;
int from; /* с какого элемента начинать поиск */
int found = 0; /* сколько было найдено */
int plength = 0;
int more = 0;
char pattern[20];
if( c >= 0400 || iscntrl(c)){ beep(); return 0; }
AddCh:
from = 0;
pattern[plength] = c;
pattern[plength+1] = '*';
pattern[plength+2] = '\0';
plength++;
More:
for(i = from; i < tbl->nitems; i++){
s = T_ITEMF(tbl, i, 0);
if( match(s, pattern)){
++found; from = i+1;
TblPointAt( tbl, i );
c = WinGetch( tbl->win );
switch(c){
case '\t': /* find next matching */
more++;
goto More;
case KEY_BACKSPACE: case '\177': case '\b':
if( plength > 1 ){
plength--;
pattern[plength] = '*';
pattern[plength+1] = '\0';
from = 0; more++;
goto More;
} else goto out;
default:
if( c >= 0400 || iscntrl(c)) return c;
if( plength >= sizeof pattern - 2 ) goto out;
goto AddCh;
}
}
}
/* не найдено */
if(more && found){ /* нет БОЛЬШЕ подходящих, но ВООБЩЕ - есть */
beep(); more = found = from = 0; goto More; }
out: beep(); return 0;
}
/* Выбор в таблице */
int TblUsualSelect( Table *tbl ){
int c, want;
tbl->key = (-1);
if( tbl->items == NULL || tbl->nitems <= 0 ) return TOTAL_NOSEL;
TblChk( tbl );
if( tbl->showMe )
if((*tbl->showMe)(tbl) <= 0 )
return (-1);
if( !tbl->win ) return TOTAL_NOSEL;
if( tbl->exposed == NO ){
TblDraw ( tbl );
}
/* Указать текущий элемент */
TblPoint( tbl, tbl->current, NO);
TblReport( tbl );
for( ;; ){
c = WinGetch(tbl->win);
INP:
if( tbl->hitkeys && tbl->handler ){
HandlerReply reply;
if( is_in(c, tbl->hitkeys)){
c = (*tbl->handler)(tbl, c, &reply);
TblReport( tbl ); /* restore scroll bar */
switch( reply ){
case HANDLER_CONTINUE: continue;
case HANDLER_NEWCHAR: goto INP;
case HANDLER_OUT: goto out;
case HANDLER_SWITCH:
default: break; /* goto switch(c) */
}
}
}
sw: switch( c ){
case KEY_LEFT:
want = tbl->current - tbl->height; goto mv;
case KEY_RIGHT:
want = tbl->current + tbl->height; goto mv;
case KEY_UP:
want = tbl->current - 1; goto mv;
case KEY_DOWN:
next:
want = tbl->current + 1; goto mv;
case KEY_HOME:
want = 0; goto mv;
case KEY_END:
want = tbl->nitems - 1; goto mv;
case KEY_NPAGE:
want = tbl->current + tbl->elems; goto mv;
case KEY_PPAGE:
want = tbl->current - tbl->elems; goto mv;
case KEY_IC:
if( T_TST(tbl, tbl->current, T_LABEL ))
T_CLR(tbl, tbl->current, T_LABEL );
else T_SET(tbl, tbl->current, T_LABEL);
if( tbl->current == tbl->nitems - 1 /* LAST */){
TblPoint(tbl, tbl->current, NO );
break;
}
TblPointAt(tbl, tbl->current );
/* if not goto next;
* but break;
* then use
* TblPoint(tbl, tbl->current, NO);
* here
*/
goto next;
case KEY_DC:
if( T_TST(tbl, tbl->current, T_HATCH ))
T_CLR(tbl, tbl->current, T_HATCH );
else T_SET(tbl, tbl->current, T_HATCH);
if( tbl->current == tbl->nitems - 1 /* LAST */){
TblPoint(tbl, tbl->current, NO );
break;
}
TblPointAt(tbl, tbl->current );
goto next;
case ESC:
case '\r':
case '\n':
goto out;
case 0: break;
default:
c = TblTrack(tbl, c);
if( c ) goto INP;
break;
}
continue;
mv: TblPointAt( tbl, want );
}
out: wnoutrefresh( tbl->win );
if( tbl->hideMe ) (*tbl->hideMe)(tbl);
return ((tbl->key = c) == ESC ? -1 : tbl->current );
}
.
# Пример 23 - simple visual shell.
# UNIX commander
#########################################################################
# Это файл Makefile для проекта uxcom - простого меню-ориентированного
# экранного интерфейса для переходов по файловой системе.
# Ключ -Iкаталог указывает из какого каталога должны браться
# include-файлы, подключаемые по #include "имяФайла".
# Проект состоит из нескольких файлов:
# Пример 17, Пример 18, Пример 19, Пример 21, Пример 23 и других.
#
# + Left Right _Commands Tools Sorttype +
# | /usr/a+---------------------008/013-+ |
# +-----------------| Главное меню |---+--+
# | .. +--------------------------+--+ | |
# | .BAD | Current directory | | | |
# | .contents.m| Root directory | | |##|
# | DUMP | Menus | | | |
# | Makefile +--------------------------+ | | |
# | PLAN | Help | | | |
# | _points | Unimplemented | | | |
# | table | Change sorttype |##| | |
# | #unbold | _Look directory history | | | |
# | #uxcom +--------------------------+ | | |
# | x.++ | Quit | | | |
# | 00 +--------------------------+ | | |
# | 11 | Redraw screen | | | |
# | LOOP_p +--------------------------+--+ | |
# | LOOP_q .c | etc | |
# | LOOP_strt .c | install | |
# +-------------------------+-------------------------+ |
# | points 165 -r--r-- | .cshrc 2509 -rw-r--r-- | |
# +-------------------------+-------------------------+ |
# | История путешествий | |
# +---------------------------------------------------+--+
#
SHELL=/bin/sh
SRCS = glob.c w.c menu.c pull.c match.c pwd.c hist.c line.c table.c \
main.c treemk.c
OBJS = glob.o w.o menu.o pull.o match.o pwd.o hist.o line.o table.o \
main.o treemk.o
# INCLUDE = /usr/include
# LIB = -lncurses
INCLUDE = -I../../src/curses
LIB = ../../src/curses/libncurses.a
DEFINES = -DUSG -DTERMIOS
CC = cc -O # стандартный C-compiler + оптимизация
#CC = gcc -O # GNU C-compiler
uxcom: $(OBJS)
$(CC) $(OBJS) -o $@ $(LIB)
sync; ls -l $@; size $@
glob.o: glob.c glob.h # это файл "Пример 18"
$(CC) -c glob.c
w.o: w.c w.h # это файл "Пример 17"
$(CC) -c $(INCLUDE) $(DEFINES) w.c
menu.o: menu.c glob.h w.h menu.h # это файл "Пример 19"
$(CC) -c $(INCLUDE) $(DEFINES) menu.c
pull.o: pull.c glob.h w.h menu.h pull.h # это файл "Пример 20"
$(CC) -c $(INCLUDE) $(DEFINES) pull.c
match.o: match.c
$(CC) -c -DMATCHONLY \
-DMATCH_ERR="TblMatchErr()" match.c
pwd.o: pwd.c
$(CC) -c -DU42 -DCWDONLY pwd.c
treemk.o: treemk.c
$(CC) -c $(DEFINES) \
-DERR_CANT_READ=tree_err_cant_read \
-DERR_NAME_TOO_LONG=tree_name_too_long \
-DTREEONLY -DU42 treemk.c
hist.o: hist.c hist.h glob.h menu.h w.h # это файл "Пример 21"
$(CC) -c $(INCLUDE) $(DEFINES) hist.c
line.o: line.c w.h glob.h menu.h hist.h line.h # "Пример 21"
$(CC) -c $(INCLUDE) $(DEFINES) line.c
table.o: table.c w.h glob.h menu.h table.h # "Пример 22"
$(CC) -c $(INCLUDE) $(DEFINES) table.c
main.o: main.c glob.h w.h menu.h hist.h line.h pull.h table.h
$(CC) -c $(INCLUDE) $(DEFINES) main.c
w.h: wcur.h
touch w.h
/* _______________________ файл main.c __________________________ */
/* Ниже предполагается, что вы раскрасили в /etc/termcap *
* выделения A_STANDOUT и A_REVERSE в РАЗНЫЕ цвета ! */
#include "w.h"
#include "glob.h"
#include "menu.h"
#include "hist.h"
#include "line.h"
#include "table.h"
#include "pull.h"
#include <signal.h>
#include <ustat.h>
#include <locale.h>
void t_enter(), t_leave();
LineEdit edit; /* редактор строки */
Hist hcwd, hedit, hpat; /* истории: */
/* посещенные каталоги, набранные команды, шаблоны имен */
Menu mwrk, msort; /* должны иметь класс static */
PullMenu pull;
typedef enum { SEL_WRK=0, SEL_PANE1, SEL_PANE2, SEL_PULL, SEL_HELP } Sel;
Sel current_menu; /* текущее активное меню */
Sel previous_menu; /* предыдущее активное меню */
#define SEL_PANE (current_menu == SEL_PANE1 || current_menu == SEL_PANE2)
typedef struct {
Table t; /* таблица с именами файлов */
DirContents d; /* содержимое каталогов */
} FileWidget;
FileWidget tpane1, tpane2; /* левая и правая панели */
FileWidget *A_pane = &tpane1; /* активная панель */
FileWidget *B_pane = &tpane2; /* противоположная панель */
#define A_tbl (&A_pane->t)
#define A_dir (&A_pane->d)
#define B_tbl (&B_pane->t)
#define B_dir (&B_pane->d)
#define TblFW(tbl) ((tbl) == A_tbl ? A_pane : B_pane)
void ExchangePanes(){ /* Обменять указатели на панели */
FileWidget *tmp = A_pane; A_pane = B_pane; B_pane = tmp;
current_menu = (current_menu == SEL_PANE1 ? SEL_PANE2 : SEL_PANE1);
}
#define Other_pane(p) ((p) == A_pane ? B_pane : A_pane)
#define Other_tbl(t) ((t) == A_tbl ? B_tbl : A_tbl )
WINDOW *panewin; /* окно, содержащее обе панели = stdscr */
typedef enum { NORUN=0, RUNCMD=1, CHDIR=2, TAG=3, FIND=4 } RunType;
#define REPEAT_KEY 666 /* псевдоклавиша "повтори выбор в меню" */
#define LEAVE_KEY 777 /* псевдоклавиша "покинь это меню" */
#define NOSELECTED (-1) /* в меню ничего пока не выбрано */
#define CENTER (COLS/2-2) /* линия раздела панелей */
int done; /* закончена ли программа ? */
char CWD[MAXLEN]; /* полное имя текущего каталога */
char SELECTION[MAXLEN]; /* имя выбранного файла */
/*-----------------------------------------------------------------*/
/* Выдать подсказку в строке редактора */
/*-----------------------------------------------------------------*/
#include <stdarg.h>
void Message(char *s, ... ){
char msg[80]; va_list args; int field_width;
va_start(args, s); vsprintf(msg, s, args); va_end(args);
wattrset (panewin, A_tbl->sel_attrib);
field_width = A_tbl->width + B_tbl->width - 3;
mvwprintw (panewin, LINES-2, tpane1.t.left+1, " %*.*s ",
-field_width, field_width, msg);
wattrset (panewin, A_tbl->bg_attrib);
wnoutrefresh(panewin);
}
/*-----------------------------------------------------------------*
* Меню порядка сортировки имен файлов. *
*-----------------------------------------------------------------*/
Info sort_info[] = {
{ "По возрастанию", 0}, { "По убыванию", 0},
{ "По суффиксу", 0}, { "Без сортировки", 0},
{ "По размеру", M_HATCH},
{ NULL, 0}
};
/* При входе в меню сортировки указать текущий тип сортировки */
void sort_show(Menu *m){
MnuPointAt(&msort, (int) sorttype);
}
/* Выбрать тип сортировки имен файлов */
static void SelectSortType(int sel){
if( sel == NOSELECTED )
sel = MnuUsualSelect(&msort, NO);
MnuHide(&msort);
current_menu = previous_menu;
if(M_REFUSED(&msort)) return;
sorttype = (Sort) sel;
A_dir->lastRead = B_dir->lastRead = 0L; /* форсировать перечитку */
/* но ничего явно не пересортировывать и не перерисовывать */
}
/*-----------------------------------------------------------------*
* Отслеживание содержимого каталогов и переинициализация меню. *
*-----------------------------------------------------------------*/
#define NON_VALID(d) ((d)->readErrors || (d)->valid == NO)
/* Сменить содержимое таблицы и списка файлов */
void InitTblFromDir(FileWidget *wd, int chdired, char *savename){
char *msg, *name; Table *tbl = &(wd->t); DirContents *d = &wd->d;
int saveind = tbl->current, saveshift = tbl->shift;
char *svname = NULL;
if(tbl->nitems > 0 ) svname = strdup(T_ITEMF(tbl, saveind, 0));
/* Несуществующие и нечитаемые каталоги выделить особо */
if( NON_VALID(d)) wattrset(tbl->win, A_REVERSE);
TblClear(tbl);
if(d->valid == NO){
msg = "Не существует"; name = d->name; goto Report;
} else if(d->readErrors){ /* тогда d->files->s == NULL */
msg = "Не читается"; name = d->name;
Report: mvwaddstr(tbl->win, tbl->top + tbl->height/2,
tbl->left + (tbl->width - strlen(name))/2, name);
mvwaddstr(tbl->win, tbl->top + tbl->height/2+1,
tbl->left + (tbl->width - strlen(msg))/2, msg);
}
wattrset(tbl->win, tbl->bg_attrib);
tbl->items = d->files; TblInit(tbl, NO);
/* Постараться сохранить позицию в таблице */
if( chdired ) TblPlaceByName(tbl, savename);
else {
if( svname == NULL || TblPlaceByName(tbl, svname) < 0 ){
tbl->shift = saveshift;
tbl->current = saveind; TblChk(tbl);
}
}
if(svname) free(svname);
}
/* Перейти в каталог и запомнить его полное имя */
int mychdir(char *newdir){ int code = chdir(newdir);
if( code < 0 ) return code;
getwd(CWD); in_the_root = (strcmp(CWD, "/") == 0);
HistAdd(&hcwd, CWD, 0); /* запомнить в истории каталогов */
t_enter(&tpane1.t); /* на рамке нарисовать имя текущего каталога */
return code;
}
/* Изменить текущий каталог и перечитать его содержимое */
int cd(char *newdir, FileWidget *wd, char *oldname){
char oldbase[MAXLEN], *s, *strrchr(char *,char);
/* Спасти в oldbase базовое имя старого каталога oldname (обычно CWD) */
if(s = strrchr(oldname, '/')) s++; else s = oldname;
strcpy(oldbase, s);
if( mychdir(newdir) < 0){ /* не могу перейти в каталог */
Message("Не могу перейти в %s", *newdir ? newdir : "???");
beep(); return (-1); }
if( ReadDir(CWD, &wd->d)){ /* содержимое изменилось */
InitTblFromDir (wd, YES, oldbase);
return 1;
}
return 0;
}
/* Проверить содержимое обеих панелей */
void checkBothPanes(){
/* Случай NON_VALID нужен только для того, чтобы Init...
восстановил "аварийную" картинку в панели */
if( ReadDir(tpane1.d.name, &tpane1.d) || NON_VALID(&tpane1.d))
InitTblFromDir(&tpane1, NO, NULL);
if( tpane1.t.exposed == NO ) TblDraw(&tpane1.t);
if( ReadDir(tpane2.d.name, &tpane2.d) || NON_VALID(&tpane2.d))
InitTblFromDir(&tpane2, NO, NULL);
if( tpane2.t.exposed == NO ) TblDraw(&tpane2.t);
}
/*-----------------------------------------------------------------*
* Ввод команд и выдача подсказки. *
*-----------------------------------------------------------------*/
/* Особая обработка отдельных клавиш в редакторе строки */
char e_move = NO; /* кнопки со стрелками <- -> двигают
курсор по строке/по таблице */
int e_hit[] = { KEY_LEFT, KEY_RIGHT, KEY_UP, KEY_DOWN,
KEY_F(0), KEY_IC,
ctrl('G'), ctrl('E'), ctrl('L'), ctrl('F'), ctrl('X'), ctrl('Y'),
-1 };
int e_handler (LineEdit *le, int c, HandlerReply *reply){
*reply = HANDLER_CONTINUE;
switch(c){
/* Перемещение по таблице без выхода из редактора строки */
case KEY_LEFT:
if( !SEL_PANE || !e_move){
*reply=HANDLER_SWITCH; return c; }
TblPointAt(A_tbl, A_tbl->current - A_tbl->height); break;
case KEY_RIGHT:
if( !SEL_PANE || !e_move){
*reply=HANDLER_SWITCH; return c; }
TblPointAt(A_tbl, A_tbl->current + A_tbl->height); break;
case KEY_DOWN:
if( !SEL_PANE){ *reply=HANDLER_SWITCH; return c; }
TblPointAt(A_tbl, A_tbl->current + 1); break;
case KEY_UP:
if( !SEL_PANE){ *reply=HANDLER_SWITCH; return c; }
TblPointAt(A_tbl, A_tbl->current - 1); break;
case KEY_F(0): /* F10 */
e_move = !e_move; break;
case KEY_IC:
if( !SEL_PANE){ *reply=HANDLER_SWITCH; return c; }
TblRetag(A_tbl, A_tbl->current, T_LABEL);
TblPointAt(A_tbl, A_tbl->current+1);
break;
/* Подстановки */
case ctrl('G'): /* подставить полное имя домашнего каталога */
LeInsStr(le, getenv("HOME")); LeInsStr(le, " "); break;
case ctrl('E'): /* подставить имя выбранного файла */
if( A_tbl->nitems )
LeInsStr(le, T_ITEMF(A_tbl, A_tbl->current, 0));
LeInsStr(le, " "); break;
case ctrl('L'): /* подставить имя выбранного файла из другой панели */
LeInsStr(le, T_ITEMF(B_tbl, B_tbl->current, 0));
LeInsStr(le, " "); break;
case ctrl('X'): case ctrl('Y'):
/* подстановка имен помеченных файлов */
{ int label = (c == ctrl('X') ? T_LABEL : T_HATCH);
register i;
for(i=0; i < A_tbl->nitems && le->len < le->maxlen; ++i )
if( T_TST(A_tbl, i, label)){
LeInsStr(le, " "); LeInsStr(le, T_ITEMF(A_tbl, i, 0));
}
} break;
case ctrl('F'): /* подставить имя текущего каталога */
LeInsStr(le, CWD); LeInsStr(le, " "); break;
}
return c;
}
/* При начале редактирования ставь курсор в конец строки */
void e_pos (LineEdit *le){ le->pos = le->len; }
/* Обозначить, что мы покинули редактор строки */
void e_hide(LineEdit *le){
le->sel_attrib = le->fr_attrib = le->bg_attrib = A_ITALICS;
LeDraw(le);
}
/* Отредактировать строку в предпоследней строке окна */
char *Edit(WINDOW *w, char *src, RunType dorun){
static char CMD[MAXLEN]; /* буфер для строки команды */
int c;
if(w != TOPW){ beep(); return NULL; }/* это должно быть верхнее окно */
keypad(w, TRUE);
/* Проинициализировать редактор строки */
switch(dorun){
case NORUN: edit.histIn = edit.histOut = NULL; break;
case RUNCMD: edit.histIn = edit.histOut = &hedit; break;
case FIND:
case TAG: edit.histIn = edit.histOut = &hpat; break;
case CHDIR: edit.histIn = &hcwd; edit.histOut = NULL; break;
}
edit.line = CMD;
edit.maxlen = sizeof(CMD)-1;
edit.top = wlines(w)-2; edit.left = 2;
edit.width = wcols (w)-4 - (1+BARWIDTH);
edit.insert = YES; edit.nc = YES;
edit.win = w;
edit.wl_attrib = edit.bg_attrib=A_REVERSE;
edit.fr_attrib=A_STANDOUT; edit.sel_attrib = A_NORMAL|A_BLINK;
edit.posMe = e_pos;
edit.hitkeys = (SEL_PANE ? e_hit : e_hit+5);
edit.handler = e_handler;
/* edit.hideMe = e_hide; вызывается ЯВНО */
/* остальные поля равны 0, т.к. edit - статическое данное */
for(;;){
strcpy(CMD, src); if(*src){ strcat(CMD, " "); }
c = LeEdit( &edit );
if( LE_REFUSED(&edit) || dorun != RUNCMD ||
!*CMD || c != '\n' ) break;
/* курсор в нижнюю строку экрана */
attrset(A_NORMAL); move(LINES-1, 0); refresh();
resetterm(); /* приостановить работу curses-а */
putchar('\n'); /* промотать экран на строку */
system(CMD); /* выполнить команду внешним Шеллом */
fprintf(stderr,"Нажми ENTER чтобы продолжить --- ");gets(CMD);
fixterm(); /* возобновить работу curses-а */
RedrawScreen(); /* перерисовать экран */
if(w == panewin){
checkBothPanes();
if(A_tbl->nitems) TblPoint(A_tbl, A_tbl->current, NO);
}
src = ""; /* во второй раз ничего не подставлять */
}
wattrset(w, A_NORMAL); /* ? */
e_hide ( &edit );
return ( *CMD && !LE_REFUSED(&edit)) ? CMD : NULL;
}
/* Выдача подсказки а также сообщений об ошибках. */
/* В этом же окне можно набирать команды (dorun==RUNCMD). */
char *help(char *msg, RunType dorun){ register i; char *s;
static char *helptext[] = {
"ESC - выход в главное меню",
"F1 - подсказка",
"INS - пометить файл",
"ctrl/E - подставить имя выбранного файла",
"ctrl/L - подставить имя из другой панели",
"ctrl/X - подставить помеченные файлы",
"ctrl/Y - подставить помеченные курсивом",
"ctrl/G - подставить имя домашнего каталога",
"ctrl/F - подставить имя текущего каталога",
"F4 - история",
"F7 - переключить режим вставки/замены",
"F10 - переключить перемещения по строке/по панели",
};
#define HELPLINES (sizeof(helptext)/sizeof helptext[0])
Sel save_current_menu = current_menu;
/* "выскакивающее" POP-UP window */
WINDOW *w = newwin(2+1+HELPLINES+1, 70, 2, (COLS-70)/2);
if( w == NULL ) return NULL;
current_menu = SEL_HELP;
wattrset(w, A_REVERSE); /* это будет инверсное окно */
werase (w); /* заполнить инверсным фоном */
wborder(w); RaiseWin(w); /* окно появляется */
if(*msg){ wattron (w, A_BOLD);
mvwaddstr(w, 1+HELPLINES, 2, msg); wattroff(w, A_BOLD);
}
for(i=0; i < HELPLINES; i++) mvwaddstr(w, 1+i, 2, helptext[i]);
s = Edit(w, "", dorun); PopWin(); /* окно исчезает */
current_menu = save_current_menu;
return s;
}
/*-----------------------------------------------------------------*
* Управляющее меню. *
*-----------------------------------------------------------------*/
int f_left(), f_right(), f_pull(), f_help(), f_sort(), f_dir(),
f_bye(), f_redraw(),f_cdroot();
/* Обратите внимание, что можно указывать не все поля структуры,
* а только первые. Остальные равны 0 */
#ifndef __GNUC__
Info mwrk_info[] = { /* строки для главного меню */
{ "\\Current directory", 0 , f_left }, /* 0 */
{ "\\Root directory", M_HATCH , f_right }, /* 1 */
{ "\\Menus", 0 , f_pull }, /* 2 */
{ "\1", /* гориз. черта */ 0 }, /* 3 */
{ "\\Help", 0 , f_help }, /* 4 */
{ "Un\\implemented", I_NOSEL }, /* 5 */
{ "Change \\sorttype", 0 , f_sort }, /* 6 */
{ "Look directory \\history", 0 , f_dir }, /* 7 */
{ "\1", /* гориз. черта */ 0 }, /* 8 */
{ "\\Quit", M_BOLD , f_bye }, /* 9 */
{ "\1", /* гориз. черта */ 0 }, /* 10 */
{ "\\Redraw screen", M_HATCH , f_redraw}, /* 11 */
{ "Chdir both panels to /", M_HATCH , f_cdroot}, /* 12 */
{ NULL, 0 }
};
#else /* GNU C-компилятор 1.37 не может инициализировать поля-union-ы */
static char _gnu_[] = "Compiled with GNU C-compiler";
Info mwrk_info[] = { /* строки для главного меню */
{ "\\Current directory", 0 },
{ "\\Root directory", M_HATCH },
{ "\\Menus", 0 },
{ "\1", /* гориз. черта */ 0 },
{ "\\Help", 0 },
{ "Un\\implemented", I_NOSEL },
{ "Change \\sorttype", 0 },
{ "Look directory \\history", 0 },
{ "\1", /* гориз. черта */ 0 },
{ "\\Quit", M_BOLD },
{ "\1", /* гориз. черта */ 0 },
{ "\\Redraw screen", M_HATCH },
{ "Chdir both panels to /", M_HATCH },
{ NULL, 0 }
};
void mwrk_init(){
mwrk_info [0].any.act = f_left;
mwrk_info [1].any.act = f_right;
mwrk_info [2].any.act = f_pull;
mwrk_info [4].any.act = f_help;
mwrk_info [6].any.act = f_sort;
mwrk_info [7].any.act = f_dir;
mwrk_info [9].any.act = f_bye;
mwrk_info[11].any.act = f_redraw;
mwrk_info[12].any.act = f_cdroot;
}
#endif
char *mwrk_help[] = {
"Перейти в левую панель", "Перейти в правую панель",
"Перейти в строчное меню", "",
"Выдать подсказку", "Не реализовано",
"Изменить тип сортировки имен", "История путешествий",
"", "Выход", "", "Перерисовка экрана",
"Обе панели поставить в корневой каталог", NULL
};
void m_help(Menu *m, int n, int among){
Message(mwrk_help[n]); }
/* Выбор в рабочем (командном) меню */
void SelectWorkingMenu(int sel){
if(sel == NOSELECTED)
sel = MnuUsualSelect( & mwrk, NO);
if( M_REFUSED(&mwrk)) help("Выбери Quit", NORUN);
else if(mwrk.items[sel].any.act)
(*mwrk.items[sel].any.act)();
if( !done) MnuHide( & mwrk );
}
f_left () { current_menu = SEL_PANE1; return 0; }
f_right() { current_menu = SEL_PANE2; return 0; }
f_pull () { current_menu = SEL_PULL; return 0; }
f_help () { help("Нажми ENTER или набери команду:", RUNCMD);
return 0; }
f_sort () { SelectSortType(NOSELECTED); return 0; }
f_dir () { Info *idir; if(idir = HistSelect(&hcwd, 20, 3))
cd(idir->s, &tpane2, CWD);
current_menu = SEL_PANE2; return 0; }
f_bye () { done++; return 0; }
f_redraw() { RedrawScreen(); return 0; }
f_cdroot() { cd("/", &tpane1, CWD);
cd("/", &tpane2, CWD); checkBothPanes();
return 0; }
/*-----------------------------------------------------------------*
* Выдача информации про файл, редактирование кодов доступа. *
*-----------------------------------------------------------------*/
void MYwaddstr(WINDOW *w, int y, int x, int maxwidth, char *s){
register pos;
for(pos=0; *s && *s != '\n' && pos < maxwidth; ++s){
wmove(w, y, x+pos);
if( *s == '\t') pos += 8 - (pos & 7);
else if( *s == '\b'){ if(pos) --pos; }
else if( *s == '\r') pos = 0;
else { ++pos; waddch(w, isprint(*s) ? *s : '?'); }
}
}
/* Просмотр начала файла в противоположной панели. */
void fastView(
char *name, /* имя файла */
unsigned mode, /* некоторые типы файлов не просматривать */
Table *otbl /* противоположная панель */
){ FILE *fp; register int x, y; char buf[512];
TblClear(otbl);
Message("Нажми ENTER для окончания. "
"ПРОБЕЛ - изменяет код доступа. "
"ESC - откатка.");
if( !ISREG(mode)) goto out;
if((fp = fopen(name, "r")) == NULL){
Message("Не могу читать %s", name); return;
}
for(y=0; y < otbl->height && fgets(buf, sizeof buf, fp); y++)
MYwaddstr(panewin, otbl->top+y, otbl->left+1,
otbl->width-2, buf);
fclose(fp);
out: wrefresh(otbl->win); /* проявить */
}
static struct attrNames{
unsigned mode; char name; char acc; int off;
} modes[] = {
{ S_IREAD, 'r', 'u', 0 },
{ S_IWRITE, 'w', 'u', 1 },
{ S_IEXEC, 'x', 'u', 2 },
{ S_IREAD >> 3, 'r', 'g', 3 },
{ S_IWRITE >> 3, 'w', 'g', 4 },
{ S_IEXEC >> 3, 'x', 'g', 5 },
{ S_IREAD >> 6, 'r', 'o', 6 },
{ S_IWRITE >> 6, 'w', 'o', 7 },
{ S_IEXEC >> 6, 'x', 'o', 8 },
};
#define NMODES (sizeof(modes)/sizeof(modes[0]))
/* Позиция в которой изображать i-ый бит кодов доступа */
#define MODE_X_POS(tbl, i) (tbl->left + DIR_SIZE + 12 + modes[i].off)
#define MODE_Y_POS(tbl) (tbl->top + tbl->height + 1)
#ifdef FILF
/* Изобразить информацию о текущем выбранном файле */
void showMode(Table *tbl, int attr){
Info *inf = & tbl->items[tbl->current]; /* файл */
register i; unsigned mode = inf->mode; /* коды */
int uid = inf->uid, gid = inf->gid; /* хозяин */
/* идентификаторы хозяина и группы процесса-коммандера */
static char first = YES; static int myuid, mygid;
WINDOW *win = tbl->win;
int xleft = tbl->left + 1, y = MODE_Y_POS(tbl);
if( first ){ first = NO; myuid = getuid(); mygid = getgid(); }
wattron (win, attr);
mvwprintw(win, y, xleft, " %*.*s %8ld ", /* имя файла */
-DIR_SIZE, DIR_SIZE,
inf->s ? (!strcmp(inf->s, "..") ? "<UP-DIR>": inf->s) :
"(EMPTY)",
inf->size);
/* тип файла (обычный|каталог|устройство) */
wattron (win, A_ITALICS|A_BOLD);
waddch (win, ISDIR(mode) ? 'd': ISDEV(mode) ? '@' : '-');
wattroff(win, A_ITALICS|A_BOLD);
/* коды доступа */
for(i=0; i < NMODES; i++){
if((modes[i].acc == 'u' && myuid == uid) ||
(modes[i].acc == 'g' && mygid == gid) ||
(modes[i].acc == 'o' && myuid != uid && mygid != gid)) ;
else wattron(win, A_ITALICS);
mvwaddch(win, y, MODE_X_POS(tbl, i),
mode & modes[i].mode ? modes[i].name : '-');
wattroff(win, A_ITALICS);
}
waddch(win, ' '); wattroff(win, attr);
}
#define newmode (tbl->items[tbl->current].mode)
/* Редактирование кодов доступа к файлам. */
int editAccessModes(FileWidget *wd){
Table *tbl = &wd->t;
Table *otbl = &(Other_pane(wd)->t); /* или Other_tbl(tbl); */
unsigned prevmode, oldmode; /* старый код доступа */
char *name; /* имя текущего файла */
WINDOW *win = tbl->win;
int position = 0, c;
for(;;){ /* Цикл выбора файлов в таблице */
name = T_ITEMF(tbl, tbl->current, 0);
oldmode = newmode; /* запомнить */
fastView(name, newmode, otbl); /* показать первые строки файла */
for(;;){ /* Цикл обработки выбранного файла */
wmove(win, MODE_Y_POS(tbl), MODE_X_POS(tbl, position));
switch(c = WinGetch(win)){
/* Некоторые клавиши вызывают перемещение по таблице */
case KEY_BACKTAB: TblPointAt(tbl, tbl->current - tbl->height); goto mv;
case '\t': TblPointAt(tbl, tbl->current + tbl->height); goto mv;
case KEY_UP: TblPointAt(tbl, tbl->current - 1); goto mv;
case KEY_DOWN: TblPointAt(tbl, tbl->current + 1); goto mv;
case KEY_HOME: TblPointAt(tbl, 0); goto mv;
case KEY_END: TblPointAt(tbl, tbl->nitems-1); goto mv;
/* Прочие клавиши предназначены для редактирования кодов доступа */
case KEY_LEFT: if(position) --position; break;
case KEY_RIGHT: if(position < NMODES-1) position++; break;
default: goto out;
case ESC: /* Восстановить старые коды */
prevmode = newmode = oldmode; goto change;
case ' ': /* Инвертировать код доступа */
prevmode = newmode; /* запомнить */
newmode ^= modes[position].mode; /* инвертировать */
change: if( chmod(name, newmode) < 0){
beep();
Message("Не могу изменить доступ к %s", name);
newmode = prevmode; /* восстановить */
} else /* доступ изменен, показать это */
showMode(tbl, A_REVERSE);
break;
}
} /* Конец цикла обработки выбранного файла */
mv: ;
} /* Конец цикла выбора файлов в таблице */
out:
/* Очистить противоположную панель после fastView(); */
Message(""); TblClear(otbl); return c;
}
#undef newmode
#else
void editAccessModes(FileWidget *wd){}
#endif
long diskFree(){
struct ustat ust; struct stat st; long freespace;
if(stat(".", &st) < 0) return 0;
ustat(st.st_dev, &ust);
freespace = ust.f_tfree * 512L; freespace /= 1024;
Message("В %*.*s свободно %ld Кб.",
-sizeof(ust.f_fname), sizeof(ust.f_fname),
*ust.f_fname ? ust.f_fname : ".", freespace);
doupdate(); /* проявить окно для Message() */
return freespace;
}
/*-----------------------------------------------------------------*
* Специальные команды, использующие обход дерева
*-----------------------------------------------------------------*/
/* Выдача сообщений об ошибках (смотри Makefile) */
int tree_err_cant_read(char *name){
Message("Не могу читать \"%s\"", name); return WARNING;
}
int tree_name_too_long(){
Message("Слишком длинное полное имя"); return WARNING;
}
char canRun; /* продолжать ли поиск */
/* Прерывание обхода по SIGINT */
void onintr_f(nsig){ canRun = NO; Message("Interrupted"); }
/* ==== место, занимаемое поддеревом ==== */
long tu(int *count){
struct stat st; register i; long sum = 0L;
*count = 0;
for(i=0; i < A_tbl->nitems ;++i )
if( T_TST(A_tbl, i, T_LABEL)){
stat(T_ITEMF(A_tbl, i, 0), &st);
#define KB(s) (((s) + 1024L - 1) / 1024L)
sum += KB(st.st_size); (*count)++;
}
return sum;
}
void diskUsage(){ long du(), size, sizetagged; int n;
char msg[512];
Message("Измеряем объем файлов..."); doupdate();
size = du("."); diskFree(); sizetagged = tu(&n);
sprintf(msg, "%ld килобайт в %s, %ld кб в %d помеченных файлах",
size, CWD, sizetagged, n);
help(msg, NORUN);
}
/* ==== поиск файла ===================== */
extern char *find_PATTERN; /* imported from treemk.c */
extern Info gargv[]; extern int gargc; /* imported from glob.c */
/* Проверить очередное имя и запомнить его, если подходит */
static int findCheck(char *fullname, int level, struct stat *st){
char *basename = strrchr(fullname, '/');
if(basename) basename++;
else basename = fullname;
if( canRun == NO ) return FAILURE; /* поиск прерван */
if( match(basename, find_PATTERN)){ /* imported from match.c */
gargv[gargc] = NullInfo; /* зачистка */
gargv[gargc].s = strdup(fullname);
gargv[gargc++].fl= ISDIR(st->st_mode) ? I_DIR : 0;
gargv[gargc] = NullInfo;
Message("%s", fullname); doupdate();
}
/* Страховка от переполнения gargv[] */
if ( gargc < MAX_ARGV - 1 ) return SUCCESS;
else { Message("Найдено слишком много имен."); return FAILURE; }
}
/* Собрать имена файлов, удовлетворяющие шаблону */
static Info *findAndCollect(char *pattern){
void (*old)() = signal(SIGINT, onintr_f);
Sort saveSort;
find_PATTERN = pattern; canRun = YES;
Message("Ищем %s от %s", pattern, CWD); doupdate();
greset(); /* смотри glob.c, gargc=0; */
walktree(CWD, findCheck, NULL, findCheck);
signal(SIGINT, old);
saveSort = sorttype; sorttype = SORT_ASC;
if(gargc) qsort( gargv, gargc, sizeof(Info), gcmps);
sorttype = saveSort;
return gargc ? blkcpy(gargv) : NULL;
}
/* Обработать собранные имена при помощи предъявления меню с ними */
void findFile(FileWidget *wd){
static Info *found; static Menu mfind;
int c; Table *tbl = & wd->t;
char *pattern = help("Введи образец для поиска, вроде *.c, "
"или ENTER для прежнего списка", FIND);
if( LE_REFUSED( &edit)) return; /* отказались от поиска */
/* Если набрана пустая строка, help() выдает NULL */
if( pattern ){ /* задан новый образец - ищем */
/* Уничтожить старый список файлов и меню */
if( found ) blkfree( found );
MnuDeinit( &mfind );
found = findAndCollect(pattern); /* поиск */
HistAdd( &hpat, pattern, 0);
/* Образуем меню из найденных файлов */
if( found ){ /* если что-нибудь нашли */
mfind.items = found;
mfind.title = pattern ? pattern : "Найденные файлы";
mfind.top = 3; mfind.left = COLS/6;
mfind.bg_attrib = A_STANDOUT; mfind.sel_attrib = A_REVERSE;
MnuInit (&mfind);
}
} /* else набрана пустая строка - просто вызываем список
* найденных ранее файлов.
*/
if( found == NULL ){
Message("Ничего не найдено"); beep(); return;
}
c = MnuUsualSelect(&mfind, NO);
/* Выбор файла в этом меню вызовет переход в каталог,
* в котором содержится этот файл */
if( !M_REFUSED( &mfind )){
char *s = M_ITEM(&mfind, mfind.current);
/* пометить выбранный элемент */
M_SET(&mfind, mfind.current, M_LABEL);
/* если это каталог - войти в него */
if( M_TST(&mfind, mfind.current, I_DIR))
cd(s, wd, CWD);
/* иначе войти в каталог, содержащий этот файл */
else { char *p; struct savech svch; /* смотри glob.h */
SAVE( svch, strrchr(s, '/')); *svch.s = '\0';
p = strdup(s); RESTORE(svch);
if( !strcmp(CWD, p)) /* мы уже здесь */
TblPlaceByName(tbl, svch.s+1); /* указать курсором */
else /* изменить каталог и указать курсором на файл s */
cd(p, wd, s);
free(p);
}
}
MnuHide(&mfind); /* спрятать меню, не уничтожая его */
}
/*-----------------------------------------------------------------*
* Работа с панелями, содержащими имена файлов двух каталогов. *
*-----------------------------------------------------------------*/
/* Восстановить элементы, затертые рамкой WinBorder */
void t_restore_corners(){
mvwaddch(panewin, LINES-3, 0, LEFT_JOIN);
mvwaddch(panewin, LINES-3, COLS-2-BARWIDTH, RIGHT_JOIN);
mvwaddch(panewin, LINES-5, 0, LEFT_JOIN);
mvwaddch(panewin, LINES-5, COLS-2-BARWIDTH, RIGHT_JOIN);
mvwaddch(panewin, 2, CENTER, TOP_JOIN);
wattron (panewin, A_BOLD);
mvwaddch(panewin, LINES-3, CENTER, BOTTOM_JOIN);
mvwaddch(panewin, LINES-5, CENTER, MIDDLE_CROSS);
wattroff(panewin, A_BOLD);
}
/* Нарисовать нечто при входе в панель. Здесь изменяется
* заголовок окна: он становится равным имени каталога,
* просматриваемого в панели */
void t_enter(Table *tbl){
WinBorder(tbl->win, tbl->bg_attrib, tbl->sel_attrib,
CWD, BAR_VER|BAR_HOR, NO);
t_restore_corners();
}
/* Стереть подсветку при выходе из панели */
void t_leave(Table *tbl){ TblDrawItem( tbl, tbl->current, NO, YES ); }
/* Рисует недостающую часть рамки, которая не изменяется впоследствии */
void t_border_common(){
WinBorder(panewin, A_tbl->bg_attrib, A_tbl->sel_attrib,
A_dir->name, BAR_VER|BAR_HOR, NO);
wattron (panewin, A_BOLD);
whorline(panewin, LINES-3, 1, COLS-1-BARWIDTH-1);
whorline(panewin, LINES-5, 1, COLS-1-BARWIDTH-1);
wverline(panewin, CENTER, A_tbl->top, A_tbl->top + A_tbl->height+2);
wattroff(panewin, A_BOLD);
t_restore_corners();
}
/* Функция, изображающая недостающие части панели при входе в нее */
int t_show(Table *tbl){
#ifdef FILF
showMode(A_tbl, A_STANDOUT); showMode(B_tbl, A_STANDOUT);
#endif
return 1;
}
void t_scrollbar(Table *tbl, int whichbar, int n, int among){
WinScrollBar(tbl->win, BAR_VER|BAR_HOR, n, among,
"Yes", tbl->bg_attrib);
#ifdef FILF
showMode(tbl, A_REVERSE);
#endif
}
/* Особая обработка клавиш при выборе в таблице */
int t_hit[] = {
'\t', KEY_F(1), KEY_F(2), KEY_F(3),
KEY_F(4), KEY_F(8), ' ', '+',
'-', ctrl('R'), ctrl('L'), ctrl('F'),
-1 };
Info t_info[] = {
{ "TAB Перейти в другую панель", 0},
{ "F1 Выдать подсказку", 0},
{ "F2 Ввести команду", 0},
{ "F3 Перейти в родительский каталог", 0},
{ "F4 Перейти в каталог по имени", 0},
{ "F8 Удалить помеченные файлы", 0},
{ "ПРОБЕЛ Редактировать коды доступа", 0},
{ "+ Пометить файлы", 0},
{ "- Снять пометки", 0},
{ "ctrl/R Перечитать каталог", 0},
{ "ctrl/L Выдать размер файлов в каталоге",0},
{ "ctrl/F Поиск файла", 0},
{ NULL, 0}
};
int t_help(){
static Menu mth; int c = 0;
if( mth.items == NULL ){
mth.items = t_info;
mth.title = "Команды в панели";
mth.top = 3; mth.left = COLS/6;
mth.bg_attrib = A_STANDOUT; mth.sel_attrib = A_REVERSE;
MnuInit (&mth);
mth.hotkeys = t_hit;
}
c = MnuUsualSelect(&mth, 0);
/* Спрятать меню, не уничтожая его. Уничтожение выглядело бы так:
* mth.hotkeys = NULL; (т.к. они не выделялись malloc()-ом)
* MnuDeinit(&mth);
*/
MnuHide(&mth);
if( M_REFUSED(&mth)) return 0; /* ничего не делать */
return t_hit[c]; /* клавиша, соответствующая выбранной строке */
}
int t_handler (Table *tbl, int c, HandlerReply *reply){
int i, cnt=0; extern int unlink(), rmdir(); char *answer;
FileWidget *wd = TblFW (tbl);
switch(c){
case '\t': /* перейти в соседнюю панель */
ExchangePanes();
*reply = HANDLER_OUT; return LEAVE_KEY; /* покинуть эту панель */
case KEY_F(1): *reply = HANDLER_NEWCHAR; return t_help();
case KEY_F(2):
(void) Edit(tbl->win, T_ITEMF(tbl, tbl->current, 0), RUNCMD);
break;
case KEY_F(3): cd(".." , wd, CWD); break;
case KEY_F(4):
if(answer = help("Введи имя каталога, в который надо перейти",CHDIR))
cd(answer , wd, CWD);
break;
case ctrl('R'): break;
case KEY_F(8):
for(i=0; i < tbl->nitems; i++)
if(T_TST(tbl, i, M_LABEL)){ int code; cnt++;
if((code = (T_TST(tbl, i, I_DIR) ? rmdir : unlink) (T_ITEMF(tbl, i,0))) < 0)
T_SET(tbl, i, M_HATCH);
}
if(cnt==0) help("Нет помеченных файлов", NORUN);
break;
case '+':
if(answer = help("Шаблон для пометки", TAG))
TblTagAll(tbl, answer, T_LABEL);
break;
case '-':
if(answer = help("Шаблон для снятия пометок", TAG))
TblUntagAll(tbl, answer, T_LABEL);
break;
case ctrl('L'): /* команда "disk usage" */
diskUsage(); break;
case ctrl('F'): /* поиск файла */
findFile(wd); break;
case ' ': /* редактирование кодов доступа */
editAccessModes(wd); break;
}
*reply = HANDLER_OUT; return REPEAT_KEY;
/* вернуться в эту же панель */
}
/* Выбор в одной из панелей. */
int SelectPane(FileWidget *wd){
Table *tbl = & wd->t;
DirContents *d = & wd->d;
int sel, retcode = 0;
RaiseWin( tbl->win );
/* войти в указанный каталог, поправить CWD */
if(mychdir( d->name ) < 0) checkBothPanes();
/* t_enter( tbl ); /* войти в указанную панель, поправить рамку */
for(;;){
/* Проверить, не устарело ли содержимое таблиц */
checkBothPanes();
if((sel = TblUsualSelect( tbl )) == TOTAL_NOSEL ){
current_menu = SEL_PULL; goto out; }
if( T_REFUSED(tbl)) break; /* нажат ESC */
if( tbl->key == LEAVE_KEY ){ retcode=1; break; }
strcpy(SELECTION, T_ITEMF(tbl, sel, 0));
if( tbl->key == REPEAT_KEY ) continue;
if(T_TST(tbl, sel, I_DIR)){ /* это каталог */
/* попытаться перейти в этот каталог */
cd(SELECTION, wd, CWD);
} else if(T_TST(tbl, sel, I_EXE)){ /* выполняемый файл */
(void) Edit(tbl->win, SELECTION, RUNCMD);
} else {
editAccessModes(wd);
/* На самом деле надо производить подбор команды по
* типу файла (набор соответствий должен программироваться
* вами в специальном файле, считываемом при запуске коммандера).
* runCommand( classify(SELECTION));
* где классификация в простейшем случае - по имени и суффиксу,
* а в более развитом - еще и по кодам доступа (включая тип файла)
* и по первой строке файла (или "магическому числу").
*/
}
} /* end for */
t_leave( tbl );
out:
if( !retcode ) current_menu = SEL_PULL; /* выход по ESC */
return retcode;
}
/*-----------------------------------------------------------------*
* Горизонтальное командное меню (вызывается по ESC). *
*-----------------------------------------------------------------*/
PullInfo pm_items [] = { /* подсказка */
{{ " \\Left ", 0 }, NULL, "Left pane" }, /* 0 */
{{ " \\Commands ", 0 }, &mwrk, "Do some commands"}, /* 1 */
{{ " \\Tools ", PM_NOSEL }, NULL, "" }, /* 2 */
{{ " \\Sorttype ", 0 }, &msort, "Change sort type"}, /* 3 */
{{ " \\Right ", 0 }, NULL, "Right pane" }, /* 4 */
{{ NULL, 0 }, NULL, NULL }
};
void p_help(PullMenu *p, int n, int among){ Message( PM_NOTE(p, n)); }
/* Выбор в меню-строке */
void SelectPullMenu(){
int c, sel; Menu *m;
for(;current_menu == SEL_PULL;){
c = PullUsualSelect(&pull);
sel = pull.current;
if( PM_REFUSED(&pull)){ current_menu = previous_menu; return;}
switch(sel){
case 0: current_menu = SEL_PANE1; return;
case 1: SelectWorkingMenu(c); return;
case 2: return; /* не бывает */
case 3: SelectSortType(c); return;
case 4: current_menu = SEL_PANE2; return;
}
}
}
/*-----------------------------------------------------------------*
* Инициализация и завершение. *
*-----------------------------------------------------------------*/
void die(int sig){
echo(); nocbreak(); mvcur(-1,-1,LINES-1,0);
refresh(); endwin (); putchar('\n');
if(sig) printf("Signal %d\n", sig);
if(sig == SIGSEGV) abort(); else exit(sig);
}
void main (void) {
setlocale(LC_ALL, ""); /* получить информацию о языке диагностик */
initscr (); /* включить curses */
signal(SIGINT, die); /* по сигналу вызывать die(); */
signal(SIGBUS, die); /* по нарушению защиты памяти */
signal(SIGSEGV,die);
refresh(); /* обновить экран: это очистит его */
noecho(); cbreak(); /* выключить эхо, включить прозрачный ввод */
/* Проинициализировать истории */
HistInit(&hcwd, 20); hcwd. mnu.title = "История пути";
HistInit(&hedit, 20); hedit.mnu.title = "История команд";
HistInit(&hpat, 8); hpat. mnu.title = "Шаблоны имен";
/* Разметить меню сортировки */
msort.items = sort_info;
msort.title = "Вид сортировки каталога";
msort.top = 1; msort.left = 2;
msort.showMe = sort_show;
msort.bg_attrib = A_NORMAL; msort.sel_attrib = A_STANDOUT;
/* MnuInit (&msort); инициализируется в pull-menu */
/* Разметить рабочее меню */
mwrk.items = mwrk_info;
mwrk.title = "Главное меню";
mwrk.top = 1; mwrk.left = COLS/3;
mwrk.handler = NULL; mwrk.hitkeys = NULL;
mwrk.bg_attrib = A_STANDOUT; mwrk.sel_attrib = A_REVERSE;
mwrk.scrollBar = m_help;
#ifdef __GNUC__
mwrk_init();
#endif
/* MnuInit (&mwrk); инициализируется в pull-menu */
/* Разметить левую и правую панели */
tpane1.t.width = CENTER - 1;
tpane2.t.width = COLS - tpane1.t.width - 2 - (2 + BARWIDTH);
tpane1.t.height = tpane2.t.height = (LINES - 8);
tpane1.t.win = tpane2.t.win = panewin = stdscr;
tpane1.t.left = 1;
tpane2.t.left = CENTER+1;
tpane1.t.top = tpane2.t.top = 3;
tpane1.t.bg_attrib = tpane2.t.bg_attrib = A_NORMAL;
tpane1.t.sel_attrib = tpane2.t.sel_attrib = A_STANDOUT;
tpane1.t.scrollBar = tpane2.t.scrollBar = t_scrollbar;
tpane1.t.hitkeys = tpane2.t.hitkeys = t_hit;
tpane1.t.handler = tpane2.t.handler = t_handler;
tpane1.t.showMe = tpane2.t.showMe = t_show;
tpane1.t.hideMe = tpane2.t.hideMe = NULL;
/* Разметить имена для файловых объектов */
tpane1.d.name = strdup("Текущий каталог");
tpane2.d.name = strdup("Корневой каталог");
/* Изобразить рамки (но пока не проявлять их)
* Это надо сделать до первого cd(), т.к. иначе при неудаче будет выдано
* сообщение, которое проявит НЕЗАВЕРШЕННУЮ картинку */
t_border_common(); t_restore_corners();
/* Доразметить левую панель */
mychdir("."); /* узнать полное имя текущего каталога в CWD[] */
/* прочитать содержимое каталога CWD в tpane1.d */
cd( CWD , &tpane1, CWD);
tpane1.t.fmt = "directory";
InitTblFromDir(&tpane1, NO, NULL);
/* Доразметить правую панель */
tpane2.t.fmt = NULL;
/* прочитать содержимое каталога "/" в tpane2.d */
cd( "/", &tpane2, CWD); /* теперь стоим в корне */
/* Вернуться в рабочий каталог */
cd( tpane1.d.name, &tpane1, CWD);
/* Нарисовать обе панели */
TblDraw(A_tbl); TblDraw(B_tbl);
/* Разметить pulldown меню */
pull.bg_attrib = A_REVERSE; pull.sel_attrib = A_NORMAL;
pull.items = pm_items; pull.scrollBar = p_help;
PullInit(&pull);
/* Основной цикл */
for(done=NO, current_menu=SEL_PANE1, A_pane= &tpane1, B_pane= &tpane2;
done == NO; ){
Message("");
if(SEL_PANE) previous_menu = current_menu;
switch(current_menu){
case SEL_WRK : SelectWorkingMenu(NOSELECTED); break;
case SEL_PULL: SelectPullMenu(); break;
case SEL_PANE1: if( SelectPane(&tpane1) < 0)
M_SET(&mwrk, 0, I_NOSEL); break;
case SEL_PANE2: if( SelectPane(&tpane2) < 0)
M_SET(&mwrk, 0, I_NOSEL); break;
}
}
die(0); /* Завершить работу */
}
.
/* Пример 24 */
/* Пример коммуникации процессов при помощи программных каналов
* (трубы, pipes).
* Данная программа превращается в две программы,
* соединенные трубами в таком порядке:
*
* stdout stdin
* /------------ PIP1 -----------> cmd2
* cmd1 <----------PIP2---------------/
* stdin stdout
*/
/* файл LOOP_strt.c */
#include <stdio.h>
#define eq(s1,s2) ( strcmp(s1,s2) == 0 ) /* истина, если строки равны */
#define SEP "---" /* разделитель команд при наборе */
main( c, v ) char **v;
{
char **p, **q;
int pid;
int PIP1[2]; /* труба cmd1-->cmd2 */
int PIP2[2]; /* труба cmd2-->cmd1 */
if( c==1 ){
printf( "Call: strt cmd1... %s cmd2...\n", SEP );
exit(1);
}
/* разбор аргументов */
v++;
/* в p - аргументы первой команды */
p = v;
while( *v && !eq( *v, SEP ))
v++;
*v = NULL;
v++;
/* в q - аргументы второй команды */
q = v;
pipe( PIP1 ); /* создаем две трубы */
pipe( PIP2 ); /* PIP[0] - открыт на чтение, PIP[1] - на запись */
if( pid = fork()){ /* развилка: порождаем процесс */
/* ПОРОЖДЕННЫЙ ПРОЦЕСС */
fprintf( stderr, "сын=%s pid=%d\n", p[0], getpid());
/* перенаправляем stdout нового процесса в PIP1 */
dup2( PIP1[1], 1 );
close( PIP1[1] );
/* канал чтения мы не будем использовать */
close( PIP1[0] );
/* перенаправляем stdin из PIP2 */
dup2( PIP2[0], 0 );
close( PIP2[0] );
/* канал записи мы не будем использовать */
close( PIP2[1] );
/* начинаем выполнять программу, содержащуюся в
* файле p[0] с аргументами p (т.е. cmd1)
*/
execvp( p[0], p );
/* возврата из сисвызова exec не бывает */
}else{
/* ПРОЦЕСС-РОДИТЕЛЬ */
fprintf( stderr, "отец=%s pid=%d\n", q[0], getpid());
/* перенаправляем stdout в PIP2 */
dup2( PIP2[1], 1 );
close( PIP2[1] ); close( PIP2[0] );
/* перенаправляем stdin из PIP1 */
dup2( PIP1[0], 0 );
close( PIP1[0] ); close( PIP1[1] );
/* запускаем cmd2 */
execvp( q[0], q );
}
}
/* Ниже приводятся тексты двух программ, которые можно запустить
* как тест. Сервер компилируется в программу cmd2,
* клиент - в программу cmd1. Если запускающая программа
* скомпилирована в strt, то наберите команду
* strt cmd1 --- cmd2
* либо strt cmd2 --- cmd1
*/
/* файл LOOP_p.c ---------------------------------------------
* Процесс-клиент (cmd1)
*/
#include <stdio.h>
int trace = 1; /* вести трассировку своих действий */
main(c , v) char **v;
{
FILE *fp; int pid;
char buf[128];
fprintf( stderr, "P: process pid=%d\n", getpid());
fp = fopen( "LOOP_p.c", "r" );
/* открываем файл с текстом этой команды */
/* читаем его построчно */
while( fgets( buf, sizeof buf, fp ) != NULL ){
if( trace ) fprintf( stderr, "P посылает: %s", buf );
/* посылаем его в стандартный вывод: трубу PIP1 */
printf( "%s", buf );
fflush( stdout );
/* ожидать ответа из трубы PIP2 */
fgets( buf, sizeof buf, stdin );
if( trace ) fprintf( stderr, "P получил: %s", buf );
}
fclose( stdout );
/* отключиться от трубы PIP1. Если этого не сделать, сервер
* не прочитает из нее EOF */
while((pid = wait(NULL)) > 0 )
fprintf( stderr, "P: %d умер\n", pid );
}
/* файл LOOP_q.c ------------------------------------------------
* процесс-сервер (cmd2)
*/
#include <stdio.h>
int trace = 1;
main(c , v) char **v;
{
char buf[128]; int pid;
fprintf( stderr, "Q: process pid=%d\n", getpid());
/* читать поступающие из трубы PIP1 строки */
while( fgets( buf, sizeof(buf), stdin ) != NULL ){
/* напечатать полученное сообщение */
if( trace ) fprintf( stderr, "Q прочел: %s", buf );
if( trace ) fprintf( stderr, "Q отвечает: OK=%s", buf );
/* ответить в трубу PIP2 */
printf( "OK=%s", buf ); fflush( stdout );
}
fclose( stdout ); /* отключиться от трубы PIP2 */
while((pid = wait(NULL)) > 0 )
fprintf( stderr, "Q: %d умер\n", pid );
}
.
/* Пример 25 */
/* Пример использования именованных "труб" (pipes) FIFO-файлов
* для коммуникации независимых процессов
* (FIFO - first in, first out : первым пришел - первым ушел).
* По мотивам книги М.Дансмура и Г.Дейвиса.
*/
/* файл P_packet.h --------------------------------------------*/
#include <sys/types.h>
#include <sys/stat.h> /* S_IFIFO */
/* структура пакета-запроса */
struct packet {
int pk_pid; /* идентификатор процесса-отправителя */
int pk_blk; /* номер блока, который надо прочитать */
int pk_code; /* код запроса */
};
/* request codes (коды запросов) */
#define RQ_READ 0 /* запрос на чтение */
#define CONNECT 1 /* запрос на соединение */
#define SENDPID 2 /* ответ на запрос соединения */
#define DISCONNECT 3 /* разрыв связи */
#define BYE 4 /* завершить сервер */
/* имена FIFO-каналов связи */
#define DNAME "datapipe"
#define CNAME "ctrlpipe"
/* размер блока информации */
#define PBUFSIZE 512
/* P_client.c --------------------------------------------------------- */
/*
* Процесс-клиент, посылающий запросы к серверу.
*/
#include <stdio.h>
#include <signal.h>
#include <fcntl.h>
#include "P_packet.h"
int datapipe, ctrlpipe;
int got_sig;
int mypid; /* идентификатор процесса-клиента */
int spid; /* идентификатор процесса-сервера */
/* waiting for signal */
#define WAITSIG while( !got_sig )
void handler(nsig){
signal( SIGUSR1, handler );
got_sig ++;
}
void init(){
extern void die();
/* Ожидать создания каналов связи */
while( (datapipe = open( DNAME, O_RDONLY | O_NDELAY )) < 0 );
while( (ctrlpipe = open( CNAME, O_WRONLY | O_NDELAY )) < 0 );
mypid = getpid(); /* my process identifier */
printf( "Client pid=%d started\n", mypid );
signal( SIGINT, die);
signal( SIGQUIT, die);
signal( SIGTERM, die);
handler(0);
}
int canRun = 1;
void die(nsig){
canRun = 0;
}
/* подключиться к серверу, запросив его pid */
connect(){
struct packet pk;
pk.pk_pid = mypid;
pk.pk_code = CONNECT;
pk.pk_blk = (-1);
got_sig = 0;
write( ctrlpipe, &pk, sizeof pk ); /* послать запрос */
/* ожидать сигнала-"толчка" */
WAITSIG;
/* прочитать ответ из канала данных */
read( datapipe, &pk, sizeof pk );
/* послать сигнал-подтверждение */
kill( pk.pk_pid, SIGUSR1 );
return pk.pk_pid;
}
void disconnect(){
struct packet pk;
pk.pk_pid = mypid;
pk.pk_code = DISCONNECT;
pk.pk_blk = (-1);
got_sig = 0;
write( ctrlpipe, &pk, sizeof pk ); /* send request */
/* wait for reply */
WAITSIG;
/* receive reply */
read( datapipe, &pk, sizeof pk );
/* confirm */
kill( pk.pk_pid, SIGUSR1 );
printf( "Disconnected.\n" );
}
request( ptr, blk, spid )
char *ptr;
int blk;
int spid;
{
struct packet pk;
pk.pk_pid = mypid;
pk.pk_blk = blk;
pk.pk_code = RQ_READ;
got_sig = 0;
write( ctrlpipe, &pk, sizeof pk );
WAITSIG;
read( datapipe, ptr, PBUFSIZE );
kill( spid, SIGUSR1 );
}
bye(){
struct packet pk;
pk.pk_pid = mypid;
pk.pk_code = BYE;
pk.pk_blk = (-1);
got_sig = 0;
write( ctrlpipe, &pk, sizeof pk ); /* send request */
exit(0);
}
/* client [номер_блока] */
main(argc, argv) char *argv[];
{
int blk;
char buffer[ PBUFSIZE ];
setbuf( stdout, NULL ); /* make unbuffered */
blk = (argv[1] ? atoi( argv[1] ) : 0);
init();
spid = connect();
printf( "Client pid=%d connected to server pid=%d\n",
mypid, spid );
/* запрос блока номер -33 соответствует запросу "завершить
* работу сервера"
*/
if( blk == -33 )
bye();
/* в цикле посылать запросы на чтение блока blk */
while( canRun ){
request( buffer, blk, spid );
printf( "\nBEG-------------------------------------\n" );
fwrite( buffer, PBUFSIZE, 1, stdout );
printf( "\nEND-------------------------------------\n" );
}
disconnect(); /* отключиться от сервера */
exit(0);
}
/* P_server.c ---------------------------------------------------------*/
/*
* Процесс-сервер, принимающий запросы и выполняющий их.
*/
#include <stdio.h>
#include <signal.h>
#include <fcntl.h>
#include "P_packet.h"
int datapipe, ctrlpipe, datafile, got_sig;
char *dataname = "/etc/passwd";
/* waiting for signal */
#define WAITSIG while( !got_sig )
void handler(nsig){
signal( SIGUSR1, handler ); /* reset trap */
got_sig++;
}
/* завершение работы сервера: уничтожить каналы связи */
void die(nsig){
unlink( CNAME ); unlink( DNAME ); exit(0);
/* Если эти файлы были открыты клиентами,
* то клиенты не умрут, хотя имена файлов и будут удалены!
*/
}
main(){
struct packet pk;
struct packet sendpk;
/* сделать стандартный вывод небуферизованным каналом */
setbuf( stdout, NULL ); /* make unbuffered */
/* создать каналы связи */
mknod( DNAME, S_IFIFO | 0666, 0 ); /* create FIFO */
mknod( CNAME, S_IFIFO | 0666, 0 ); /* create FIFO */
/* по этим сигналам будет вызываться функция die() */
signal( SIGINT, die );
signal( SIGQUIT, die );
signal( SIGTERM, die );
/* Открыть управляющий канал связи. O_NDELAY означает,
* что файл открывается для "чтения без ожидания",
* т.е. если канал пуст (нет заявок), то системный вызов
* read() не будет "спать", дожидаясь появления информации,
* а просто вернет 0 (прочитано 0 байт).
* Этот флаг применим также к чтению с терминала.
*/
ctrlpipe = open( CNAME, O_RDONLY | O_NDELAY );
if( ctrlpipe < 0 ){
printf( "Can't open %s\n", CNAME );
die(0);
}
datafile = open( dataname, O_RDONLY );
if( datafile < 0 ){
printf( "Can't open %s\n", dataname );
die(0);
}
/* заранее формируем пакет для ответов */
sendpk.pk_code = SENDPID;
sendpk.pk_pid = getpid(); /* server's pid */
sendpk.pk_blk = (-1);
printf( "Server pid=%d\n", getpid());
handler(0);
for(;;){
int n;
static long i = 0L;
/* active spin loop */
printf( "%20ld\r", i++ );
/* опрашивать канал насчет поступления запросов */
while((n = read( ctrlpipe, &pk, sizeof(pk))) > 0 ){
putchar( '\n' );
if( n != sizeof pk ){
printf( "Wrong packet size\n" );
continue;
}
/* обработать прочитанный запрос */
process( &pk, &sendpk );
}
}
die(0);
}
process( pkp, spkp )
struct packet *pkp, *spkp;
{
char pbuf[ PBUFSIZE ];
/* Запись в FIFO-файл будет произведена только если
* он уже открыт для чтения
*/
datapipe = open( DNAME, O_WRONLY | O_NDELAY );
printf( "REQUEST TYPE_%d from pid=%d blk=%d\n",
pkp->pk_code, pkp->pk_pid, pkp->pk_blk );
switch( pkp -> pk_code ){
case CONNECT: /* ответить своим идентификатором процесса */
write( datapipe, spkp, sizeof( struct packet ));
break;
case RQ_READ: /* ответить блоком информации из файла */
/* read block # pk_blk */
lseek( datafile, pkp -> pk_blk * (long)PBUFSIZE, 0 );
read( datafile, pbuf, PBUFSIZE );
write( datapipe, pbuf, PBUFSIZE );
break;
case DISCONNECT: /* подтвердить отключение */
printf( "Client pid=%d finished\n", pkp -> pk_pid );
write ( datapipe, spkp, sizeof( struct packet ));
break;
case BYE: /* завершиться */
printf( "Server terminated.\n" );
kill( pkp-> pk_pid, SIGKILL );
die(0);
default:
printf( "Unknown packet type %d\n", pkp -> pk_code );
break;
}
close( datapipe );
/* "подтолкнуть" отправителя сигналом */
got_sig = 0;
kill( pkp -> pk_pid , SIGUSR1 );
printf( "Waiting for reply... " );
/* ждать сигнала-подтверждения от клиента */
WAITSIG;
printf( "server continued\n" );
}
.
/* Пример 26 */
/* Общение процессов при помощи общей памяти и семафоров.
* Вызов: shms &
* shmc a & shmc b & shmc c &
*/
/* --------------------------- файл shm.h ----------------------- */
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <signal.h>
#include <errno.h>
extern errno; /* Системный код ошибки */
struct connect { /* Структура почтового ящика */
int pid; int msgnum; int max;
char message[128]; /* текст сообщения */
};
#define NSEMS 3 /* число семафоров */
/* Имена семафоров */
#define EMPTY 0 /* 1 - ящик пуст; 0 - содержит письмо */
#define NOTEMPTY 1 /* негатив для EMPTY */
#define ACCESS 2 /* 1 - ящик доступен (закрыт);
* 0 - ящик уже открыт кем-то еще */
/* Значения семафоров */
#define YES 1
#define NO 0
/* Операции */
#define OPEN 1
#define CLOSE (-1)
#define TEST_NO 0
#ifdef COMMENT
Алгоритм одновременного изменения семафоров: semop
Дано:
аргумент: число семафоров : nsems
аргумент: величины изменения : sem_op[i]
в ядре: текущие значения семафоров группы sem_id: sem[i]
Алгоритм:
again: Сохранить значения всех семафоров (для отмены изменений);
for(i=0; i<nsems; i++)
/* OPEN */ if( sem_op[i] > 0 ){
sem[i] += sem_op[i];
разбудитьЖдущихСобытие( "sem[i]++" );
/* CLOSE */ }else if( sem_op[i] < 0 ){
if((newsm = sem[i] + sem_op[i]) >= 0 ){
sem[i] = newsm;
if( sem[i] == 0 )
разбудитьЖдущихСобытие( "sem[i]==0" );
}else{
восстановитьВсеСемафоры;
ждатьСобытие( "sem[i]++" );
goto again;
}
/* TEST0 */ }else{ /* sem_op[i] == 0 */
if( sem[i] != 0 ){
восстановитьВсеСемафоры;
ждатьСобытие( "sem[i]==0" );
goto again;
}
}
Алгоритм синхронизации в нашей схеме КЛИЕНТ-СЕРВЕР:
|----------------------------------------------------------------|
|семафоры: EMPTY ACCESS |
|----------------------------------------------------------------|
|начальное значение: YES YES |
|----------------------------------------------------------------|
СЕРВЕР
|================================================================|
|loop: |
|----------------------------------------------------------------|
|ждать: NO YES |
|сделать: NO(test0) NO(close) |
|----------------------------------------------------------------|
| прочесть почту; |
|----------------------------------------------------------------|
|из: NO NO |
|сделать: YES(open) YES(open) |
|----------------------------------------------------------------|
| goto loop; |
|================================================================|
КЛИЕНТ
|================================================================|
|loop: |
|----------------------------------------------------------------|
|ждать: YES YES |
|сделать: YES(test!=0) NO(close) |
|----------------------------------------------------------------|
| записать почту; |
|----------------------------------------------------------------|
|из: YES NO |
|сделать: NO(close) YES(open) |
|----------------------------------------------------------------|
| goto loop; |
|================================================================|
К сожалению, операции test!=0 не существует - приходится вводить
дополнительный семафор NOTEMPTY, негативный для EMPTY:
|----------------------------------------------------------------|
|семафоры: EMPTY NOTEMPTY ACCESS |
|----------------------------------------------------------------|
|начальное значение: YES NO YES |
|----------------------------------------------------------------|
СЕРВЕР
|================================================================|
|loop: |
|----------------------------------------------------------------|
|ждать: NO - YES |
|сделать: NO(test0) - NO(close) |
|----------------------------------------------------------------|
| прочесть почту; |
|----------------------------------------------------------------|
|из: NO YES NO |
|сделать: YES(open) NO(close) YES(open) |
|----------------------------------------------------------------|
| goto loop; |
|================================================================|
КЛИЕНТ
|================================================================|
|loop: |
|----------------------------------------------------------------|
|ждать: - NO YES |
|сделать: - NO(test0) NO(close) |
|----------------------------------------------------------------|
| записать почту; |
|----------------------------------------------------------------|
|из: YES NO NO |
|сделать: NO(close) YES(open) YES(open) |
|----------------------------------------------------------------|
| goto loop; |
|================================================================|
#endif /*COMMENT*/
/* Общая часть сервера и клиента ------------------------------- */
key_t key = 1917; /* Уникальный ключ для доступа */
int shm_id; /* Дескриптор для доступа к общей памяти */
int sem_id; /* Дескриптор для доступа к семафорам */
char name[40]; /* имя программы */
char far *addr;
struct connect far *caddr;
struct sembuf ops[NSEMS];
/* EMPTY NOTEMPTY ACCESS */
short values[NSEMS] = { YES, NO, YES };
void semtell(msg, name) char *msg, *name; { int i;
semctl(sem_id, NSEMS, GETALL, values);
printf( "%s %-10s: значения семафоров:", name, msg);
for(i=0; i < NSEMS; i++) printf( " %d", values[i]);
putchar('\n');
}
void inisem(){
register i;
for(i=0; i < NSEMS; i++ ) ops[i].sem_flg = 0;
}
/* --------------------------- файл shms.c ----------------------- */
/* Shared memory server */
#include "shm.h"
int npack; /* номер сообщения */
void cleanup(sig){
/* Уничтожить сегмент общей памяти (это нужно делать явно) */
shmctl( shm_id, IPC_RMID, NULL );
/* Уничтожить семафоры */
semctl( sem_id, NSEMS, IPC_RMID, NULL );
if( npack ) printf( "\t** Всего было %d сообщений **\n", npack+1);
exit(0);
}
void main(){
register i; int pid = getpid();
FILE *fout;
sprintf( name, "Server-%03d", pid );
for( i = 1; i <= SIGTERM; i++ )
signal( i, cleanup );
/* Создать разделяемый сегмент */
if((shm_id = shmget( key, sizeof(struct connect),
0644 | IPC_CREAT )) < 0 ){
perror( "shmget" ) ; exit(1);
}
/* Подключить общий сегмент к произвольному адресу */
if((addr = (char far *) shmat( shm_id, NULL, 0 )) == NULL ){
perror( "shmat" ); cleanup();
}
caddr = (struct connect far *) addr;
/* Создать группу из NSEMS семафоров */
if((sem_id = semget( key, NSEMS, 0644 |IPC_CREAT |IPC_EXCL)) < 0){
if(errno == EEXIST){ printf( "Сервер уже запущен\n");exit(2); }
else{ perror( "semget" ); cleanup(); }
}
/* Загрузить начальные значения семафоров */
semctl( sem_id, NSEMS, SETALL, values );
setbuf(stdout, NULL);
inisem(); printf( "Server is up now. Читай файл MESSAGES.\n");
fout = fopen( "MESSAGES", "w");
for(;;npack++){
printf( "%s: ждет почты\n", name );
semtell("Вход", name);
ops[0].sem_num = EMPTY; ops[0].sem_op = TEST_NO;
ops[1].sem_num = ACCESS; ops[1].sem_op = CLOSE;
semop( sem_id, ops, 2 /* сразу два семафора */);
printf( "%s: GOT-%02d/%02d от %d \"%s\"\n", name,
caddr->msgnum, caddr->max, caddr->pid, caddr->message);
fprintf( fout, "#%03d %02d/%02d от %d \"%s\"\n", npack,
caddr->msgnum, caddr->max, caddr->pid, caddr->message);
if( ! strcmp(caddr->message, "-exit" )){
printf( "%s: завершает работу.\n", name );
cleanup();
}
semtell("Выход", name);
ops[0].sem_num = EMPTY ; ops[0].sem_op = OPEN;
ops[1].sem_num = NOTEMPTY; ops[1].sem_op = CLOSE;
ops[2].sem_num = ACCESS ; ops[2].sem_op = OPEN;
semop( sem_id, ops, 3 /* сразу три семафора */);
}
/*NOTREACHED*/
}
/* --------------------------- файл shmc.c ----------------------- */
/* Shared memory client */
#include "shm.h"
void ignsigs(sig){
register i;
for( i = 1; i <= SIGTERM; i++ )
signal( i, ignsigs );
printf( "Клиент игнорирует сигналы,\n\
чтобы не оставлять закрытых семафоров в случае своей смерти.\n" );
}
void main(argc, argv) char **argv; {
int pid = getpid();
int i, ntimes = 60;
if( argc < 2 ){
fprintf( stderr, "Вызов: %s сообщение [числоПовторов]\n", argv[0] );
fprintf( stderr, "сообщение \"-exit\" завершает сервер\n");
fprintf( stderr, "сообщение \"-info\" выдает значения семафоров\n");
exit(1);
}
if( argc > 2 ) ntimes = atoi(argv[2]);
sprintf( name, "Client-%03d", pid);
ignsigs(); srand( pid );
/* Получить доступ к разделяемому сегменту */
if((shm_id = shmget( key, sizeof(struct connect), 0644)) < 0 ){
perror( "shmget" ); exit(2);
}
/* Подключить общий сегмент к произвольному адресу */
if((addr = (char far *) shmat( shm_id, NULL, 0 )) == NULL ){
perror( "shmat" ); exit(3);
}
caddr = (struct connect far *) addr;
/* Получить доступ к семафорам */
if((sem_id = semget( key, NSEMS, 0644)) < 0 ){
perror( "semget" ); exit(4);
}
setbuf(stdout, NULL);
inisem();
if( !strcmp(argv[1], "-info")){
semtell("Информация", name); exit(0);
}
for( i=0; i < ntimes; i++ ){
printf( "%s: ждет пустого ящика\n", name);
semtell("Вход", name);
ops[0].sem_num = NOTEMPTY; ops[0].sem_op = TEST_NO;
ops[1].sem_num = ACCESS ; ops[1].sem_op = CLOSE;
if( semop( sem_id, ops, 2 /* сразу два семафора */) < 0)
goto err;
caddr->pid = pid; caddr->msgnum = i; caddr->max = ntimes;
strncpy( caddr->message, argv[1],
sizeof(caddr->message) - 1);
printf( "%s: PUT-%02d \"%s\"\n", name, i, argv[1]);
semtell("Выход", name);
ops[0].sem_num = EMPTY ; ops[0].sem_op = CLOSE;
ops[1].sem_num = NOTEMPTY; ops[1].sem_op = OPEN;
ops[2].sem_num = ACCESS ; ops[2].sem_op = OPEN;
if( semop( sem_id, ops, 3 /* сразу три семафора */) < 0)
goto err;
if( rand()%2 ) sleep(2); /* пауза */
}
shmdt( addr ); /* Отключиться от общего сегмента */
exit(0);
err:
perror("semop");
exit(5);
}
.
/* Пример 27 */
/* Коммуникация процессов при помощи псевдо-терминала.
* Данная программа позволяет сохранять полный протокол работы
* экранной программы в файл.
* Не экранные программы данная версия НЕ трассирует,
* поскольку сама работает в "прозрачном" режиме.
*
* Вариацией данной программы может служить использование
* системного вызова select() вместо запуска нескольких процессов.
*
* Программа также иллюстрирует "дерево" из 5 процессов.
* Данная версия написана для UNIX System V.
* TRACE__
* \ \ master slave
* |экран<======\(Reader)=======!~!<====(целевая )
* / <==\ | ! !====>(программа)
* \ | !P! |
* | | !T! |
* . . . . | | !Y! (Slave)-->Управляет
* клавиатура=|===|=>(Writer)=>!_! | \ семафором
* | | | | \
* | #####starter################## \
* |...................................|
* ftty
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/signal.h>
#include <termio.h>
#include <sys/stat.h>
#include <fcntl.h>
extern int exit ();
extern char *ttyname ();
extern FILE * fopen ();
extern errno;
#define SEMAPHORE "/tmp/+++" /* семафорный файл */
#define TRACE "./TRACE" /* файл с протоколом */
/* псевдотерминал связи */
/* master - это часть, которая ведет себя как ФАЙЛ и умеет
* реагировать на некоторые специальные ioctl()-и */
#define PTY "/dev/ptyp0" /* master */
/* slave - это часть, которая ведет себя как драйвер терминалов */
#define TTYP "/dev/ttyp0" /* slave */
int ptyfd;
FILE * ftrace = NULL;
/* при прерывании завершить работу процесса "писателя" */
onintr () {
closeVisual ();
fprintf (stderr, "\rwriter finished\r\n");
exit (0);
}
/* завершение работы процесса-"читателя" */
bye () {
if (ftrace)
fclose (ftrace);
fprintf (stderr, "\rreader finished\r\n");
exit (0);
}
int visual = 0;
struct termio old,
new;
/* настроить режимы работы терминала на "прозрачный" режим */
initVisual () {
ioctl (0, TCGETA, &old);
new = old;
new.c_iflag &= ~ICRNL;
new.c_lflag &= ~(ECHO | ICANON);
new.c_oflag &= ~(TAB3 | ONLCR);
new.c_cc[VMIN] = 1;
new.c_cc[VTIME] = 0;
/* new.c_cc[VINTR] = ctrl('C'); */
new.c_cc[VQUIT] = 0;
new.c_cc[VERASE] = 0;
new.c_cc[VKILL] = 0;
}
/* включить прозрачный режим */
openVisual () {
if (visual) return;
visual = 1;
ioctl (0, TCSETAW, &new);
}
/* выключить прозрачный режим */
closeVisual () {
if (!visual) return;
visual = 0;
ioctl (0, TCSETAW, &old);
}
struct stat st;
main (argc, argv) char **argv; {
int r, /* pid процесса-"читателя" */
w; /* pid процесса-"писателя" */
if (argc == 1) {
fprintf (stderr, "pty CMD ...\n");
exit (1);
}
initVisual ();
if((ptyfd = open ( PTY , O_RDWR)) < 0){
fprintf(stderr, "Cannot open pty\n"); exit(2);
}
/* запустить процесс чтения с псевдодисплея */
r = startReader ();
/* запустить процесс чтения с клавиатуры */
w = startWriter ();
sleep (2);
/* запустить протоколируемый процесс */
startSlave (argv + 1, r, w);
/* дождаться окончания всех потомков */
while (wait (NULL) > 0);
exit (0);
}
/* запуск протоколируемого процесса */
startSlave (argv, r, w) char **argv; {
FILE * ftty;
int pid;
int tfd;
char *tty = ttyname (1); /* полное имя нашего терминала */
if (!(pid = fork ())) {
/* PTY SLAVE process */
ftty = fopen (tty, "w"); /* Для выдачи сообщений */
setpgrp (); /* образовать новую группу процессов ;
* лишиться управляющего терминала */
/* закрыть стандартные ввод, вывод, вывод ошибок */
close (0);
close (1);
close (2);
/* первый открытый терминал станет управляющим для процесса,
* не имеющего управляющего терминала.
* Открываем псевдотерминал (slave) в качестве стандартных
* ввода, вывода и вывода ошибок
*/
open ( TTYP, O_RDWR);
open ( TTYP, O_RDWR);
tfd = open ( TTYP, O_RDWR);
if (tfd < 0) {
fprintf (ftty, "\rSlave: can't read/write pty\r\n");
kill(r, SIGKILL); kill(w, SIGKILL); exit (1);
}
/* запускаем целевую программу */
if (!(pid = fork ())) {
fprintf (ftty, "\rCreating %s\r\n", SEMAPHORE);
fflush (ftty);
/* создаем семафорный файл */
close (creat (SEMAPHORE, 0644));
fprintf (ftty, "\rStart %s\r\n", argv[0]);
fclose(ftty);
/* заменить ответвившийся процесс программой,
* указанной в аргументах
*/
execvp (argv[0], argv);
exit (errno);
}
/* дожидаться окончания целевой программы */
while (wait (NULL) != pid);
/* уничтожить семафор, что является признаком завершения
* для процессов чтения и записи
*/
unlink (SEMAPHORE);
fprintf (ftty, "\rDied.\r\n");
fflush (ftty);
/* убить процессы чтения и записи */
/* terminate reader & writer */
kill (r, SIGINT); kill (w, SIGINT);
exit (0);
}
return pid;
}
/* Пара master-процессов чтения и записи */
/* запуск процесса чтения с псевдотерминала (из master-части) */
startReader () {
char c[512];
int pid;
int n;
if (!(pid = fork ())) {
/* читать данные с ptyp на экран и в файл трассировки */
signal (SIGINT, bye);
/* ожидать появления семафора */
while (stat (SEMAPHORE, &st) < 0);
fprintf (stderr, "\rReader: Hello\r\n");
ftrace = fopen (TRACE, "w");
/* работать, пока существует семафорный файл */
while (stat (SEMAPHORE, &st) >= 0) {
/* прочесть очередные данные */
n = read (ptyfd, c, 512);
if( n > 0 ) {
/* записать их на настоящий терминал */
fwrite( c, sizeof(char), n, stdout );
/* и в файл протокола */
fwrite( c, sizeof(char), n, ftrace );
fflush (stdout);
}
}
bye ();
}
return pid;
}
/* запуск процесса чтения данных с клавиатуры и записи
* их на "псевдоклавиатуру". Эти данные протоколировать не надо,
* так как их эхо-отобразит сам псевдотерминал
*/
startWriter () {
char c;
int pid;
if (!(pid = fork ())) {
/* читать клавиатуру моего терминала и выдавать это в ptyp */
openVisual (); /* наш терминал - в прозрачный режим */
signal (SIGINT, onintr);
while (stat (SEMAPHORE, &st) < 0);
fprintf (stderr, "\rWriter: Hello\r\n");
/* работать, пока существует семафорный файл */
while (stat (SEMAPHORE, &st) >= 0) {
read (0, &c, 1); /* читать букву с клавиатуры */
write (ptyfd, &c, 1); /* записать ее на master-pty */
}
onintr (); /* завершиться */
}
return pid;
}
.
/* Пример 28 */
/* Оценка фрагментированности тома файловой системы
* (неупорядоченности блоков в файлах).
* Иллюстрация работы с файловой системой UNIX напрямую,
* в обход ядра системы. Для этого вы должны иметь права
* суперпользователя !!! Данная программа относится к классу
* "системных" (администраторских) программ.
* Эта программа предполагает каноническую файловую систему V7
* ("старую"), а не ту, которая используется начиная с BSD/4.2 и
* в которой все устроено несколько сложнее и эффективнее.
* Поэтому вы должны будете модифицировать эту программу для
* использования в современных UNIX-системах.
* По мотивам книги М.Дансмура и Г.Дейвиса.
*/
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/ino.h> /* struct dinode: disk inode */
#include <sys/stat.h> /* struct stat */
#include <sys/dir.h> /* struct direct */
char blkflag; /* печатать ли номера блоков файла */
/* Отведение памяти в куче с выдачей ошибки, если нет памяти */
char *MyAlloc( n ){
extern char *malloc();
char *ptr;
ptr = malloc( n );
if( ptr == NULL ){
fprintf( stderr, "Cannot allocate %d bytes\n", n );
exit(77);
}
return ptr;
}
char DEV[] = "/dev" ; /* каталог, где лежат все файлы устройств */
/* Определить имя устройства по его st_dev номеру.
* Поиск - по каталогу /dev
*/
char *whichdev( dev ) dev_t dev;
{
struct stat s;
struct direct d;
long i;
int fd; /* дескриптор чтения каталога */
long dsize; /* число слотов каталога */
char *devname;
if( stat( DEV, &s ) < 0 ){
fprintf( stderr, "Cannot stat %s\n", DEV );
exit(1);
}
if((fd = open( DEV, O_RDONLY )) < 0 ){
fprintf( stderr, "Cannot read %s\n", DEV );
exit(2);
}
dsize = s.st_size / sizeof( struct direct );
/* читать каталог */
for( i = 0 ; i < dsize ; i++ ){
char leaf[ DIRSIZ + 1 ];
if( read( fd, &d, sizeof d ) != sizeof d ){
fprintf( stderr, "Cannot read %s\n", DEV );
exit(14);
}
if( ! d.d_ino ) continue; /* пустой слот */
strncpy( leaf, d.d_name, DIRSIZ );
leaf[ DIRSIZ ] = '\0';
devname = MyAlloc( strlen( DEV ) + 1 + strlen( leaf ) + 1 );
/* /dev / xxxx \0 */
sprintf( devname, "%s/%s", DEV, leaf );
if( stat( devname, &s ) < 0 ){
fprintf( stderr, "Cannot stat %s\n", devname );
exit(3);
}
if( (s.st_mode & S_IFMT ) == S_IFBLK && s.st_rdev == dev ){
close(fd);
return devname;
} else free( devname );
}
close( fd );
return NULL;
}
/* Файловая система UNIX: константы подстроены под ДЕМОС 2.2 */
/* размер блока файловой системы */
#define BLOCK 1024 /* либо станд. константа BSIZE из <sys/param.h> */
/* число адресов блоков в косвенном блоке */
#define NAPB (BLOCK/sizeof(daddr_t))
#define LNAPB ((long) NAPB )
/* число I-узлов в блоке I-файла */
#ifndef INOPB
# define INOPB (BLOCK/sizeof(struct dinode))
#endif
/* I-узлы - "паспорта" файлов. I-узлы расположены в начале диска,
в области, называемой I-файл. В I-узле файла содержатся:
размер файла, коды доступа, владелец файла, и.т.п.
В частности - адреса блоков файла хранятся в массиве di_addr:
0 :
... сначала DIR0 адресов первых блоков
IX1: 1 адрес косвенного блока, содержащего адреса еще NAPB блоков
IX2: 1 адрес косв. блока, содержащего адреса NAPB косв. блоков
IX3: 1 адрес косв. блока, содержащего адреса NAPB косв. блоков,
содержащих адреса еще NAPB косв. блоков
Сисвызов stat() выдает как раз часть информации из I-узла.
Поле d_ino в каталоге хранит номер I-узла файла.
*/
/* число адресных полей по 3 байта в I-узле */
#define NADDR 7
/* число прямо адресуемых блоков */
#define DIR0 ((long)(NADDR-3))
/* число прямых и первых косвенных блоков */
#define DIR1 (DIR0 + LNAPB)
/* число прямых, первых и вторых косвенных блоков */
#define DIR2 (DIR0 + LNAPB + LNAPB*LNAPB)
/* число прямых, вторых и третьих косвенных блоков */
#define DIR3 (DIR0 + LNAPB + LNAPB*LNAPB + LNAPB*LNAPB*LNAPB)
/* индекс адреса первичного блока косвенности */
#define IX1 (NADDR-3)
/* индекс адреса вторичного блока косвенности */
#define IX2 (NADDR-2)
/* индекс адреса третичного блока косвенности */
#define IX3 (NADDR-1)
/* Выдать физический номер блока диска,
* соответствующий логическому блоку файла
*/
daddr_t bmap( fd, ip, lb )
int fd; /* raw диск */
daddr_t lb; /* логический блок */
struct dinode *ip; /* дисковый I-узел */
{
long di_map[ NADDR ];
long dd_map[ NAPB ];
/* перевести 3х байтовые адреса в daddr_t */
l3tol( di_map, ip->di_addr, NADDR );
if( lb < DIR0 )
return di_map[ lb ];
if( lb < DIR1 ){
lb -= DIR0;
lseek( fd, di_map[ IX1 ] * BLOCK, 0 );
read( fd, dd_map, BLOCK );
return dd_map[ lb % LNAPB ];
}
if( lb < DIR2 ){
lb -= DIR1;
lseek( fd, di_map[ IX2 ] * BLOCK, 0 );
read( fd, dd_map, BLOCK );
lseek( fd, dd_map[ lb / LNAPB ] * BLOCK, 0 );
read( fd, dd_map, BLOCK );
return dd_map[ lb % LNAPB ];
}
if( lb < DIR2 ){
lb -= DIR2;
lseek( fd, di_map[ IX3 ] * BLOCK, 0 );
read( fd, dd_map, BLOCK );
lseek( fd, dd_map[ lb / (LNAPB*LNAPB) ] * BLOCK, 0 );
read( fd, dd_map, BLOCK );
lseek( fd, dd_map[ lb % (LNAPB*LNAPB) ] * BLOCK, 0 );
read( fd, dd_map, BLOCK );
return dd_map[ lb % LNAPB ];
}
fprintf( stderr, "Strange block %ld\n", lb );
exit(4);
}
/* Рассчитать фрагментацию файла,
то есть среднее расстояние между блоками файла.
Норма равна фактору интерливинга для данного устройства.
N
SUM | p(j) - p(j-1) |
j = 2
F = ----------------------------------
N
p(j) - номер физ.блока диска, соответствующего
логич. блоку j
Замечания:
1) I-узлы нумеруются с 1 (а не с 0), 0 - признак пустого
места в каталоге (d_ino == 0).
2) I-файл начинается со 2-ого блока диска (0-boot, 1-superblock)
3) если файл пуст - он не содержит блоков, N = 0, F = 0
4) если блок не отведен ("дырка"), то его адрес равен 0L
*/
double xabs( l ) daddr_t l;
{
return ( l < (daddr_t) 0 ? -l : l );
}
double getfrag( dev, ino )
char *dev; /* имя диска */
ino_t ino; /* I-узел файла */
{
struct dinode db;
int fd; /* дескриптор диска */
daddr_t i; /* лог. блок */
daddr_t op; /* физ.блок */
daddr_t ip;
daddr_t nb; /* длина файла (блоков) */
long ni = 0L; /* число интервалов между блоками */
double ifrag = 0.0;
if((fd = open( dev, O_RDONLY )) < 0 ){
fprintf( stderr, "Cannot read %s\n", dev );
perror( "open" );
exit(5);
}
/* прочитать I-узел с номером ino.
* Файл I-узлов размещен на диске начиная со 2 блока
* по INOPB узлов в блоке.
*/
lseek( fd, (( 2 + ((ino-1)/INOPB)) * (long)BLOCK ) +
( sizeof(struct dinode) * ((ino-1) % INOPB)), 0 );
if( read( fd, &db, sizeof db ) != sizeof db ){
fprintf( stderr, "Cannot read %s\n", dev );
perror( "read" );
exit(6);
}
/* вычислить размер файла в блоках */
nb = ((long) db.di_size + BLOCK - 1) / BLOCK;
printf( "%4ld blk%s\t" , nb, nb > 1 ? "s" : " " );
/* игнорировать пустой файл */
if( nb == 0L ){
close(fd);
return 0.0;
}
/* вычислить фрагментацию */
op = bmap( fd, &db, 0L ); /* 0-block */
if( blkflag ) printf( "%ld ", op );
for( i = 1 ; i < nb ; i++ ){
ip = bmap( fd, &db, i );
if( blkflag ) printf( "%ld ", ip );
/* адреса, равные 0, следует игнорировать ("дырки") */
if( ip && op ){
ni++;
ifrag += xabs( ip - op );
}
if( ip ) op = ip;
}
close ( fd );
if( blkflag ) putchar( '\n' );
return ni ? (ifrag/ni) : 0.0 ;
}
double process( name ) char *name;
{
struct stat ss;
char *dn;
double f;
/* определяем имя устройства, на котором расположен
* файл name */
if( stat( name, &ss ) < 0 ){
fprintf( stderr, "Cannot stat %s\n", name );
exit(8);
}
/* printf( "major %d minor %d", major(ss.st_dev), minor(ss.st_dev)); */
if((dn = whichdev( ss.st_dev )) == NULL){
fprintf( stderr, "Cannot determine device\n" );
exit(9);
}
printf( "%-14s on %-12s %12.3f\n",
name, dn, f = getfrag(dn, ss.st_ino ));
free( dn );
return f;
}
usage( name ) char *name; {
fprintf( stderr, "Usage: %s [-b] file ...\n" , name );
exit(7);
}
main(ac, av) char *av[];
{
double fr = 0.0;
int n = 0;
if( ac < 2 )
usage( av[0] );
if( !strcmp( av[1], "-b" )){
blkflag = 1;
av++;
ac--;
}
while( av[1] ){
fr += process( av[1] );
n++;
av++;
}
if( n > 1 )
printf( "\nAverage %12.3f\n", fr / n );
exit(0);
}
.
/* Пример 29 */
/*
* Программа восстановления блоков удаленного файла.
* Работает на канонической файловой системе UNIX (ДЕМОС).
* Просматривает список свободных блоков диска.
*
* Эта программа позволяет восстановить блоки ТОЛЬКО ЧТО удаленного файла.
* Как только вы удалили нужный файл, немедленно прекратите любую
* работу на машине и даже отмонтируйте диск с удаленным файлом.
* Затем, находясь на ДРУГОМ диске, вызовите эту программу.
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/param.h> /* BSIZE */
#include <sys/filsys.h> /* struct filsys */
#include <sys/fblk.h> /* struct fblk */
#include <fcntl.h>
#include <ctype.h>
/*
#define BSIZE 1024 размер блока файловой системы
*/
int fd; /* raw disk */
int fdout; /* дескриптор для спасенных блоков на ДРУГОМ диске */
char blk[ BSIZE ], /* буфер для прочитанного блока */
sublk[ BSIZE ]; /* буфер для суперблока */
/* структура суперблока */
struct filsys *super = (struct filsys *) sublk;
/* счетчик */
long n = 0L;
main( ac, av ) char *av[];
{
daddr_t bno; /* номер блока из списка свободных */
extern daddr_t alloc();
if( ac < 2 ){
fprintf( stderr, "Usage: %s disk\n", av[0] );
exit(1);
}
if((fd = open( av[1], O_RDONLY )) < 0 ){
fprintf( stderr, "Can't read %s\n", av[1] );
exit(2);
}
sync(); /* syncronize */
printf( "Вы должны находиться на ДРУГОМ диске, нежели %s,\n", av[1] );
printf( "чтобы блоки файлов, в которые будут записаны спасаемые\n");
printf( "блоки, выделялись на другом устройстве и не портили\n" );
printf( "список свободных блоков на %s\n\n", av[1] );
fflush( stdout ); sleep(2);
/* прочесть суперблок */
lseek( fd, (long) BSIZE, 0 );
read( fd, sublk, BSIZE );
fprintf( stderr, "%ld free blocks at %s (%6.6s)\n" ,
super->s_tfree, av[1],
super->s_fpack );
/* Просмотр свободных блоков. Список свободных блоков
* имеет организацию LIFO (стек), поэтому блоки
* в списке могут идти не в том порядке,
* в котором они шли в файле. Учтите, что в файле
* кроме блоков, содержащих текст файла,
* бывают также косвенные адресные блоки !
*/
while((bno = alloc()) >= 0L ){
save( bno );
}
printf( "total %ld\n", n );
exit(0);
}
/* Извлечь очередной блок из списка свободных блоков */
daddr_t alloc(){
daddr_t bno;
if( super -> s_nfree <= 0 ) /* число адресов своб. блоков,
* хранимых в суперблоке */
goto nospace;
/* читаем номер блока из списка свободных */
bno = super -> s_free[ --super -> s_nfree ];
if( bno == (daddr_t) 0 )
goto nospace;
if( super -> s_nfree <= 0 ){
/* Продолжение списка - не в суперблоке,
* а в специальном дополнительном блоке файловой системы.
*/
printf( "Indirect block %ld\n", bno );
lseek( fd, (long) BSIZE * bno , 0 );
read ( fd, blk, BSIZE );
super -> s_nfree = ((struct fblk *)blk) -> df_nfree ;
memcpy( (char *) (super -> s_free),
(char *) (((struct fblk *) blk) -> df_free ),
sizeof( super->s_free));
}
if( super -> s_nfree <= 0 ||
super -> s_nfree > NICFREE ){
fprintf( stderr, "Bad free count %d\n", super->s_nfree );
goto nospace;
}
if( super -> s_tfree ) /* кол-во свободных блоков */
super -> s_tfree --;
return bno;
nospace:
super -> s_nfree = 0;
super -> s_tfree = 0;
return (-1L); /* конец списка */
}
/* пересылка участка памяти длиной n байт */
memcpy( to, from, n )
register char *to, *from;
register n;
{
while( n > 0 ){
*to++ = *from++;
n--;
}
}
save( bno ) daddr_t bno;
{
register i;
char answer[ 20 ];
printf( "block %ld-------------------\n", bno );
lseek( fd, bno * BSIZE , 0 );
read ( fd, blk, BSIZE );
for( i=0; i < BSIZE; i++ )
putchar(isprint(blk[i]) || isspace(blk[i]) ? blk[i] : '.' );
printf( "\n\7===> save block %ld ? ", bno );
fflush( stdout );
gets( answer );
if( *answer == 'y' || *answer == 'Y' ){
sprintf( answer, "#%012ld", n );
fdout = creat( answer, 0644 );
if( fdout < 0 ){
fprintf( stderr, "Can't create %s\n", answer );
exit(3);
}
write( fdout, blk, BSIZE );
close( fdout );
}
n++;
}
.
/* Пример 30 */
/* /bin/cc -M2 -Ml -DMATCHONLY -LARGE dosfs.c match.c -o dosfs
* Копирование файлов с дискеты, записанной в MS DOS, в UNIX.
* Предполагается, что ваша UNIX-машина имеет соответствующий драйвер
* для чтения дискет, сформатированных на IBM PC.
* match.c - файл, содержащий текст функции match().
*/
#include <stdio.h>
#include <fcntl.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
extern char *malloc(); /* выделитель памяти */
extern char *strrchr(); /* поиск последнего вхождения буквы */
extern long lseek();
void readBoot(), readFAT(), readRootDir(), main(), line(), getFile(),
doDirectory(), mkname(), enterDir(), countFree(), traceclu();
int fd; /* дескриптор файла - дисковода */
FILE *mapfp; /* файл трассировки */
int trace = 0; /* трассировка пока выключена */
int ask = 1; /* спрашивать ли подтверждение на перезапись файлов */
int dironly = 0; /* 1: только показывать имена, файлы не скидывать */
typedef unsigned char uchar;
/*typedef unsigned short ushort; Есть в sys/types.h */
/* Формат сектора загрузки */
struct boot {
char jmp[3]; /* команда jmp */
char label[8]; /* название системы */
char bfs[2]; /* размер boot-сектора */
uchar sectorsPerCluster; /* число секторов в кластере */
char fatoff[2]; /* смещение до начала FAT */
uchar copies; /* число копий FAT */
char dirsize[2]; /* число записей в корневом каталоге */
char sectors[2]; /* размер дискеты в секторах */
uchar desc; /* описатель типа дискеты */
char FATsize[2]; /* размер FAT в секторах */
char sectorsPerTrack[2]; /* число секторов на трек */
char sides[2]; /* число сторон (1, 2) */
char hidden[2]; /* число спрятанных секторов */
} *boot;
#define SECTOR 512 /* Размер сектора в байтах */
int CLU; /* Размер кластера в байтах */
int SPC; /* Размер кластера в секторах */
int SECT; /* Число секторов на дискете */
long capacity; /* емкость дискеты в байтах */
ushort MAXCLU; /* максимальный номер кластера + 1 */
int NDIR; /* Число слотов в корневом каталоге */
int DIRSIZE; /* Длина корневого каталога в байтах */
int ENTRperCLUSTER; /* Количество слотов в одном кластере каталога */
int SPF; /* Размер FAT в секторах */
int FATSIZE; /* Размер FAT в байтах */
int FATSTART; /* Смещение до FAT в байтах */
int NFAT; /* Количество копий FAT */
uchar DESC; /* Описатель типа дискеты */
int DATACLU; /* Начало области данных (номер физич. кластера) */
int bit16 = 0; /* 1 если FAT использует 16-битные поля, а не 12 */
/* Преобразование char[] в integer */
#define INT(s) ( * (short *)s)
#define LONG(s) ( * (long *)s)
/* Формат одной записи каталога. */
struct dir{
char name[8]; /* имя файла */
char ext[3]; /* расширение (суффикс) */
uchar attrib; /* атрибуты файла */
char unused[10];
char creat_time[2]; /* время создания */
char creat_date[2]; /* дата создания */
char firstCluster[2]; /* начальный кластер */
char size[4]; /* размер в байтах */
};
#define isdir(attr) (attr & 0x10) /* Является ли каталогом ? */
#define islabel(attr) (attr & 0x08) /* Метка тома ? */
#define eq(s1, s2) (!strcmp(s1, s2)) /* сравнение строк на == */
struct dir *droot; /* Содержимое корневого каталога */
char *FAT1; /* File Allocation Table, копия 1 */
char *FAT2; /* копия 2 */
char cwd[256] = ""; /* Текущий каталог в DOS. "" - корневой */
char *root = "/tmp"; /* Каталог в UNIX, куда копируются файлы */
char *pattern = NULL; /* шаблон базового имени */
char *dirpattern; /* каталог (не шаблон) */
char newname[256]; /* буфер дла генерации имен */
char cluster[4098]; /* буфер для чтения кластера */
/* Чтение n байт по адресу s */
Read(fd, s, n) char *s;
{
int nn = read(fd, s, n);
if(nn != n ){
fprintf(stderr, "Ошибка чтения: %d вместо %d\n", nn, n);
perror( "read" ); exit(1);
}
return nn;
}
/* Позиционирование головок */
long Lseek(fd, off, how) long off;
{
long offf;
if((offf = lseek(fd, off, how)) < 0){
fprintf(stderr, "Ошибка lseek(%ld,%d)\n", off, how);
}
return offf;
}
/* Отведение памяти и ее зачистка */
char *Malloc(n) unsigned n;{
char *ptr = malloc(n);
register unsigned i;
if( !ptr){
fprintf(stderr, "Не могу malloc(%u)\n", n ); exit(2);
}
for(i=0; i < n ; i++ ) ptr[i] = 0;
/* Можно было бы использовать ptr = calloc(1,n); эта функция
* как раз отводит и очищает память */
return ptr;
}
/* Нарисовать горизонтальную черту */
void line(c) char c;{
register i;
for(i=0; i < 78; i++) putchar(c);
putchar('\n');
}
/* Обработка псевдо-имен устройств. Используются имена для XENIX */
char *drive(name) char *name;
{
if( eq(name, "360")) return "/dev/fd048ds9";
if( eq(name, "720")) return "/dev/fd096ds9";
if( eq(name, "1.2")) return "/dev/fd096ds15";
return name;
}
/* Создать каталог */
char command[512]; /* буфер дла формирования команд */
mkdir(name, mode) char *name;
{
int retcode; struct stat st;
if( stat(name, &st) >= 0 &&
(st.st_mode & S_IFMT) == S_IFDIR ) return 0; /* уже есть */
sprintf(command, "mkdir \"%s\"", name );
retcode = system(command); /* выполнить команду, записанную в command */
chmod(name, mode & 0777); /* установить коды доступа */
return retcode; /* 0 - успешно */
}
/* Открыть файл, создавая (если надо) недостаюшие каталоги */
FILE *fmdopen(name, mode)
char *name, *mode;
{
extern errno; char *s; FILE *fp;
if( fp = fopen(name, mode)) return fp; /* OK */
/* иначе файл не смог создаться */
/* if( errno != ENOENT ) return NULL; /* из-за недостатка прав */
/* Пробуем создать все каталоги по пути к файлу */
if((s = strrchr(name, '/' )) == NULL ) return NULL;
*s = '\0'; md(name); *s = '/';
return fopen(name, mode);
}
/* Рекурсивный mkdir */
md(path) char *path;
{ struct stat st; char *s; int code;
if( !*path) return 0; /* корневой каталог "/" */
if( stat(path, &st) >= 0 ){ /* существует */
if((st.st_mode & S_IFMT) == S_IFDIR) return 0; /* OK */
printf( "%s - не каталог\n", path ); return 1; /* FAIL */
}
if( s = strrchr(path, '/')){
*s = '\0'; code = md(path); *s = '/';
if( code ) return code; /* Облом */
}
sprintf(command, "mkdir \"%s\"", path );
return system(command); /* 0 если OK */
}
/* Сконструировать имя файла в стиле UNIX.
* В MS DOS все буквы в именах - большие */
void mkname( res, n, e ) char *res, *n, *e;
{ /* res - результат, n - имя, e - суффикс */
register i; char *start = res;
if( n[0] == 0x05 ) n[0] = 0xE5; /* подставной символ */
for(i=0; i < 8 && n[i] && n[i] != ' ' ; i++)
*res++ = n[i];
if( e[0] != ' ')
*res++ = '.';
for(i=0; i < 3 && e[i] && e[i] != ' ' ; i++)
*res++ = e[i];
*res = '\0';
while( *start ){
if( isalpha(*start) && isupper(*start))
*start = tolower(*start);
start++;
}
}
/* ------------------------------------------------------- */
/* Получить запись из FAT для кластера clu */
ushort numCluster(clu) ushort clu;
{ ushort n;
if( clu >= MAXCLU )
printf( "Слишком большой номер кластера %03X >= %03X\n",
clu, MAXCLU );
if( bit16 ){ /* 16 бит на номер кластера */
n = INT( &FAT1[ 2*clu ]);
n &= 0xFFFF;
return n;
} /* иначе 12 бит на номер кластера */
n = clu + clu/2 ;
n = INT( &FAT1[n] );
if( clu % 2 ){ /* нечетный */
n >>= 4;
}
n &= 0xFFF;
return n;
}
/* Узнать следующий кластер файла. 0 если последний */
ushort nextCluster(clu) ushort clu;
{
clu = numCluster(clu);
if( clu >= (bit16 ? 0xFFF8 : 0xFF8 ))
return 0; /* EOF */
return clu;
}
/* Прочесть кластер и сохранить его в файле и буфере */
getCluster(clu, fp, size, buffer)
ushort clu; /* логический кластер (2..) */
FILE *fp; /* файл для спасения */
long size; /* осталось дописать */
char *buffer; /* буфер для кластера */
{
long offset;
int rd, howmuchtoread;
if( size <= 0L ){
printf( "CLUSTER %03X лишний\n", clu ); exit(3);
}
/* Вычислить смещение. Кластеры нумеруются начиная с #2 */
offset = (clu - 2 + DATACLU) * (long) CLU;
Lseek(fd, offset, 0);
/* Сколько байт прочесть ? */
howmuchtoread = (size > CLU) ? CLU : size;
rd = Read(fd, buffer, howmuchtoread);
if( fp != NULL )
fwrite(buffer, 1, rd, fp);
return ( rd < 0 ) ? 0 : rd;
}
/* ------------------------------------------------------------------
* dosfs -rPATH файлы скидываются в каталог PATH, а не в /tmp
* dosfs ... "шаблон" сбрасываются только файлы с подходящими
* именами, например:
* dosfs 1.2 "/*.c" *.c из корня дискеты
* dosfs 1.2 "/dir1/*.c" *.c из каталога /dir1
* dosfs 1.2 "*.c" *.c из всех каталогов
* dosfs -d только просмотр каталогов, без сброса файлов
* Пример: dosfs -qr. 360
*/
void main(argc, argv) char *argv[];
{
if( argc < 2 ) goto usage;
if( *argv[1] == '-' ){ /* разбор ключей */
char *keys = &argv[1][1];
while(*keys){
switch(*keys){
case 't': /* включить трассировку */
trace++;
if((mapfp = fopen( ".Map", "w" )) == NULL )
trace = 0;
break;
case 'q': /* без запросов (quiet) */
ask = 0; break;
case 'r': /* переназначить root */
root = keys+1; goto breakwhile;
case 'd': /* dosfs -d == команда dir */
dironly++; break;
}
keys++;
}
breakwhile:
argc--; argv++;
}
if( argc < 2 ) goto usage;
if( pattern = argv[2] ){ /* может быть NULL */
char *s = strrchr(pattern, '/');
if(s){ /* PATH/PATTERN */
dirpattern = pattern; /* PATH */
*s = '\0'; pattern = s+1; /* PATTERN */
}else{ /* просто PATTERN */
dirpattern = NULL;
}
}
setbuf(stdout, NULL); /* отменить буферизацию */
readBoot(drive(argv[1]));
readFAT();
countFree();
readRootDir();
exit(0);
usage:
printf( "Вызов: dosfs [-dqtrDIR] устройство [\"шаблон\"]\n" );
exit(4);
}
/* Прочесть boot-sector, вычислить разные параметры дискеты */
void readBoot(dsk) char *dsk;
{
char BOOT[SECTOR];
int skips, sides;
if((fd = open( dsk, O_RDONLY)) < 0 ){
fprintf(stderr, "Не могу читать %s\n", dsk); exit(5);
}
/* нулевой сектор дискеты - boot */
Read(fd, BOOT, SECTOR);
boot = (struct boot *) BOOT;
line('-');
printf( "Сформатировано \"%8.8s\"\n", boot->label );
printf( "Размер boot-сектора %d байт\n", INT(boot->bfs));
printf( "Кластер содержит %d секторов\n",
SPC = boot->sectorsPerCluster );
printf( "Дискета содержит %d секторов ",
SECT = INT(boot->sectors));
capacity = SECT * (long) SECTOR;
printf( "(%ld KB)\n", capacity / 1024L );
printf( "На треке %d секторов\n", INT(boot->sectorsPerTrack));
sides = INT(boot->sides);
printf( "Диск имеет %d сторон%c\n\n", sides, sides==1? 'у':'ы');
printf( "Смещение до FAT %d сектор\n",
skips = INT(boot->fatoff));
printf( "Имеется %d копии FAT\n", NFAT = boot->copies );
printf( "FAT занимает %d секторов\n\n", SPF = INT(boot->FATsize));
printf( "Корневой каталог содержит %d записей\n\n",
NDIR = INT(boot->dirsize));
printf( "Описатель дискеты = %02X\t(", DESC = boot->desc );
switch( DESC ){
case 0xFF: printf( "double sided, 8 sectors per track" ); break;
case 0xFE: printf( "single sided, 8 sectors per track" ); break;
case 0xFD: printf( "double sided, 9 sectors per track" ); break;
case 0xFC: printf( "single sided, 9 sectors per track" ); break;
case 0xF9: printf( "double sided, 15 sectors per track"); break;
case 0xF8: printf( "Winchester" ); bit16++; break;
default: printf( "неизвестный тип" ); break;
}
printf( ")\n");
printf( "На диске %d спрятанных секторов\n", INT(boot->hidden));
/* Вычислить характеристики */
CLU = SECTOR * SPC; /* размер кластера в байтах */
FATSIZE = SECTOR * SPF; /* длина FAT в байтах */
FATSTART = SECTOR * skips; /* смещение в байтах до FAT */
/* длина корневого каталога в байтах */
DIRSIZE = NDIR * sizeof(struct dir);
/* физический номер первого кластера данных */
DATACLU = ((long) FATSTART +
(long) FATSIZE * NFAT +
(long) DIRSIZE ) / CLU;
printf( "Первый кластер данных (физ.) = %d\n", DATACLU );
/* число записей каталога в кластере */
ENTRperCLUSTER = CLU / sizeof(struct dir);
/* число секторов для данных */
MAXCLU = (SECT - DATACLU * SPC);
/* число кластеров для данных */
MAXCLU = MAXCLU / SPC;
/* логические номера кластеров идут с #2 */
MAXCLU += 2;
}
/* Прочесть File Allocation Table (таблицу размещения файлов) */
void readFAT(){
register int i;
FAT1 = Malloc(FATSIZE);
Lseek(fd, (long) FATSTART, 0);
Read(fd, FAT1, FATSIZE);
if(NFAT > 1){
FAT2 = Malloc(FATSIZE);
Read(fd, FAT2, FATSIZE);
/* Сравнить копии FAT */
for(i=0; i < FATSIZE; i++ )
if(FAT1[i] != FAT2[i]){
printf( "копии FAT различаются в %d/%d\n",
i, FATSIZE );
break;
}
free( FAT2 );
}
if( DESC != FAT1[0] )
printf( "У FAT другой описатель: %02X\n", FAT1[0] & 0xFF );
}
/* Прочесть корневой каталог дискеты.
* Он расположен сразу же после копий FAT
*/
void readRootDir(){
if( DIRSIZE % SECTOR )
printf( "Размер каталога не кратен сектору\n" );
Lseek(fd, (long)FATSTART + (long)FATSIZE * NFAT, 0);
droot = (struct dir *) Malloc(DIRSIZE);
Read(fd, droot, DIRSIZE );
/* NDIR должно быть 112 для 360K и 720K
* 224 для 1.2 Mb
*/
if( !dironly ) mkdir( root, 0755 );
line('-');
doDirectory(0, NDIR, droot);
}
/* Обработать каталог (напечатать, спасти файлы, обойти подкаталоги) */
#define PRINT \
for(j=0; j < level; j++ ) printf( " " ); /* отступ */ \
printf( "%02d\t%s/%-14s %12ld %s\n", \
strt + i, \
cwd, \
basename, \
size, \
isdir(dd[i].attrib) ? "<DIR>" : \
islabel(dd[i].attrib) ? "<LAB>" : "" )
void doDirectory(strt, entries, dd)
struct dir dd[];
{
register i, j;
char basename[40];
static int level = 0;
int need_to_get; /* надо ли сбрасывать */
/* line('-'); */
for(i=0; i < entries; i++ ){
uchar c; long size;
if((c = *dd[i].name) == 0xE5 || !c)
continue; /* файл стерт (дыра) */
mkname(basename, dd[i].name, dd[i].ext);
size = LONG(dd[i].size); /* размер файла */
/* проверить шаблон имени, если нужно */
if( !pattern || /* pattern задан и */
( (!dirpattern || eq(cwd, dirpattern)) &&
match(basename, pattern)
)
){ PRINT; need_to_get = !dironly; }
else need_to_get = 0;
if(isdir(dd[i].attrib)){
/* себя и родителя проигнорировать */
if( eq(basename, "." ) || eq(basename, ".."))
continue;
level++; /* У каталогов почему-то size == 0 */
enterDir( basename, INT(dd[i].firstCluster), need_to_get);
level--;
} else if( islabel(dd[i].attrib)){
printf( "Volume label:%11.11s\n", dd[i].name );
} else if( need_to_get )
getFile ( basename, INT(dd[i].firstCluster), size);
}
/* line('#'); */
}
/* Прочесть файл в UNIX-ную файловую систему */
void getFile(name, clu, size)
char *name; /* имя файла */
ushort clu; /* начальный кластер */
long size; /* размер */
{
FILE *fp; /* файл куда сохранять */
struct stat st;
ushort nclu = 0;/* порядковый номер кластера */
sprintf(newname, "%s%s/%s", root, cwd, name );
if( ask && stat(newname, &st) >= 0 ){
char answer[30];
fprintf(stderr, "%s уже существует, перезаписать? ",
newname);
gets(answer);
if( *answer != 'y' ) return;
fprintf( stderr, "\tOK\n" );
}
if((fp = fmdopen( newname, "w" )) == NULL){
printf( "Не могу создать %s\n", newname );
return;
}
if( trace ) fprintf( mapfp, "\n%s/%s:", cwd, name );
while( clu ){
if( trace ) traceclu(nclu++, clu);
size -= getCluster(clu, fp, size, cluster);
clu = nextCluster(clu);
}
fclose(fp);
}
/* Обработать подкаталог */
void enterDir(name, clu, create)
char *name; /* имя */
ushort clu; /* начальный кластер */
{
char *tail, *myCluster;
struct dir *dsub;
ushort nclu;
int nentries; /* число записей в каталоге */
/* Коррекция cwd */
tail = cwd + strlen(cwd);
*tail = '/'; strcpy(tail+1, name);
if( create ){ /* создать */
sprintf( newname, "%s%s", root, cwd );
mkdir ( newname, 0755);
}
if( trace ) fprintf( mapfp, "\nDIR %s:", cwd);
myCluster = Malloc( sizeof cluster );
dsub = (struct dir *) myCluster;
nentries = nclu = 0;
while( clu ){
if( trace ) traceclu(nclu++, clu);
/* Прочесть очередной кластер каталога */
getCluster(clu, NULL,(long) CLU, myCluster);
/* Обработать имена в этом кластере */
doDirectory(nentries, ENTRperCLUSTER, dsub);
nentries += ENTRperCLUSTER;
/* Взять следующий кластер */
clu = nextCluster(clu);
}
*tail = '\0'; free(myCluster);
}
/* Подсчет свободных и плохих кластеров. */
void countFree(){
int isFree = 0; /* свободные кластеры */
int isBad = 0; /* сбойные кластеры */
int isReserved = 0; /* спрятанные кластеры */
register ushort n = 0;
register ushort clu; /* текущий анализируемый кластер */
int nline = 300;
if( trace ) fprintf(mapfp, "\t\tFAT chart\n");
for(clu=0; clu < MAXCLU; clu++){
if( clu >= 2 ){
n = numCluster(clu);
if( n == 0 ) isFree++;
if( n == (bit16 ? 0xFFF7 : 0xFF7)) isBad++;
if( n >= (bit16 ? 0xFFF0 : 0xFF0 ) &&
n < (bit16 ? 0xFFF7 : 0xFF7 )) isReserved++;
}
if( trace ){
if( nline >= 8){
nline = 0; fprintf( mapfp, "\n%03X:\t", clu );
} else nline++;
fprintf( mapfp, "%03X ", n );
}
}
line('=');
printf( "Свободно %ld, испорчено %ld, резерв %d кластеров\n",
(long)isFree * CLU, /* в байтах */
(long)isBad * CLU, isReserved );
}
void traceclu(nclu, clu) ushort nclu, clu;
{
if( nclu % 16 == 0 )
fprintf( mapfp, "\n\t" );
fprintf( mapfp, "%03X ", clu );
}
#ifdef LOCAL_MALLOC
/*
Обратите внимание, что в этой программе память отводится malloc()
и освобождается free() по принципу стека (LIFO).
Мы могли бы переопределить стандартные функции malloc() и free(),
заставив их работать со статической памятью! (Если мы напишем
свою функцию с именем, как у стандартной, то будет использоваться
НАША функция).
*/
static char allocArena[32 * 1024];
static char *top = allocArena;
char *malloc(n){ char *ptr;
/* округлить до целого числа слов */ /* деление с остатком */
/* число int-ов: */ n = (n + (sizeof(int)-1)) / sizeof(int);
/* число char-ов:*/ n *= sizeof(int);
ptr = top; top += n; return ptr;
}
free(ptr) char *ptr; { top = ptr; }
#endif /*LOCAL_MALLOC*/
.
/* Пример 31 */
/* Интроспективная программа: печатает сама себя */
#include <stdio.h>
char *text[] = {
"#include <stdio.h>",
"char *text[] = {",
" NULL};",
"/* Программа, печатающая свой собственный текст */",
"main(){ int i;",
" puts(text[0]); puts(text[1]);",
" for(i=0; text[i]; i++) putq(text[i]);",
" for(i=2; text[i]; i++) puts(text[i]);",
"}",
"putq(s) char *s; {",
" printf(\"\\t\\\"\");",
" while(*s){",
" if(*s == '\"') printf(\"\\\\\\\"\");",
" else if(*s == '\\\\') printf(\"\\\\\\\\\");",
" else putchar(*s);",
" s++;",
" }",
" printf(\"\\\",\\n\");",
"}",
NULL};
/* Программа, печатающая свой собственный текст */
main(){ int i;
puts(text[0]); puts(text[1]);
for(i=0; text[i]; i++) putq(text[i]);
for(i=2; text[i]; i++) puts(text[i]);
}
putq(s) char *s; {
printf("\t\"");
while(*s){
if(*s == '"') printf("\\\"");
else if(*s == '\\') printf("\\\\");
else putchar(*s);
s++;
}
printf("\",\n");
}
.
/* Пример 32 */
/* C beautify: программа cb.c, форматирующая исходный
* текст программы на Си. Текст взят из дистрибутива UNIX */
#include <stdio.h>
#include <stdlib.h>
#define gets getlex
#define puts putlex
/* прототипы */
void main(int argc, char *argv[]);
void ptabs( void );
int getch( void );
void puts( void );
int lookup( char *tab[] );
int gets( void );
void gotelse( void );
int getnl( void );
void comment( void );
int slevel[10];
int clevel = 0;
int spflg[20][10];
int sind [20][10];
int siflev[10];
int sifflg[10];
int iflev = 0;
int ifflg = -1;
int level = 0;
int ind[10] = { 0,0,0,0,0,0,0,0,0,0 };
int eflg = 0;
int paren = 0;
int pflg[10] = { 0,0,0,0,0,0,0,0,0,0 };
char lchar;
char pchar;
int aflg = 0;
int ct;
int stabs[20][10];
int qflg = 0;
char *wif[] = { "if",NULL};
char *welse[] = { "else", NULL};
char *wfor[] = { "for" , NULL};
char *wds[] = { "case","default", NULL};
int j = 0;
char string[200];
char cc;
int sflg = 1;
int peek = -1;
int tabs = 0;
int lastchar;
int c;
void main(int argc, char *argv[])
{
if( argc > 1 ){
if( freopen( argv[1], "r", stdin ) == NULL ){
fprintf(stderr, "Can't open %s\n", argv[1] );
exit(1);
}
}
if( argc > 2 ){
if( freopen( argv[2], "w", stdout ) == NULL ){
fprintf(stderr, "Can't create %s\n", argv[2] );
exit(1);
}
}
while((c = getch()) != EOF){
switch(c){
case ' ':
case '\t':
if(lookup(welse) == 1){
gotelse();
if(sflg == 0 || j > 0) string[j++] = c;
puts();
sflg = 0;
if(getnl() == 1){
puts();
printf("\n");
sflg = 1;
pflg[level]++;
tabs++;
}
continue;
}
if(sflg == 0 || j > 0) string[j++] = c;
continue;
case '\n':
if((eflg = lookup(welse)) == 1) gotelse();
puts();
printf("\n");
sflg = 1;
if(eflg == 1){
pflg[level]++;
tabs++;
}
else
if(pchar == lchar)
aflg = 1;
continue;
case '{':
if(lookup(welse) == 1) gotelse();
siflev[clevel] = iflev;
sifflg[clevel] = ifflg;
iflev = ifflg = 0;
clevel++;
if(sflg == 1 && pflg[level] != 0){
pflg[level]--;
tabs--;
}
string[j++] = c;
puts(); getnl(); puts(); printf("\n");
tabs++;
sflg = 1;
if(pflg[level] > 0){
ind[level] = 1;
level++;
slevel[level] = clevel;
}
continue;
case '}':
clevel--;
if((iflev = siflev[clevel]-1) < 0) iflev = 0;
ifflg = sifflg[clevel];
if(pflg[level] >0 && ind[level] == 0){
tabs -= pflg[level];
pflg[level] = 0;
}
puts();
tabs--;
ptabs();
if((peek = getch()) == ';'){
printf("%c;", c);
peek = -1;
}
else printf("%c", c);
getnl(); puts(); printf("\n");
sflg = 1;
if(clevel < slevel[level])if(level > 0) level--;
if(ind[level] != 0){
tabs -= pflg[level];
pflg[level] = 0;
ind[level] = 0;
}
continue;
case '"':
case '\'':
string[j++] = c;
while((cc = getch()) != c){
string[j++] = cc;
if(cc == '\\'){
string[j++] = getch();
}
if(cc == '\n'){
puts();
sflg = 1;
}
}
string[j++] = cc;
if(getnl() == 1){
lchar = cc;
peek = '\n';
}
continue;
case ';':
string[j++] = c;
puts();
if(pflg[level] > 0 && ind[level] == 0){
tabs -= pflg[level];
pflg[level] = 0;
}
getnl(); puts(); printf("\n");
sflg = 1;
if(iflev > 0)
if(ifflg == 1){
iflev--; ifflg = 0;
}
else iflev = 0;
continue;
case '\\':
string[j++] = c;
string[j++] = getch();
continue;
case '?':
qflg = 1;
string[j++] = c;
continue;
case ':':
string[j++] = c;
if(qflg == 1){
qflg = 0;
continue;
}
if(lookup(wds) == 0){
sflg = 0;
puts();
}
else{
tabs--; puts(); tabs++;
}
if((peek = getch()) == ';'){
printf(";");
peek = -1;
}
getnl(); puts(); printf("\n");
sflg = 1;
continue;
case '/':
string[j++] = c;
if((peek = getch()) != '*') continue;
string[j++] = peek;
peek = -1;
comment();
continue;
case ')':
paren--;
string[j++] = c;
puts();
if(getnl() == 1){
peek = '\n';
if(paren != 0) aflg = 1;
else if(tabs > 0){
pflg[level]++;
tabs++;
ind[level] = 0;
}
}
continue;
case '#':
string[j++] = c;
while((cc = getch()) != '\n') string[j++] = cc;
string[j++] = cc;
sflg = 0;
puts();
sflg = 1;
continue;
case '(':
string[j++] = c;
paren++;
if(lookup(wfor) == 1){
while((c = gets()) != ';');
ct=0;
cont:
while((c = gets()) != ')'){
if(c == '(') ct++;
}
if(ct != 0){
ct--; goto cont;
}
paren--;
puts();
if(getnl() == 1){
peek = '\n';
pflg[level]++;
tabs++;
ind[level] = 0;
}
continue;
}
if(lookup(wif) == 1){
puts();
stabs[clevel][iflev] = tabs;
spflg[clevel][iflev] = pflg[level];
sind[clevel][iflev] = ind[level];
iflev++;
ifflg = 1;
}
continue;
default:
string[j++] = c;
if(c != ',') lchar = c;
}
}
}
void ptabs( void ){
int i;
for(i=0; i < tabs; i++) printf("\t");
}
int getch( void ){
if(peek < 0 && lastchar != ' ' && lastchar != '\t')
pchar = lastchar;
lastchar = (peek<0) ? getc(stdin) : peek;
peek = -1;
return(lastchar);
}
void puts( void ){
if(j > 0){
if(sflg != 0){
ptabs();
sflg = 0;
if(aflg == 1){
aflg = 0;
if(tabs > 0) printf(" ");
}
}
string[j] = '\0';
printf("%s",string);
j = 0;
}
else{
if(sflg != 0){
sflg = 0; aflg = 0;
}
}
}
int lookup( char *tab[] )
{
char r;
int l,kk,k,i;
if(j < 1) return(0);
kk=0;
while(string[kk] == ' ') kk++;
for(i=0; tab[i] != 0; i++){
l=0;
for(k=kk;(r = tab[i][l++]) == string[k] && r != '\0';k++);
if(r == '\0' &&
(string[k] < 'a' || string[k] > 'z' || k >= j))
return(1);
}
return(0);
}
int gets( void ){
char ch;
beg:
if((ch = string[j++] = getch()) == '\\'){
string[j++] = getch();
goto beg;
}
if(ch == '\'' || ch == '"'){
while((cc = string[j++] = getch()) != ch)
if(cc == '\\') string[j++] = getch();
goto beg;
}
if(ch == '\n'){
puts();
aflg = 1;
goto beg;
}
else return(ch);
}
void gotelse( void ){
tabs = stabs[clevel][iflev];
pflg[level] = spflg[clevel][iflev];
ind[level] = sind [clevel][iflev];
ifflg = 1;
}
int getnl( void ){
while((peek = getch()) == '\t' || peek == ' '){
string[j++] = peek;
peek = -1;
}
if((peek = getch()) == '/'){
peek = -1;
if((peek = getch()) == '*'){
string[j++] = '/';
string[j++] = '*';
peek = -1;
comment();
}
else string[j++] = '/';
}
if((peek = getch()) == '\n'){
peek = -1;
return(1);
}
return(0);
}
void comment( void ){
rep:
while((c = string[j++] = getch()) != '*')
if(c == '\n'){
puts();
sflg = 1;
}
gotstar:
if((c = string[j++] = getch()) != '/'){
if(c == '*') goto gotstar;
goto rep;
}
}
|