/*
 *  TEX Device Driver  ver 2.00-
 *  copyright(c) 1988,89 by TSG, 1990-93 by SHIMA
 *
 *  PRINTER DRIVER ver 1.50
 *  device.c : print out module
 *             3rd edition
 *
 *  1990, Modified by SHIMA
 *  バッファード出力、基本ルーチンのアセンブラ化、HMAX による効率アップなど
 * このモジュールは，当然device-dependentである．
 *  1992, Slightly extended for Kyocera L-880 & HP LaserJet+ by Oh-Yeah?
 *
 *  1992, *.cfgファイルにdpiの項目を追加 by sempa
 *
 *	modified for non PC-9801 machines by sempa 1992
 *
 *  *.cfgファイルを 環境変数 `TEXCFG'のパスからオープンするように変更 by sempa 1992 Apr
 *  (「dviprt -pofile」の`file'にパス指定がない場合)
 *
 *	very slightly modified against warnings by Oh-Yeah? 25 May 1992
 *
 *  modified for LIPS3 by SHIMA 30 June 1992
 *  modified for ESC/Page by H.Tomiie, 31 July 1992
 *  modified for LIPS3 + ESC/Page by H.Tomiie and OkI, 30 Sept. 1992
 *  modified for hex mode by I. Matsuda, 6 Dec. 1992
 */

#define	PR_BUF_SIZE		256
/*
  *  プリンタ出力用のバッファ・バッファサイズ（ブロック単位で出力）
  *  p_sub.asm, p_subnec.asm での値と同じでなくてはならない。
  */

#define	STR_EXP_SIZE	32

#define	HWMIN		1
/*
  *  横方向の HMIN*8 ドットの範囲が、空白ならば、続く空白と合わせて、プリン
  *  タヘッドの移動命令を送る。但し、それ以降も行末までが空白ならば、何も送
  *  らない（デフォールト）。
  */

#define	HWMAX		(1440/8)
/*
  *  ESC/P　のときは (1440/8) 以下でなくてはならない。
  *  ビットイメージ、または、移動命令を１度に送る横幅のドット数の最大値
  */

#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
#include <string.h>
#include <dir.h>
#include <conio.h>
#include <io.h>
#define _DEF_DOS_H_
#include "dd.h"
#include "prtout.h"
#include "err.h"
#include "prtctl.h"
#include <ctype.h>

#define ESC 		0x1b
#define CR  		0x0d
#define LF  		0x0a

#ifdef	PC9801
#define	PRN_BIOS	0x1a
#else /* J3100, AX, DOSV */
#define	PRN_BIOS	0x17
#define	_ZF			0x40
#endif

int pr_buf_len;
BOOL f_pause;
int ff_flg = -1;
BOOL f_top_margin;
BOOL f_hex_mode = FALSE;
BOOL f_r_format;
char pr_buf[PR_BUF_SIZE+4];
/*
char str_exp[STR_EXP_SIZE];
*/
#define	str_exp	tmp_buf
char *f_out;
FILE *fp_out;
char *prt_initialize;

static int vert_byte_unit = V_BDIV;	/* ピン数／８ */
/* static int vert_bit_unit = V_BDIV << 3; */ /* プリンタのピン数 */
/* 下の #define を頼って lips3.c, escpage.c から exern しているので注意すること */
#define	vert_bit_unit vert_divide

static uint hwmin = HWMIN;
static uint hwmax = HWMAX;
static uint mult_const;
static int num_cr;	/* CR/LF を送るべき数 */
static long trans_byte;
static int Page;
static int f_files = -1;

extern DIMENSION dviout_dimension;
extern PIXEL vert_divide;

#ifndef	PC9801	/* J-3100,AX,DOS/V */
char *pr_port_str = NULL;
static int pr_port_num = 0;
static int pr_ioport;
static int pr_port_wait = 0;
#endif

#ifdef HIRES98
extern int hireso;
#endif

#ifdef	JDWN
extern int f_gaiji;
#endif

#ifdef	LBP
static int last_H;
static int last_V;
int f_rotate;
int f_vh_ratio;
int f_copy;
BOOL f_lbp_comp;
char *youshi;
extern int f_use_lfont;
LBP_FUNC *lbpf;

#ifdef	LIPS3
extern LBP_FUNC lips3_func;
#endif

#ifdef	ESCPAGE
extern LBP_FUNC escpage_func;
#endif

#ifdef	FAX
char *f_fax;
void fax_encode_block(int, int, HUGE_BUF*, int);
void set_FAX_ctl(void);
#endif

int k_base_l = 10000;	/* This is relative according to the
								size of Kanji in LBP.  1000 is
								corresponds the size of Kanji.
								A larger number means that Kanji
								fonts move to lower position		*/

int k_base_d = 1;	/*  Arrange of dot height of scalable
								Kanji fonts.
								A larger number means that Kanji
								fonts move to lower position	*/

int k_scale_l = 10000;	/* This is factor for Kanji width adjust-
								ment of LBP scalable font. It was used
								after multiplied by `k_scale' factor.*/

int lbp_orig_dpi = 0;	/* LIPS3本来の dpi 値．-dpiによる縮小・
								拡大印刷時にもフォントピッチ指定コマンド
								のｃｐｉ計算を正しく行なうため必要． */

int k_width_adj = 0;	/* dot単位で計算された漢字幅にさらに
								この値を加えて補正を加える．この値は
								ESC/Pgaeでセットされる*/
int last_dir = -1;	/* 最後に描画したフォントの種類．LIPS3では
					 * これが異なるときには再呼びだしが必要になる
					 * 0:DownLoad Vertical 1:DL Horizontal 2:漢字 3:Bit Image.
					 * 0:DnLd l->r, 1:DnLd b->t, 2:DnLd r->l 3:DnLd t->d
					 * 4:漢字 l->r, 5:漢字 b->t, 6:漢字 r->l 7:漢字 t->d */

#endif

/* init.c prtinit.c */
extern char far *cfg_path;
extern BOOL f_lbp_prt;

/*	buffer.c	*/
char *marea(unsigned int);

/* main.c */
int no_extension(char *);
extern DVIFILE_INFO dvifile;

