Новые книги

GNU Emacs is the most popular and widespread of the Emacs family of editors. It is also the most powerful and flexible. Unlike all other text editors, GNU Emacs is a complete working environment—you can stay within Emacs all day without leaving.

, 3rd Edition tells readers how to get started with the GNU Emacs editor. It is a thorough guide that will also "grow" with you: as you become more proficient, this book will help you learn how to use Emacs more effectively. It takes you from basic Emacs usage (simple text editing) to moderately complicated customization and programming.The third edition of

describes Emacs 21.3 from the ground up, including new user interface features such as an icon-based toolbar and an interactive interface to Emacs customization. A new chapter details how to install and run Emacs on Mac OS X, Windows, and Linux, including tips for using Emacs effectively on those platforms.

, third edition, covers:

Learning GNU Emacs

Learning GNU Emacs

Learning GNU Emacs

• How to edit files with Emacs

• Using the operating system shell through Emacs

• How to use multiple buffers, windows, and frames

• Customizing Emacs interactively and through startup files

• Writing macros to circumvent repetitious tasks

• Emacs as a programming environment for Java, C++, and Perl, among others

• Using Emacs as an integrated development environment (IDE)

• Integrating Emacs with CVS, Subversion and other change control systems for projects with multiple developers

• Writing HTML, XHTML, and XML with Emacs

• The basics of Emacs Lisp

The book is aimed at new Emacs users, whether or not they are programmers. Also useful for readers switching from other Emacs implementations to GNU Emacs.
В книге собраны и обобщены советы по решению различных проблем, которые рано или поздно возникают при эксплуатации как экономичных нетбуков, так и современных настольных моделей. Все приведенные рецепты опробованы на практике и разбиты по темам: аппаратные средства персональных компьютеров, компьютерные сети и подключение к Интернету, установка, настройка и ремонт ОС Windows, работа в Интернете, защита от вирусов. Рассмотрены не только готовые решения внезапно возникающих проблем, но и ответы на многие вопросы, которые возникают еще до покупки компьютера. Приведен необходимый минимум технических сведений, позволяющий принять осознанное решение.

Компакт-диск прилагается только к печатному изданию книги.

Примеры.

Пример 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   = -Isrc/curses
LIB       = src/curses/libncurses.a
DEFINES   = -DUSG -DTERMIOS
CC        = cc  -O            # стандартный C-compiler + оптимизация
#CC       = gcc -O            # GNU C-compiler
uxcom: $(OBJS)
	$(CC) $(OBJS) -o $@ $(LIB)
	sync; ls -l $@; size $@
glob.o: glob.c glob.h   # это файл "Пример 18"
	$(CC) -c glob.c
w.o: w.c w.h            # это файл "Пример 17"
	$(CC) -c $(INCLUDE) $(DEFINES) w.c
menu.o: menu.c glob.h w.h menu.h   # это файл "Пример 19"
	$(CC) -c $(INCLUDE) $(DEFINES) menu.c
pull.o: pull.c glob.h w.h menu.h pull.h # это файл "Пример 20"
	$(CC) -c $(INCLUDE) $(DEFINES) pull.c
match.o: match.c
	$(CC) -c -DMATCHONLY \
	      -DMATCH_ERR="TblMatchErr()" match.c
pwd.o: pwd.c
	$(CC) -c -DU42 -DCWDONLY pwd.c
treemk.o: treemk.c
	$(CC) -c $(DEFINES) \
	      -DERR_CANT_READ=tree_err_cant_read     \
	      -DERR_NAME_TOO_LONG=tree_name_too_long \
	      -DTREEONLY -DU42 treemk.c
hist.o: hist.c hist.h glob.h menu.h w.h  # это файл "Пример 21"
	$(CC) -c $(INCLUDE) $(DEFINES) hist.c
line.o: line.c w.h glob.h menu.h hist.h line.h  # "Пример 21"
	$(CC) -c $(INCLUDE) $(DEFINES) line.c
table.o: table.c w.h glob.h menu.h table.h      # "Пример 22"
	$(CC) -c $(INCLUDE) $(DEFINES) table.c
main.o: main.c glob.h w.h menu.h hist.h line.h pull.h table.h
	$(CC) -c $(INCLUDE) $(DEFINES) main.c
w.h:    wcur.h
	touch w.h
/* _______________________ файл main.c __________________________ */
/* Ниже предполагается, что вы раскрасили в /etc/termcap          *
 * выделения A_STANDOUT и A_REVERSE в РАЗНЫЕ цвета !              */
#include "w.h"
#include "glob.h"
#include "menu.h"
#include "hist.h"
#include "line.h"
#include "table.h"
#include "pull.h"
#include <signal.h>
#include <ustat.h>
#include <locale.h>
void t_enter(), t_leave();
LineEdit    edit;                     /* редактор строки           */
Hist        hcwd, hedit, hpat;        /* истории:                  */
/* посещенные каталоги, набранные команды, шаблоны имен            */
Menu        mwrk, msort;              /* должны иметь класс static */
PullMenu    pull;
typedef enum { SEL_WRK=0, SEL_PANE1, SEL_PANE2, SEL_PULL, SEL_HELP } Sel;
Sel current_menu;       /* текущее активное меню                   */
Sel previous_menu;      /* предыдущее активное меню                */
#define SEL_PANE (current_menu == SEL_PANE1 || current_menu == SEL_PANE2)
typedef struct {
	Table t;        /* таблица с именами файлов                */
	DirContents d;  /* содержимое каталогов                    */
} FileWidget;
FileWidget tpane1, tpane2;    /* левая и правая панели             */
FileWidget *A_pane = &tpane1; /* активная панель                   */
FileWidget *B_pane = &tpane2; /* противоположная панель            */
#define A_tbl   (&A_pane->t)
#define A_dir   (&A_pane->d)
#define B_tbl   (&B_pane->t)
#define B_dir   (&B_pane->d)
#define TblFW(tbl) ((tbl) == A_tbl ? A_pane : B_pane)
void ExchangePanes(){  /* Обменять указатели на панели */
     FileWidget *tmp = A_pane; A_pane = B_pane; B_pane = tmp;
     current_menu = (current_menu == SEL_PANE1 ? SEL_PANE2 : SEL_PANE1);
}
#define Other_pane(p)  ((p) == A_pane ? B_pane : A_pane)
#define Other_tbl(t)   ((t) == A_tbl  ? B_tbl  : A_tbl )
WINDOW *panewin;        /* окно, содержащее обе панели = stdscr */
typedef enum { NORUN=0, RUNCMD=1, CHDIR=2, TAG=3, FIND=4 } RunType;
#define REPEAT_KEY 666  /* псевдоклавиша "повтори выбор в меню"    */
#define LEAVE_KEY  777  /* псевдоклавиша "покинь это меню"         */
#define NOSELECTED (-1) /* в меню ничего пока не выбрано           */
#define CENTER  (COLS/2-2) /* линия раздела панелей                */
int done;               /* закончена ли программа ?                */
char CWD[MAXLEN];       /* полное имя текущего каталога            */
char SELECTION[MAXLEN]; /* имя выбранного файла                    */
/*-----------------------------------------------------------------*/
/* Выдать подсказку в строке редактора                             */
/*-----------------------------------------------------------------*/
#include <stdarg.h>
void Message(char *s, ... ){
  char msg[80]; va_list args; int field_width;
  va_start(args, s); vsprintf(msg, s, args); va_end(args);
  wattrset    (panewin,     A_tbl->sel_attrib);
  field_width = A_tbl->width + B_tbl->width - 3;
  mvwprintw   (panewin, LINES-2, tpane1.t.left+1, " %*.*s ",
	       -field_width, field_width, msg);
  wattrset    (panewin, A_tbl->bg_attrib);
  wnoutrefresh(panewin);
}
/*-----------------------------------------------------------------*
 *      Меню порядка сортировки имен файлов.                       *
 *-----------------------------------------------------------------*/
