Хрестоматия по программированию на Си в Unix

     Такси микроавтобус в аэропорт на www.minivan.taxi. |   

Поиск всех возможных коэффициентов a0


Пример 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 ); }
© Copyright А. Богатырев, 1992-95
Си в UNIX
| |


Пример 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;


Пример 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' };


Пример 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 );


Пример 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;


Пример 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(); } }


Пример 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); }
© Copyright А. Богатырев, 1992-95
Си в UNIX
| |


Пример 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>


Пример 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)


Пример 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;


Пример 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)


Пример 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' ); }
© Copyright А. Богатырев, 1992-95
Си в UNIX
| |


Пример 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"


Пример 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 ]; }


Пример 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 )


Пример 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


Пример 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 */


Пример 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);


Пример 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 | |----------------------------------------------------------------|


Пример 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;


Пример 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); }


Пример 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); }


Пример 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' ); } }
© Copyright А. Богатырев, 1992-95
Си в UNIX
| |


Пример 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; /* Количество слотов в одном кластере каталога */


Пример 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; }


Пример 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++; }


Пример 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 ); }
© Copyright А. Богатырев, 1992-95
Си в UNIX
| |


Пример 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); }


Пример 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); /* не найдено */ }


Пример 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 */
© Copyright А. Богатырев, 1992-95
Си в UNIX
| |


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); }
© Copyright А. Богатырев, 1992-95
Си в UNIX
| |


/* _______________________ файл 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
© Copyright А. Богатырев, 1992-95
Си в UNIX
| |


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() ? */


#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(); */ }
© Copyright А. Богатырев, 1992-95
Си в UNIX
| |


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) */ }
© Copyright А. Богатырев, 1992-95
Си в UNIX
| |


/* _______________________ файл 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 */
© Copyright А. Богатырев, 1992-95
Си в UNIX
| |


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 ); }
© Copyright А. Богатырев, 1992-95
Си в UNIX
| |


/* _______________________ файл 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); /* Завершить работу */ }
© Copyright А. Богатырев, 1992-95
Си в UNIX
| |


/* файл 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 ); }
© Copyright А. Богатырев, 1992-95
Си в UNIX
| |


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" ); }
© Copyright А. Богатырев, 1992-95
Си в UNIX
| |


СЕРВЕР |================================================================| |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); }
© Copyright А. Богатырев, 1992-95
Си в UNIX
| |


/* 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; }
© Copyright А. Богатырев, 1992-95
Си в UNIX
| |


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); }
© Copyright А. Богатырев, 1992-95
Си в UNIX
| |


/* Извлечь очередной блок из списка свободных блоков */ 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++; }
© Copyright А. Богатырев, 1992-95
Си в UNIX
| |


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; } }
© Copyright А. Богатырев, 1992-95
Си в UNIX
| |


/* обрезать концевые пробелы */ 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 */ }
© Copyright А. Богатырев, 1992-95
Си в UNIX
| |


*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 ); }
© Copyright А. Богатырев, 1992-95
Си в UNIX
| |


/* удаление по ключу */ 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'); }
© Copyright А. Богатырев, 1992-95
Си в UNIX
| |


/* модифицировать запись с индексом 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(); }
© Copyright А. Богатырев, 1992-95
Си в UNIX
| |

Содержание раздела