/* p_out.c local routine (they are used in lips3.c, escpage.c, too.)*/
void pr_putnc(char *, int);
void pr_puts(char *);
void pr_putsn(char *, int);
void pr_putc(char);

static void pr_crlf(void);

void p_sub(ulong, HUGE_BUF *, int);	/* p_sub.asm	*/
void p_subnec(ulong, HUGE_BUF *, int);	/* p_subnec.asm */
void p_subhex(int hmax, HUGE_BUF *ptr, int length, int type);
void p_sublbp(int hmax, HUGE_BUF *ptr, int bitno, int length, int type);

void get_prt_ctl(char *filename);

/*
 * 8n×8 ドットのビットマップ・バッファのデータを 8n バイトのプリンタ用の
 * データに変えて、プリンタに送る。
 * 実際には、pr_buf_len を参照して、プリンタ出力用のバッファに直接書き込み
 * バッファが一杯になったら、Ｃの関数 pr_flush() を呼んで書き込む。
 * ビットマップの左から順に縦の列 8n ドットを、n バイトに直して、8 回送る。
 * 縦の n バイトは、上から順に送る。１バイトのうち、上の方が上位のビットに
 * なるように変換するのが p_sub() で、p_subnec() はその逆である。
 * 送るべきビットマップデータの先頭アドレスが２番目の変数、最初の変数は、１行
 * のバイト数（隣接する下のドットとのビットマップバッファでのバイト単位でのア
 * ドレス差)、n は、３番目の変数で与えられる。
 */

void conv_bit(ulong, HUGE_BUF *);	/* convert bit pattern */

void pr_new_page(void);
void pr_flush(void);
void device_pause(void)
{
}

void device_cont(void)
{
}

static char NUL4[4] = {0,0,0,0};

static char *line_feed = "\x02\x00\x00\x00\x0d\x0a";
static char *form_feed = "\x02\x00\x00\x00\x0d\x0c";
static char *after_bit_image = NUL4;
static char *bit_row_header = NUL4;

static char prt_ctl[MAX_PRINTER] =
{
/* ctrl of printer
	 * lower 4 bits represents the vertical length by  bytes
     */

	VERT_DIVIDE / 8,						/* ESC/P printer */

	(VERT_DIVIDE / 8) | TOP_IS_LOW,			/* PC-PR printer */

	(VERT_DIVIDE / 8) | TOP_IS_LOW			/*  NM   printer */

#ifdef	LIPS3
	,(8 / 8) | LEFT_IS_HIGH | NON_MOVING	/* lips3 */
#endif
#ifdef	ESCPAGE
	,(8 / 8) | LEFT_IS_HIGH					/* ESCPage */
#endif
#ifdef	FAX
	,(8 / 8) | LEFT_IS_HIGH					/* FAX */
#endif
};

static char *dot_cr[MAX_PRINTER] =
{
/* codes of start job for printer */

	"\x07\x00\x00\x00"		/* ESC/P printer */
	"\x1b" "3\x18"			/* 3 byte: 24/180 inch line feed */
	"\x1b" "?Z" "\x27"		/* 4 byte: ESC Z ... -> ESC * m \x27 ... */
	,

	"\x06\x00\x00\x00"		/* PC-PR printer */
	"\x1b" "T18"			/* 4 byte: 18/120 = 24/160 inch line feed */
	"\x1b" ">"				/* 2 byte: print in one direction */
	,

	"\x0a\x00\x00\x00"		/* NM printer */
	"\x1a" "C"				/* 2 byte: 180 dpi for bits image */
	"\x1a" "F"				/* 2 byte:  1/120 inch pitch */
	"\x1b" "T16"			/* 4 byte: 24/180 inch line feed */
	"\x1b" ">"				/* 2 byte: print one direction */

#ifdef	LIPS3
	,
	NUL4					/* Use call lips3_init() */
#endif
#ifdef	ESCPAGE
	,
	NUL4					/* Use call escpage_init() */
#endif
#ifdef	FAX
	,
	"\x10\x00\x00\x00"		/* Header for StarFAX (fine mode)*/
	"SF"					/* 2 byte: id */
	"\x01"					/* 1 byte: version */
	"\x00\x00\x00\x00\x00"	/* 5 byte: _[5] */
	"\x00\x40\x00\x00"		/* 4 byte: FIF[4]: fine Mode */
	"\x00\x00\x00\x00"		/* 4 byte: __[4] */
#endif
};

static char *nrm_cr[MAX_PRINTER] =
{
/* codes of end of job for the  printer */

	"\x04\x00\x00\x00"		/* ESC/P printer */
	"\x0d\x0c"
	"\x1b" "2"				/* 2 byte: 1/6 inch line feed */
	,

	"\x06\x00\x00\x00"		/* PC-PR printer */
	"\x0d\x0c"
	"\x1b" "A"				/* 2 byte: 1/6 inch line feed */
	"\x1b" "]"				/* 2 byte: print in both direction */
	,

	"\x06\x00\x00\x00"		/* NM printer */
	"\x0d\x0c"
	"\x1b" "A"				/* 2 byte: 1/6 inch line feed */
	"\x1b" "]"				/* 2 byte: print in both direction */

#ifdef	LIPS3
	,
	"\x08\x00\x00\x00"		/* LIPS3 */
	"\x0d\x0c"				/* 2 byte: CR & FF */
	"\x1b" "<"				/* 2 byte: Software reset */
	"\x90" "0J" "\x9c"		/* 4 byte: End job */
#endif
#ifdef	ESCPAGE
	,
	"\x06\x00\x00\x00"		/* ESC/Page printer */
	"\x0d" "\x0c"
	"\x1d" "rhE"
#endif
#ifdef	FAX
	,
	"\x09\x00\x00\x00"		/* FAX: RTC */
	"\x00\x08\x80"
	"\x00\x08\x80"
	"\x00\x08\x80"
#endif
};

/*			format to output string with a number 

  len[1], flag[1], pos[1], f_mode[1], data[len]

  len                  : the length of data

  flag   & 0x03        : shift left by this number (x1, x2, x4, x8)
  flag   & 0x80        : continue to the next block?

  pos                  : top of the position of the number in data

  f_mode & 0x80        : multiply by vert_byte_unit?
  f_mode & 0x40        : ISO number?
  f_mode & 0x08        : multiply by constant?
  f_mode & 0x07 =    0 : No number
                     1 : Binary Low to High
                     2 : Binary High to Low
                     3 : 3 digit decimal
                     4 : 4 digit decimal
                     5 : 5 digit decimal
                     6 : variable decimal
                     7 : not use
 */

