#include <dos.h>
#include <bios.h>
#include <alloc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <io.h>
#include <ctype.h>

typedef struct _DFCB_
{
  unsigned handl_num;
  unsigned char access_mode;
  unsigned reserv1;
  unsigned dev_info;
  void far *driver;
  unsigned first_clu;
  unsigned time;
  unsigned date;
  unsigned long fl_size;
  unsigned long offset;
  unsigned reserv2;
  unsigned reserv7;
  unsigned reserv3;
  char reserv4;
  char filename[11];
  char reserv5[6];
  unsigned ownr_psp;
  unsigned reserv6;
  unsigned last_clu;
  char reserv8[4];
} DFCB;
typedef DFCB far* LPDFCB;

typedef struct _DFT_
{
  struct _DFT_ far *next;
  unsigned file_count;
  DFCB dfcb;
} SFT;
typedef SFT far* LPSFT;

typedef struct
{
  unsigned mcb_seg;
  void far *dev_cb;
  void far *file_tab;
  void far *clock_dr;
  void far *con_dr;
  unsigned max_btbl;
  void far *disk_buf;
  void far *drv_info;
  void far *fcb_tabl;
  unsigned fcb_size;
  unsigned char num_bdev;
  unsigned char lastdriv;
} CVT;
typedef CVT far* LPCVT;

typedef struct _EBPB_
{
  unsigned sectsize;
  char clustsize;
  unsigned ressecs;
  char fatcnt;
  unsigned rootsize;
  unsigned totsecs;
  char media;
  unsigned fatsize;
  unsigned seccnt;
  unsigned headcnt;
  unsigned hiddensec_low;
  unsigned hiddensec_hi;
  unsigned long drvsecs;
} EBPB;

typedef struct _BOOT_
{
  char jmp[3];
  char oem[8];
  EBPB bpb;
  char drive;
  char reserved;
  char signature;
  unsigned volser_lo;
  unsigned volser_hi;
  char label[11];
  char fat_format[8];
  char boot_code[450];
} BOOT;

LPSFT get_fsft(LPCVT cvt);
LPSFT get_nsft(LPSFT sft);
void show(DFCB far *);
int getboot(BOOT far *boot, int drive);

union REGS   regs;
struct SREGS sregs;