Info sort_info[] = {
    { "По возрастанию", 0}, { "По убыванию",    0},
    { "По суффиксу",    0}, { "Без сортировки", 0},
    { "По размеру",     M_HATCH},
    { NULL, 0}
};
/* При входе в меню сортировки указать текущий тип сортировки */
void sort_show(Menu *m){
    MnuPointAt(&msort, (int) sorttype);
}
/* Выбрать тип сортировки имен файлов */
static void SelectSortType(int sel){
    if( sel == NOSELECTED )
	sel = MnuUsualSelect(&msort, NO);
    MnuHide(&msort);
    current_menu = previous_menu;
    if(M_REFUSED(&msort)) return;
    sorttype = (Sort) sel;
    A_dir->lastRead = B_dir->lastRead = 0L; /* форсировать перечитку */
    /* но ничего явно не пересортировывать и не перерисовывать       */
}
/*-----------------------------------------------------------------*
 *  Отслеживание содержимого каталогов и переинициализация меню.   *
 *-----------------------------------------------------------------*/
#define NON_VALID(d)  ((d)->readErrors || (d)->valid == NO)
/* Сменить содержимое таблицы и списка файлов */
void InitTblFromDir(FileWidget *wd, int chdired, char *savename){
     char *msg, *name; Table *tbl = &(wd->t); DirContents *d = &wd->d;
     int saveind  = tbl->current, saveshift = tbl->shift;
     char *svname = NULL;
     if(tbl->nitems > 0 ) svname = strdup(T_ITEMF(tbl, saveind, 0));
  /* Несуществующие и нечитаемые каталоги выделить особо */
     if( NON_VALID(d)) wattrset(tbl->win, A_REVERSE);
     TblClear(tbl);
     if(d->valid == NO){
	msg = "Не существует"; name = d->name; goto Report;
     } else if(d->readErrors){ /* тогда d->files->s == NULL */
	msg = "Не читается";   name = d->name;
Report: mvwaddstr(tbl->win, tbl->top + tbl->height/2,
		  tbl->left + (tbl->width - strlen(name))/2, name);
	mvwaddstr(tbl->win, tbl->top + tbl->height/2+1,
		  tbl->left + (tbl->width - strlen(msg))/2, msg);
     }
     wattrset(tbl->win, tbl->bg_attrib);
     tbl->items = d->files; TblInit(tbl, NO);
     /* Постараться сохранить позицию в таблице */
     if( chdired ) TblPlaceByName(tbl, savename);
     else {
	 if( svname == NULL || TblPlaceByName(tbl, svname) < 0 ){
	     tbl->shift   = saveshift;
	     tbl->current = saveind; TblChk(tbl);
	 }
     }
     if(svname) free(svname);
}
/* Перейти в каталог и запомнить его полное имя  */
int mychdir(char *newdir){ int code = chdir(newdir);
    if( code < 0 ) return code;
    getwd(CWD); in_the_root = (strcmp(CWD, "/") == 0);
    HistAdd(&hcwd, CWD, 0); /* запомнить в истории каталогов */
    t_enter(&tpane1.t);     /* на рамке нарисовать имя текущего каталога */
    return code;
}
/* Изменить текущий каталог и перечитать его содержимое */
int cd(char *newdir, FileWidget *wd, char *oldname){
    char oldbase[MAXLEN], *s, *strrchr(char *,char);
 /* Спасти в oldbase базовое имя старого каталога oldname (обычно CWD) */
    if(s = strrchr(oldname, '/')) s++; else s = oldname;
    strcpy(oldbase, s);
    if( mychdir(newdir) < 0){ /* не могу перейти в каталог */
	Message("Не могу перейти в %s", *newdir ? newdir : "???");
	beep(); return (-1); }
    if( ReadDir(CWD, &wd->d)){ /* содержимое изменилось */
	InitTblFromDir (wd, YES, oldbase);
	return 1;
    }
    return 0;
}
/* Проверить содержимое обеих панелей */
void checkBothPanes(){
   /* Случай NON_VALID нужен только для того, чтобы Init...
      восстановил "аварийную" картинку в панели */
      if( ReadDir(tpane1.d.name, &tpane1.d) || NON_VALID(&tpane1.d))
	  InitTblFromDir(&tpane1, NO, NULL);
      if( tpane1.t.exposed == NO ) TblDraw(&tpane1.t);
      if( ReadDir(tpane2.d.name, &tpane2.d) || NON_VALID(&tpane2.d))
	  InitTblFromDir(&tpane2, NO, NULL);
      if( tpane2.t.exposed == NO ) TblDraw(&tpane2.t);
}
/*-----------------------------------------------------------------*
 *    Ввод команд и выдача подсказки.                              *
 *-----------------------------------------------------------------*/
/* Особая обработка отдельных клавиш в редакторе строки */
char  e_move = NO; /* кнопки со стрелками <- -> двигают
      курсор по строке/по таблице */
int e_hit[] = { KEY_LEFT, KEY_RIGHT, KEY_UP, KEY_DOWN,
    KEY_F(0), KEY_IC,
    ctrl('G'), ctrl('E'), ctrl('L'), ctrl('F'), ctrl('X'), ctrl('Y'),
    -1 };