static char *dotg[MAX_PRINTER] =
{
/* printing bit image */

	"\x04\x03\x02\x01"		/* ESC/P printer */
	"\x1b" "Z\x00\x00"
	,

	"\x06\x03\x02\x04"		/* PC-PR printer */
	"\x1b" "J0000"
	,

	"\x06\x03\x02\x04"		/* NM printer */
	"\x1b" "J0000"
#ifdef	LIPS3
	,
	"\x05\x83\x00\x86"		/* LIPS3 */
	"\x9b" "%u;" "\x00"
	"\x05\x00\x00\x36"
	"%u.r"
#endif
#ifdef	ESCPAGE
	,
	"\x05\x83\x00\x06"		/* ESC/Page */
	"\x1d" "%u;" "\x00"
	"\x09\x03\x00\x06"
	"%u;8bi{I" "\x00"
#endif
#ifdef	FAX
	,
	NUL4					/* FAX: NULL */
#endif
};

static char *dots[MAX_PRINTER] =
{
/* dots spacing */

	"\x04\x03\x02\x01"		/* ESC/P printer */
	"\x1b\x5c\x00\x00"
	,

	"\x09\x03\x02\x04"		/* PC-PR printer */
	"\x1b" "U0000\x00\x00\x00"
	,

	"\x09\x03\x02\x04"		/* NM printer */
	"\x1b" "U0000\x00\x00\x00"

#ifdef	LIPS3
	,
	"\x05\x03\x00\x06"		/* LIPS3 */
	"\x9b" "%ua"
#endif
#ifdef ESCPAGE
	,
	"\x05\x03\x00\x06"		/* ESC/Page */
	"\x1d" "%uH" "\x00"
#endif
#ifdef	FAX
	,
	NUL4					/* FAX: NULL */
#endif
};

static uint _get_uint(FILE * fptr)
{

	uint dl, dh;

	dl = (uchar)getc(fptr);
	dh = (uchar)getc(fptr);
	return (dl + (dh << 8));
}

static char *get_len(char *buf)
{
	uchar *temp;
	int flag;

	temp = (uchar *)buf;
	do {
		flag = temp[1];
		temp[1] |= 3 - ((temp[3] & DIVIDE_ALL) >> 4);
		temp += temp[0] + 4;
	} while (flag & 0x80);
	return ((char *)temp);
}

void get_prt_ctl(char *filename)
	/* Read the configuration file for Printer Device
 */
{
	uint size;
	int i, ch,prtype;
	char fname[MAXPATH], *srcpath;
	char file[MAXPATH + 4];
	uchar *prtctl_buf;
	FILE *fptr;

	prtype = dviout_dimension.prt_type;

	if (*filename == 0)
		strcpy(fname, prt_ctr_file);
	else {
		strncpy(fname, filename, MAXPATH);
		if (no_extension(fname))
			strcat(fname, ".cfg");
	}
	if (strpbrk(fname, "/\\:") != NULL) {	/* check path prefixed */
		strncpy(file, fname, MAXPATH);
	}
	else {
		{
			char *d = file;
			char far *s = cfg_path;
			int n = MAXPATH;

			while ((*d++ = *s++) != 0 && n-- > 0);
		}
		/* ↑ strcpy((char far *)file, cfg_path); */
		strcat(file, fname);
		if (access(file, 0) == -1 &&
			(srcpath = searchpath(fname)) != NULL) {
			strncpy(file, srcpath, MAXPATH);
		}
	}

	if ((fptr = fopen(file, "rb")) == NULL) 
		error(PROGRAM_STOP, "Can't open configuration file \"%s\"", file);
	for (i = 16; i > 0; --i) {
		fprintf(stderr, "%c", ch = (uchar)getc(fptr));
		if (ch == 0)
			while (--i > 0)
				(void)getc(fptr);
	}
	fprintf(stderr, "\n");

	/* skip 16 bytes */

	if ((ch = getc(fptr)) == 'Q')
		f_r_format = FALSE;
	else if (ch == 'R')
		f_r_format = TRUE;
	else
		goto ptrcfg_err;
	prt_ctl[prtype] = (uchar)getc(fptr);
	if ((vert_byte_unit = (prt_ctl[prtype] & 0x0f)) == 0)
		goto ptrcfg_err;
	if (prt_ctl[prtype] & HEX_MODE)
		f_hex_mode = TRUE;

	if (getc(fptr) != 0xff)
		goto ptrcfg_err;
	ch = getc(fptr);
	size = (int)filelength(fileno(fptr)) - ch;

	mult_const = (uchar)getc(fptr);
	(void)getc(fptr);
	(void)getc(fptr);
	hwmin = _get_uint(fptr);
	hwmax = _get_uint(fptr);
	if (f_r_format) {
		ch = _get_uint(fptr);
		if (dviout_dimension.dpi == 0)
			dviout_dimension.dpi = ch;
	}
	if ((hwmin | hwmax) == 0) {
ptrcfg_err:
		error(FILE_FAULT, "Maybe old format:%s", file);
	}
	prtctl_buf = (uchar *)marea(size);
	for (i = 0; i < size; i++)
		prtctl_buf[i] = (uchar)getc(fptr);

	dot_cr[prtype] = (char *)prtctl_buf;
	nrm_cr[prtype] = get_len((char *)prtctl_buf);
	dotg[prtype] = get_len(nrm_cr[prtype]);
	dots[prtype] = get_len(dotg[prtype]);
	vert_bit_unit = (PIXEL)vert_byte_unit *8;

	line_feed = get_len(dots[prtype]);
	form_feed = get_len(line_feed);
	after_bit_image = get_len(form_feed);
	bit_row_header = get_len(after_bit_image);
	get_len(bit_row_header);
	fclose(fptr);
}

