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;
}
}