#include <dos.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
/*== типы и структуры данных ==*/
#define byte unsigned char
#define word unsigned int
#define dword unsigned long
#define daddr struct DADDR
struct DADDR { /* физический дисковый адрес */
 byte h; /* головка */
 word s, /* сектор */
    t, /* дорожка */
    ts; /* сектор, дорожка упакованные */
 };
struct PART { /* структура элемента раздела */
 byte Boot, Begin_Hd; word Begin_SecTrk;
 byte SysCode, End_Hd; word End_SecTrk;
 dword RelSec, Size;
 };
struct MBR { /* стpуктуpа главной загpузочной записи */
 char LoadCode[0x1be]; struct PART rt[4]; word EndFlag;
 };
struct BootRec { /* структура корневой записи */
 byte jmp[3], ident[8]; word SectSize;
 byte ClustSize; word ResSect;
 byte FatCnt; word RootSize ,TotSecs;
 byte Media; word FatSize, TrkSecs, HeadCnt;
 word HidnSecL, HidnSecH; dword LongTotSecs;
 byte Drive, reserved1, DOS4_flag;
 dword VolNum; char VolLabel[11], FatForm[8];
 };
struct Dir_Item { /* структура элемента оглавления */
  char fname[11]; byte attr; char reserved[10];
  word time, date, cl; dword size;
  };
/*== описания функций ==*/
void Read_Mbr(void);
void Read_Boot(void);
void Get_First(void);
void Read_Fat(void);
void Read_13(void *mem);
void Sect_to_Daddr(dword sect);
dword Clust_to_Sect(word clust);
word Next_Clust(word clust);
char *Get_Name(char *s, char *d);
int  Find_Name();
void End_of_Job(int n);
/*== переменные ==*/
struct PART part;   /* текущий элемент раздела */
byte buff1[512];    /* буфер MBR и boot */
struct MBR *mbr;    /* указатель на таблицу разделов */
struct BootRec *boot; /* указатель на корневую запись */
byte buff2[512];    /* буфер каталога и текста */
struct Dir_Item *dir; /* указатель на часть каталога */
char *text;      /* указатель на текстовый буфер */
byte *fat;       /* указатель на FAT */
char job[81];     /* строка-задание */
char *jobptr;     /* текущий указатель в job */
char cname[12];    /* текущее имя для поиска */
byte Fdisk;      /* физический номер диска */
daddr caddr;      /* текуший дисковый адрес */
dword sect;      /* текуший номер сектора */
word clust;      /* текуший номер кластера */
byte fat16;      /* признак формата FAT */
dword fsize;      /* размер файла */
int dirnum;      /* номер элемента в оглавлении */
dword FirstSect;    /* абс.сектор начала */
byte rootdir=1;    /* признак корневого каталога
             или подкаталога (1/0) */