void pr_putc(char x)
	/*  １バイトプリンター出力ルーチン  */
{
	pr_buf[pr_buf_len] = x;
	if (++pr_buf_len >= PR_BUF_SIZE)
		pr_flush();
}

void pr_flush(void)
	/* プリンタ出力用のバッファ・フラッシュ */
{
	union REGS inregs, outregs;
	struct SREGS sregs;
	int i;
	static unsigned int num, h_num;
	unsigned char c;
	int j, k;

#ifdef	PC9801
	inregs.x.ax = 0x0400;
	int86(0x18, &inregs, &outregs);
	if ((outregs.h.ah & 1) == 1)/* Check for ESC key */
# else							/* J3100, AX, DOSV */
	inregs.h.ah = 0x01;
	int86(0x16, &inregs, &outregs);
	if (((outregs.x.flags & _ZF) == 0) && (outregs.h.al == 0x1b))
#endif
	{
		fprintf(stderr, "\nAbort(y/n)? ");
		for (;;) {
			if ((c = getch()) == 'y' || c == 'Y')
				exit(1);
			else if (c == 'n' || c == 'N') {
				fprintf(stderr, "n\n");
				break;
			}
		}
	}
	if (pr_buf_len > 0) {
		if (f_out == NULL) {
#ifdef PC9801
			BOOL repeat;
			
			sregs.es = FP_SEG(pr_buf);
			inregs.x.bx = FP_OFF(pr_buf);
			inregs.x.cx = pr_buf_len;
			do {
				inregs.h.ah = 0x30;
				int86x(PRN_BIOS, &inregs, &outregs, &sregs);
#ifdef HIRES98
				if (hireso) {
					char ch;
					BOOL perr;

					repeat = TRUE;
					switch (outregs.h.ah) {
					  case 0:	/*Ready */
						  repeat = FALSE;
					  case 1:	/* Busy */
					  case 2:	/* TimeOut */
						  break;
					  case 3:	/* OffLine */
						  fprintf(stderr, "\r\nPrinter Off Line.");
						  break;
					  case 4:	/* Paper Error */
						  fprintf(stderr, "\r\nPaper Error.");
						  break;
					  case 5:	/* No Connection */
						  fprintf(stderr, "\r\nPrinter not Powered"
								  " or Connected");
						  break;
					}
					if (outregs.h.ah > 2) {
						fprintf(stderr, "\nWaiting Recovery, or Abort?<Y>");
					}
					/* ステータスを監視し，エラーが取れたら自動再開 */
					do {
						perr = 1;
						while (perr && (kbhit() == 0)) {
							inregs.h.ah = 0x12;
							int86(PRN_BIOS, &inregs, &inregs);
							if (inregs.h.ah == 0)
								perr = 0;
						}
						if (perr == 0) {
							if (outregs.h.ah > 2)
								fprintf(stderr, "..Recoverd.\n");
							break;
						}
						ch = getch();
						ch = toupper(ch);
						if (ch == 'Y') {	/* STOP PROGRAM */
							/* device error なので device_endは呼べない */
							/* 当然 error() も使えない */
							fprintf(stderr, "Y\nPrinting is Aborted.\n");
							exit(0);
							/* error( STOP_PROGRAM, "Printing is Aborted.");*/
						}
					} while (1);
					repeat = (outregs.x.cx != 0);
				}
				else
#endif /* HIRES98 */
				{				/*normal mode*/
					repeat = (outregs.h.ah > 0) && outregs.x.cx != 0;
				}
				inregs.x.bx = outregs.x.bx;
				inregs.x.cx = outregs.x.cx;
			} while(repeat);
#else /* J3100, AX, DOS/V */
			if (pr_port_num == 0) {
#ifdef	J3100					/* J-3100 : BIOS 経由の出力 */
				sregs.es = FP_SEG(pr_buf);
				inregs.x.bx = FP_OFF(pr_buf);
				inregs.x.cx = pr_buf_len;
				do {
					inregs.x.ax = 0x300;
					inregs.x.dx = 0;
					int86x(PRN_BIOS, &inregs, &outregs, &sregs);
					inregs.x.bx = outregs.x.bx;
					inregs.x.cx = outregs.x.cx;
				} while (outregs.h.ah & 1);
#else /* AX, DOS/V : BIOS 経由の出力 */
				for (i = 0; i < pr_buf_len; i++) {
					do {
						inregs.h.ah = 0x02;
						inregs.x.dx = 0;
						int86(0x17, &inregs, &outregs);
					} while ((outregs.h.ah & 0x29) != 0 || (outregs.h.ah & 0x80) == 0);
					inregs.h.ah = 0;
					inregs.h.al = pr_buf[i];
					int86(PRN_BIOS, &inregs, &outregs);
				}
#endif /* J3100, AX, DOS/V : IO-PORT 直接アクセスによる出力 */
			}
			else {
				for (i = 0; i < pr_buf_len; i++) {
					while (!(inportb(pr_ioport + 1) & 0x80));	/* wait busy */
					outportb(pr_ioport, pr_buf[i]);
					c = inportb(pr_ioport + 2) & 0xDF;
					outportb(pr_ioport + 2, c | 0x01);	/* out strobe */
					for (k = pr_port_wait; k >= 0; k--)	/* for wait */
						c &= 0xFE;
					outportb(pr_ioport + 2, c );	/* reset strobe */
				}
			}
#endif /* ALL MACHINES */
		}
		else {					/*  ``if( f_out == NULL )'' */
			if (*f_out == 0) {
				for (i = 0; i < pr_buf_len;) {
					if ((num & 0xf) == 0)
						printf("%04X: ", h_num++);
					printf((++num & 0xf) == 0 ? "%02X\n" : "%02X ",
						   (uchar)(pr_buf[i++]));
				}
			}
			else {
				fwrite(pr_buf, pr_buf_len, 1, fp_out);
			}
		}
		trans_byte += pr_buf_len;
		pr_buf_len = 0;
	}
}

void pr_putnc(char *str, int num)
	/*  n 文字をプリンターに出力する  */

{
	while (num--) {
		pr_buf[pr_buf_len] = *str++;
		if (++pr_buf_len >= PR_BUF_SIZE)
			pr_flush();
	}
}