int e_handler (LineEdit *le, int c, HandlerReply *reply){
    *reply = HANDLER_CONTINUE;
    switch(c){
/* Перемещение по таблице без выхода из редактора строки */
    case KEY_LEFT:
	 if( !SEL_PANE || !e_move){
	      *reply=HANDLER_SWITCH; return c; }
	 TblPointAt(A_tbl, A_tbl->current - A_tbl->height); break;
    case KEY_RIGHT:
	 if( !SEL_PANE || !e_move){
	      *reply=HANDLER_SWITCH; return c; }
	 TblPointAt(A_tbl, A_tbl->current + A_tbl->height); break;
    case KEY_DOWN:
	 if( !SEL_PANE){ *reply=HANDLER_SWITCH; return c; }
	 TblPointAt(A_tbl, A_tbl->current + 1); break;
    case KEY_UP:
	 if( !SEL_PANE){ *reply=HANDLER_SWITCH; return c; }
	 TblPointAt(A_tbl, A_tbl->current - 1); break;
    case KEY_F(0):      /* F10 */
	 e_move = !e_move; break;
    case KEY_IC:
	 if( !SEL_PANE){ *reply=HANDLER_SWITCH; return c; }
	 TblRetag(A_tbl, A_tbl->current, T_LABEL);
	 TblPointAt(A_tbl, A_tbl->current+1);
	 break;
/* Подстановки */
    case ctrl('G'): /* подставить полное имя домашнего каталога */
	 LeInsStr(le, getenv("HOME")); LeInsStr(le, " "); break;
    case ctrl('E'): /* подставить имя выбранного файла */
	 if( A_tbl->nitems )
	     LeInsStr(le, T_ITEMF(A_tbl, A_tbl->current, 0));
	 LeInsStr(le, " "); break;
    case ctrl('L'): /* подставить имя выбранного файла из другой панели */
	 LeInsStr(le, T_ITEMF(B_tbl, B_tbl->current, 0));
	 LeInsStr(le, " "); break;
    case ctrl('X'): case ctrl('Y'):
    /* подстановка имен помеченных файлов */
    {    int label = (c == ctrl('X') ? T_LABEL : T_HATCH);
	 register i;
	 for(i=0; i < A_tbl->nitems && le->len < le->maxlen; ++i )
	     if( T_TST(A_tbl, i, label)){
		 LeInsStr(le, " "); LeInsStr(le, T_ITEMF(A_tbl, i, 0));
	     }
    } break;
    case ctrl('F'): /* подставить имя текущего каталога */
	 LeInsStr(le, CWD); LeInsStr(le, " "); break;
   }
   return c;
}
/* При начале редактирования ставь курсор в конец строки */
void e_pos (LineEdit *le){ le->pos = le->len; }
/* Обозначить, что мы покинули редактор строки */
void e_hide(LineEdit *le){
     le->sel_attrib = le->fr_attrib = le->bg_attrib = A_ITALICS;
     LeDraw(le);
}
/* Отредактировать строку в предпоследней строке окна */
char *Edit(WINDOW *w, char *src, RunType dorun){
    static char CMD[MAXLEN];   /* буфер для строки команды */
    int c;
    if(w != TOPW){ beep(); return NULL; }/* это должно быть верхнее окно */
    keypad(w, TRUE);
 /* Проинициализировать редактор строки */
    switch(dorun){
    case NORUN:  edit.histIn = edit.histOut = NULL;   break;
    case RUNCMD: edit.histIn = edit.histOut = &hedit; break;
    case FIND:
    case TAG:    edit.histIn = edit.histOut = &hpat;  break;
    case CHDIR:  edit.histIn = &hcwd; edit.histOut = NULL; break;
    }
    edit.line   = CMD;
    edit.maxlen = sizeof(CMD)-1;
    edit.top    = wlines(w)-2; edit.left = 2;
    edit.width  = wcols (w)-4 - (1+BARWIDTH);
    edit.insert = YES; edit.nc = YES;
    edit.win    = w;
    edit.wl_attrib  = edit.bg_attrib=A_REVERSE;
    edit.fr_attrib=A_STANDOUT; edit.sel_attrib = A_NORMAL|A_BLINK;
    edit.posMe   = e_pos;
    edit.hitkeys = (SEL_PANE ? e_hit : e_hit+5);
    edit.handler = e_handler;
    /* edit.hideMe  = e_hide; вызывается ЯВНО */
    /* остальные поля равны 0, т.к. edit - статическое данное */
    for(;;){
	strcpy(CMD, src); if(*src){ strcat(CMD, " "); }
	c = LeEdit( &edit );
	if( LE_REFUSED(&edit) || dorun != RUNCMD ||
	    !*CMD || c != '\n' ) break;
	/* курсор в нижнюю строку экрана */
	attrset(A_NORMAL); move(LINES-1, 0); refresh();
	resetterm();    /* приостановить работу curses-а    */
	putchar('\n');  /* промотать экран на строку        */
	system(CMD);    /* выполнить команду внешним Шеллом */
	fprintf(stderr,"Нажми ENTER чтобы продолжить --- ");gets(CMD);
	fixterm();      /* возобновить работу curses-а      */
	RedrawScreen(); /* перерисовать экран               */
	if(w == panewin){
	   checkBothPanes();
	   if(A_tbl->nitems) TblPoint(A_tbl, A_tbl->current, NO);
	}
	src = ""; /* во второй раз ничего не подставлять */
    }
    wattrset(w, A_NORMAL); /* ? */
    e_hide ( &edit );
    return ( *CMD && !LE_REFUSED(&edit)) ? CMD : NULL;
}
/* Выдача подсказки а также сообщений об ошибках.         */
/* В этом же окне можно набирать команды (dorun==RUNCMD). */
char *help(char *msg, RunType dorun){ register i; char *s;
    static char *helptext[] = {
	"ESC    - выход в главное меню",
	"F1     - подсказка",
	"INS    - пометить файл",
	"ctrl/E - подставить имя выбранного файла",
	"ctrl/L - подставить имя из другой панели",
	"ctrl/X - подставить помеченные файлы",
	"ctrl/Y - подставить помеченные курсивом",
	"ctrl/G - подставить имя домашнего каталога",
	"ctrl/F - подставить имя текущего каталога",
	"F4     - история",
	"F7     - переключить режим вставки/замены",
	"F10    - переключить перемещения по строке/по панели",
    };
#define HELPLINES (sizeof(helptext)/sizeof helptext[0])
    Sel save_current_menu = current_menu;
    /* "выскакивающее" POP-UP window */
    WINDOW *w = newwin(2+1+HELPLINES+1, 70, 2, (COLS-70)/2);
    if( w == NULL ) return NULL;
    current_menu = SEL_HELP;
    wattrset(w, A_REVERSE);    /* это будет инверсное окно  */
    werase  (w);               /* заполнить инверсным фоном */
    wborder(w);  RaiseWin(w);  /* окно появляется */
    if(*msg){                            wattron (w, A_BOLD);
      mvwaddstr(w, 1+HELPLINES, 2, msg); wattroff(w, A_BOLD);
    }
    for(i=0; i < HELPLINES; i++) mvwaddstr(w, 1+i, 2, helptext[i]);
    s = Edit(w, "", dorun); PopWin(); /* окно исчезает */
    current_menu = save_current_menu;
    return s;
}
/*-----------------------------------------------------------------*
 *   Управляющее меню.                                             *
 *-----------------------------------------------------------------*/
