"аварийную" картинку в панели */ if( ReadDir(tpane1.d.name, &tpane1.d) || NON_VALID(&tpane1.d)) InitTblFromDir(&tpane1, NO, NULL); if( tpane1.t.exposed == NO ) TblDraw(&tpane1.t); if( ReadDir(tpane2.d.name, &tpane2.d) || NON_VALID(&tpane2.d)) InitTblFromDir(&tpane2, NO, NULL); if( tpane2.t.exposed == NO ) TblDraw(&tpane2.t); } /*-----------------------------------------------------------------* * Ввод команд и выдача подсказки. * *-----------------------------------------------------------------*/ /* Особая обработка отдельных клавиш в редакторе строки */ char e_move = NO; /* кнопки со стрелками <- -> двигают курсор по строке/по таблице */ int e_hit[] = { KEY_LEFT, KEY_RIGHT, KEY_UP, KEY_DOWN, KEY_F(0), KEY_IC, ctrl('G'), ctrl('E'), ctrl('L'), ctrl('F'), ctrl('X'), ctrl('Y'), -1 }; int e_handler (LineEdit *le, int c, HandlerReply *reply){ *reply = HANDLER_CONTINUE; switch(c){ /* Перемещение по таблице без выхода из редактора строки */ case KEY_LEFT: if( !SEL_PANE || !e_move){ *reply=HANDLER_SWITCH; return c; } TblPointAt(A_tbl, A_tbl->current - A_tbl->height); break; case KEY_RIGHT: if( !SEL_PANE || !e_move){ *reply=HANDLER_SWITCH; return c; } TblPointAt(A_tbl, A_tbl->current + A_tbl->height); break; case KEY_DOWN: if( !SEL_PANE){ *reply=HANDLER_SWITCH; return c; } TblPointAt(A_tbl, A_tbl->current + 1); break; case KEY_UP: if( !SEL_PANE){ *reply=HANDLER_SWITCH; return c; } TblPointAt(A_tbl, A_tbl->current - 1); break; case KEY_F(0): /* F10 */ e_move = !e_move; break; case KEY_IC: if( !SEL_PANE){ *reply=HANDLER_SWITCH; return c; } TblRetag(A_tbl, A_tbl->current, T_LABEL); TblPointAt(A_tbl, A_tbl->current+1); break; /* Подстановки */ case ctrl('G'): /* подставить полное имя домашнего каталога */ LeInsStr(le, getenv("HOME")); LeInsStr(le, " "); break; case ctrl('E'): /* подставить имя выбранного файла */ if( A_tbl->nitems ) LeInsStr(le, T_ITEMF(A_tbl, A_tbl->current, 0)); LeInsStr(le, " "); break; case ctrl('L'): /* подставить имя выбранного файла из другой панели */ LeInsStr(le, T_ITEMF(B_tbl, B_tbl->current, 0)); LeInsStr(le, " "); break; case ctrl('X'): case ctrl('Y'): /* подстановка имен помеченных файлов */ { int label = (c == ctrl('X') ? T_LABEL : T_HATCH); register i; for(i=0; i < A_tbl->nitems && le->len < le->maxlen; ++i ) if( T_TST(A_tbl, i, label)){ LeInsStr(le, " "); LeInsStr(le, T_ITEMF(A_tbl, i, 0)); } } break; case ctrl('F'): /* подставить имя текущего каталога */ LeInsStr(le, CWD); LeInsStr(le, " "); break; } return c; } /* При начале редактирования ставь курсор в конец строки */ void e_pos (LineEdit *le){ le->pos = le->len; } /* Обозначить, что мы покинули редактор строки */ void e_hide(LineEdit *le){ le->sel_attrib = le->fr_attrib = le->bg_attrib = A_ITALICS; LeDraw(le); } /* Отредактировать строку в предпоследней строке окна */ char *Edit(WINDOW *w, char *src, RunType dorun){ static char CMD[MAXLEN]; /* буфер для строки команды */ int c; if(w != TOPW){ beep(); return NULL; }/* это должно быть верхнее окно */ keypad(w, TRUE); /* Проинициализировать редактор строки */ switch(dorun){ case NORUN: edit.histIn = edit.histOut = NULL; break; case RUNCMD: edit.histIn = edit.histOut = &hedit; break; case FIND: case TAG: edit.histIn = edit.histOut = &hpat; break; case CHDIR: edit.histIn = &hcwd; edit.histOut = NULL; break; } edit.line = CMD; edit.maxlen = sizeof(CMD)-1; edit.top = wlines(w)-2; edit.left = 2; edit.width = wcols (w)-4 - (1+BARWIDTH); edit.insert = YES; edit.nc = YES; edit.win = w; edit.wl_attrib = edit.bg_attrib=A_REVERSE; edit.fr_attrib=A_STANDOUT; edit.sel_attrib = A_NORMAL|A_BLINK; edit.posMe = e_pos; edit.hitkeys = (SEL_PANE ? e_hit : e_hit+5); edit.handler = e_handler; /* edit.hideMe = e_hide; вызывается ЯВНО */ /* остальные поля равны 0, т.к. edit - статическое данное */ for(;;){ strcpy(CMD, src); if(*src){ strcat(CMD, " "); } c = LeEdit( &edit ); if( LE_REFUSED(&edit) || dorun != RUNCMD || !*CMD || c != '\n' ) break; /* курсор в нижнюю строку экрана */ attrset(A_NORMAL); move(LINES-1, 0); refresh(); resetterm(); /* приостановить работу curses-а */ putchar('\n'); /* промотать экран на строку */ system(CMD); /* выполнить команду внешним Шеллом */ fprintf(stderr,"Нажми ENTER чтобы продолжить --- ");gets(CMD); fixterm(); /* возобновить работу curses-а */ RedrawScreen(); /* перерисовать экран */ if(w == panewin){ checkBothPanes(); if(A_tbl->nitems) TblPoint(A_tbl, A_tbl->current, NO); } src = ""; /* во второй раз ничего не подставлять */ } wattrset(w, A_NORMAL); /* ? */ e_hide ( &edit ); return ( *CMD && !LE_REFUSED(&edit)) ? CMD : NULL; } /* Выдача подсказки а также сообщений об ошибках. */ /* В этом же окне можно набирать команды (dorun==RUNCMD). */ char *help(char *msg, RunType dorun){ register i; char *s; static char *helptext[] = { "ESC - выход в главное меню", "F1 - подсказка", "INS - пометить файл", "ctrl/E - подставить имя выбранного файла", "ctrl/L - подставить имя из другой панели", "ctrl/X - подставить помеченные файлы", "ctrl/Y - подставить помеченные курсивом", "ctrl/G - подставить имя домашнего каталога", "ctrl/F - подставить имя текущего каталога", "F4 - история", "F7 - переключить режим вставки/замены", "F10 - переключить перемещения по строке/по панели", }; #define HELPLINES (sizeof(helptext)/sizeof helptext[0]) Sel save_current_menu = current_menu; /* "выскакивающее" POP-UP window */ WINDOW *w = newwin(2+1+HELPLINES+1, 70, 2, (COLS-70)/2); if( w == NULL ) return NULL; current_menu = SEL_HELP; wattrset(w, A_REVERSE); /* это будет инверсное окно */ werase (w); /* заполнить инверсным фоном */ wborder(w); RaiseWin(w); /* окно появляется */ if(*msg){ wattron (w, A_BOLD); mvwaddstr(w, 1+HELPLINES, 2, msg); wattroff(w, A_BOLD); } for(i=0; i < HELPLINES; i++) mvwaddstr(w, 1+i, 2, helptext[i]); s = Edit(w, "", dorun); PopWin(); /* окно исчезает */ current_menu = save_current_menu; return s; } /*-----------------------------------------------------------------* * Управляющее меню. * *-----------------------------------------------------------------*/ int f_left(), f_right(), f_pull(), f_help(), f_sort(), f_dir(), f_bye(), f_redraw(),f_cdroot(); /* Обратите внимание, что можно указывать не все поля структуры, * а только первые. Остальные равны 0 */ #ifndef __GNUC__ Info mwrk_info[] = { /* строки для главного меню */ { "\\Current directory", 0 , f_left }, /* 0 */ { "\\Root directory", M_HATCH , f_right }, /* 1 */ { "\\Menus", 0 , f_pull }, /* 2 */ { "\1", /* гориз. черта */ 0 }, /* 3 */ { "\\Help", 0 , f_help }, /* 4 */ { "Un\\implemented", I_NOSEL }, /* 5 */ { "Change \\sorttype", 0 , f_sort }, /* 6 */ { "Look directory \\history", 0 , f_dir }, /* 7 */ { "\1", /* гориз. черта */ 0 }, /* 8 */ { "\\Quit", M_BOLD , f_bye }, /* 9 */ { "\1", /* гориз. черта */ 0 }, /* 10 */ { "\\Redraw screen", M_HATCH , f_redraw}, /* 11 */ { "Chdir both panels to /", M_HATCH , f_cdroot}, /* 12 */ { NULL, 0 } }; #else /* GNU C-компилятор 1.37 не может инициализировать поля-union-ы */ static char _gnu_[] = "Compiled with GNU C-compiler"; Info mwrk_info[] = { /* строки для главного меню */ { "\\Current directory", 0 }, { "\\Root directory", M_HATCH }, { "\\Menus", 0 }, { "\1", /* гориз. черта */ 0 }, { "\\Help", 0 }, { "Un\\implemented", I_NOSEL }, { "Change \\sorttype", 0 }, { "Look directory \\history", 0 }, { "\1", /* гориз. черта */ 0 }, { "\\Quit", M_BOLD }, { "\1", /* гориз. черта */ 0 }, { "\\Redraw screen", M_HATCH }, { "Chdir both panels to /", M_HATCH }, { NULL, 0 } }; void mwrk_init(){ mwrk_info [0].any.act = f_left; mwrk_info [1].any.act = f_right; mwrk_info [2].any.act = f_pull; mwrk_info [4].any.act = f_help; mwrk_info [6].any.act = f_sort; mwrk_info [7].any.act = f_dir; mwrk_info [9].any.act = f_bye; mwrk_info[11].any.act = f_redraw; mwrk_info[12].any.act = f_cdroot; } #endif char *mwrk_help[] = { "Перейти в левую панель", "Перейти в правую панель", "Перейти в строчное меню", "", "Выдать подсказку", "Не реализовано", "Изменить тип сортировки имен", "История путешествий", "", "Выход", "", "Перерисовка экрана", "Обе панели поставить в корневой каталог", NULL }; void m_help(Menu *m, int n, int among){ Message(mwrk_help[n]); } /* Выбор в рабочем (командном) меню */ void SelectWorkingMenu(int sel){ if(sel == NOSELECTED) sel = MnuUsualSelect( & mwrk, NO); if( M_REFUSED(&mwrk)) help("Выбери Quit", NORUN); else if(mwrk.items[sel].any.act) (*mwrk.items[sel].any.act)(); if( !done) MnuHide( & mwrk ); } f_left () { current_menu = SEL_PANE1; return 0; } f_right() { current_menu = SEL_PANE2; return 0; } f_pull () { current_menu = SEL_PULL; return 0; } f_help () { help("Нажми ENTER или набери команду:", RUNCMD); return 0; } f_sort () { SelectSortType(NOSELECTED); return 0; } f_dir () { Info *idir; if(idir = HistSelect(&hcwd, 20, 3)) cd(idir->s, &tpane2, CWD); current_menu = SEL_PANE2; return 0; } f_bye () { done++; return 0; } f_redraw() { RedrawScreen(); return 0; } f_cdroot() { cd("/", &tpane1, CWD); cd("/", &tpane2, CWD); checkBothPanes(); return 0; } /*-----------------------------------------------------------------* * Выдача информации про файл, редактирование кодов доступа. * *-----------------------------------------------------------------*/ void MYwaddstr(WINDOW *w, int y, int x, int maxwidth, char *s){ register pos; for(pos=0; *s && *s != '\n' && pos < maxwidth; ++s){ wmove(w, y, x+pos); if( *s == '\t') pos += 8 - (pos & 7); else if( *s == '\b'){ if(pos) --pos; } else if( *s == '\r') pos = 0; else { ++pos; waddch(w, isprint(*s) ? *s : '?'); } } } /* Просмотр начала файла в противоположной панели. */ void fastView( char *name, /* имя файла */ unsigned mode, /* некоторые типы файлов не просматривать */ Table *otbl /* противоположная панель */ ){ FILE *fp; register int x, y; char buf[512]; TblClear(otbl); Message("Нажми ENTER для окончания. " "ПРОБЕЛ - изменяет код доступа. " "ESC - откатка."); if( !ISREG(mode)) goto out; if((fp = fopen(name, "r")) == NULL){ Message("Не могу читать %s", name); return; } for(y=0; y < otbl->height && fgets(buf, sizeof buf, fp); y++) MYwaddstr(panewin, otbl->top+y, otbl->left+1, otbl->width-2, buf); fclose(fp); out: wrefresh(otbl->win); /* проявить */ } static struct attrNames{ unsigned mode; char name; char acc; int off; } modes[] = { { S_IREAD, 'r', 'u', 0 }, { S_IWRITE, 'w', 'u', 1 }, { S_IEXEC, 'x', 'u', 2 }, { S_IREAD >> 3, 'r', 'g', 3 }, { S_IWRITE >> 3, 'w', 'g', 4 }, { S_IEXEC >> 3, 'x', 'g', 5 }, { S_IREAD >> 6, 'r', 'o', 6 }, { S_IWRITE >> 6, 'w', 'o', 7 }, { S_IEXEC >> 6, 'x', 'o', 8 }, }; #define NMODES (sizeof(modes)/sizeof(modes[0])) /* Позиция в которой изображать i-ый бит кодов доступа */ #define MODE_X_POS(tbl, i) (tbl->left + DIR_SIZE + 12 + modes[i].off) #define MODE_Y_POS(tbl) (tbl->top + tbl->height + 1) #ifdef FILF /* Изобразить информацию о текущем выбранном файле */ void showMode(Table *tbl, int attr){ Info *inf = & tbl->items[tbl->current]; /* файл */ register i; unsigned mode = inf->mode; /* коды */ int uid = inf->uid, gid = inf->gid; /* хозяин */ /* идентификаторы хозяина и группы процесса-коммандера */ static char first = YES; static int myuid, mygid; WINDOW *win = tbl->win; int xleft = tbl->left + 1, y = MODE_Y_POS(tbl); if( first ){ first = NO; myuid = getuid(); mygid = getgid(); } wattron (win, attr); mvwprintw(win, y, xleft, " %*.*s %8ld ", /* имя файла */ -DIR_SIZE, DIR_SIZE, inf->s ? (!strcmp(inf->s, "..") ? "<UP-DIR>": inf->s) : "(EMPTY)", inf->size); /* тип файла (обычный|каталог|устройство) */ wattron (win, A_ITALICS|A_BOLD); waddch (win, ISDIR(mode) ? 'd': ISDEV(mode) ? '@' : '-'); wattroff(win, A_ITALICS|A_BOLD); /* коды доступа */ for(i=0; i < NMODES; i++){ if((modes[i].acc == 'u' && myuid == uid) || (modes[i].acc == 'g' && mygid == gid) || (modes[i].acc == 'o' && myuid != uid && mygid != gid)) ; else wattron(win, A_ITALICS); mvwaddch(win, y, MODE_X_POS(tbl, i), mode & modes[i].mode ? modes[i].name : '-'); wattroff(win, A_ITALICS); } waddch(win, ' '); wattroff(win, attr); } #define newmode (tbl->items[tbl->current].mode) /* Редактирование кодов доступа к файлам. */ int editAccessModes(FileWidget *wd){ Table *tbl = &wd->t; Table *otbl = &(Other_pane(wd)->t); /* или Other_tbl(tbl); */ unsigned prevmode, oldmode; /* старый код доступа */ char *name; /* имя текущего файла */ WINDOW *win = tbl->win; int position = 0, c; for(;;){ /* Цикл выбора файлов в таблице */ name = T_ITEMF(tbl, tbl->current, 0); oldmode = newmode; /* запомнить */ fastView(name, newmode, otbl); /* показать первые строки файла */ for(;;){ /* Цикл обработки выбранного файла */ wmove(win, MODE_Y_POS(tbl), MODE_X_POS(tbl, position)); switch(c = WinGetch(win)){ /* Некоторые клавиши вызывают перемещение по таблице */ case KEY_BACKTAB: TblPointAt(tbl, tbl->current - tbl->height); goto mv; case '\t': TblPointAt(tbl, tbl->current + tbl->height); goto mv; case KEY_UP: TblPointAt(tbl, tbl->current - 1); goto mv; case KEY_DOWN: TblPointAt(tbl, tbl->current + 1); goto mv; case KEY_HOME: TblPointAt(tbl, 0); goto mv; case KEY_END: TblPointAt(tbl, tbl->nitems-1); goto mv; /* Прочие клавиши предназначены для редактирования кодов доступа */ case KEY_LEFT: if(position) --position; break; case KEY_RIGHT: if(position < NMODES-1) position++; break; default: goto out; case ESC: /* Восстановить старые коды */ prevmode = newmode = oldmode; goto change; case ' ': /* Инвертировать код доступа */ prevmode = newmode; /* запомнить */ newmode ^= modes[position].mode; /* инвертировать */ change: if( chmod(name, newmode) < 0){ beep(); Message("Не могу изменить доступ к %s", name); newmode = prevmode; /* восстановить */ } else /* доступ изменен, показать это */ showMode(tbl, A_REVERSE); break; } } /* Конец цикла обработки выбранного файла */ mv: ; } /* Конец цикла выбора файлов в таблице */ out: /* Очистить противоположную панель после fastView(); */ Message(""); TblClear(otbl); return c; } #undef newmode #else void editAccessModes(FileWidget *wd){} #endif long diskFree(){ struct ustat ust; struct stat st; long freespace; if(stat(".", &st) < 0) return 0; ustat(st.st_dev, &ust); freespace = ust.f_tfree * 512L; freespace /= 1024; Message("В %*.*s свободно %ld Кб.", -sizeof(ust.f_fname), sizeof(ust.f_fname), *ust.f_fname ? ust.f_fname : ".", freespace); doupdate(); /* проявить окно для Message() */ return freespace; } /*-----------------------------------------------------------------* * Специальные команды, использующие обход дерева *-----------------------------------------------------------------*/ /* Выдача сообщений об ошибках (смотри Makefile) */ int tree_err_cant_read(char *name){ Message("Не могу читать \"%s\"", name); return WARNING; } int tree_name_too_long(){ Message("Слишком длинное полное имя"); return WARNING; } char canRun; /* продолжать ли поиск */ /* Прерывание обхода по SIGINT */ void onintr_f(nsig){ canRun = NO; Message("Interrupted"); } /* ==== место, занимаемое поддеревом ==== */ long tu(int *count){ struct stat st; register i; long sum = 0L; *count = 0; for(i=0; i < A_tbl->nitems ;++i ) if( T_TST(A_tbl, i, T_LABEL)){ stat(T_ITEMF(A_tbl, i, 0), &st); #define KB(s) (((s) + 1024L - 1) / 1024L) sum += KB(st.st_size); (*count)++; } return sum; } void diskUsage(){ long du(), size, sizetagged; int n; char msg[512]; Message("Измеряем объем файлов..."); doupdate(); size = du("."); diskFree(); sizetagged = tu(&n); sprintf(msg, "%ld килобайт в %s, %ld кб в %d помеченных файлах", size, CWD, sizetagged, n); help(msg, NORUN); } /* ==== поиск файла ===================== */ extern char *find_PATTERN; /* imported from treemk.c */ extern Info gargv[]; extern int gargc; /* imported from glob.c */ /* Проверить очередное имя и запомнить его, если подходит */ static int findCheck(char *fullname, int level, struct stat *st){ char *basename = strrchr(fullname, '/'); if(basename) basename++; else basename = fullname; if( canRun == NO ) return FAILURE; /* поиск прерван */ if( match(basename, find_PATTERN)){ /* imported from match.c */ gargv[gargc] = NullInfo; /* зачистка */ gargv[gargc].s = strdup(fullname); gargv[gargc++].fl= ISDIR(st->st_mode) ? I_DIR : 0; gargv[gargc] = NullInfo; Message("%s", fullname); doupdate(); } /* Страховка от переполнения gargv[] */ if ( gargc < MAX_ARGV - 1 ) return SUCCESS; else { Message("Найдено слишком много имен."); return FAILURE; } } /* Собрать имена файлов, удовлетворяющие шаблону */ static Info *findAndCollect(char *pattern){ void (*old)() = signal(SIGINT, onintr_f); Sort saveSort; find_PATTERN = pattern; canRun = YES; Message("Ищем %s от %s", pattern, CWD); doupdate(); greset(); /* смотри glob.c, gargc=0; */ walktree(CWD, findCheck, NULL, findCheck); signal(SIGINT, old); saveSort = sorttype; sorttype = SORT_ASC; if(gargc) qsort( gargv, gargc, sizeof(Info), gcmps); sorttype = saveSort; return gargc ? blkcpy(gargv) : NULL; } /* Обработать собранные имена при помощи предъявления меню с ними */ void findFile(FileWidget *wd){ static Info *found; static Menu mfind; int c; Table *tbl = & wd->t; char *pattern = help("Введи образец для поиска, вроде *.c, " "или ENTER для прежнего списка", FIND); if( LE_REFUSED( &edit)) return; /* отказались от поиска */ /* Если набрана пустая строка, help() выдает NULL */ if( pattern ){ /* задан новый образец - ищем */ /* Уничтожить старый список файлов и меню */ if( found ) blkfree( found ); MnuDeinit( &mfind ); found = findAndCollect(pattern); /* поиск */ HistAdd( &hpat, pattern, 0); /* Образуем меню из найденных файлов */ if( found ){ /* если что-нибудь нашли */ mfind.items = found; mfind.title = pattern ? pattern : "Найденные файлы"; mfind.top = 3; mfind.left = COLS/6; mfind.bg_attrib = A_STANDOUT; mfind.sel_attrib = A_REVERSE; MnuInit (&mfind); } } /* else набрана пустая строка - просто вызываем список * найденных ранее файлов. */ if( found == NULL ){ Message("Ничего не найдено"); beep(); return; } c = MnuUsualSelect(&mfind, NO); /* Выбор файла в этом меню вызовет переход в каталог, * в котором содержится этот файл */ if( !M_REFUSED( &mfind )){ char *s = M_ITEM(&mfind, mfind.current); /* пометить выбранный элемент */ M_SET(&mfind, mfind.current, M_LABEL); /* если это каталог - войти в него */ if( M_TST(&mfind, mfind.current, I_DIR)) cd(s, wd, CWD); /* иначе войти в каталог, содержащий этот файл */ else { char *p; struct savech svch; /* смотри glob.h */ SAVE( svch, strrchr(s, '/')); *svch.s = '\0'; p = strdup(s); RESTORE(svch); if( !strcmp(CWD, p)) /* мы уже здесь */ TblPlaceByName(tbl, svch.s+1); /* указать курсором */ else /* изменить каталог и указать курсором на файл s */ cd(p, wd, s); free(p); } } MnuHide(&mfind); /* спрятать меню, не уничтожая его */ } /*-----------------------------------------------------------------* * Работа с панелями, содержащими имена файлов двух каталогов. * *-----------------------------------------------------------------*/ /* Восстановить элементы, затертые рамкой WinBorder */ void t_restore_corners(){ mvwaddch(panewin, LINES-3, 0, LEFT_JOIN); mvwaddch(panewin, LINES-3, COLS-2-BARWIDTH, RIGHT_JOIN); mvwaddch(panewin, LINES-5, 0, LEFT_JOIN); mvwaddch(panewin, LINES-5, COLS-2-BARWIDTH, RIGHT_JOIN); mvwaddch(panewin, 2, CENTER, TOP_JOIN); wattron (panewin, A_BOLD); mvwaddch(panewin, LINES-3, CENTER, BOTTOM_JOIN); mvwaddch(panewin, LINES-5, CENTER, MIDDLE_CROSS); wattroff(panewin, A_BOLD); } /* Нарисовать нечто при входе в панель. Здесь изменяется * заголовок окна: он становится равным имени каталога, * просматриваемого в панели */ void t_enter(Table *tbl){ WinBorder(tbl->win, tbl->bg_attrib, tbl->sel_attrib, CWD, BAR_VER|BAR_HOR, NO); t_restore_corners(); } /* Стереть подсветку при выходе из панели */ void t_leave(Table *tbl){ TblDrawItem( tbl, tbl->current, NO, YES ); } /* Рисует недостающую часть рамки, которая не изменяется впоследствии */ void t_border_common(){ WinBorder(panewin, A_tbl->bg_attrib, A_tbl->sel_attrib, A_dir->name, BAR_VER|BAR_HOR, NO); wattron (panewin, A_BOLD); whorline(panewin, LINES-3, 1, COLS-1-BARWIDTH-1); whorline(panewin, LINES-5, 1, COLS-1-BARWIDTH-1); wverline(panewin, CENTER, A_tbl->top, A_tbl->top + A_tbl->height+2); wattroff(panewin, A_BOLD); t_restore_corners(); } /* Функция, изображающая недостающие части панели при входе в нее */ int t_show(Table *tbl){ #ifdef FILF showMode(A_tbl, A_STANDOUT); showMode(B_tbl, A_STANDOUT); #endif return 1; } void t_scrollbar(Table *tbl, int whichbar, int n, int among){ WinScrollBar(tbl->win, BAR_VER|BAR_HOR, n, among, "Yes", tbl->bg_attrib); #ifdef FILF showMode(tbl, A_REVERSE); #endif } /* Особая обработка клавиш при выборе в таблице */ int t_hit[] = { '\t', KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(8), ' ', '+', '-', ctrl('R'), ctrl('L'), ctrl('F'), -1 }; Info t_info[] = { { "TAB Перейти в другую панель", 0}, { "F1 Выдать подсказку", 0}, { "F2 Ввести команду", 0}, { "F3 Перейти в родительский каталог", 0}, { "F4 Перейти в каталог по имени", 0}, { "F8 Удалить помеченные файлы", 0}, { "ПРОБЕЛ Редактировать коды доступа", 0}, { "+ Пометить файлы", 0}, { "- Снять пометки", 0}, { "ctrl/R Перечитать каталог", 0}, { "ctrl/L Выдать размер файлов в каталоге",0}, { "ctrl/F Поиск файла", 0}, { NULL, 0} }; int t_help(){ static Menu mth; int c = 0; if( mth.items == NULL ){ mth.items = t_info; mth.title = "Команды в панели"; mth.top = 3; mth.left = COLS/6; mth.bg_attrib = A_STANDOUT; mth.sel_attrib = A_REVERSE; MnuInit (&mth); mth.hotkeys = t_hit; } c = MnuUsualSelect(&mth, 0); /* Спрятать меню, не уничтожая его. Уничтожение выглядело бы так: * mth.hotkeys = NULL; (т.к. они не выделялись malloc()-ом) * MnuDeinit(&mth); */ MnuHide(&mth); if( M_REFUSED(&mth)) return 0; /* ничего не делать */ return t_hit[c]; /* клавиша, соответствующая выбранной строке */ } int t_handler (Table *tbl, int c, HandlerReply *reply){ int i, cnt=0; extern int unlink(), rmdir(); char *answer; FileWidget *wd = TblFW (tbl); switch(c){ case '\t': /* перейти в соседнюю панель */ ExchangePanes(); *reply = HANDLER_OUT; return LEAVE_KEY; /* покинуть эту панель */ case KEY_F(1): *reply = HANDLER_NEWCHAR; return t_help(); case KEY_F(2): (void) Edit(tbl->win, T_ITEMF(tbl, tbl->current, 0), RUNCMD); break; case KEY_F(3): cd(".." , wd, CWD); break; case KEY_F(4): if(answer = help("Введи имя каталога, в который надо перейти",CHDIR)) cd(answer , wd, CWD); break; case ctrl('R'): break; case KEY_F(8): for(i=0; i < tbl->nitems; i++) if(T_TST(tbl, i, M_LABEL)){ int code; cnt++; if((code = (T_TST(tbl, i, I_DIR) ? rmdir : unlink) (T_ITEMF(tbl, i,0))) < 0) T_SET(tbl, i, M_HATCH); } if(cnt==0) help("Нет помеченных файлов", NORUN); break; case '+': if(answer = help("Шаблон для пометки", TAG)) TblTagAll(tbl, answer, T_LABEL); break; case '-': if(answer = help("Шаблон для снятия пометок", TAG)) TblUntagAll(tbl, answer, T_LABEL); break; case ctrl('L'): /* команда "disk usage" */ diskUsage(); break; case ctrl('F'): /* поиск файла */ findFile(wd); break; case ' ': /* редактирование кодов доступа */ editAccessModes(wd); break; } *reply = HANDLER_OUT; return REPEAT_KEY; /* вернуться в эту же панель */ } /* Выбор в одной из панелей. */ int SelectPane(FileWidget *wd){ Table *tbl = & wd->t; DirContents *d = & wd->d; int sel, retcode = 0; RaiseWin( tbl->win ); /* войти в указанный каталог, поправить CWD */ if(mychdir( d->name ) < 0) checkBothPanes(); /* t_enter( tbl ); /* войти в указанную панель, поправить рамку */ for(;;){ /* Проверить, не устарело ли содержимое таблиц */ checkBothPanes(); if((sel = TblUsualSelect( tbl )) == TOTAL_NOSEL ){ current_menu = SEL_PULL; goto out; } if( T_REFUSED(tbl)) break; /* нажат ESC */ if( tbl->key == LEAVE_KEY ){ retcode=1; break; } strcpy(SELECTION, T_ITEMF(tbl, sel, 0)); if( tbl->key == REPEAT_KEY ) continue; if(T_TST(tbl, sel, I_DIR)){ /* это каталог */ /* попытаться перейти в этот каталог */ cd(SELECTION, wd, CWD); } else if(T_TST(tbl, sel, I_EXE)){ /* выполняемый файл */ (void) Edit(tbl->win, SELECTION, RUNCMD); } else { editAccessModes(wd); /* На самом деле надо производить подбор команды по * типу файла (набор соответствий должен программироваться * вами в специальном файле, считываемом при запуске коммандера). * runCommand( classify(SELECTION)); * где классификация в простейшем случае - по имени и суффиксу, * а в более развитом - еще и по кодам доступа (включая тип файла) * и по первой строке файла (или "магическому числу"). */ } } /* end for */ t_leave( tbl ); out: if( !retcode ) current_menu = SEL_PULL; /* выход по ESC */ return retcode; } /*-----------------------------------------------------------------* * Горизонтальное командное меню (вызывается по ESC). * *-----------------------------------------------------------------*/ PullInfo pm_items [] = { /* подсказка */ {{ " \\Left ", 0 }, NULL, "Left pane" }, /* 0 */ {{ " \\Commands ", 0 }, &mwrk, "Do some commands"}, /* 1 */ {{ " \\Tools ", PM_NOSEL }, NULL, "" }, /* 2 */ {{ " \\Sorttype ", 0 }, &msort, "Change sort type"}, /* 3 */ {{ " \\Right ", 0 }, NULL, "Right pane" }, /* 4 */ {{ NULL, 0 }, NULL, NULL } }; void p_help(PullMenu *p, int n, int among){ Message( PM_NOTE(p, n)); } /* Выбор в меню-строке */ void SelectPullMenu(){ int c, sel; Menu *m; for(;current_menu == SEL_PULL;){ c = PullUsualSelect(&pull); sel = pull.current; if( PM_REFUSED(&pull)){ current_menu = previous_menu; return;} switch(sel){ case 0: current_menu = SEL_PANE1; return; case 1: SelectWorkingMenu(c); return; case 2: return; /* не бывает */ case 3: SelectSortType(c); return; case 4: current_menu = SEL_PANE2; return; } } } /*-----------------------------------------------------------------* * Инициализация и завершение. * *-----------------------------------------------------------------*/ void die(int sig){ echo(); nocbreak(); mvcur(-1,-1,LINES-1,0); refresh(); endwin (); putchar('\n'); if(sig) printf("Signal %d\n", sig); if(sig == SIGSEGV) abort(); else exit(sig); } void main (void) { setlocale(LC_ALL, ""); /* получить информацию о языке диагностик */ initscr (); /* включить curses */ signal(SIGINT, die); /* по сигналу вызывать die(); */ signal(SIGBUS, die); /* по нарушению защиты памяти */ signal(SIGSEGV,die); refresh(); /* обновить экран: это очистит его */ noecho(); cbreak(); /* выключить эхо, включить прозрачный ввод */ /* Проинициализировать истории */ HistInit(&hcwd, 20); hcwd. mnu.title = "История пути"; HistInit(&hedit, 20); hedit.mnu.title = "История команд"; HistInit(&hpat, 8); hpat. mnu.title = "Шаблоны имен"; /* Разметить меню сортировки */ msort.items = sort_info; msort.title = "Вид сортировки каталога"; msort.top = 1; msort.left = 2; msort.showMe = sort_show; msort.bg_attrib = A_NORMAL; msort.sel_attrib = A_STANDOUT; /* MnuInit (&msort); инициализируется в pull-menu */ /* Разметить рабочее меню */ mwrk.items = mwrk_info; mwrk.title = "Главное меню"; mwrk.top = 1; mwrk.left = COLS/3; mwrk.handler = NULL; mwrk.hitkeys = NULL; mwrk.bg_attrib = A_STANDOUT; mwrk.sel_attrib = A_REVERSE; mwrk.scrollBar = m_help; #ifdef __GNUC__ mwrk_init(); #endif /* MnuInit (&mwrk); инициализируется в pull-menu */ /* Разметить левую и правую панели */ tpane1.t.width = CENTER - 1; tpane2.t.width = COLS - tpane1.t.width - 2 - (2 + BARWIDTH); tpane1.t.height = tpane2.t.height = (LINES - 8); tpane1.t.win = tpane2.t.win = panewin = stdscr; tpane1.t.left = 1; tpane2.t.left = CENTER+1; tpane1.t.top = tpane2.t.top = 3; tpane1.t.bg_attrib = tpane2.t.bg_attrib = A_NORMAL; tpane1.t.sel_attrib = tpane2.t.sel_attrib = A_STANDOUT; tpane1.t.scrollBar = tpane2.t.scrollBar = t_scrollbar; tpane1.t.hitkeys = tpane2.t.hitkeys = t_hit; tpane1.t.handler = tpane2.t.handler = t_handler; tpane1.t.showMe = tpane2.t.showMe = t_show; tpane1.t.hideMe = tpane2.t.hideMe = NULL; /* Разметить имена для файловых объектов */ tpane1.d.name = strdup("Текущий каталог"); tpane2.d.name = strdup("Корневой каталог"); /* Изобразить рамки (но пока не проявлять их) * Это надо сделать до первого cd(), т.к. иначе при неудаче будет выдано * сообщение, которое проявит НЕЗАВЕРШЕННУЮ картинку */ t_border_common(); t_restore_corners(); /* Доразметить левую панель */ mychdir("."); /* узнать полное имя текущего каталога в CWD[] */ /* прочитать содержимое каталога CWD в tpane1.d */ cd( CWD , &tpane1, CWD); tpane1.t.fmt = "directory"; InitTblFromDir(&tpane1, NO, NULL); /* Доразметить правую панель */ tpane2.t.fmt = NULL; /* прочитать содержимое каталога "/" в tpane2.d */ cd( "/", &tpane2, CWD); /* теперь стоим в корне */ /* Вернуться в рабочий каталог */ cd( tpane1.d.name, &tpane1, CWD); /* Нарисовать обе панели */ TblDraw(A_tbl); TblDraw(B_tbl); /* Разметить pulldown меню */ pull.bg_attrib = A_REVERSE; pull.sel_attrib = A_NORMAL; pull.items = pm_items; pull.scrollBar = p_help; PullInit(&pull); /* Основной цикл */ for(done=NO, current_menu=SEL_PANE1, A_pane= &tpane1, B_pane= &tpane2; done == NO; ){ Message(""); if(SEL_PANE) previous_menu = current_menu; switch(current_menu){ case SEL_WRK : SelectWorkingMenu(NOSELECTED); break; case SEL_PULL: SelectPullMenu(); break; case SEL_PANE1: if( SelectPane(&tpane1) < 0) M_SET(&mwrk, 0, I_NOSEL); break; case SEL_PANE2: if( SelectPane(&tpane2) < 0) M_SET(&mwrk, 0, I_NOSEL); break; } } die(0); /* Завершить работу */ } /* Пример 24 */ /* Пример коммуникации процессов при помощи программных каналов * (трубы, pipes). * Данная программа превращается в две программы, * соединенные трубами в таком порядке: * * stdout stdin * /------------ PIP1 -----------> cmd2 * cmd1 <----------PIP2---------------/ * stdin stdout */ /* файл LOOP_strt.c */ #include <stdio.h> #define eq(s1,s2) ( strcmp(s1,s2) == 0 ) /* истина, если строки равны */ #define SEP "---" /* разделитель команд при наборе */ main( c, v ) char **v; { char **p, **q; int pid; int PIP1[2]; /* труба cmd1-->cmd2 */ int PIP2[2]; /* труба cmd2-->cmd1 */ if( c==1 ){ printf( "Call: strt cmd1... %s cmd2...\n", SEP ); exit(1); } /* разбор аргументов */ v++; /* в p - аргументы первой команды */ p = v; while( *v && !eq( *v, SEP )) v++; *v = NULL; v++; /* в q - аргументы второй команды */ q = v; pipe( PIP1 ); /* создаем две трубы */ pipe( PIP2 ); /* PIP[0] - открыт на чтение, PIP[1] - на запись */ if( pid = fork()){ /* развилка: порождаем процесс */ /* ПОРОЖДЕННЫЙ ПРОЦЕСС */ fprintf( stderr, "сын=%s pid=%d\n", p[0], getpid()); /* перенаправляем stdout нового процесса в PIP1 */ dup2( PIP1[1], 1 ); close( PIP1[1] ); /* канал чтения мы не будем использовать */ close( PIP1[0] ); /* перенаправляем stdin из PIP2 */ dup2( PIP2[0], 0 ); close( PIP2[0] ); /* канал записи мы не будем использовать */ close( PIP2[1] ); /* начинаем выполнять программу, содержащуюся в * файле p[0] с аргументами p (т.е. cmd1) */ execvp( p[0], p ); /* возврата из сисвызова exec не бывает */ }else{ /* ПРОЦЕСС-РОДИТЕЛЬ */ fprintf( stderr, "отец=%s pid=%d\n", q[0], getpid()); /* перенаправляем stdout в PIP2 */ dup2( PIP2[1], 1 ); close( PIP2[1] ); close( PIP2[0] ); /* перенаправляем stdin из PIP1 */ dup2( PIP1[0], 0 ); close( PIP1[0] ); close( PIP1[1] ); /* запускаем cmd2 */ execvp( q[0], q ); } } /* Ниже приводятся тексты двух программ, которые можно запустить * как тест. Сервер компилируется в программу cmd2, * клиент - в программу cmd1. Если запускающая программа * скомпилирована в strt, то наберите команду * strt cmd1 --- cmd2 * либо strt cmd2 --- cmd1 */ /* файл LOOP_p.c --------------------------------------------- * Процесс-клиент (cmd1) */ #include <stdio.h> int trace = 1; /* вести трассировку своих действий */ main(c , v) char **v; { FILE *fp; int pid; char buf[128]; fprintf( stderr, "P: process pid=%d\n", getpid()); fp = fopen( "LOOP_p.c", "r" ); /* открываем файл с текстом этой команды */ /* читаем его построчно */ while( fgets( buf, sizeof buf, fp ) != NULL ){ if( trace ) fprintf( stderr, "P посылает: %s", buf ); /* посылаем его в стандартный вывод: трубу PIP1 */ printf( "%s", buf ); fflush( stdout ); /* ожидать ответа из трубы PIP2 */ fgets( buf, sizeof buf, stdin ); if( trace ) fprintf( stderr, "P получил: %s", buf ); } fclose( stdout ); /* отключиться от трубы PIP1. Если этого не сделать, сервер * не прочитает из нее EOF */ while((pid = wait(NULL)) > 0 ) fprintf( stderr, "P: %d умер\n", pid ); } /* файл LOOP_q.c ------------------------------------------------ * процесс-сервер (cmd2) */ #include <stdio.h> int trace = 1; main(c , v) char **v; { char buf[128]; int pid; fprintf( stderr, "Q: process pid=%d\n", getpid()); /* читать поступающие из трубы PIP1 строки */ while( fgets( buf, sizeof(buf), stdin ) != NULL ){ /* напечатать полученное сообщение */ if( trace ) fprintf( stderr, "Q прочел: %s", buf ); if( trace ) fprintf( stderr, "Q отвечает: OK=%s", buf ); /* ответить в трубу PIP2 */ printf( "OK=%s", buf ); fflush( stdout ); } fclose( stdout ); /* отключиться от трубы PIP2 */ while((pid = wait(NULL)) > 0 ) fprintf( stderr, "Q: %d умер\n", pid ); } /* Пример 25 */ /* Пример использования именованных "труб" (pipes) FIFO-файлов * для коммуникации независимых процессов * (FIFO - first in, first out : первым пришел - первым ушел). * По мотивам книги М.Дансмура и Г.Дейвиса. */ /* файл P_packet.h --------------------------------------------*/ #include <sys/types.h> #include <sys/stat.h> /* S_IFIFO */ /* структура пакета-запроса */ struct packet { int pk_pid; /* идентификатор процесса-отправителя */ int pk_blk; /* номер блока, который надо прочитать */ int pk_code; /* код запроса */ }; /* request codes (коды запросов) */ #define RQ_READ 0 /* запрос на чтение */ #define CONNECT 1 /* запрос на соединение */ #define SENDPID 2 /* ответ на запрос соединения */ #define DISCONNECT 3 /* разрыв связи */ #define BYE 4 /* завершить сервер */ /* имена FIFO-каналов связи */ #define DNAME "datapipe" #define CNAME "ctrlpipe" /* размер блока информации */ #define PBUFSIZE 512 /* P_client.c --------------------------------------------------------- */ /* * Процесс-клиент, посылающий запросы к серверу. */ #include <stdio.h> #include <signal.h> #include <fcntl.h> #include "P_packet.h" int datapipe, ctrlpipe; int got_sig; int mypid; /* идентификатор процесса-клиента */ int spid; /* идентификатор процесса-сервера */ /* waiting for signal */ #define WAITSIG while( !got_sig ) void handler(nsig){ signal( SIGUSR1, handler ); got_sig ++; } void init(){ extern void die(); /* Ожидать создания каналов связи */ while( (datapipe = open( DNAME, O_RDONLY | O_NDELAY )) < 0 ); while( (ctrlpipe = open( CNAME, O_WRONLY | O_NDELAY )) < 0 ); mypid = getpid(); /* my process identifier */ printf( "Client pid=%d started\n", mypid ); signal( SIGINT, die); signal( SIGQUIT, die); signal( SIGTERM, die); handler(0); } int canRun = 1; void die(nsig){ canRun = 0; } /* подключиться к серверу, запросив его pid */ connect(){ struct packet pk; pk.pk_pid = mypid; pk.pk_code = CONNECT; pk.pk_blk = (-1); got_sig = 0; write( ctrlpipe, &pk, sizeof pk ); /* послать запрос */ /* ожидать сигнала-"толчка" */ WAITSIG; /* прочитать ответ из канала данных */ read( datapipe, &pk, sizeof pk ); /* послать сигнал-подтверждение */ kill( pk.pk_pid, SIGUSR1 ); return pk.pk_pid; } void disconnect(){ struct packet pk; pk.pk_pid = mypid; pk.pk_code = DISCONNECT; pk.pk_blk = (-1); got_sig = 0; write( ctrlpipe, &pk, sizeof pk ); /* send request */ /* wait for reply */ WAITSIG; /* receive reply */ read( datapipe, &pk, sizeof pk ); /* confirm */ kill( pk.pk_pid, SIGUSR1 ); printf( "Disconnected.\n" ); } request( ptr, blk, spid ) char *ptr; int blk; int spid; { struct packet pk; pk.pk_pid = mypid; pk.pk_blk = blk; pk.pk_code = RQ_READ; got_sig = 0; write( ctrlpipe, &pk, sizeof pk ); WAITSIG; read( datapipe, ptr, PBUFSIZE ); kill( spid, SIGUSR1 ); } bye(){ struct packet pk; pk.pk_pid = mypid; pk.pk_code = BYE; pk.pk_blk = (-1); got_sig = 0; write( ctrlpipe, &pk, sizeof pk ); /* send request */ exit(0); } /* client [номер_блока] */ main(argc, argv) char *argv[]; { int blk; char buffer[ PBUFSIZE ]; setbuf( stdout, NULL ); /* make unbuffered */ blk = (argv[1] ? atoi( argv[1] ) : 0); init(); spid = connect(); printf( "Client pid=%d connected to server pid=%d\n", mypid, spid ); /* запрос блока номер -33 соответствует запросу "завершить * работу сервера" */ if( blk == -33 ) bye(); /* в цикле посылать запросы на чтение блока blk */ while( canRun ){ request( buffer, blk, spid ); printf( "\nBEG-------------------------------------\n" ); fwrite( buffer, PBUFSIZE, 1, stdout );