word lastsect;     /* последний сектор при чтении */
byte fatalloc=0;    /* признак выделения памяти */
/*-------------------------------------------------*/
main() {
int n;
 /* ввод имени файла */
 printf("Укажите имя файла >"); scanf("%s",job);
 /* перевод в верхний регистр */
 strupr(job);
 /* проверка правильности идентификатора диска */
 if ((!isalpha(job[0]))||(job[1]!=':')||(job[2]!='\\')) {
  printf("%c%c%c -",job[0],job[1],job[2]); End_of_Job(0);
  }
 jobptr=job+3;
 if (job[0]>'A') {
  /* для жесткого диска - физический номер и чтение MBR */
  Fdisk=0x80; Read_Mbr();
  }
 else /* для гибкого диска - физический номер */
  Fdisk=job[0]-'A';
 Read_Boot(); /* чтение boot-сектора */
 Read_Fat(); /* чтение FAT */
 dir=(struct Dir_Item *)buff2;
 do { /* движение по каталогам */
  if (!rootdir)
   clust=dir[dirnum].cl; /* начальный кластер */
  /* выделение следующего элемента из строки-задания */
  jobptr=Get_Name(jobptr,cname);
  do { /* пока не дойдем до последнего кластера */
   if (rootdir) { /* корневой каталог */
    /* нач.сектор корневого огл. и число секторов */
    sect=boot->ResSect+boot->FatSize*boot->FatCnt;
    lastsect=boot->RootSize*32/boot->SectSize+sect;
    }
   else { /* подкаталог */
    sect=Clust_to_Sect(clust);
    lastsect=boot->ClustSize+sect;
    }
   /* посекторное чтение всего корневого каталога
     или одного кластера подкаталога */
   for (; sect<lastsect; sect++) {
    Sect_to_Daddr(sect); Read_13(dir);
    /* поиск имени в прочитанном секторе */
    if ((dirnum=Find_Name())>=0) goto FIND;
    }
   /* до последнего кластера подкаталога */
   } while (clust=Next_Clust(clust));
  /* весь каталог просмотрен, а имя не найдено
    - ошибка */
  printf("%s -",cname);
  if (jobptr==NULL) End_of_Job(4); else End_of_Job(5);
FIND: /* имя найдено */
  rootdir=0;
  } while (jobptr!=NULL);
 /* найдено имя файла */
 /* из оглавления получаем размер и 1-й кластер */
 fsize=dir[dirnum].size; clust=dir[dirnum].cl;
 text=(char *)buff2;
 /* движение по файлу */
 do {
  sect=Clust_to_Sect(clust);
  lastsect=sect+boot->ClustSize;
  /* посекторное чтение кластера */
  for ( ; sect<lastsect; sect++) {
   Sect_to_Daddr(sect); Read_13(text);
   /* распечатка прочитанного кластера */
   for (n=0;n<boot->SectSize;n++) {
    printf("%c",text[n]);
    /* контроль длины файла */
    if (!(--fsize)) goto EOJ;
    }
   }
  } while (clust=Next_Clust(clust));
 /* если кластеры закончились, а длина не исчерпана
   - ошибка */
 printf("\n%s -",job); End_of_Job(6);
EOJ: /* нормальное завершение программы */
 printf("\n"); End_of_Job(7);
}
/*---------------------------------------*/
/* чтение MBR и поиск требуемого раздела */
void Read_Mbr(void) {
 int i;
 char ndrive;
 word *EndList;
 caddr.h=0; caddr.ts=1; ndrive='C';
 mbr=(struct MBR *)buff1;
NEXT: Read_13(buff1);
 for (EndList=(word *)&mbr->rt[(i=0)];
   (*EndList!=0xaa55)&&(mbr->rt[i].Size>0L);
   EndList=(word *)&mbr->rt[++i]) {
  if (mbr->rt[i].SysCode==5) {
   caddr.h=mbr->rt[i].Begin_Hd;
   caddr.ts=mbr->rt[i].Begin_SecTrk;
   goto NEXT;
   }
  if (ndrive==job[0]) {
   movmem(&mbr->rt[i],&part,sizeof(struct PART));
   return;
   }
  else ndrive++;
  }
 /* требуемый раздел не найден */
 printf("%c: -",job[0]); End_of_Job(1);
}
/*---------------------*/
/* чтение boot-сектора */
void Read_Boot(void) {
 if (Fdisk<0x80) { caddr.h=0; caddr.ts=1; }
 else
  { caddr.h=part.Begin_Hd; caddr.ts=part.Begin_SecTrk; }
 Read_13(buff1);
 boot=(struct BootRec *)buff1;__ _.Get_First();
}
/*------------*/
/* чтение FAT */
void Read_Fat(void) {
dword s, ls;
byte *f;
 fat=(byte *)malloc(boot->FatSize*boot->SectSize);
 if (fat==NULL)
  { printf("Размещение FAT -"); End_of_Job(3); }
 fatalloc=1;
 s=boot->ResSect; ls=s+boot->FatSize;
 for (f=fat; s<ls; s++) {
  Sect_to_Daddr(s); Read_13(f);
  f+=boot->SectSize;
  }
 /* установка формата FAT */
 if (Fdisk>=0x80)
  if (part.SysCode==1) fat16=0; else fat16=1;
 else fat16=0;
}
/*--------------------------------------------*/
/* чтение 1 сектора при помощи прерывания 13. */
void Read_13(void *mem) {
/* mem - адрес в ОП */
union REGS rr;
struct SREGS sr;
 rr.h.ah=2; rr.h.al=1; rr.h.dl=Fdisk;
 rr.h.dh=caddr.h; rr.x.cx=caddr.ts;
 sr.es=FP_SEG(mem); rr.x.bx=FP_OFF(mem);
 int86x(0x13,&rr,&rr,&sr);
 /* Проверка ошибок чтения */
 if (rr.x.cflag&1) {
  printf("%u -",rr.h.ah); End_of_Job(2);
  }
}
/*-------------------------------------------------*/
/* определение абс.номера сектора начала лог.диска */
void Get_First(void) {
 word s, t;
 if (Fdisk<0x80) FirstSect=0;
 else {
  /* формирование # сектора из физич. дискового адреса */
  t=(part.Begin_SecTrk>>8)|((part.Begin_SecTrk<<2)&0x300);
  s=part.Begin_SecTrk&0x3f;
  FirstSect=(((dword)t*boot->HeadCnt)+part.Begin_Hd)*
   boot->TrkSecs+s-1;
  }
}
/*--------------------------------------------------------*/
/* формирование физического дискового адреса из # сектора */
void Sect_to_Daddr(dword sect) {
/* sect - номер сектора, caddr - адрес на диске */
dword s;
 if (Fdisk>=0x80) sect+=FirstSect;
 caddr.s=sect%boot->TrkSecs+1;
 s=sect/boot->TrkSecs;
 caddr.h=s%boot->HeadCnt;
 caddr.t=s/boot->HeadCnt;
 caddr.ts=(caddr.t<<8)|caddr.s|((caddr.t&0x300)>>2);
}
/*----------------------------------------------*/
/* вычисление номера сектора по номеру кластера */
dword Clust_to_Sect(word clust) {
/* clust - номер кластера, возвращает номер сектора */
 dword ds, s;
 ds=boot->ResSect+boot->FatSize*boot->FatCnt+
   boot->RootSize*32/boot->SectSize;
 s=ds+(clust-2)*boot->ClustSize;
 return(s);
}
/*------------------------------------*/
/* выборка следующего кластера по FAT */
word Next_Clust(word clust) {
/* clust - номер кластера, возвращает номер след.кластера
  или 0 - если следующего нет */
 word m, s;
 if (rootdir) return(0);
 if (!fat16) {
  m=(clust*3)/2; s=*(word *)(fat+m);
  if(clust%2) /* нечетный элемент */ s>>=4;
  else /* четный элемент */ s=*s1&0x0fff;
  if (s>0x0fef) return(0);
  else return(s);
  }
 else {
  m=clust*2; s=*(word *)(fat+m);
  if (s>0xffef) return(0);
  else return(s);
  }
}
/*-------------------------------------------------*/
/* выделение очередного элемента из строки-задания */
char *Get_Name(char *s, char *d) {
/* s - строка задания, d - выделенный элемент, возвращает
  указатель на новое начало строки задания. */
char *p,*r; int i;
 for(i=0;i<11;d[i++]=' '); d[11]='\0';
 if ((p=strchr(s,'\\'))==NULL) {
  /* последний элемент строки - имя файла */
  /* перезапись имени */
  for(r=s,i=0; (i<8)&&*r&&(*r!='.'); i++,r++) *(d+i)=*r;
  /* перезапись расширения */
  if (*r) for(i=0,r++; (i<3)&&*r; i++,r++) *(d+8+i)=*r;
  return(NULL);
  }
 else {
  /* очередной элемент - имя подкаталога */
  *p='\0';
  for(r=s,i=0; (i<11)&&*r; i++,r++) *(d+i)=*r;
  return(p+1);
  }
}
/*------------------------*/
/* поиск имени в каталоге */
int Find_Name() {__ _.int j;
/* cname - искомое имя, возвращает индекс найденного
  элемента в массиве dir или (-1) */
 for (j=0; j<boot->SectSize/sizeof(struct Dir_Item); j++) {
  if (dir[j].fname[0]=='\0') {
   /* конец использованных элементов каталога */
   printf("%s -",cname);
   if (jobptr==NULL) End_of_Job(4); else End_of_Job(5);
   }
  if ((byte)dir[j].fname[0]!=0xe5) {
   if (memcmp(dir[j].fname,cname,11)==0) {
   /* если имя совпало, то:
     - при поиске файла элемент не должен иметь
      атрибутов "подкаталог" или "метка тома",
     - при поиске подкаталога элемент должен иметь
      атрибут "подкаталог" */
    if (jobptr==NULL) {
     if ( !(dir[j].attr&0x18) ) return(j);
     }
    else if (dir[j].attr&0x10) return(j);
    }
   }
  }
 return(-1);
}
/*------------------------------------*/
/* завершение (при n=0-5 - аварийное) */
void End_of_Job(int n) {
/* n - номер сообщения */
char *msg[] = {
  "неправильный идентификатор диска",
  "лог.диск отсутствует",
  "ошибка чтения",
  "нехватка памяти",
  "подкаталог не найден",
  "файл не найден",
  "непредусмотренный конец файла",
  "======== КОНЕЦ ФАЙЛА ======="
  };
 /* освобождение памяти */
 if (fatalloc) free(fat);
 /* выдача сообщения */
 printf(" %s\nНажмите любую клавишу...\n\7",msg[n]);
 getch();
 /* завершение программы */
 exit(0);
}