int f_left(), f_right(), f_pull(), f_help(), f_sort(), f_dir(),
    f_bye(),  f_redraw(),f_cdroot();
/* Обратите внимание, что можно указывать не все поля структуры,
 * а только первые. Остальные равны 0 */
#ifndef __GNUC__
Info mwrk_info[] = {    /* строки для главного меню      */
    { "\\Current directory",       0       , f_left  }, /* 0 */
    { "\\Root directory",          M_HATCH , f_right }, /* 1 */
    { "\\Menus",                   0       , f_pull  }, /* 2 */
    { "\1", /* гориз. черта */     0                 }, /* 3 */
    { "\\Help",                    0       , f_help  }, /* 4 */
    { "Un\\implemented",           I_NOSEL           }, /* 5 */
    { "Change \\sorttype",         0       , f_sort  }, /* 6 */
    { "Look directory \\history",  0       , f_dir   }, /* 7 */
    { "\1", /* гориз. черта */     0                 }, /* 8 */
    { "\\Quit",                    M_BOLD  , f_bye   }, /* 9 */
    { "\1", /* гориз. черта */     0                 }, /* 10 */
    { "\\Redraw screen",           M_HATCH , f_redraw}, /* 11 */
    { "Chdir both panels to /",    M_HATCH , f_cdroot}, /* 12 */
    { NULL, 0 }
};
#else /* GNU C-компилятор 1.37 не может инициализировать поля-union-ы */
static char _gnu_[] = "Compiled with GNU C-compiler";
Info mwrk_info[] = {    /* строки для главного меню      */
    { "\\Current directory",            0       },
    { "\\Root directory",               M_HATCH },
    { "\\Menus",                        0       },
    { "\1", /* гориз. черта */          0       },
    { "\\Help",                         0       },
    { "Un\\implemented",                I_NOSEL },
    { "Change \\sorttype",              0       },
    { "Look directory \\history",       0       },
    { "\1", /* гориз. черта */          0       },
    { "\\Quit",                         M_BOLD  },
    { "\1", /* гориз. черта */          0       },
    { "\\Redraw screen",                M_HATCH },
    { "Chdir both panels to /",         M_HATCH },
    { NULL, 0 }
};
void mwrk_init(){
    mwrk_info [0].any.act = f_left;
    mwrk_info [1].any.act = f_right;
    mwrk_info [2].any.act = f_pull;
    mwrk_info [4].any.act = f_help;
    mwrk_info [6].any.act = f_sort;
    mwrk_info [7].any.act = f_dir;
    mwrk_info [9].any.act = f_bye;
    mwrk_info[11].any.act = f_redraw;
    mwrk_info[12].any.act = f_cdroot;
}
#endif
char *mwrk_help[] = {
      "Перейти в левую панель",  "Перейти в правую панель",
      "Перейти в строчное меню", "",
      "Выдать подсказку",        "Не реализовано",
      "Изменить тип сортировки имен", "История путешествий",
      "", "Выход", "", "Перерисовка экрана",
      "Обе панели поставить в корневой каталог", NULL
};
void m_help(Menu *m, int n, int among){
     Message(mwrk_help[n]);    }
/* Выбор в рабочем (командном) меню */
void SelectWorkingMenu(int sel){
    if(sel == NOSELECTED)
       sel = MnuUsualSelect( & mwrk, NO);
    if( M_REFUSED(&mwrk)) help("Выбери Quit", NORUN);
    else if(mwrk.items[sel].any.act)
	  (*mwrk.items[sel].any.act)();
    if( !done) MnuHide( & mwrk );
}
f_left ()  { current_menu = SEL_PANE1; return 0; }
f_right()  { current_menu = SEL_PANE2; return 0; }
f_pull ()  { current_menu = SEL_PULL;  return 0; }
f_help ()  { help("Нажми ENTER или набери команду:", RUNCMD);
	     return 0; }
f_sort ()  { SelectSortType(NOSELECTED); return 0; }
f_dir  ()  { Info *idir; if(idir = HistSelect(&hcwd, 20, 3))
	       cd(idir->s, &tpane2, CWD);
	    current_menu = SEL_PANE2;    return 0; }
f_bye   () { done++;                     return 0; }
f_redraw() { RedrawScreen();             return 0; }
f_cdroot() { cd("/", &tpane1, CWD);
	     cd("/", &tpane2, CWD); checkBothPanes();
	     return 0;                             }
/*-----------------------------------------------------------------*
 *  Выдача информации про файл, редактирование кодов доступа.      *
 *-----------------------------------------------------------------*/
