/*

8086|Printman/TEXT 'Athena' Version 1.00a
 With 8086|Printman Library Version 1.20

Copyright (c) 1995 Delmonta

コンパイル時の注意:
	(1)8086|Printman Library Ver1.20以上を必ずリンクすること
	(2)クォートで括った文字列を分解せずにmainに渡す
	   スタートアップルーチンを必ずリンクすること

参考:
	(1)浮動小数点演算は使っていない。printfなどは小数に対応していない
	   サブセット版をリンクしてもよい。
*/

/*------------------------ヘッダのインクルード-------------------------------*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#include<jctype.h>
#include<jstring.h>

#include<DEL_PRN.H>
/*---------------------------------マクロ関数--------------------------------*/
#ifdef	DEBUG
	#define	DEBUGPRINT(f)	printf f
#else
	#define	DEBUGPRINT(f)
#endif

#define	BYTEWIDTH(c)	(1+((c)>=256))		/* cが全角なら2、半角なら1  */
#define	CEIL(a,b)	(((a)+(b)-1)/(b))	/* aをbで割り端数を切り上げ */
#ifndef	DOT2MM
	#define	DOT2MM(n)	((n)*127L/900)	/* ドット数からmmへの換算   */
#endif
/*----------------------------------型と定数---------------------------------*/
typedef	unsigned short	ushort;

#define	LINE_MAXCHARNUM	256		/* 1行の最大の文字数          */
#define	HEADER_YSPACE	36		/* ヘッダの後にあけるドット数 */
#define	FOOTER_YSPACE	36		/* フッタの後にあけるドット数 */

static	const	int	Papersize[2][8] =
{
	{1189, 841,594,420,297,210,148,105},	/* A0〜A6 */
	{1456,1030,728,515,364,257,182,128},	/* B0〜B6 */
};

static	const	char	Lastillegal_tbl[] = 
	"‘“（〔［｛〈《「『【＜`([{｢<";
static	const	char	Firstillegal_tbl[] =
	"゛゜、。，．・：；？！’”）〕］｝〉》」』】＞ﾞﾟ､｡,.･;:?!')]}｣>/"
	"ぁぃぅぇぉゃゅょっァィゥェォャュョッゎヮヵヶｧｨｩｪｫｬｭｮｯ";
/*--------------------------------変数---------------------------------------*/
static	char	*Pathname;	/* 現在出力中のファイルのフルパス*/
static	char	*Filename;	/* 同           パス名を除く部分 */
static	int	Pagenum;	/* ページ番号                    */
static	int	Linenum;	/* 現在Linebufにたまっている行数 */

extern	enum PRNBIOS	Prn_machine = PRNBIOS_TOWNS;	/*     (-C) */
extern	enum PRNMODE	Prn_mode = PRNMODE_ESCP;	/*     (-P) */

static	struct	/* コマンドラインから受け取ったオプション ; 単位は基本的にmm */
{
	struct	{int x,y;}	papersize;	/* 用紙サイズ  (-S) */
	struct	{int u,l;}	pmargin;	/* 物理マージン(-L) */
	struct	{int u,l,r,d;}	lmargin;	/* 論理マージン(-M) */
	char			*header;	/* ヘッダ      (-H) */
	char			*footer;	/* フッタ      (-F) */
	struct	{int num,space;}block;		/* 段組        (-K) */
	struct	{int x,y;}	charnum;	/* 段の字数    (-N) */
	struct	{int s,e;int n;}area;		/* 出力する範囲(-A) */

	int			tabstop;	/* タブ幅      (-T) */
	int			istategaki;	/* 縦書きの指定(-W) */

	int			tostdout;	/* 標準出力へ (-P!) */
} Options = {{182,257},{9,3},{20,10,10,20},NULL,NULL,{1,10},{80,40},{1,9999,0},
						8,0, 0};

static	struct	/* 変換した書式パラメータ ; 単位は全てドット＝1/180インチ */
{
	struct	{int x,y;}	blocksize;	/* 段の大きさ               */
	int			xwidth;		/* 印刷可能領域の横幅       */
	struct	{int u,l;}	margin;		/* 物理ﾏｰｼﾞﾝを差引いたﾏｰｼﾞﾝ */
	int			blockspace;	/* 段間スペース             */
} Format;