int main(int argc, char *argv[])
{
  CVT far *cvt;
  SFT far *sft;
  unsigned i,j,k;
  DFCB far *dfcb, far *file_dfcb;
  int handle, flag, disk;

  BOOT far *boot_rec;
  int status;
  char *buf;

  char drive[128], dir[128];
  char fname[20], ext[10];
  char name[12];

  printf("Информация об открытых файлах DOS, "
    "Frolov A., (C) 1995\n");

  // Открываем файл, для которого будем
  // получать список кластеров
  handle = open(argv[1], O_BINARY);
  if(handle == 0)
  {
    printf("Ошибка при открытии файла\n");
    return(-1);
  }

  // Разбиваем путь к файлу на компоненты:
  //   - диск;
  //   - каталог;
  //   - имя файла;
  //   - расширение имени
  _splitpath(argv[1], drive, dir, fname, ext);

  if(drive[0] == '\0' || dir[0] == '\0' || argc < 2)
  {
    printf("\nУкажите полный путь к файлу\n");
    return(-1);
  }

  printf("Исследуем расположение файла '%s'",
    argv[1]);

  // Комбинируем строку из имени и расширения
  strcpy(name, fname);
  for(i = 0; i < 8; i++)
  {
    if(name[i] == 0) break;
  }
  for(; i < 8; i++) name[i] = ' ';
  name[8] = 0;

  strcat(name, &ext[1]);
  for(i = 8; i < 12; i++)
  {
    if(name[i] == 0) break;
  }
  for(; i < 12; i++) name[i] = ' ';
  name[12] = 0;

  // Преобразуем строку имени в заглавные буквы
  strupr(name);

  // Вычисляем номер диска
  drive[0] = toupper(drive[0]);
  disk = drive[0] - 'A';

  // Получаем адрес векторной таблицы связи
  regs.h.ah = 0x52;
  intdosx(&regs, &regs, &sregs);

  // Передвигаем указатель на поле msb_seg
  cvt = (LPCVT)MK_FP(sregs.es, regs.x.bx - 2);

  // Адрес начала таблицы файлов
  sft = get_fsft(cvt);

  // Сбрасываем флаг поиска файла
  flag = 0;

  for(;;)
  {
    // Конец таблицы файлов
    if(sft == (SFT far *)NULL) break;
    i = sft->file_count;

    for(j=0;j<i;j++)
    {
      dfcb = (&(sft->dfcb)) + j;

      // Ищем файл в таблице открытых файлов
      k = _fmemcmp((const void far*)name,
        (const void far*)dfcb->filename, 11);
      if(k == 0)
      {
	printf("\nDFCB файла:                   "
               " %Fp", dfcb);

        // Запоминаем адрес таблицы
        // для найденного файла
        file_dfcb = dfcb;

        // Показываем содержимое таблицы
        show(file_dfcb);
	flag = 1;
	break;
      }
    }
    if(flag == 1) break;

    sft = get_nsft(sft);
  }

  if(flag == 0)
  {
    printf("Файл не найден");
    close(handle);
    return(-1);
  }

  // Заказываем буфер для чтения загрузочной записи
  boot_rec = (BOOT far*)farmalloc(sizeof(*boot_rec));
  if(boot_rec == NULL)
  {
    printf("Мало памяти");
    close(handle);
    return(-1);
  }

  // Читаем загрузочную запись в буфер
  status = getboot((BOOT far*)boot_rec, disk);

  // Вычисляем размер кластера в байтах
  i = boot_rec->bpb.clustsize * boot_rec->bpb.sectsize;
  printf("Размер кластера, байт :        %d",i);

  // Если произошла ошибка (например, неправильно указано
  // обозначение диска), завершаем работу программы
  if(status)
  {
    printf("\nОшибка при чтении загрузочного сектора");
    close(handle);
    return(-1);
  }

  buf = (char*)malloc(i);
  if(buf == NULL)
  {
    printf("Мало памяти");
    close(handle);
    return(-1);
  }

  printf("\nСписок кластеров файла:\n");

  // Читаем файл по кластерам, выводим номер
  // последнего прочитанного кластера, который
  // берем из таблицы файлов
  for(;;)
  {
    read(handle, buf, i);
    if(eof(handle)) break;
    printf("%u ",file_dfcb->last_clu);
  }

  close(handle);
  farfree(boot_rec);
  free(buf);
  return(0);
}

// Функция для отображения содержимого таблицы файлов
void show(DFCB far *dfcb)
{
  int k;

  printf("\nИмя файла:                     ");
  for(k = 0; k < 11; k++)
  {
    putchar(dfcb->filename[k]);
  }

  printf("\nКоличество идентификаторов:    %d\n"
    "Режим доступа:                 %d\n"
    "Поле reserv1:                  %04X\n"
    "Информация об устройстве:      %04X\n"
    "Адрес драйвера:                %Fp\n"
    "Начальный кластер:             %u\n"
    "Время:                         %04X\n"
    "Дата:                          %04X\n"
    "Размер файла в байтах:         %ld\n"
    "Текущее смещение в файле:      %ld\n"
    "Поле reserv2:                  %04X\n"
    "Последний прочитанный кластер: %u\n"
    "Сегмент PSP владельца файла:   %04X\n"
    "Поле reserv7:                  %u\n",
    dfcb->handl_num, dfcb->access_mode,
    dfcb->reserv1, dfcb->dev_info,
    dfcb->driver, dfcb->first_clu,
    dfcb->time,	dfcb->date, dfcb->fl_size,
    dfcb->offset, dfcb->reserv2, dfcb->last_clu,
    dfcb->ownr_psp, dfcb->reserv7);
}

LPSFT get_nsft(LPSFT sft)
{
  LPSFT sft_next;

  sft_next = sft->next;
  if(FP_OFF(sft_next) == 0xffff)
    return((LPSFT)NULL);

  return(sft_next);
}

LPSFT get_fsft(LPCVT cvt)
{
  LPSFT sft;
  sft = (LPSFT)cvt->file_tab;
  return(sft);
}

int getboot(BOOT far *boot, int drive)
{
  struct
  {
    unsigned long first_sect;
    unsigned nsect;
    void far* buf;
  } cb;

  cb.first_sect = 0;
  cb.nsect = 1;
  cb.buf = (void far*)boot;

  _BX  = FP_OFF(&cb);
  _DS = FP_SEG(&cb);
  _CX  = 0xffff;
  _DX  = 0;
  _AX  = drive;
  asm int 25h
  asm pop ax
  asm jc err

  return 0;
err:
  return 1;
}