void MYwaddstr(WINDOW *w, int y, int x, int maxwidth, char *s){
     register pos;
     for(pos=0; *s && *s != '\n' && pos < maxwidth; ++s){
	 wmove(w, y, x+pos);
	      if( *s == '\t')  pos += 8 - (pos & 7);
	 else if( *s == '\b'){ if(pos)  --pos; }
	 else if( *s == '\r')  pos = 0;
	 else { ++pos; waddch(w, isprint(*s) ? *s : '?'); }
     }
}
/* Просмотр начала файла в противоположной панели.            */
void fastView(
     char *name,    /* имя файла                              */
     unsigned mode, /* некоторые типы файлов не просматривать */
     Table *otbl    /* противоположная панель                 */
){   FILE *fp; register int x, y; char buf[512];
     TblClear(otbl);
     Message("Нажми ENTER для окончания. "
	     "ПРОБЕЛ - изменяет код доступа. "
	     "ESC - откатка.");
     if( !ISREG(mode)) goto out;
     if((fp = fopen(name, "r")) == NULL){
	   Message("Не могу читать %s", name); return;
     }
     for(y=0; y < otbl->height && fgets(buf, sizeof buf, fp); y++)
	 MYwaddstr(panewin, otbl->top+y, otbl->left+1,
		   otbl->width-2, buf);
     fclose(fp);
out: wrefresh(otbl->win);   /* проявить */
}
static struct attrNames{
	unsigned mode; char name; char acc; int off;
} modes[] = {
	{ S_IREAD,       'r', 'u',  0    },
	{ S_IWRITE,      'w', 'u',  1    },
	{ S_IEXEC,       'x', 'u',  2    },
	{ S_IREAD  >> 3, 'r', 'g',  3    },
	{ S_IWRITE >> 3, 'w', 'g',  4    },
	{ S_IEXEC  >> 3, 'x', 'g',  5    },
	{ S_IREAD  >> 6, 'r', 'o',  6    },
	{ S_IWRITE >> 6, 'w', 'o',  7    },
	{ S_IEXEC  >> 6, 'x', 'o',  8    },
};
#define NMODES (sizeof(modes)/sizeof(modes[0]))
/* Позиция в которой изображать i-ый бит кодов доступа */
#define MODE_X_POS(tbl, i) (tbl->left + DIR_SIZE + 12 + modes[i].off)
#define MODE_Y_POS(tbl)    (tbl->top  + tbl->height + 1)
#ifdef FILF
/* Изобразить информацию о текущем выбранном файле */
void showMode(Table *tbl, int attr){
   Info *inf   = & tbl->items[tbl->current];    /* файл   */
   register i; unsigned mode = inf->mode;       /* коды   */
   int     uid = inf->uid, gid = inf->gid;      /* хозяин */
   /* идентификаторы хозяина и группы процесса-коммандера */
   static char first = YES; static int myuid, mygid;
   WINDOW *win = tbl->win;
   int xleft   = tbl->left + 1, y = MODE_Y_POS(tbl);
   if( first ){ first = NO; myuid = getuid(); mygid = getgid(); }
   wattron  (win, attr);
   mvwprintw(win, y, xleft, " %*.*s %8ld ",  /* имя файла */
      -DIR_SIZE, DIR_SIZE,
       inf->s ? (!strcmp(inf->s, "..") ? "<UP-DIR>": inf->s) :
		"(EMPTY)",
       inf->size);
   /* тип файла (обычный|каталог|устройство) */
   wattron (win, A_ITALICS|A_BOLD);
   waddch  (win, ISDIR(mode) ? 'd': ISDEV(mode) ? '@' : '-');
   wattroff(win, A_ITALICS|A_BOLD);
   /* коды доступа */
   for(i=0; i < NMODES; i++){
       if((modes[i].acc == 'u' && myuid == uid) ||
	  (modes[i].acc == 'g' && mygid == gid) ||
	  (modes[i].acc == 'o' && myuid != uid && mygid != gid)) ;
       else     wattron(win, A_ITALICS);
       mvwaddch(win, y, MODE_X_POS(tbl, i),
		mode & modes[i].mode ? modes[i].name : '-');
       wattroff(win, A_ITALICS);
   }
   waddch(win, ' '); wattroff(win, attr);
}
#define newmode (tbl->items[tbl->current].mode)
/* Редактирование кодов доступа к файлам. */
int editAccessModes(FileWidget *wd){
    Table *tbl  = &wd->t;
    Table *otbl = &(Other_pane(wd)->t); /* или Other_tbl(tbl); */
    unsigned prevmode, oldmode;         /* старый код доступа  */
    char *name;                         /* имя текущего файла  */
    WINDOW *win = tbl->win;
    int position = 0, c;
    for(;;){  /* Цикл выбора файлов в таблице */
	name = T_ITEMF(tbl, tbl->current, 0);
	oldmode = newmode;             /* запомнить */
	fastView(name, newmode, otbl); /* показать первые строки файла */
	for(;;){  /* Цикл обработки выбранного файла */
	   wmove(win, MODE_Y_POS(tbl), MODE_X_POS(tbl, position));
	   switch(c = WinGetch(win)){
/* Некоторые клавиши вызывают перемещение по таблице */
case KEY_BACKTAB: TblPointAt(tbl, tbl->current - tbl->height); goto mv;
case '\t':        TblPointAt(tbl, tbl->current + tbl->height); goto mv;
case KEY_UP:      TblPointAt(tbl, tbl->current - 1); goto mv;
case KEY_DOWN:    TblPointAt(tbl, tbl->current + 1); goto mv;
case KEY_HOME:    TblPointAt(tbl, 0);                goto mv;
case KEY_END:     TblPointAt(tbl, tbl->nitems-1);    goto mv;
/* Прочие клавиши предназначены для редактирования кодов доступа */
	   case KEY_LEFT:  if(position) --position; break;
	   case KEY_RIGHT: if(position < NMODES-1)  position++; break;
	   default: goto out;
	   case ESC:    /* Восстановить старые коды */
		prevmode = newmode = oldmode; goto change;
	   case ' ':    /* Инвертировать код доступа */
		prevmode = newmode;              /* запомнить */
		newmode ^= modes[position].mode; /* инвертировать */
change:         if( chmod(name, newmode) < 0){
		    beep();
		    Message("Не могу изменить доступ к %s", name);
		    newmode = prevmode; /* восстановить */
		} else /* доступ изменен, показать это */
		    showMode(tbl, A_REVERSE);
		break;
	   }
	} /* Конец цикла обработки выбранного файла */
mv:     ;
    } /* Конец цикла выбора файлов в таблице */
out:
    /* Очистить противоположную панель после fastView(); */
    Message(""); TblClear(otbl); return c;
}
#undef newmode
#else
void editAccessModes(FileWidget *wd){}
#endif
long diskFree(){
      struct ustat ust; struct stat st; long freespace;
      if(stat(".", &st) < 0) return 0;
      ustat(st.st_dev, &ust);
      freespace = ust.f_tfree * 512L; freespace /= 1024;
      Message("В %*.*s свободно %ld Кб.",
	 -sizeof(ust.f_fname), sizeof(ust.f_fname),
	 *ust.f_fname ? ust.f_fname : ".", freespace);
      doupdate();  /* проявить окно для Message() */
      return freespace;
}
/*-----------------------------------------------------------------*
 *   Специальные команды, использующие обход дерева
 *-----------------------------------------------------------------*/