void pr_puts(char *str)
	/*  文字列をプリンターに出力する  */
{
	while (*str != 0) {
		pr_buf[pr_buf_len] = *str++;
		if (++pr_buf_len >= PR_BUF_SIZE)
			pr_flush();
	}
}

void pr_putsn(char *str, int num)
	/* 文字列に数を代入してプリンタに出力する */
{
	sprintf(str_exp, str, num);
	pr_puts(str_exp);
}

static void pr_numb(char *str, uint num0)
	/* フォーマットつき出力 */
{
	int f_mode, mode, len, pos, num, flag;

	do {
		num = num0;
		len = (uchar)(*str++);
		flag = (uchar)(*str++);
		pos = (uchar)(*str++);
		f_mode = (uchar)(*str++);

		if (f_mode) {
			num <<= (flag & 3);
			if (f_mode & TOTAL_BYTE)
				num *= vert_byte_unit;
			if (f_mode & MULT_CONST)
				num *= mult_const;

			switch (mode = f_mode & 0x7) {

			  case (BINARY_LTOH):
				  str[pos] = num & 0xff;
				  str[pos + 1] = num >> 8;
				  break;

			  case (BINARY_HTOL):
				  str[pos] = num >> 8;
				  str[pos + 1] = num & 0xff;
				  break;

			  case (DECIMAL_3):
			  case (DECIMAL_4):
			  case (DECIMAL_5):
				  sprintf(str_exp, "%05u", num);
				  mode = DECIMAL_5 - mode;
				  while (mode < 5)
					  str[pos++] = str_exp[mode++];
				  if (f_mode & ISO_NUMBER)
					  str_exp[pos - 1] += 0x10;
				  break;

			  case (DECIMAL_V):
				  sprintf(str_exp, str, num);
				  mode = strlen(str_exp);
				  if (f_mode & ISO_NUMBER)
					  str_exp[mode - 1] += 0x10;
				  pr_puts(str_exp);
				  goto cont;

			  default:
				  break;
			}
		}
		pr_putnc(str, len);
cont:	str += len;
	} while (flag & 0x80);
}

void get_sheet(void)
{
	int i;

	pr_numb(dotg[dviout_dimension.prt_type], 1);
	for (i = vert_bit_unit; i > 0; i--) {
		if (*bit_row_header)
			pr_numb(bit_row_header, 1);
		pr_putc(0);
	}
	pr_numb(after_bit_image, 1);
	pr_putc(0x0d);
}

#ifdef LBP
/* PRT_CODE がデフォルトと異なる LBP 等のコード初期化 */
/* change_prt_ctl → get_prt_ctl の順で実行される。 */
void set_LBP_prt_ctl(int hunit, int vunit)
{
#ifdef	LIPS3
	if (dviout_dimension.prt_type == LBP_LIPS3) {
		line_feed = "\x01\x00\x00\x00\x85";
		lbpf = &lips3_func;
#ifdef ESCPAGE
		goto to_common;
#else
		vert_bit_unit = 8;
		hwmax = 500;
		hwmin = hunit; 	/*  minimal unit */
		vert_bit_unit *= vunit;
#endif
		}
#endif
#ifdef ESCPAGE
	if (dviout_dimension.prt_type == LBP_ESCPAGE) {
		f_lbp_comp = FALSE;
		lbpf = &escpage_func;
to_common:
		vert_bit_unit = 8;
		hwmax = 500;
		hwmin = hunit; 	/*  minimal unit */
		vert_bit_unit *= vunit;
	}
#endif
}
#endif	/* LBP */

#define hex_to_dig(c)   ((c>='0'&&c<='9')?(c-'0'):(toupper(c)-('A'-10)))

void device_init(DIMENSION *dim)
	/* プリンターの初期化 */
{
	char file[MAXPATH + 4], *srcpath;
	int ch, i, num;
	FILE *fptr;

	ENTER("device_init");

	vert_byte_unit = vert_bit_unit/8;
#ifdef	FAX
	if (f_fax && f_files < 0) set_FAX_ctl();
#endif
	if (f_out != NULL && *f_out != 0){
		if (f_files >= 0) fclose(fp_out);
		else{
			i = 0;
			f_files = strlen(f_out);
			while (f_files-- >= 0){
				if (f_out[f_files] == '!'){
					if (++i == 3) break;
				}
				else i = 0;
			}
		}
		if (f_files >= 0){
			i = f_out[f_files+3];
			sprintf(f_out + f_files, "%03d", Page+1);
			f_out[f_files+3] = i;
		}
		if ((fp_out = fopen(f_out, "wb")) == NULL)
			error(PROGRAM_STOP, "Can't open \"%s\"", f_out);
	}

#ifdef LBP
	if (f_lbp_prt)
		(*(lbpf->init)) ();
#endif

#ifdef JDWN
	if (f_gaiji) goto prf;
#endif

	pr_numb(dot_cr[dviout_dimension.prt_type], 0);

	if (f_top_margin)
		get_sheet();
prf:
	if (*prt_initialize == 0)
		goto skp;
	if (strpbrk(prt_initialize, "/\\:") != NULL) {	/* check path prefixed */
		strncpy(file, prt_initialize, MAXPATH);
	}
	else {
		{
			char *d = file;
			char far *s = cfg_path;
			int n = MAXPATH;

			while ((*d++ = *s++) != 0 && n-- > 0);
		}
		/* ↑ strncpy((char far *)file, cfg_path, MAXPATH); */
		strcat(file, prt_initialize);
		if (access(file, 0) == -1 &&
			(srcpath = searchpath(prt_initialize)) != NULL) {
			strncpy(file, srcpath, MAXPATH);
		}
	}
	if ((fptr = fopen(file, "rb")) == NULL)
		error(PROGRAM_STOP, "Can't open %s", file);
	while ((ch = getc(fptr)) != EOF) {
		if (ch != '\\') {
			if (ch > ' ')
				pr_putc(ch);
		}
		else {
			if ((i = getc(fptr)) == '\\') {
				while ((ch = getc(fptr)) != 0x0a
					   && ch != 0x0d & ch != EOF);
				continue;
			};

			switch (i){
				case 'w':
				case 'W':
						num = dim->output_width;
pnum0:					if (i < 'a') num /= 8;
pnum:					ch = getc(fptr);

						switch (ch){
							case 'b':
								pr_putc(num & 0xff);
								pr_putc(num >> 8);
								break;

							case 'B':
								pr_putc(num >> 8);
								pr_putc(num & 0xff);
								break;

							case 'H':
								pr_putsn("%04X", num);
								break;

							default:
								ungetc(ch, fptr);
								pr_putsn("%d", num);
								break;
						}
						break;

				case 'h':
				case 'H':
						num = dim->output_height;
						goto pnum0;

				case 'r':
						num = dim->dpi;
						goto pnum;

				case 'R':
						num = dim->DPI;
						goto pnum;

				case 'p':
						num = Page+1;
						goto pnum;

				case 'f':
						pr_puts(dvifile.file_name);
						break;

				default:
					i = hex_to_dig(i);
					ch = getc(fptr);
					pr_putc(i * 16 + hex_to_dig(ch));
			}
		}
	}
	fclose(fptr);
skp:
#ifdef LBP
	if (f_lbp_prt
#ifdef JDWN
		&& !f_gaiji
#endif
	)
		(*(lbpf->init2)) (f_rotate, f_copy);	/****/
#endif

#ifndef	PC9801					/* J-3100, AX, DOS/V */
	if (pr_port_str != NULL) {
		pr_port_num = (int)(pr_port_str[0]) - (int)'0';
		if ((0 < pr_port_num) && (pr_port_num < 5)) {
			if ((pr_ioport = peek(0x40, 6 + pr_port_num * 2)) == 0) {
				fprintf(stderr, "\nLPT%d: is Not Assigend!\n", pr_port_num);
				exit(1);
			}
		}
		else if (pr_port_num != 0) {
			fprintf(stderr, 
				"\nIllegal Printer Port Number: %d (see -P option)\n",
				pr_port_num);
			exit(1);
		}
		if (pr_port_str[1] == ';')
			if ((pr_port_wait = atoi(pr_port_str + 2)) < 0)
				pr_port_wait = 0;
	}
#endif
	END();
}

