т */ 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 ); 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" ); } /* Пример 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 | |----------------------------------------------------------------| СЕРВЕР |================================================================| |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); } /* Пример 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; /* 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; } /* Пример 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); } 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); } /* Пример 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); } /* Извлечь очередной блок из списка свободных блоков */ 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++; } /* Пример 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; /* Количество слотов в одном кластере каталога */ 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; } }