/* Выдача сообщений об ошибках (смотри Makefile) */
int tree_err_cant_read(char *name){
    Message("Не могу читать \"%s\"", name); return WARNING;
}
int tree_name_too_long(){
    Message("Слишком длинное полное имя"); return WARNING;
}
char canRun;  /* продолжать ли поиск */
/* Прерывание обхода по SIGINT */
void onintr_f(nsig){ canRun = NO; Message("Interrupted"); }
/* ==== место, занимаемое поддеревом ==== */
long tu(int *count){
   struct stat st; register i; long sum = 0L;
   *count = 0;
   for(i=0; i < A_tbl->nitems ;++i )
      if( T_TST(A_tbl, i, T_LABEL)){
	  stat(T_ITEMF(A_tbl, i, 0), &st);
#define KB(s)   (((s) + 1024L - 1) / 1024L)
	  sum += KB(st.st_size); (*count)++;
      }
   return sum;
}
void diskUsage(){ long du(), size, sizetagged; int n;
  char msg[512];
  Message("Измеряем объем файлов..."); doupdate();
  size = du(".");   diskFree();  sizetagged = tu(&n);
  sprintf(msg, "%ld килобайт в %s, %ld кб в %d помеченных файлах",
		size,          CWD, sizetagged,   n);
  help(msg, NORUN);
}
/* ==== поиск файла ===================== */
extern char *find_PATTERN;             /* imported from treemk.c */
extern Info gargv[]; extern int gargc; /* imported from glob.c   */
/* Проверить очередное имя и запомнить его, если подходит */
static int findCheck(char *fullname, int level, struct stat *st){
    char *basename = strrchr(fullname, '/');
    if(basename) basename++;
    else         basename = fullname;
    if( canRun == NO ) return FAILURE;   /* поиск прерван         */
    if( match(basename, find_PATTERN)){  /* imported from match.c */
	gargv[gargc]     = NullInfo;  /* зачистка */
	gargv[gargc].s   = strdup(fullname);
	gargv[gargc++].fl= ISDIR(st->st_mode) ? I_DIR : 0;
	gargv[gargc]     = NullInfo;
	Message("%s", fullname); doupdate();
    }
    /* Страховка от переполнения gargv[] */
    if   ( gargc < MAX_ARGV - 1 ) return SUCCESS;
    else { Message("Найдено слишком много имен."); return FAILURE; }
}
/* Собрать имена файлов, удовлетворяющие шаблону */
static Info *findAndCollect(char *pattern){
     void (*old)() = signal(SIGINT, onintr_f);
     Sort saveSort;
     find_PATTERN = pattern; canRun = YES;
     Message("Ищем %s от %s", pattern, CWD); doupdate();
     greset();  /* смотри glob.c, gargc=0; */
     walktree(CWD, findCheck, NULL, findCheck);
     signal(SIGINT, old);
     saveSort = sorttype; sorttype = SORT_ASC;
     if(gargc) qsort( gargv, gargc, sizeof(Info), gcmps);
     sorttype = saveSort;
     return gargc ? blkcpy(gargv) : NULL;
}
/* Обработать собранные имена при помощи предъявления меню с ними */
void findFile(FileWidget *wd){
     static Info *found; static Menu mfind;
     int c; Table *tbl = & wd->t;
     char *pattern = help("Введи образец для поиска, вроде *.c, "
			  "или ENTER для прежнего списка", FIND);
     if( LE_REFUSED( &edit)) return; /* отказались от поиска */
     /* Если набрана пустая строка, help() выдает NULL       */
     if( pattern ){            /* задан новый образец - ищем */
	 /* Уничтожить старый список файлов и меню */
	 if( found ) blkfree( found );
	 MnuDeinit( &mfind );
	 found = findAndCollect(pattern); /* поиск */
	 HistAdd( &hpat, pattern, 0);
	 /* Образуем меню из найденных файлов */
	 if( found ){  /* если что-нибудь нашли */
	   mfind.items     =  found;
	   mfind.title     =  pattern ? pattern : "Найденные файлы";
	   mfind.top       =  3; mfind.left = COLS/6;
	   mfind.bg_attrib =  A_STANDOUT; mfind.sel_attrib = A_REVERSE;
	   MnuInit (&mfind);
	 }
     } /* else набрана пустая строка - просто вызываем список
	* найденных ранее файлов.
	*/
     if( found == NULL ){
	 Message("Ничего не найдено"); beep(); return;
     }
     c = MnuUsualSelect(&mfind, NO);
     /* Выбор файла в этом меню вызовет переход в каталог,
      * в котором содержится этот файл */
     if( !M_REFUSED( &mfind )){
	char *s = M_ITEM(&mfind, mfind.current);
	/* пометить выбранный элемент */
	M_SET(&mfind, mfind.current, M_LABEL);
	/* если это каталог - войти в него */
	if( M_TST(&mfind, mfind.current, I_DIR))
	       cd(s, wd, CWD);
	/* иначе войти в каталог, содержащий этот файл */
	else { char *p; struct savech svch; /* смотри glob.h */
	     SAVE( svch, strrchr(s, '/'));   *svch.s = '\0';
	     p = strdup(s); RESTORE(svch);
	     if( !strcmp(CWD, p))               /* мы уже здесь     */
		 TblPlaceByName(tbl, svch.s+1); /* указать курсором */
	     else /* изменить каталог и указать курсором на файл s  */
		 cd(p, wd, s);
	     free(p);
	}
     }
     MnuHide(&mfind);  /* спрятать меню, не уничтожая его */
}
/*-----------------------------------------------------------------*
 *   Работа с панелями, содержащими имена файлов двух каталогов.   *
 *-----------------------------------------------------------------*/
/* Восстановить элементы, затертые рамкой WinBorder */
void t_restore_corners(){
    mvwaddch(panewin, LINES-3, 0,               LEFT_JOIN);
    mvwaddch(panewin, LINES-3, COLS-2-BARWIDTH, RIGHT_JOIN);
    mvwaddch(panewin, LINES-5, 0,               LEFT_JOIN);
    mvwaddch(panewin, LINES-5, COLS-2-BARWIDTH, RIGHT_JOIN);
    mvwaddch(panewin, 2,       CENTER, TOP_JOIN);
    wattron (panewin, A_BOLD);
    mvwaddch(panewin, LINES-3, CENTER, BOTTOM_JOIN);
    mvwaddch(panewin, LINES-5, CENTER, MIDDLE_CROSS);
    wattroff(panewin, A_BOLD);
}
/* Нарисовать нечто при входе в панель. Здесь изменяется
 * заголовок окна: он становится равным имени каталога,
 * просматриваемого в панели */