void pr_crlf(void)
{
	/* 行送り */
	while (num_cr) {
		num_cr--;
		pr_numb(line_feed, 0);
	}
}

void pr_new_page(void)
	/* ページ送り */
{
	union REGS regs;

	Page++;
	if (ff_flg != -1) {
		/* LBP Printer 袋綴じ対応 */
#ifdef LBP
		if (f_lbp_prt) {
			if (Page & 1) {
				(*(lbpf->move_homepoint)) (0, ff_flg);	/* 論理原点の設定 */
				num_cr = 0;
			}
			else {
				pr_numb(form_feed, 0);
				(*(lbpf->move_homepoint)) (0, 0);	/****/
				num_cr = 0;
			}
		}
		else
#endif
		{
			if ((ff_flg|(Page & 1)) == 0) goto ff;
			pr_crlf();
		}
	}
	else {
ff:		pr_numb(form_feed, 0);
		num_cr = 0;
	}
	pr_flush();
	if (f_files >= 0 && Page < 999) device_init(&dviout_dimension);
#ifdef	LBP
	if (f_lbp_prt)
		(*(lbpf->move_cap)) (-1, -1);	/* CAPの初期化*/
#endif
	if (f_pause) {
		fprintf(stderr, "\nPush any key to continue (or ESC -> abort).\n");
#ifdef	PC9801
		while(kbhit())
			getch();
		if (getch() == ESC)
			exit(1);
#else /* J3100, AX, DOSV */
		regs.h.ah = 0;
		int86( 0x16, &regs, &regs );
		if ((regs.h.al & 0xFF) == ESC)
/*		if ((bisokey(0) & 0xFF) == ESC)	*/
			exit(1);
#endif
		fprintf(stderr, "\x1b[1A\x1b[0K");
	}
	if (f_top_margin)
		get_sheet();
}

void device_end(void)
	/* プリンターの後始末 */
{
	ENTER("device_end");

	pr_numb(nrm_cr[dviout_dimension.prt_type], 0);
	pr_flush();
#ifdef LBP
	if (f_lbp_prt)
		(*(lbpf->end)) ();		/****/
#endif
	printf("\nSent %lu bytes.\n", trans_byte);
	END();
}

/* static int rev( int data ) */
/*  ビットの重みを反転させるルーチン　*/
 /*  MSB ... LSB  ->  LSB ... MSB      *//*
    See conv_bit.asm
{
    int i, j, tmp;

    tmp = 0;
    for( i = 1, j=128 ; j > 0 ; i +=i, j = j>>1 )
        if( data & i )
            tmp |= j;

    return( tmp );

}
*/

/*　　　＃＃＃　プリンター出力ルーチン　＃＃＃
 *
 *  p_out( int hmax, int vmax, HSTR top )
 *
 *　　このルーチンを使用する前にプリンターの初期化を要する。
 *
 *   （注意）実際にプリンターに出力するのはvmaxのうち
 *  　　　　３で割り切れる最大値だけである
 */

static void p_out(int h_max, int v_max, HUGE_BUF *top)
	/*　ここでのｈｍａｘ，ｖｍａｘはバイト単位。      */
	/*ｔｏｐはビットマップの先頭を示すポインタ。      */
	/*ビットマップを８ビットのブロックに分けて、*/
	/*ブロックごとにプリンターに出力する            　*/

