Страница:
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; /* список стал пуст */
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; /* список стал пуст */