static	struct	LINEBUF
{
	int	charnum;
	int	bytenum;
	ushort	*buf;
} *Linebuf;
/*****************************************************************************/
/*                  8086|Printman Libraryに直接関係する部分                  */
/*****************************************************************************/
/*-------------------------------エラー処理----------------------------------*/
extern	enum PRNHERR	prn_harderr(void)
{
	char	c;

	fputs("\nプリンタエラーです.\n中止<A>, 再試行<R>, 無視<I>?",stderr);
rep:
	c = getch();
	switch(c)
	{
	case 'A':
	case 'a':
		fputs("A\n",stderr);
		exit(1);
	case 'R':
	case 'r':
		fputs("R\n",stderr);
		return PRNHERR_RETRY;
	case 'I':
	case 'i':
		fputs("I\n",stderr);
		return PRNHERR_IGNORE;
	default:
		putc('\a',stderr);
		goto rep;
	}
}
/*--------------------------------改行処理-----------------------------------*/
/* prn_linefeed()＋MSXプリンタでは一度に99/180インチまでしか改行できない     */
/* (ESC/Pは255/180、PC-PRでは99/120)ので、それより大きい場合は複数回に分割し */
/* て処理する                                                                */
/*---------------------------------------------------------------------------*/
static	void	linefeed(int n)
{
	while	(n>=64)
	{
		prn_linefeed(64);
		n -= 64;
	}

	prn_linefeed(n);
}
/*****************************************************************************/
/*                      文字列処理・ファイル入力                             */
/*****************************************************************************/
/*----------------------------ファイル入力-----------------------------------*/
static	int	Ungetc_num = 0;
static	ushort	Ungetc_buf[LINE_MAXCHARNUM+1];	/* xungetc()用のバッファ */
static	FILE	*Fp;

ushort	xgetc(void)
{
	ushort	c;

	if	(Ungetc_num)
		return Ungetc_buf[--Ungetc_num];

	c = getc(Fp);
	if	(iskanji(c))
		return (c<<8) + getc(Fp);
	else
		return c;
}