{

	HUGE_BUF *ptr;
	HUGE_BUF *ptr_z;
	BUFFER *ptr_f;
	BUFFER *tmpbuf;
	uint x, y, hmin, hmax, htmax, vmax, hsend;	/* ともにバイト単位 */
	uint skip, exist;
	int i, j, f_nul, type, f_huge, f_non_move;
	uint byte;
#ifdef	LBP
	uint old_byte, repeat, total, bsize;
	uint f_comp;
#endif

#ifdef	FAX
	if (f_fax){
		fax_encode_block(h_max, v_max, top, 0);
		return;
	}
#endif
	if ((long)h_max * (long)(vert_bit_unit + 1) < 0xffe0L)
		f_huge = 0;
	else
		f_huge = 1;
	vmax = v_max;
	if ((type = (prt_ctl[dviout_dimension.prt_type] & TYPE_BIT))
		== LEFT_IS_LOW)
		conv_bit(((ulong)(vmax)) * h_max * 8, top);
	f_non_move = prt_ctl[dviout_dimension.prt_type] & NON_MOVING;
	for (y = 0; y < vmax; y += vert_byte_unit) {
		ptr_z = top + (ulong)h_max *(y * 8);

		for (hmin = skip = exist = 0; hmin < h_max;) {
			if (h_max > hmin + hwmin)
				hmax = hmin + hwmin;
			else
				hmax = h_max;
			if (*dots[dviout_dimension.prt_type] == 0)
				goto f_exist;
			/* support dots spaces */
			for (f_nul = 0, x = hmin; x < hmax; ++x) {
				if (f_huge) {
					ptr = ptr_z + x;
					for (i = vert_bit_unit; i > 0; --i) {
						if (*ptr == 0)
							ptr += h_max;
						else {
							f_nul = 1;
							goto f_space;
						}
					}
				}
				else {
					ptr_f = (BUFFER *)ptr_z + x;
					for (i = vert_bit_unit; i > 0; --i) {
						if (*ptr_f == 0)
							ptr_f += h_max;
						else {
							f_nul = 1;
							goto f_space;
						}
					}
				}
			}

f_space:		if (f_nul == 0) {
				/* spaces */
				skip += hwmin;	/* store spaces */
				if (exist != 0)
					goto prt_out;
				goto next_block;
			}
			/* not spaces */
			pr_crlf();
			while (skip != 0) {
				hsend = (skip > hwmax) ? hwmax : skip;
				pr_numb(dots[dviout_dimension.prt_type], hsend);
				skip -= hsend;
			}

f_exist:	exist += (hmax - hmin);
			if (hmax != h_max)
				goto next_block;/* not EOL */
			else
				hmin = hmax;

prt_out:	pr_crlf();
			hmin -= exist;
			if (f_non_move)
				skip += exist;

			while (exist != 0) {
				hsend = (uint)((exist > hwmax) ? hwmax : exist);
				htmax = hmin + hsend;
#ifdef	LBP
				f_comp = f_lbp_comp;
				if (!f_comp)
#endif
non_comp:			pr_numb(dotg[dviout_dimension.prt_type], hsend);
				if (type & FLAG_LBP) {
					ptr = ptr_z + hmin;
#ifdef	LBP
					if (f_comp){
						old_byte = 256;
						total = repeat = 0;
						bsize = vert_bit_unit*hsend;
						if((tmpbuf = get_work(bsize)) == NULL)
							error(NO_MEMORY, "Working");
						if(bsize >= 16) bsize -= 16;
					}
#endif
					for (i = vert_bit_unit; i > 0; ptr += h_max, i--) {
						if (*bit_row_header)
							pr_numb(bit_row_header, hsend);
						for (ptr_f = (BUFFER *)ptr, j = hsend; j > 0; j--) {
							byte = *ptr_f++;
#ifdef	LBP
							if (f_comp){
								if (byte != old_byte){
									old_byte = byte;
									if(repeat != 0){
										tmpbuf[total++] = repeat - 1;
										repeat = 0;
									}
								}else{
									if (++repeat > 255){
										tmpbuf[total++] = repeat - 1;
										repeat = 0;
										old_byte = 256;
										continue;
									}
									else if (repeat > 1) continue;
								}
								tmpbuf[total++] = byte;
								if (total > bsize){
									f_comp = 0;
									goto non_comp;
								}
								continue;
							}
#endif
							if (f_hex_mode)	pr_putsn("%02X", byte);
							else			pr_putc(byte);
						}
					}
#ifdef	LBP
					if (f_comp){
						if (repeat != 0) tmpbuf[total++] = repeat - 1;
						pr_flush();
						sprintf(pr_buf,
							"\x9b%u;%u;%u;9;%u.r",
							total, hsend, lbp_orig_dpi, vert_bit_unit);
						pr_buf_len = strlen(pr_buf);
						while(total--) pr_putc(*tmpbuf++);
					}
#endif
				}
				else {
					for (x = hmin; x < htmax; ++x) {
						ptr = ptr_z + x;
						if (f_hex_mode) {
							p_subhex(h_max, ptr, vert_byte_unit, type);
						} else {
							if ((type & TOP_IS_LOW) == 0)
								p_sub(h_max, ptr, vert_byte_unit);
							else
								p_subnec(h_max, ptr, vert_byte_unit);
						}
					}
				}

				if (*after_bit_image)
					pr_numb(after_bit_image, hsend);
				exist -= hsend;
				hmin = htmax;
			}
next_block:	hmin = hmax;
		}
		num_cr++;
	}
}

static void pl_out(int h_max, int v_max, HUGE_BUF *top)
	/*　ここでのｈｍａｘ，ｖｍａｘはバイト単位。      */
	/*ｔｏｐはビットマップの先頭を示すポインタ。      */
	/*ビットマップを８ビットのブロックに分けて、*/
	/*ブロックごとにプリンターに出力する            　*/