void t_enter(Table *tbl){
     WinBorder(tbl->win, tbl->bg_attrib, tbl->sel_attrib,
	       CWD, BAR_VER|BAR_HOR, NO);
     t_restore_corners();
}
/* Стереть подсветку при выходе из панели */
void t_leave(Table *tbl){ TblDrawItem( tbl, tbl->current, NO, YES ); }
/* Рисует недостающую часть рамки, которая не изменяется впоследствии */
void t_border_common(){
    WinBorder(panewin, A_tbl->bg_attrib, A_tbl->sel_attrib,
	      A_dir->name, BAR_VER|BAR_HOR, NO);
    wattron (panewin, A_BOLD);
    whorline(panewin, LINES-3, 1, COLS-1-BARWIDTH-1);
    whorline(panewin, LINES-5, 1, COLS-1-BARWIDTH-1);
    wverline(panewin, CENTER, A_tbl->top, A_tbl->top + A_tbl->height+2);
    wattroff(panewin, A_BOLD);
    t_restore_corners();
}
/* Функция, изображающая недостающие части панели при входе в нее */
int t_show(Table *tbl){
#ifdef FILF
     showMode(A_tbl, A_STANDOUT); showMode(B_tbl, A_STANDOUT);
#endif
     return 1;
}
void t_scrollbar(Table *tbl, int whichbar, int n, int among){
     WinScrollBar(tbl->win, BAR_VER|BAR_HOR, n, among,
		  "Yes", tbl->bg_attrib);
#ifdef FILF
     showMode(tbl, A_REVERSE);
#endif
}
/* Особая обработка клавиш при выборе в таблице */
int t_hit[] = {
   '\t',        KEY_F(1),       KEY_F(2),       KEY_F(3),
   KEY_F(4),    KEY_F(8),       ' ',            '+',
   '-',         ctrl('R'),      ctrl('L'),      ctrl('F'),
   -1 };
Info t_info[] = {
  { "TAB    Перейти в другую панель",        0},
  { "F1     Выдать подсказку",               0},
  { "F2     Ввести команду",                 0},
  { "F3     Перейти в родительский каталог", 0},
  { "F4     Перейти в каталог по имени",     0},
  { "F8     Удалить помеченные файлы",       0},
  { "ПРОБЕЛ Редактировать коды доступа",     0},
  { "+      Пометить файлы",                 0},
  { "-      Снять пометки",                  0},
  { "ctrl/R Перечитать каталог",             0},
  { "ctrl/L Выдать размер файлов в каталоге",0},
  { "ctrl/F Поиск файла",                    0},
  { NULL, 0}
};
int t_help(){
   static Menu mth; int c = 0;
   if( mth.items == NULL ){
       mth.items     =  t_info;
       mth.title     =  "Команды в панели";
       mth.top       =  3; mth.left = COLS/6;
       mth.bg_attrib =  A_STANDOUT; mth.sel_attrib = A_REVERSE;
       MnuInit (&mth);
       mth.hotkeys   = t_hit;
   }
   c = MnuUsualSelect(&mth, 0);
   /* Спрятать меню, не уничтожая его. Уничтожение выглядело бы так:
    *   mth.hotkeys = NULL; (т.к. они не выделялись malloc()-ом)
    *   MnuDeinit(&mth);
    */
   MnuHide(&mth);
   if( M_REFUSED(&mth)) return 0;             /* ничего не делать */
   return t_hit[c];  /* клавиша, соответствующая выбранной строке */
}
int t_handler (Table *tbl, int c, HandlerReply *reply){
    int i, cnt=0; extern int unlink(), rmdir(); char *answer;
    FileWidget  *wd = TblFW (tbl);
    switch(c){
    case '\t':  /* перейти в соседнюю панель */
	ExchangePanes();
	*reply = HANDLER_OUT; return LEAVE_KEY; /* покинуть эту панель */
    case KEY_F(1): *reply = HANDLER_NEWCHAR; return t_help();
    case KEY_F(2):
	   (void) Edit(tbl->win, T_ITEMF(tbl, tbl->current, 0), RUNCMD);
	   break;
    case KEY_F(3): cd(".." , wd, CWD); break;
    case KEY_F(4):
      if(answer = help("Введи имя каталога, в который надо перейти",CHDIR))
	 cd(answer , wd, CWD);
      break;
    case ctrl('R'): break;
    case KEY_F(8):
      for(i=0; i < tbl->nitems; i++)
	 if(T_TST(tbl, i, M_LABEL)){  int code; cnt++;
if((code = (T_TST(tbl, i, I_DIR) ? rmdir : unlink) (T_ITEMF(tbl, i,0))) < 0)
	    T_SET(tbl, i, M_HATCH);
      }
      if(cnt==0) help("Нет помеченных файлов", NORUN);
      break;
    case '+':
      if(answer = help("Шаблон для пометки", TAG))
	 TblTagAll(tbl, answer, T_LABEL);
      break;
    case '-':
      if(answer = help("Шаблон для снятия пометок", TAG))
	 TblUntagAll(tbl, answer, T_LABEL);
      break;
    case ctrl('L'):     /* команда "disk usage" */
      diskUsage(); break;
    case ctrl('F'):     /* поиск файла */
      findFile(wd); break;
    case ' ':           /* редактирование кодов доступа */
      editAccessModes(wd); break;
    }
    *reply = HANDLER_OUT; return REPEAT_KEY;
    /* вернуться в эту же панель */
}
/* Выбор в одной из панелей. */
int SelectPane(FileWidget *wd){
    Table *tbl       = & wd->t;
    DirContents *d   = & wd->d;
    int sel, retcode = 0;
    RaiseWin( tbl->win );
 /* войти в указанный каталог, поправить CWD  */
    if(mychdir( d->name ) < 0) checkBothPanes();
    /* t_enter( tbl );  /* войти в указанную панель, поправить рамку */
    for(;;){
      /* Проверить, не устарело ли содержимое таблиц */
      checkBothPanes();
      if((sel = TblUsualSelect( tbl )) == TOTAL_NOSEL ){
	  current_menu = SEL_PULL; goto out;           }
      if( T_REFUSED(tbl)) break; /* нажат ESC */
      if( tbl->key == LEAVE_KEY  ){ retcode=1; break; }
      strcpy(SELECTION, T_ITEMF(tbl, sel, 0));
      if( tbl->key == REPEAT_KEY ) continue;
      if(T_TST(tbl, sel, I_DIR)){ /* это каталог */
      /* попытаться перейти в этот каталог */
	 cd(SELECTION, wd, CWD);
      } else if(T_TST(tbl, sel, I_EXE)){ /* выполняемый файл */
	 (void) Edit(tbl->win, SELECTION, RUNCMD);
      } else {
	 editAccessModes(wd);
      /* На самом деле надо производить подбор команды по
       * типу файла (набор соответствий должен программироваться
       * вами в специальном файле, считываемом при запуске коммандера).
       *        runCommand( classify(SELECTION));
       * где классификация в простейшем случае - по имени и суффиксу,
       * а в более развитом - еще и по кодам доступа (включая тип файла)
       * и по первой строке файла (или "магическому числу").
       */
       }
    }  /* end for */
    t_leave( tbl );
out:
    if( !retcode ) current_menu = SEL_PULL; /* выход по ESC */
    return retcode;
}
/*-----------------------------------------------------------------*
 *   Горизонтальное командное меню (вызывается по ESC).            *
 *-----------------------------------------------------------------*/