void	xungetc(ushort c)
{
	Ungetc_buf[Ungetc_num++] = c;
}
/*----------------------行末禁則文字かどうかの判定---------------------------*/
static	int	islastillegal(ushort c)
{
	char	buf[3];

	if	(BYTEWIDTH(c)==2)
	{
		buf[0] = c>>8;
		buf[1] = c;
		buf[2] = '\0';
	}
	else
	{
		buf[0] = c;
		buf[1] = '\0';
	}

	return jstrstr(Lastillegal_tbl,buf)!=NULL;
}
/*----------------------行頭禁則文字かどうかの判定---------------------------*/
static	int	isfirstillegal(ushort c)
{
	char	buf[3];

	if	(BYTEWIDTH(c)==2)
	{
		buf[0] = c>>8;
		buf[1] = c;
		buf[2] = '\0';
	}
	else
	{
		buf[0] = c;
		buf[1] = '\0';
	}

	return jstrstr(Firstillegal_tbl,buf)!=NULL;
}
/*****************************************************************************/
/*                             プリンタ出力                                  */
/*****************************************************************************/
/*----------------------パラメータを表示して確認をとる-----------------------*/
/* 一部に、一度ドット数に換算してからさらにもう一度mmに直しているために二重の*/
/* 誤差が発生している部分がある                                              */
/*---------------------------------------------------------------------------*/
static	void	showoption(void)
{

	fputs("機種設定     : ",stderr);
	if	(Options.tostdout)	fputs("標準出力へ出力\n",stderr);
	else
	{
		switch	(Prn_machine)
		{
		case PRNBIOS_NEC98:	fputs("PC-9800"  ,stderr); break;
		case PRNBIOS_TOWNS:	fputs("FMR/TOWNS",stderr); break;
		case PRNBIOS_IBMPC:	fputs("DOS/V"    ,stderr); break;
		case PRNBIOS_J3100:	fputs("J-3100/AX",stderr); break;
		}

		fprintf(stderr," ＋ ");

		switch	(Prn_mode)
		{
		case PRNMODE_ESCP:	fputs("ESC/P-J84",stderr); break;
		case PRNMODE_MSX:	fputs("MSX"      ,stderr); break;
		case PRNMODE_PCPR:	fputs("PC-PR201H",stderr); break;
		case PRNMODE_FMPR:	fputs("FMPR"     ,stderr); break;
		}

		putc('\n',stderr);
	}

	fprintf(stderr,"\n印刷するﾌｧｲﾙ : %s\n",Pathname);

	fprintf(stderr, "物理マージン : 上%dmm  左%dmm\n"
			"\n"
			"用紙サイズ   : 横%dmm×縦%dmm\n"
			"論理マージン : 上%dmm  左%dmm  右%dmm 下%dmm\n"

			"印字方向     : %s書き\n"
			"出力する範囲 : %d〜%dページ(ページ番号 %+d)\n"

			"1字の幅      : 全角あたり  横%d×縦%dドット\n"
			"字詰め       : %d字×%d行\n"
			"段組         : %d段(段幅%dmm",

		Options.pmargin  .u, Options.pmargin  .l,
		Options.papersize.x, Options.papersize.y,
		Options.lmargin  .u, Options.lmargin  .l,
		Options.lmargin  .r, Options.lmargin  .d,

		(Options.istategaki ? "縦" : "横" ),
		Options.area.s,Options.area.e,Options.area.n,

		Format.blocksize.x*2/Options.charnum.x,
		Format.blocksize.y  /Options.charnum.y,

		Options.charnum.x, Options.charnum.y,
		Options.block.num, (int)DOT2MM(Format.blocksize.x));

	if	(Options.block.num>=2)
		fprintf(stderr," 段間%dmm",Options.block.space);

	fputs(")\n\nヘッダ       : ",stderr);
	if	(Options.header==NULL)
		fputs("(なし)",stderr);
	else
		fprintf(stderr,"%s",Options.header);

	fputs("\nフッタ       : ",stderr);
	if	(Options.footer==NULL)
		fputs("(なし)",stderr);
	else
		fprintf(stderr,"%s",Options.footer);

	fputs("\n\nこれでよろしいですか(y/n)",stderr);

	while	(1)
	{
		switch	(getch())
		{
		case 'Y':
		case 'y':
		case '\r':
		case '\n':
			fputs("Y\n",stderr);
			return;
		case 'N':
		case 'n':
		case '\3':
			fputs("N\n",stderr);
			exit(1);
		default:
			putc('\a',stderr);
		}
	}
}
/*--------------------------------初期化-------------------------------------*/
static	void	init(char *filename)
{
	Pathname = filename;	/* strdupは不要 */

	Filename = jstrrchr(Pathname,'\\');
	if	(Filename==NULL)
	{
		if	(Pathname[1]==':')
			Filename = Pathname+2;
		else
			Filename = Pathname;
	}
	else
		Filename++;

	Fp = fopen(filename,"r");
	if	(Fp==NULL)
	{
		fprintf(stderr,"ファイル %s がオープンできません\n",filename);
		exit(1);
	}

	Pagenum = 1;

	Linebuf = (struct LINEBUF *)malloc
		(sizeof(struct LINEBUF)*Options.charnum.y*Options.block.num);
	if	(Linebuf==NULL)
	{
		fprintf(stderr,"メモリ不足です\n");
		exit(1);
	}

	if	(Options.tostdout)
	{
		Format.xwidth =   Options.charnum.x*Options.block.num
				+ Options.block.space*(Options.block.num-1);
		return;
	}
	else	/* !Options.tostdout */
	{
		if	(Options.pmargin.u>Options.lmargin.u ||
			 Options.pmargin.l>Options.lmargin.l)
		{
			fprintf(stderr,"論理マージンが物理マージンより小さくなっています.\n");
			exit(1);
		}

		Format.blocksize.x = MM2DOTS(Options.papersize.x
			- Options.lmargin.l - Options.lmargin.r
			- (Options.block.num-1)*Options.block.space)
			/ Options.block.num;

		Format.blocksize.y = MM2DOTS(Options.papersize.y
			- Options.lmargin.u - Options.lmargin.d );

		if	(Format.blocksize.x<=0 || Format.blocksize.y<=0)
		{
			fprintf(stderr,"マージンが多すぎて印刷可能範囲がありません.\n");
			exit(1);
		}

		Format.xwidth = MM2DOTS(Options.papersize.x
			- Options.lmargin.l - Options.lmargin.r);

		Format.blockspace=MM2DOTS(Options.block.space);
		Format.margin.u  =MM2DOTS(Options.lmargin.u-Options.pmargin.u);
		Format.margin.l  =MM2DOTS(Options.lmargin.l-Options.pmargin.l);

		if	(Options.header!=NULL)
			Format.blocksize.y -= HEADER_YSPACE+24;
		if	(Options.footer!=NULL)
			Format.blocksize.y -= FOOTER_YSPACE+24;

		showoption();
	}
}
/*--------------------------ヘッダの特殊コードの処理-------------------------*/
/* 返した内容は次に呼び出すまで有効。返した領域を自由に書き換えてもよい      */
/*---------------------------------------------------------------------------*/
static	char	*compile_header(char *header)
{
	static	char	buf[256];
	char		*p = buf;
	char		c;

	while	((c=*header++)!='\0')
	{
		if	(c!='%')
		{
			*p++ = c;
			if	(iskanji(c))
				*p++ = *header++;

			continue;
		}

		switch	(c=toupper(*header++))
		{
		case 'P': p += sprintf(p,"%s",Pathname);		break;
		case 'F': p += sprintf(p,"%s",Filename);		break;
		case 'N': p += sprintf(p,"%d",Pagenum+Options.area.n);	break;
		default : *p++ = c;
		}
	}
	*p = '\0';
	return buf;
}
/*-----------------------ページの出力(標準出力へ)----------------------------*/
static	void	putpage_to_stdout(void)
{
	int	i,j,k;	/* ループカウンタ          */
	int	line;	/* putpage()のコメント参照 */

	line = CEIL(Linenum,Options.block.num);

	if	(Options.header!=NULL)
	{
		char	*p = compile_header(Options.header);

		printf("%*s%s\n\n",(Format.xwidth-strlen(p))/2,"",p);
	}

	for	(i=0 ; i<line ; i++)
	{
		for	(j=0 ; ; )
		{
			int	n=0;

			struct	LINEBUF	buf;

			if	(j*line+i>=Linenum)
			{
				printf("%*s",Options.charnum.x,"");
				goto next;
			}

			buf = Linebuf[j*line+i];

			for	(k=0 ; k<buf.charnum ; k++)
			{
				n += BYTEWIDTH(buf.buf[k]);

				if	(BYTEWIDTH(buf.buf[k])==2)
					putchar(buf.buf[k]>>8);

				putchar((unsigned char)buf.buf[k]);
			}

			free(buf.buf);

		next:
			if	(++j==Options.block.num)
				break;

			printf("%*s",Options.charnum.x-n+Options.block.space,"");
		}
		putchar('\n');
	}

	for	(i=line ; i<Options.charnum.y ; i++)
		putchar('\n');

	if	(Options.footer!=NULL)
	{
		char	*p = compile_header(Options.footer);

		printf("\n%*s%s\n",(Format.xwidth-strlen(p))/2,"",p);
	}

	for	(i=0 ; i<Options.lmargin.d ; i++)
		putchar('\n');

	Pagenum++;
	Linenum = 0;
}
/*-----------------------ページの出力(プリンタへ)----------------------------*/
static	void	putpage(void)
{
	int	i,j,k;		/* ループカウンタ */
	int	line;		/* 印刷する物理行数;ceil(バッファの行数/段数)*/
	int	feeded = 0;	/* これまでに出力された改行の量 */

	if	(Pagenum<Options.area.s || Pagenum>Options.area.e)
	{
		for	(i=0 ; i<Linenum ; i++)
			free(Linebuf[i].buf);

		goto end_of_output;
	}

	if	(Options.tostdout)
	{
		putpage_to_stdout();
		return;
	}

	while	(kbhit())
		;

	fprintf(stderr,"%dページを印刷します 何かキーを押してください",Pagenum);	if	(getch()=='\3')
	{
		fputs("^C\n",stderr);
		exit(0);
	}
	if	(Pagenum==1 || Pagenum==Options.area.s)
		prn_init();

	if	(Options.istategaki)
		prn_tategaki(PRNTATE_NOHALF);

	fprintf(stderr,"\n%dページを印刷中です\n",Pagenum);

	linefeed(Format.margin.u);
	if	(Options.header!=NULL)
	{
		char	*s = compile_header(Options.header);

		prn_setcarridge(Format.margin.l + 
			(Format.xwidth - strlen(s)*PRN_CHRWIDTH/2)/2);

		prn_putstr(s);
		linefeed(HEADER_YSPACE+24);
	}

	line = CEIL(Linenum,Options.block.num);
	for	(i=0 ; i<line ; i++)
	{
		int	pos;

		for	(j=0,pos=Format.margin.l ; j<Options.block.num ; 
			j++ , pos += Format.blocksize.x+Format.blockspace)
		{
			struct	LINEBUF	buf;
			int		n;	/* 出力されたバイト数 */

			if	(j*line+i>=Linenum)
				continue;

			buf = Linebuf[j*line+i];
			n   = 0;

			for	(k=0 ; k<buf.charnum ; k++)
			{
				prn_setcarridge(pos + (long)n*
					Format.blocksize.x/buf.bytenum);

				prn_putchar(buf.buf[k]);
				n += BYTEWIDTH(buf.buf[k]);
			}

			free(buf.buf);
		}

				/* 丸め誤差が出ないように面倒な計算をする */
				/* longで計算するのはｵｰﾊﾞｰﾌﾛｰ防止のため   */
		j = (i+1L)*(long)Format.blocksize.y/Options.charnum.y;
		linefeed(j-feeded);
		feeded = j;
	}

	linefeed(Format.blocksize.y - feeded);

	if	(Options.footer!=NULL)
	{
		char	*s = compile_header(Options.footer);

		linefeed(FOOTER_YSPACE);

		prn_setcarridge(Format.margin.l + 
			(Format.xwidth - strlen(s)*PRN_CHRWIDTH/2)/2);

		prn_putstr(s);
		linefeed(24);
	}

	prn_tategaki(PRNTATE_NONE);
	prn_formfeed();
end_of_output:
	Pagenum++;
	Linenum = 0;
}
/*----------------------最終ページの出力 & 後始末----------------------------*/
static	void	lastpage(void)
{
	if	(Linenum)
		putpage();
	free(Linebuf);
	fclose(Fp);
}
/*------------------------行単位でバッファに格納-----------------------------*/
static	void	putout(ushort *buf,int charnum,int bytenum,int islf)
{
	static	int	nocheck = 0;	/* その行だけ禁則処理を行わないﾌﾗｸﾞ */

	ushort	c,nextchar;

	if	(islf)
	{
		bytenum = Options.charnum.x;
		goto output_main;
	}

	nextchar = xgetc();
	if	(nextchar=='\n' || nextchar==EOF)
		goto output_main;

	if	(nocheck)
	{
		nocheck = 0;
		xungetc(nextchar);
		goto output_main;
	}

	while	(bytenum>=Options.charnum.x/2)	/* while(bytenum)とすべきか? */
	{
		c = buf[charnum-1];
		if	(islastillegal(c) || isfirstillegal(nextchar))
		{
			charnum--;
			xungetc(nextchar);
			nextchar = c;
			bytenum -= BYTEWIDTH(c);
		}
		else
		{
			xungetc(nextchar);
			goto output_main;
		}
	}

	/* 禁則処理で長さが半分未満になった場合 */
	xungetc(nextchar);
	while	(charnum)
		xungetc(buf[--charnum]);
	nocheck = 1;
	return;

output_main:
	if	(charnum)
	{
		Linebuf[Linenum].buf = (ushort*)malloc(charnum*sizeof(ushort));
		if	(Linebuf[Linenum].buf==NULL)
		{
			fprintf(stderr,"メモリ不足です\n");
			exit(1);
		}

		memcpy(Linebuf[Linenum].buf,buf,charnum*sizeof(ushort));
	}

	Linebuf[Linenum].charnum = charnum;
	Linebuf[Linenum].bytenum = bytenum;

	if	(++Linenum==Options.block.num*Options.charnum.y)
		putpage();
}
/*---------------------プリンタ出力のメインルーチン--------------------------*/
static	void	xmain(char *filename)
{
	int	charnum = 0;	/* 現在入力中の文字列の文字数 */
	int	bytenum = 0;	/* 現在入力中の文字列のバイト数 */

	ushort	buf[LINE_MAXCHARNUM];
	ushort	c;

	init(filename);

	while	((c=xgetc())!=EOF)
	{
		if	(c=='\n')
		{
			putout(buf,charnum,bytenum,1);
			charnum = bytenum = 0;
			continue;
		}
		else if	(c=='\t')
		{
			int	n = bytenum + Options.tabstop - 
					bytenum%Options.tabstop;

			if	(n>Options.charnum.x)
				n = Options.charnum.x;

			for	( ; bytenum<n ; bytenum++)
				buf[charnum++] = ' ';

			if	(n==Options.charnum.x)
			{
				putout(buf,charnum,bytenum,1);
				charnum=bytenum=0;
				continue;
			}
		}
		else if	(BYTEWIDTH(c)==1 && iscntrl(c))
			continue;
		else
		{
			buf[charnum++] = c;
			bytenum += BYTEWIDTH(c);
		}

		if	(bytenum>=Options.charnum.x)
		{
			if	(bytenum>Options.charnum.x)
			{
				xungetc(buf[--charnum]);
				bytenum -= 2;	/* はみ出すのは全角のみ */
			}

			putout(buf,charnum,bytenum,0);
			charnum = bytenum = 0;
		}
	}

	if	(charnum)
		putout(buf,charnum,bytenum,1);

	lastpage();
}
/*****************************************************************************/
/*                              オプションの解析                             */
/*****************************************************************************/
/*--------------------------S(用紙サイズ)オプション--------------------------*/
static	int	option_s(char *s)
{
	int	a,b;

	if	(isdigit(s[0]))
	{
		return sscanf(s,"%u,%u",&Options.papersize.x,
					&Options.papersize.y)==2;
	}

	a = toupper(s[0])-'A';
	if	(a!=0 && a!=1)
		return 0;

	b = s[1]-'0';
	if	(b<0 || 6
<b)
		return 0;

	if	(s[2]=='R' || s[2]=='r' || s[2]=='\0')
	{
		Options.papersize.x = Papersize[a][b+1];
		Options.papersize.y = Papersize[a][b  ];
	}
	else if	(s[2]=='L' || s[2]=='l')
	{
		Options.papersize.x = Papersize[a][b  ];
		Options.papersize.y = Papersize[a][b+1];
	}
	else
		return 0;

	return 1;
}
/*----------------------C(コンピュータの機種)オプション----------------------*/
static	int	option_c(char *s)
{
	if	(*s=='N' || *s=='n' || *s=='9')	Prn_machine=PRNBIOS_NEC98;
	else if	(*s=='F' || *s=='f')		Prn_machine=PRNBIOS_TOWNS;
	else if (*s=='I' || *s=='i')		Prn_machine=PRNBIOS_IBMPC;
	else if	(*s=='J' || *s=='j' || *s=='A' || *s=='a')
						 Prn_machine=PRNBIOS_J3100;
	else
		return 0;

	return 1;
}
/*-------------------------P(プリンタの機種)オプション-----------------------*/
static	int	option_p(char *s)
{
	if	(*s=='!')
	{
		Options.tostdout  = 1;
		Options.lmargin.d = 1;
		return 1;
	}

	Options.tostdout = 0;

	if	(*s=='E' || *s=='e')	Prn_mode=PRNMODE_ESCP;
	else if	(*s=='M' || *s=='m')	Prn_mode=PRNMODE_MSX;
	else if	(*s=='P' || *s=='p')	Prn_mode=PRNMODE_PCPR;
	else if	(*s=='F' || *s=='f')	Prn_mode=PRNMODE_FMPR;
	else
		return 0;

	return 1;
}
/*----------------------------?(ヘルプ)オプション----------------------------*/
/* このオプションの出力は通常のstderrではなくstdoutにする(リダイレクトできる */
/* ように)                                                                   */
/*---------------------------------------------------------------------------*/
static	void	option_help(void)
{
	printf(	"書式 : ATHENA [オプション] ファイル名\n"
		"\n"
		"オプション:\n"
		"     -C{NEC|FM|IBM|J31|AX}           コンピュータの機種\n"
		"     -P{ESCP|MSX|PCPR|FMPR}          プリンタの機種\n"
		"     -S横幅,縦幅/-S{A|B}{3|4|5|6}[L] 用紙サイズ\n"
		"     -L上,左  -M上,左,右,下          物理マージン,論理マージン\n"
		"     -K段数,段間スペース             段組の指定\n"
		"     -N横,縦                         段の字数\n"
		"     -T数値                          タブ幅\n"
		"     -W[{0|1}]                       縦書きの指定\n"
		"     -H\"ヘッダ\",-F\"フッタ\"           ヘッダ,フッタ\n"
		"     -A開始頁,終了頁[,頁番号ｵﾌｾｯﾄ]   印刷する範囲の指定\n"
		"\n"
		"     @ファイル名                     パラメータをファイルで指定\n"
		"\n"
		"オプションでの長さの指定はmm単位です\n");
	exit(0);
}
/*-----------------------オプション解析のメインルーチン----------------------*/
static	void	option(char *s)
{
	switch	(toupper(s[1]))
	{
	case 'C':
		if	(!option_c(s+2))
			goto error;
		break;
	case 'P':
		if	(!option_p(s+2))
			goto error;
		break;
	case 'S':
		if	(!option_s(s+2))
			goto error;
		break;

	case 'L':
		sscanf(s+2,"%d,%d",&Options.pmargin.u,&Options.pmargin.l);
		break;
	case 'M':
		sscanf(s+2,"%u,%u,%u,%u",&Options.lmargin.u,
		    &Options.lmargin.l,&Options.lmargin.r,&Options.lmargin.d);
		break;
	case 'H':
		if	(Options.header!=NULL)
			free(Options.header);

		if	(s[2]=='\0')
			Options.header = NULL;
		else if	((Options.header=strdup(s+2))==NULL)
			goto nomem;
		break;
	case 'F':
		if	(Options.footer!=NULL)
			free(Options.footer);

		if	(s[2]=='\0')
			Options.footer = NULL;
		else if	((Options.footer=strdup(s+2))==NULL)
			goto nomem;
		break;
	case 'K':
		sscanf(s+2,"%u,%u",&Options.block.num,&Options.block.space);
		break;
	case 'N':
		sscanf(s+2,"%u,%u",&Options.charnum.x,&Options.charnum.y);
		break;
	case 'A':
		sscanf(s+2,"%u,%u,%d",&Options.area.s,&Options.area.e,
						&Options.area.n);
		if	(Options.area.s>Options.area.e)
			goto error;

		break;
	case 'T':
		sscanf(s+2,"%u",&Options.tabstop);
		break;
	case 'W':
		if	(s[2]=='1' || s[2]=='\0') /* 単に-Wは-W1とみなす */
			Options.istategaki = 1;
		else
			Options.istategaki = 0;
		break;

	case '?':	option_help();	/* 返ってこないからbreakは要らない */

	default:	goto error;
	}

	return;

error:
	fprintf(stderr,	"不正なオプションです:%s\n\n"
			"ヘルプを見るには ATHENA -?としてください.\n",s);
	exit(1);

nomem:
	fprintf(stderr,"メモリ不足です\n");
	exit(1);
}
/*****************************************************************************/
/*                            スタートアップ                                 */
/*****************************************************************************/
/*------------------------オプションの読みとりに使う変数---------------------*/
static	FILE	*Responsefile = NULL;	/* レスポンスファイル               */
static	char	*Argbuf;		/* 現在読み込んであるバッファの内容 */

