// =====================================================
// Печать на принтере с помощью функций BIOS
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW:    http://www.glasnet.ru/~frolov
//            или
//         http://www.dials.ccas.ru/frolov
// =====================================================
#include <dos.h>
#include <stdio.h>
#include <conio.h>

union REGS rg;
int printchar(int chr);
int error(char chr, int status);

int main(int argc, char *argv[]) 
{
  FILE *srcfile;

  // Открываем файл, заданный первым параметром
  // в командной строке.
  // Если при запуске программы оператор забыл
  // указать имя файла, выводим напоминающее сообщение
  if( (srcfile = fopen( argv[1], "rb" )) == NULL ) 
  {
    printf("\nЗадайте имя файла в качестве параметра");
    return (-1);
  }

  // Читаем файл по одному символу, полученный из файла
  // символ выводим на принтер при помощи функции printchar
  for(;;) 
  {
    printchar(fgetc(srcfile));
    if(feof(srcfile)) 
      break;
  }

  // Закрываем файл
  fclose(srcfile);
  
  return 0;
}

// ------------------------------------
// Эта функция выводит один символ
// на первый принтер (LPT1)
// ------------------------------------
int printchar(int chr) 
{
  int status;

  // Повторяем в цикле выдачу символа на принтер
  // до тех пор, пока он не будет выведен без
  // ошибок, либо пока оператор не отменит
  // распечатку файла
  for(;;) 
  {
    // Дублируем распечатываемый символ на экране
    putch(chr);

    // Вызываем функцию 00h прерывания INT 17h -
    // распечатка символа на принтере.
    // В регистре DX задаем номер принтера LPT1 - это 0
    rg.h.ah = 0;
    rg.h.al = chr;
    rg.x.dx = 0;

    int86(0x17, &rg, &rg);

    // Запоминаем байт состояния принтера
    // после вывода символа
    status = rg.h.ah;

    // Проверяем наличие ошибок. Нас интересуют биты:
    //
    //    0 - задержка при печати
    //    3 - ошибка ввода/вывода
    //    4 - принтер в состоянии ONLINE (1) или OFFLINE (0)
    //    5 - конец бумаги
    if((status & 0x39) != 0x10) 
    {
      // Вызываем функцию обработки ошибки error(). Эта
      // функция возвращает 0, если оператор желает
      // повторить печать символа, или 1 - если
      // оператор отменяет печать
      if(error(chr, status)) 
      {
        printf("\nПечать завершилась аварийно");
        return -1;
      }
    }
    else 
      break;
  }
}

// ------------------------------------
// Функция выводит на экран состояние
// принтера и запрашивает у оператора
// требуемые действия - повторить
// печать символа или отменить печать
// ------------------------------------
int error(char chr, int status) 
{
  // Выводим состояние принтера после ошибки
  printf("\nОшибка принтера %02.2X"
       "\n\nСостояние принтера:"
       "\n-------------------", status);

  if(status & 1)
    printf("\nТаймаут при печати");

  if(status & 8)
    printf("\nОшибка ввода/вывода");

  if(!(status & 0x10))
    printf("\nПринтер находится в состоянии OFFLINE");

  if(status & 0x20)
    printf("\nКонец бумаги");

  printf("\n\nДля отмены печати нажмите клавишу ESC,"
    "\nдля повтора - любую другую клавишу\n");

  if(getch() == 27) 
    return 1;
  else 
    return 0;
}