PullInfo pm_items [] = {                 /* подсказка */
  {{ " \\Left ",     0 },        NULL,   "Left pane"       }, /* 0 */
  {{ " \\Commands ", 0 },        &mwrk,  "Do some commands"}, /* 1 */
  {{ " \\Tools ",    PM_NOSEL }, NULL,   ""                }, /* 2 */
  {{ " \\Sorttype ", 0 },        &msort, "Change sort type"}, /* 3 */
  {{ " \\Right ",    0 },        NULL,   "Right pane"      }, /* 4 */
  {{ NULL,           0 },        NULL,   NULL }
};
void p_help(PullMenu *p, int n, int among){ Message( PM_NOTE(p, n)); }
/* Выбор в меню-строке */
void SelectPullMenu(){
    int c, sel; Menu *m;
    for(;current_menu == SEL_PULL;){
	c = PullUsualSelect(&pull);
	sel = pull.current;
	if( PM_REFUSED(&pull)){ current_menu = previous_menu; return;}
	switch(sel){
	case 0: current_menu = SEL_PANE1; return;
	case 1: SelectWorkingMenu(c);     return;
	case 2:                           return;  /* не бывает */
	case 3: SelectSortType(c);        return;
	case 4: current_menu = SEL_PANE2; return;
	}
    }
}
/*-----------------------------------------------------------------*
 *   Инициализация и завершение.                                   *
 *-----------------------------------------------------------------*/
void die(int sig){
     echo(); nocbreak(); mvcur(-1,-1,LINES-1,0);
     refresh(); endwin (); putchar('\n');
     if(sig) printf("Signal %d\n", sig);
     if(sig == SIGSEGV) abort(); else exit(sig);
}
void main (void) {
    setlocale(LC_ALL, "");  /* получить информацию о языке диагностик */
    initscr ();          /* включить curses */
    signal(SIGINT, die); /* по сигналу вызывать die(); */
    signal(SIGBUS, die); /* по нарушению защиты памяти */
    signal(SIGSEGV,die);
    refresh();           /* обновить экран: это очистит его */
    noecho(); cbreak();  /* выключить эхо, включить прозрачный ввод */
/* Проинициализировать истории */
    HistInit(&hcwd,  20); hcwd. mnu.title = "История пути";
    HistInit(&hedit, 20); hedit.mnu.title = "История команд";
    HistInit(&hpat,   8); hpat. mnu.title = "Шаблоны имен";
/* Разметить меню сортировки   */
    msort.items     = sort_info;
    msort.title     = "Вид сортировки каталога";
    msort.top       = 1; msort.left = 2;
    msort.showMe    = sort_show;
    msort.bg_attrib = A_NORMAL; msort.sel_attrib = A_STANDOUT;
    /* MnuInit (&msort); инициализируется в pull-menu */
/* Разметить рабочее меню */
    mwrk.items      =  mwrk_info;
    mwrk.title      = "Главное меню";
    mwrk.top        = 1;    mwrk.left = COLS/3;
    mwrk.handler    = NULL; mwrk.hitkeys = NULL;
    mwrk.bg_attrib  = A_STANDOUT; mwrk.sel_attrib = A_REVERSE;
    mwrk.scrollBar  = m_help;
#ifdef __GNUC__
    mwrk_init();
#endif
    /* MnuInit (&mwrk); инициализируется в pull-menu */
/* Разметить левую и правую панели */
    tpane1.t.width      = CENTER - 1;
    tpane2.t.width      = COLS - tpane1.t.width - 2 - (2 + BARWIDTH);
    tpane1.t.height     = tpane2.t.height = (LINES - 8);
    tpane1.t.win        = tpane2.t.win  = panewin = stdscr;
    tpane1.t.left       = 1;
    tpane2.t.left       = CENTER+1;
    tpane1.t.top        = tpane2.t.top    = 3;
    tpane1.t.bg_attrib  = tpane2.t.bg_attrib  = A_NORMAL;
    tpane1.t.sel_attrib = tpane2.t.sel_attrib = A_STANDOUT;
    tpane1.t.scrollBar  = tpane2.t.scrollBar  = t_scrollbar;
    tpane1.t.hitkeys    = tpane2.t.hitkeys    = t_hit;
    tpane1.t.handler    = tpane2.t.handler    = t_handler;
    tpane1.t.showMe     = tpane2.t.showMe     = t_show;
    tpane1.t.hideMe     = tpane2.t.hideMe     = NULL;
/* Разметить имена для файловых объектов */
    tpane1.d.name = strdup("Текущий каталог");
    tpane2.d.name = strdup("Корневой каталог");
/* Изобразить рамки (но пока не проявлять их)
 * Это надо сделать до первого cd(), т.к. иначе при неудаче будет выдано
 * сообщение, которое проявит НЕЗАВЕРШЕННУЮ картинку */
    t_border_common(); t_restore_corners();
/* Доразметить левую панель */
    mychdir(".");  /* узнать полное имя текущего каталога в CWD[] */
    /* прочитать содержимое каталога CWD в tpane1.d */
    cd( CWD , &tpane1, CWD);
    tpane1.t.fmt        = "directory";
    InitTblFromDir(&tpane1, NO, NULL);
/* Доразметить правую панель */
    tpane2.t.fmt = NULL;
    /* прочитать содержимое каталога "/" в tpane2.d */
    cd( "/", &tpane2, CWD); /* теперь стоим в корне */
/* Вернуться в рабочий каталог */
    cd( tpane1.d.name, &tpane1, CWD);
/* Нарисовать обе панели */
    TblDraw(A_tbl); TblDraw(B_tbl);
/* Разметить pulldown меню */
    pull.bg_attrib  = A_REVERSE; pull.sel_attrib = A_NORMAL;
    pull.items      = pm_items;  pull.scrollBar  = p_help;
    PullInit(&pull);
/* Основной цикл */
    for(done=NO, current_menu=SEL_PANE1, A_pane= &tpane1, B_pane= &tpane2;
	done == NO; ){
	Message("");
	if(SEL_PANE) previous_menu = current_menu;
	switch(current_menu){
	case SEL_WRK :  SelectWorkingMenu(NOSELECTED); break;
	case SEL_PULL:  SelectPullMenu();              break;
	case SEL_PANE1: if( SelectPane(&tpane1) < 0)
			    M_SET(&mwrk, 0, I_NOSEL);  break;
	case SEL_PANE2: if( SelectPane(&tpane2) < 0)
			    M_SET(&mwrk, 0, I_NOSEL);  break;
	}
    }
    die(0);     /* Завершить работу */
}

© Copyright А. Богатырев, 1992-95
Си в UNIX

Назад | Содержание | Вперед