static	char	**Argv;			/* コマンドラインのパラメータ       */
/*--------------------行バッファからのトークンの切り出し---------------------*/
/* gettoken()が返した値は、次に呼び出すまで有効。自由に書き換えてよい        */
/*---------------------------------------------------------------------------*/
#define	gettoken_init(s)	(Argbuf=(s))

static	char	*gettoken(void)
{
	int	isinsidequote = 0;
	char	*ret;

	while	(isspace(*Argbuf))
		Argbuf++;

	if	(*Argbuf=='\0')
		return NULL;

	for	(ret=Argbuf ; ; Argbuf++)
	{
		char	c = *Argbuf;

		if	(c=='\0')
		{
			if	(isinsidequote)
			{
					/* このエラーメッセージはLSI Cの */
					/* 標準のものに合わせた。        */
				fprintf(stderr,"Unmatched quote.\n");
				exit(1);
			}
			else
				break;
		}

		if	(c=='"')
			isinsidequote = !isinsidequote;

		if	(!isinsidequote && isspace(c))
		{
				*Argbuf = '\0';
				break;
		}

		if	(iskanji(c) || (isinsidequote && c=='^') )
			Argbuf++;
	}

	return ret;
}
/*------------------コマンドラインパラメータ入力の初期化---------------------*/
static	void	init_arg(char **argv)
{
	Argv = argv+1;
	gettoken_init(strdup(getenv("ATHENA")));
}
/*--------------------------パラメータの獲得---------------------------------*/
/* この関数が返したバッファは、次にこの関数を呼び出すまで有効。              */
/*---------------------------------------------------------------------------*/
static	char	*getarg(void)
{
	char		*s;
	static	char	*buf;
rep1:
	s = gettoken();
	if	(s!=NULL)
		return s;
rep2:
	if	(Responsefile==NULL)
	{
		if	(Argv[0]==NULL || Argv[0][0]!='@')
			return *Argv++;
		Responsefile = fopen(Argv[0]+1,"r");
		if	(Responsefile==NULL)
		{
			fprintf(stderr,"ファイル%sがオープンできません\n",
								Argv[0]+1);
			exit(1);
		}
	}

	if	(fgets(buf,sizeof(buf),Responsefile)!=NULL)
	{
		gettoken_init(buf);
		goto rep1;
	}
	else
	{
		fclose(Responsefile);
		Responsefile = NULL;
		goto rep2;
	}
}
/*---------------------------メインルーチン----------------------------------*/
extern	int	main(int argc,char **argv)
{
	char	*s;

	fputs("テキスト印刷 8086|Printman/TEXT 'Athena' Version 1.00a\n\n",stderr);

	init_arg(argv);

	while	((s=getarg())!=NULL)
	{
		if	(*s=='-')
			option(s);
		else
			xmain(s);
	}

	return 0;
}
/*-------------------------------コラム--------------------------------------*/
/* このプログラムは次のようなことを仮定している。                            */
/*     (1)半角の文字コードはASCIIあるいはそれを拡張した形式                  */
/*     (2)全角の文字コードはシフトJISやEUCのように、ASCIIと共存できる形式    */
/*        (ただし、シフトJIS以外のときはiskanji()の差し替えが必要)           */
/*     (3)定数EOFはwchar_tのどの有効な文字とも異なる                         */
/*     (4)charは8ビット                                                      */
/*     (5)unsignedとsignedの内部表現は同じ                                   */
/*        (計算の都合上(例えば1U<-1は1U<UINT_MAXとみなされ真となる)変数を    */
/*         signedで宣言しておきながら、sscanf()では"%u"を使用している)       */
/*--------------------------End of Athena.c----------------------------------*/