{

	HUGE_BUF *ptr;
	HUGE_BUF *ptr_z;
	uint x, y, ymax, vmin, vtmax, vmax, hsend;	/* ともにバイト単位 */
	uint skip, exist;
	uchar byte;
	int i, j, f_nul, type, f_non_move;

#ifdef	FAX
	if (f_fax){
		fax_encode_block(v_max, h_max, top, 1);
		return;
	}
#endif
	if ((type = (prt_ctl[dviout_dimension.prt_type] & TYPE_BIT))
		== TOP_IS_LOW)
		conv_bit(((ulong)(h_max)) * v_max * 8, top);
	f_non_move = prt_ctl[dviout_dimension.prt_type] & NON_MOVING;

	for (x = 0; x < h_max; x += vert_byte_unit) {
		ptr_z = top + (ulong)(v_max * 8 - 1) * h_max + (ulong)x;

		for (vmin = skip = exist = 0; vmin < v_max;) {
			if (v_max > vmin + hwmin)
				vmax = vmin + hwmin;
			else
				vmax = v_max;

			if (*dots[dviout_dimension.prt_type] == 0)
				goto f_exist;
			/* support dots spaces */
			for (f_nul = 0, y = vmin << 3, ymax = vmax << 3; y < ymax; y++) {
				ptr = ptr_z - (ulong)y *h_max;

				for (i = vert_byte_unit; i > 0; --i) {
					if (*ptr == 0)
						ptr++;
					else {
						f_nul = 1;
						goto f_space;
					}
				}
			}

f_space:	if (f_nul == 0) {
				/* spaces */
				skip += hwmin;	/* store spaces */
				if (exist != 0)
					goto prt_out;
				goto next_block;
			}
			/* not spaces */
			pr_crlf();
			while (skip != 0) {
				hsend = (skip > hwmax) ? hwmax : skip;
				pr_numb(dots[dviout_dimension.prt_type], hsend);
				skip -= hsend;
			}

f_exist:	exist += (vmax - vmin);
			if (vmax != v_max)
				goto next_block;/* not EOL */
			else
				vmin = vmax;

prt_out:	pr_crlf();
			vmin -= exist;
			if (f_non_move)
				skip += exist;
			while (exist != 0) {
				hsend = (uint)((exist > hwmax) ? hwmax : exist);
				vtmax = vmin + hsend;
				pr_numb(dotg[dviout_dimension.prt_type], hsend);
				ptr = ptr_z - (ulong)vmin *8 * h_max;

				if (type & FLAG_LBP) {
					for (i = vert_byte_unit; i > 0; ptr++, i--) {
						for (j = 7; j >= 0; j--) {
							if (*bit_row_header)
								pr_numb(bit_row_header, hsend);
							p_sublbp(h_max, ptr, j, hsend, type);
						}
					}
				}
				else {
					/* debugged by 佐々木浩氏, size optimized by OkI */
					for (y = 0, ymax = hsend << 3; y < ymax; y++, ptr -= h_max)
						for (i = 0; i < vert_byte_unit; i++) {
							byte = (i < (h_max - x)) ? *(ptr + (ulong)i) : 0;
							if (f_hex_mode) {
								if (pr_buf_len >= PR_BUF_SIZE - 4)
									pr_flush();
								sprintf(pr_buf+pr_buf_len, "%02X", (uint)byte);
								pr_buf_len +=2;
							} else {
								pr_buf[pr_buf_len] = byte;
								if (++pr_buf_len >= PR_BUF_SIZE)
								pr_flush();
							}
					}
				}
				if (*after_bit_image)
					pr_numb(after_bit_image, hsend);
				exist -= hsend;
				vmin = vtmax;
			}
next_block:	vmin = vmax;
		}
		num_cr++;
	}
}

void device_clear(OUTPUT_INFO *out)
{
#ifdef	LBP
	if (f_lbp_prt) {
		num_cr = 0;
		(*(lbpf->move_cap)) (
								(out->split > 1) ? (out->split - 1) * dviout_dimension.buf_height : 0, 0);
		(*(lbpf->move_cap)) (-1, -1);	/*CAPの初期化*/
	}
#endif
}

BOOL device_out(OUTPUT_INFO *out)
	/* プリンターに出力 */
{
	ENTER("device_out");
#ifdef LBP
	last_dir = -1;				/* ラスタイメージ送出 */
#endif

	if (f_out != NULL && *f_out == 0)
		printf("\n");
	if (out->print_direction == VERTICAL)
		pl_out(out->byte_width, out->byte_height, out->bitmap_ptr);
	else
		p_out(out->byte_width, out->byte_height, out->bitmap_ptr);
	pr_flush();
#ifdef	LBP
	if (f_lbp_prt)
		(*(lbpf->move_cap)) (-1, -1);	/*CAPの初期化*/
#endif
	RETURN(NextPart);
}

void p_subhex(int hmax, HUGE_BUF *ptr, int length, int type)
	/* p_sub, p_subnec の hex mode 版 */
{
	HUGE_BUF *ptr_tmp;
	int i, j, k;
	uchar byte, mask;
	mask = 0x80;
	for (i = 0; i < 8;i++) {
		ptr_tmp = ptr;
		for (j = 0; j < length; j++) {
			byte = 0;
			if (type == TOP_IS_HIGH) {
				for (k = 0; k < 8; k++) {
					byte <<= 1;
					if (*ptr_tmp & mask)
						byte |= 0x01;
					ptr_tmp += hmax;
				}
			} else { /* TOP_IS_LOW */
				for (k = 0; k < 8; k++) {
					byte >>= 1;
					if (*ptr_tmp & mask)
						byte |= 0x80;
					ptr_tmp += hmax;
				}
			}
			if (pr_buf_len >= PR_BUF_SIZE - 4)
				pr_flush();
			sprintf(pr_buf+pr_buf_len, "%02X", (uint)byte);
			pr_buf_len +=2;
		}
		mask >>= 1;
	}
}

void p_sublbp(int hmax, HUGE_BUF *ptr, int bitno, int length, int type)
	/* 垂直方向の１ラインをプリンタに出力 */
	/* 従来の get_column_byte() を拡張 */
{
	int i;
	uchar byte, mask;
#ifdef	FAX
	int pos = 0;
#endif
	mask = 0x01 << bitno;
	while (length--) {
		byte = 0;
		if (type == LEFT_IS_HIGH) {
			for (i = 0; i < 8; i++) {
				byte <<= 1;
				if (*ptr & mask)
					byte |= 0x01;
				ptr -= hmax;
			}
		} else { /* LEFT_IS_LOW */
			for (i = 0; i < 8; i++) {
				byte >>= 1;
				if (*ptr & mask)
					byte |= 0x80;
				ptr -= hmax;
			}
		}
#ifdef	FAX
		if (f_fax)
			tmp_buf[pos++] = byte;
		else
#endif
		if (f_hex_mode) {
			if (pr_buf_len >= PR_BUF_SIZE - 4)
				pr_flush();
			sprintf(pr_buf+pr_buf_len, "%02X", (uint)byte);
			pr_buf_len +=2;
		} else {
			pr_buf[pr_buf_len] = byte;
			if (++pr_buf_len >= PR_BUF_SIZE)
				pr_flush();
		}
	}
}

/* end of file : p_out.c */

#ifdef LBP
#include "lbp.c"
#endif

#ifdef	FAX
#include "p_fax.c"
#endif
