Хрестоматия по программированию на Си в Unix
char valid; /* существует ли этот каталог вообще */
short readErrors; /* != 0, если каталог не читается */
} DirContents;
/* Виды сортировки имен в каталоге */
typedef enum { SORT_ASC, SORT_DESC, SORT_SUFX,
SORT_NOSORT, SORT_SIZE } Sort;
extern Sort sorttype; extern int in_the_root;
int gcmps (const void *p1, const void *p2);
Info *blkcpy(Info *v); void blkfree(Info *v);
Info *glob(char **patvec, char *dirname);
Info *glb(char *pattern, char *dirname);
int ReadDir(char *dirname, DirContents *d);
struct savech{ char *s, c; };
#define SAVE(sv, str) (sv).s = (str); (sv).c = *(str)
#define RESTORE(sv) if((sv).s) *(sv).s = (sv).c
/* _______________________ файл glob.c __________________________ */
#include "glob.h"
int in_the_root = NO; /* читаем корневой каталог ? */
Sort sorttype = SORT_SUFX; /* сортировка имен по суффиксу */
Info NullInfo = { NULL, 0 }; /* и прочие поля = 0 (если есть) */
char *strdup(const char *s){
char *p = malloc(strlen(s)+1); if(p)strcpy(p, s); return p; }
/* Содержится ли любой из символов в строке ? */
int any(register char *s, register char *p){
while( *s ){ if( strchr(p, *s)) return YES; s++; }
return NO;
}
/* Найти последнюю точку в имени */
static char *lastpoint (char *s)
{ register char *last; static char no[] = "";
if((last = strchr(s, '.')) == NULL) return no;
/* если имя начинается с точки - не считать ее */
return( last == s ? no : last );
}
/* Сравнение строк с учетом их суффиксов */
int strsfxcmp (register char *s1, register char *s2){
char *p1, *p2, c1, c2; int code;
p1 = lastpoint (s1); p2 = lastpoint (s2);
if (code = strcmp (p1, p2)) return code; /* суффиксы разные */
/* иначе: суффиксы равны. Сортируем по головам */
c1 = *p1; c2 = *p2; *p1 = '\0'; *p2 = '\0'; /* временно */
code = strcmp (s1, s2);
*p1 = c1; *p2 = c2; return code;
}
/* Функция сортировки */
int gcmps(const void *p1, const void *p2){
Info *s1 = (Info *) p1, *s2 = (Info *) p2;
switch( sorttype ){
default:
case SORT_ASC: return strcmp(s1->s, s2->s);
case SORT_DESC: return -strcmp(s1->s, s2->s);
case SORT_SUFX: return strsfxcmp(s1->s, s2->s);
case SORT_NOSORT: return (-1);
#ifdef FILF
case SORT_SIZE: return (s1->size < s2->size ? -1 :
s1->size == s2->size ? 0 : 1 );
#endif
}
}
/* Копирование блока */
Info *blkcpy(Info *v){
register i, len;
Info *vect = (Info *) malloc(((len=blklen(v)) + 1) * sizeof(Info));
for(i=0; i < len; i++ ) vect[i] = v[i];
vect[len] = NullInfo; return vect;
}
/* Измерение длины блока */
int blklen(Info *v){
int i = 0;
while( v->s ) i++, v++;
return i;
}
/* Очистка блока (уничтожение) */
void blkfree(Info *v){
Info *all = v;
while( v->s )
free((char *) v->s ), v++;
free((char *) all );
}
/* Сравнение двух блоков */
int blkcmp( register Info *p, register Info *q ){
while( p->s && q->s && !strcmp(p->s, q->s) &&
(p->fl & I_SYS) == (q->fl & I_SYS)){ p++; q++; }
if( p->s == NULL && q->s == NULL )
return 0; /* совпадают */
return 1; /* различаются */
}
char globchars [] = "*?[";
Info gargv[MAX_ARGV]; int gargc;
static short readErrors;
void greset() { gargc = 0; readErrors = 0; }
/* Расширить шаблон имен файлов в сами имена */
static void globone(char *pattern, char dirname[]){
extern char *strdup(); struct stat st;
DIR *dirf; struct dirent *d;
if( any(pattern, globchars) == NO ){ /* no glob */
gargv[gargc] = NullInfo;
gargv[gargc].s = strdup(pattern);
gargc++;
gargv[gargc] = NullInfo;
return;
}
if((dirf = opendir(dirname)) == NULL){ readErrors++; goto out; }
while(d = readdir(dirf)){
if(match(d->d_name, pattern)){
char fullname[512];
if( sorttype != SORT_NOSORT && !strcmp(d->d_name, "."))
continue;
/* В корневом каталоге имя ".." следует пропускать */
if( in_the_root && !strcmp(d->d_name, "..")) continue;
/* Проверка на переполнение */
if( gargc == MAX_ARGV - 1){
free(gargv[gargc-1].s);
gargv[gargc-1].s = strdup(" Слишком много файлов!!!");
gargv[gargc-1].fl = I_SYS;
break;
}
gargv[gargc] = NullInfo;
gargv[gargc].s = strdup(d->d_name);
sprintf(fullname, "%s/%s", dirname, d->d_name);
if(stat(fullname, &st) < 0) gargv[gargc].fl |= I_NOSEL;
else if(isdir(st)) gargv[gargc].fl |= I_DIR;
else if(isexe(st)) gargv[gargc].fl |= I_EXE;
#ifdef FILF
gargv[gargc].size = st.st_size;
gargv[gargc].uid = st.st_uid;
gargv[gargc].gid = st.st_gid;
gargv[gargc].mode = st.st_mode;
#endif
gargc++;
}
}
closedir(dirf);
out: gargv[ gargc ] = NullInfo;
}
/* Расширить несколько шаблонов */
Info *glob(char **patvec, char *dirname){
greset();
while(*patvec){ globone(*patvec, dirname); patvec++; }
qsort(gargv, gargc, sizeof(Info), gcmps);
return blkcpy(gargv);
}
Info *glb(char *pattern, char *dirname){ char *pv[2];
pv[0] = pattern; pv[1] = NULL; return glob(pv, dirname);
}
/* Прочесть содержимое каталога, если оно изменилось:
* Вернуть: 0 - каталог не менялся;
* 1 - изменился;
* 1000 - изменился рабочий каталог (chdir);
* -1 - каталог не существует;
*/
int ReadDir(char *dirname, DirContents *d){
struct stat st; Info *newFiles;
int save = YES; /* сохранять метки у файлов ? */
int dirchanged = NO; /* сделан chdir() ? */
/* каталог мог быть удален, а мы об этом не извещены */
if( stat(dirname, &st) < 0 ){
d->valid = NO; d->lastRead = 0L;
if(d->files) blkfree(d->files);
d->files = blkcpy( &NullInfo );
return (-1); /* не существует */
} else d->valid = YES;
/* не изменился ли адрес каталога, хранимого в *d ? */
if(d->ino != st.st_ino || d->dev != st.st_dev){ /* изменился */
d->ino = st.st_ino; d->dev = st.st_dev;
save = NO; d->lastRead = 0L; dirchanged = YES;
}
/* не изменилось ли имя каталога ? */
if( !d->name || strcmp(d->name, dirname)){
if(d->name) free(d->name); d->name = strdup(dirname);
/* save=NO; d->lastRead = 0; */
}
/* проверим, был ли модифицирован каталог ? */
if( save==YES && d->files && st.st_mtime == d->lastRead )
return 0; /* содержимое каталога не менялось */
d->lastRead = st.st_mtime;
newFiles = glb("*", d->name); /* прочесть содержимое каталога */
if(save == YES && d->files){
register Info *p, *q;
if( !blkcmp(newFiles, d->files)){
blkfree(newFiles); return 0; /* не изменилось */
} /* иначе сохранить пометки */
for(p= d->files; p->s; p++)
for(q= newFiles; q->s; ++q)
if( !strcmp(p->s, q->s)){
q->fl |= p->fl & ~I_SYS; break;
}
}
if(d->files) blkfree(d->files);
d->files = newFiles; d->readErrors = readErrors;
return 1 + (dirchanged ? 999:0);
/* каталог изменился */
}
/* Пример 19 */
/* ________________________файл menu.h __________________________ */
/* РОЛЛИРУЕМОЕ МЕНЮ */
/* _______________________________________________________________*/
#include
#include
#define M_HOT '\\' /* горячий ключ */
#define M_CTRL '\1' /* признак горизонтальной черты */
#define MXEND(m) XEND((m)->win,(m)->scrollok)
#define NOKEY (-33) /* горячего ключа нет */
#define MAXLEN MAXPATHLEN /* макс. длина имен файлов */
typedef enum { /* Коды, возвращаемые handler-ом (HandlerReply *reply) */
HANDLER_OUT = 0, /* выйти из функции выбора */
HANDLER_CONTINUE = 1, /* читать очередную букву */
HANDLER_NEWCHAR = 2, /* пойти на анализ кода handler-ом. */
HANDLER_SWITCH = 3, /* пойти на switch() */
HANDLER_AGAIN = 4 /* перезапустить всю функцию выбора */
} HandlerReply;
typedef struct _Menu { /* паспорт меню */
int nitems; /* число элементов меню */
Info *items; /* сам массив элементов */
int *hotkeys; /* "горячие" клавиши */
int key; /* клавиша, завершившая выбор */
int current; /* текущая строка списка */
int shift; /* сдвиг окна от начала меню */
int scrollok; /* окно роллируемое ? */
WINDOW *win; /* окно для меню */
int left, top, height, width; /* координаты меню на экране и
размер окна win */
int textwidth, textheight; /* размер подокна выбора */
int bg_attrib; /* атрибут фона окна */
int sel_attrib; /* атрибут выбранной строки */
char *title; /* заголовок меню */
Point savep;
void (*showMe) (struct _Menu *m);
void (*scrollBar) (struct _Menu *m, int n, int among);
int *hitkeys; /* клавиши, обрабатываемые особо */
int (*handler) (struct _Menu *m, int c, HandlerReply *reply);
} Menu;
/* Структура окна с меню:
*--------------* +0
| ЗАГОЛОВОК | +1
*-----------*--* +2
|+ стр1ааа | | +3
| стр2ббб |##| <- scroll bar шириной BARWIDTH
| стр3ввв | |
*___________|__*
|DX| len |DX|BS|
*/
/* Метки у элементов меню */
#define M_BOLD I_DIR /* яркая строка */
#define M_HATCH 0x08 /* строка тусклая */
#define M_LFT 0x10 /* для использования в pulldown menu */
#define M_RGT 0x20 /* для использования в pulldown menu */
#define M_LABEL 0x40 /* строка имеет метку */
#define M_LEFT (-111)
#define M_RIGHT (-112)
#define TOTAL_NOSEL (-I_NOSEL)
#define M_SET(m, i, flg) (((m)->items)[i]). fl |= (flg)
#define M_CLR(m, i, flg) (((m)->items)[i]). fl &= ~(flg)
#define M_TST(m, i, flg) ((((m)->items)[i]).fl & (flg))
#define M_ITEM(m, i) ((((m)->items)[i]).s)
/* Прототипы */
int MnuInit (Menu *m); void MnuDeinit (Menu *m);
void MnuDrawItem (Menu * m, int y, int reverse, int selection);
int MnuNext (Menu *m); int MnuPrev (Menu *m);
int MnuFirst(Menu *m); int MnuLast (Menu *m);
int MnuPgUp (Menu *m); int MnuPgDn (Menu *m);
int MnuThis (Menu *m); int MnuHot (Menu *m, unsigned c);
int MnuName (Menu *m, char *name);
void MnuDraw (Menu *m); void MnuHide(Menu *m);
void MnuPointAt (Menu *m, int y);
void MnuPoint (Menu *m, int line, int eraseOld);
int MnuUsualSelect (Menu *m, int block);
int is_in(register int c, register int s[]);
char *MnuConvert (char *s, int *pos);
#define M_REFUSED(m) ((m)->key < 0 || (m)->key == ESC )
#define MNU_DY 1
/* _______________________ файл menu.c __________________________ */
#include "w.h"
#include "glob.h"
#include "menu.h"
#include
/* ---------------- implementation module ------------------------- */
/* Не входит ли символ в специальный набор? Массив завершается (-1) */
int is_in(register int c, register int s[]){