/*
 *			TeX Device Driver dviout for Windows95/NT
 *					Copyright by SHIMA, 1996
 *
 *		Module between User interface of Win32API and Kernel
 *
 */

#include "dviout.h"
#include <io.h>
#ifdef MSVC
#include "msvcdir.h"
#else
#include <dir.h>
#endif
#include "option.h"
#include "err.h"
#include "inter.h"
#include "dviread.h"
#ifdef	USE_SUBFONT
#include "subfont.h"
#endif

extern DIMENSION dviout_dimension;

#ifdef	RAWOUT
extern BOOL f_lbp_prt;
extern BOOL f_dviprt;
extern BOOL f_rawerr;
extern int	ff_flg;
#endif

/* option.c */
int var_set(ARG_TABLE *, char *);

/* prtinit.c */
void Startvfn(void);
void initialize(DVIFILE_INFO *, DIMENSION *);
#ifdef	DVI_ADD
void DeleteAddFile(int, char *);
char *GetAddFile(char *);
#endif

/* pret.c */
void SetHyperSearch(char *s);
void FreeMacro(void);
void CheckMacro(void);
void FreePause(void);
void FreeDvioutInitialize();

/* putdvi.c */
BOOL print_page(DVIFILE_INFO *, DIMENSION *);

/* main.c */
char *name_link(char *, char *);
int no_extension(char *);

/* wndproc.c */
void PrintKeyTable(char *, int);
void ChangeWaitCursor(int);

/* buffer.c */
int resume_fontfile(int);
void font_flush(void);
void *marea(unsigned int);

/* put_dvi.c */
void put_dvi(DVIFILE_INFO *, DIMENSION *, int, char **);

/* epsbox.c */
void reset_gsps(void);
void chg_pathsep(uchar *, char);
extern int colordepth;

/* vfont.c */
char *DefineFTT(char *);

/* fontdef.c */
void resolve_font(char *);
extern char *font_root_path;
extern char *font_search_path;
extern char *new_tfm_search_path;
extern char *new_subfont_path;
extern char *texpk_org;

/* wmain.c */
void DisplayMessage(char *);
int SetPoint(int, int, int);

char *def_dvifile(int, char **, char *);

#define	OPTION_FLG	'-'

extern DVIFILE_INFO dvifile;
extern f_init;
extern char *vfn_org;

static start_page;
char *hypertag;
int  id_dvi;
int  id_page;
int  ff_macro;
int  name_pos;
BOOL g_bCheckColorSpecial;

extern int f_2page;
static POINT coverpos;
extern BOOL f_nopause;
extern char waiting_macro[];
extern int count_src_special; /* pret.c */
extern char *exec_src_sp;
	
static char g_szFileName[MAX_PATH];
static char g_szOldFileName[MAX_PATH];
static char g_szCoverName[MAX_PATH];
static int cover_page;
static POINT cover_old;
static BOOL f_reading;

#ifdef	WIN32G
static char *keepspecial;
#endif

static char *const default_dvifile_ext = "dvi";

static struct INIPARA {
	char *para;
	char *val;	
} inipara[] = {
  {"sFont", 
	"cmex*lasy*lcircle*line*logo*msam*euex*ascgrp*txex cmsy*cmbsy*msbm*eusm*eusb*txsy*txbsy*"},
  {"TEXFONTS",	"^r\\tfm\\\\"},
  {"macro",		"!A!Zdy!pdl !A!N!Q!F;!A!T;!ADX;fn!b !A!Z!pdf !A!Z!pdl \"-OX=0mm -OY=0mm !I\""},
#ifdef	TTFONT
  {"TEXSUBF",	"^r\\sfd\\\\;^r\\..\\ttf2pk\\\\;^r\\..\\ttf2tfm\\\\"}
#endif
};

char *INIT_PARA =
"dpi=300 G=goth;jisg sdpi=150 BMP=4:4:800 ToEdit=^c^V"
#if	defined(NO_OUTP) && !defined(WIN32G)
" -O"
#endif
;

/*
 *  Get the value of given *tbl in *buf
 *  Return: the length of *buf
 *
 *  mode     TEXROOT=NULL  y=NULL    y=A4L      dpi=300(default)
 *    -1        ^$                     A4L          300
 *     0                             y=A4L\n
 *     2        ^$         y=A4P\n   y=A4L\n    dpi=300\n
 *  other       ^$                   y=A4L\n    dpi=300\n
 */
static int GetParaAnySub(char *buf, ARG_TABLE *tbl, int mode)
{
	char *bpt, *s;
	uchar *pt;
	int	val, sep_ch;

	*buf = 0;
	if(mode != -1)
		sprintf(buf, "%s=", tbl->option);
	bpt = buf + strlen(buf);

	switch(tbl->type){
		case BOOLTYPE:
			if(!mode && *((BOOL *)(tbl->var)) ==  tbl->def)
				break;
			sprintf(bpt, "%c", (*((BOOL *)(tbl->var)))?'+':'-');
next:		if(mode != -1)
				strcat(bpt, "\n");
			else if(!strcmp(bpt, "^$"))
				*bpt = 0;
			return strlen(buf);

		case INTEGER:
		case LONGINT:
			val = *((int *)(tbl->var));
			switch(*(tbl->option)){
				case 'e':
					if(!*(tbl->option+1) && val == 1000)
						val = 0;
					break;
				case 'h':
					if(!strcmp(tbl->option, "hyperoff"))
						val &= 3;
					break;
				case 'd':
					if(!strcmp(tbl->option, "dpiv") && val == GetXDpi())
						val = 0;
					break;
			}
			if(val == tbl->def && !mode)
				break;
			if(val < 1024 && val > -1024)
				sprintf(bpt, "%d", val);
			else
				sprintf(bpt, "0x%x", val);
			goto next;

		case STRING:
			if(*((char **)(tbl->var)) == NULL || 
				!*((char **)(tbl->var))[0]){
				if( !mode )
					break;
				sprintf(bpt, "^$");
				goto next;
			}
			s = *((char **)(tbl->var));
			if(!mode && tbl->def == 0){
				if(!strcmp(s, GetParaStringDefault(tbl->option)))
					break;
				if(!strcmp(tbl->option, "TEXROOT") && !strcmp(s, "^T\\fonts"))
					break;
			}else
set_str:	if(  tbl->def != 0
			  && !mode
			  && !strcmp(s, option_str[tbl->def]) )
				break;
			if(mode && !strcmp(tbl->option,"BMP")){
				sprintf(bpt, "%d:%d:%d", GetXGray(), GetYGray(), SetGamma(0));
				goto next;
			}
			for(pt = s; *pt; ){
				if(*pt++ < 0x21){
					sep_ch = (strchr(s, 0x22))?
						0x27:0x22;
					*bpt++ = sep_ch;
					strcpy(bpt, s);
					bpt += strlen(bpt);
					*bpt++ = sep_ch;
					*bpt = 0;
					goto next;
				}
			}
			strcpy(bpt, s);
			goto next;

		case PROCEDURE:
			if(!strcmp(tbl->option, "key")){
				*(bpt = buf) = 0;
				if(mode == -1){
					PrintKeyTable(bpt, -1);
				}else{
					PrintKeyTable(buf, mode?1:0);
					if(*buf)
						strcat(buf, "\n");
				}
				return strlen(buf);
			}
			if(tbl->top == 0 || (s = option_str[tbl->top]) == NULL){
				if(mode && *(tbl->option) == 'y' && !*(tbl->option+1)){
					strcpy(bpt, "A4P");
					goto next;
				}
				break;
			}
			if(!mode && !*s
			  && (tbl->def == 0 || option_str[tbl->def] == NULL) )
				break;
			goto set_str;

		case LENGTH:
			val = *((int *)(tbl->var));
			if(val < tbl->def + 4 && val > tbl->def - 4)
				val = tbl->def;
			if(!mode && (val == tbl->def
			  || !strcmp(tbl->option, "PH") || !strcmp(tbl->option, "PW")) )
				break;
			sprintf(bpt, "%.1fmm", (float)SP_TO_MM(val));
			goto next;
	}
	*buf = 0;
	return 0;
}

char *GetParaAny(char *para)
{
	ARG_TABLE *opt;

	opt = (ARG_TABLE *)SetPara(para, CHECK_OPTION);
	if(opt == NULL)
		return NULL;
	GetParaAnySub(tmp_buf, opt, -1);
	return tmp_buf;
}

/*  use common_work
 *  mode  1: get parameter values in a string
 *       -1: set parameter values and release memory
 *
 *    char *buf = MareaPara("y BMP", 1);		// keep parameters
 *    ....
 *         MareaPara(buf, -1);					// recover parameters
 */
char *MareaPara(char *para, int mode)
{
	ARG_TABLE *opt;
	char key[0x12];
	int i, len;

	tmp_buf[0] = len = 0;
	if(mode == 1){
		while(*para){
			if(*para <= ' '){
				para++;
				continue;
			}
			for(i = 0; para[i] > ' '; i++){
				if(i >= 0x10)
					break;
				key[i] = para[i];
			}
			key[i] = 0;
			opt = (ARG_TABLE *)SetPara(para, CHECK_OPTION);
			para += i;
			if(opt == NULL)
				continue;
			len += GetParaAnySub(tmp_buf + len, opt, 1);
		}
		return dup_string(tmp_buf);
	}else if(mode == -1){
		if(para != NULL){
			SetPara(para, SET_OPTION);
			Free0(para);
		}
	}
	return NULL;
}

#ifdef	WIN32G
void KeepParaSpecial(char *para)
{
	if(keepspecial == NULL)
		keepspecial = MareaPara(para, 1);
}

void RestoreParaSpecial(void)
{
	if(keepspecial != NULL){
		MareaPara(keepspecial, -1);
		keepspecial = NULL;
	}
}

/*
 *  Get background color
 */
int GetBackGround(void)
{
	if( (f_bcolor & 0xffffff) != 0xffffff )
		return RGB((f_bcolor>>16) & 0xff, (f_bcolor>>8) & 0xff, 
			f_bcolor & 0xff);
	else
		return (f_b_bcolor && SetGamma(0)<0)?0:0xffffff;
}
#endif

char *PrintAllPara(int mode)
{
	ARG_TABLE *tbl;
	char *s, *t;

	t = (s = tmp_buf) + 0x2000;
	for(tbl = SetPara(NULL, GET_TOP); tbl->option && s < t; tbl++){
		if(mode == 2 && !strcmp(tbl->option, "key"))
			continue;
		s += GetParaAnySub(s, tbl, mode);
	}
	return(tmp_buf);
}

int KeepPara(int val)
{
#define	MAX_KEEPPARA	8

	int i, val0;
	static char *para[MAX_KEEPPARA];
	static unsigned char xdpi[MAX_KEEPPARA];
	static unsigned char ydpi[MAX_KEEPPARA];
	static signed   char gray[MAX_KEEPPARA];

	if(val == -1){
		for(i=0; i < MAX_KEEPPARA && para[i] != NULL; i++);
		if(i >= MAX_KEEPPARA)
			return -1;
		para[i] = dup_string(PrintAllPara(2));
		xdpi[i] = GetXGray();
		ydpi[i] = GetYGray();
		gray[i] = SetGamma(0);
		return i;
	}else if(val == -2){
		for(i=0; i < MAX_KEEPPARA; i++){
			Free0(para[i]);
			para[i] = NULL;
		}
		return 0;
	}
	if( (val0 = (val & 0xff)) >= MAX_KEEPPARA || para[val0] == NULL )
		return -2;
	if(!(val0 & 0x100))
		SetPara(para[val0], SET_OPTION);
	Free0(para[val0]);
	para[val0] = NULL;
	SetXYGray(xdpi[val0], ydpi[val0]);
	SetGamma(gray[val0]);
	return val0;
}

void SetParaFlag(char *para, BOOL val)
{
	char tmp[256];
	int	i;

	strcpy(tmp, para);
	i = strlen(tmp);
	tmp[i] = '=';
	tmp[i+1] = (val)?'+':'-';
	tmp[i+2] = 0;
	SetPara(tmp, SET_OPTION);
}

BOOL GetParaFlag(char *para)
{
	ARG_TABLE *opt;

	opt = (ARG_TABLE *)SetPara(para, CHECK_OPTION);
	return(opt != NULL)?
		*((BOOL *)(opt->var)):FALSE;
}

BOOL GetParaFlagDefault(char *para)
{
	ARG_TABLE *opt;

	opt = (ARG_TABLE *)SetPara(para, CHECK_OPTION);
	return(opt != NULL)?opt->def:FALSE;
}

int SetParaStringDirect(char *para, char *arg)
{
	ARG_TABLE *opt;

	opt = (ARG_TABLE *)SetPara(para, CHECK_OPTION);
	if(opt==NULL)
		return -1;
	return var_set(opt, arg);
}

void SetParaString(char *para, char *val)
{
	char tmp[0x3020];

	strcpy(tmp, para);
	strcat(tmp, "=");
	if(val != NULL)
		strcat(tmp, val);
	SetPara(tmp, SET_OPTION);
}

char *GetParaString(char *para)
{
	ARG_TABLE *opt;

	opt = (ARG_TABLE *)SetPara(para, CHECK_OPTION);
	if(opt == NULL)
		return NULL;
	if(opt->type != HIDDEN_PROCEDURE && opt->type != PROCEDURE)
		return (*(char **)(opt->var));
	else
		return(opt->top != 0)?
			option_str[opt->top]:NULL;
}

char *GetParaStringDefault(char *para)
{
	ARG_TABLE *opt;
	int i;

	if(!strcmp(para, "vfn"))
		return vfn_org;
	for(i = 0; i < sizeof(inipara)/sizeof(struct INIPARA); i++){
		if(!strcmp(para, inipara[i].para))
			return inipara[i].val;
	}
	opt = (ARG_TABLE *)SetPara(para, CHECK_OPTION);
	return (opt == NULL || opt->def == 0)?
		"":option_str[opt->def];
}

void SetParaInt(char *para, int val)
{
	char tmp[256];
	int	i;

	strcpy(tmp, para);
	i = strlen(tmp);
	tmp[i] = '=';
	sprintf(tmp+i+1, "%d", val);
	SetPara(tmp, SET_OPTION);
}

int GetParaInt(char *para)
{
	ARG_TABLE *opt;

	opt = (ARG_TABLE *)SetPara(para, CHECK_OPTION);
	return(opt != NULL)?
		*((int *)(opt->var)):WRONG_INT;
}

int GetParaIntDefault(char *para)
{
	ARG_TABLE *opt;

	opt = (ARG_TABLE *)SetPara(para, CHECK_OPTION);
	return(opt != NULL)?opt->def:WRONG_INT;
}


void SetStartPage(int num)
{
	start_page = num;
}

int GetStartPage(void)
{
	return start_page;
}

int GetTotalPage(void)
{
	return dviout_dimension.total_page;
}

int GetMaxNombrePage(void)
{
	return dviout_dimension.max_nombre_page;
}

int GetCurrentPage(void)
{
	return dviout_dimension.start_page;
}

int ChgPage(int page)
{
	return
		dviout_dimension.start_page = dviout_dimension.end_page
		= CheckPage(page);
}

char *GetOpenName(void)
{
	return dvifile.file_name?dvifile.file_name:"";
}

int TransPage(int page)
{
	return (dviout_dimension.total_page)?
		dviout_dimension.page_index[CheckPage(page)].number:0;
}

int SearchPage(int page, int skip)
{
	int i;

	for(i = (skip>0)?skip+1:1; i <= dviout_dimension.total_page; i++){
		if(dviout_dimension.page_index[i].number == page)
				return i;
	}
	return 0;
}

#ifdef	HYPERTEX
int IsHyperTag(void)
{
	return ((f_hyper & F_H_IGNORE) || !id_dvi
	  || dviout_dimension.page_index == NULL)?
		-2:dviout_dimension.page_index[dviout_dimension.start_page].hyper0;
}
#endif

int GetTextXSize(void)
{
	return
		dviout_dimension.text_width;
}

int GetTextYSize(void)
{

	return
		dviout_dimension.text_height;
}

int GetBufXSize(void)
{
	return
		dviout_dimension.buf_width;
}

int GetBufYSize(void)
{
	return
		dviout_dimension.buf_height;
}

int GetXDpi(void)
{
	return dviout_dimension.dpi;
}

int GetYDpi(void)
{
	return dviout_dimension.DPI;
}

void ReSetDpi(void)
{
	dviout_dimension.dpi = dviout_dimension.DPI = 0;
	f_init |= INIT_METRIC|INIT_FONT;
}

void ClearFont(void)
{
	f_init |= INIT_FONT;
}

void ReSetMetric(void)
{
	f_init |= INIT_METRIC;
}

int GetMagnification(void)
{
	return dviout_dimension.mag;
}

static struct ftime ftime;
int fsize_read;

void ResetFile(void)
{
	fsize_read = 0;
}

char *GetFTime(void)
{
	FILE *fo;
	struct ftime ftime0;
	static char tmp[0x20];

	if(  strcmp(current_name, dvifile.file_name)
	 && (fo = fopenf(current_name, "rb")) != NULL ){
		getftime(fileno(fo), &ftime0);
		fclose(fo);
		goto set;
	}else if(ftime.ft_year != 0){
		memmove(&ftime0, &ftime, sizeof(struct ftime));
set:	sprintf(tmp, "%4d-%2d-%2d  %2d:%02d:%02d",
			1980 + ftime0.ft_year, ftime0.ft_month, ftime0.ft_day,
			ftime0.ft_hour, ftime0.ft_min, ftime0.ft_tsec*2);
	}
	else tmp[0] = 0;
	return tmp;
}

int CheckRenew(int wait)
{

	static struct ftime ftime_old;
	static int size_old;
	struct ftime ftime0;
	FILE *fp;
	int size, pos, code, level, count, f_dif;

#define	END_DVI	223
#define ID 2
#define ID_PTEX 3
#define	INIT_WAIT	10
#define	MORE_WAIT	10
	if(dvifile.file_name == NULL || f_reading ||
	  (fp = fopenf(dvifile.file_name, "rb")) == NULL)
		return 0;
	if(!(*(uint*)&ftime)){
		level = 2;
		goto quit;
	}
	count = 0;
rep:
	getftime(fileno(fp), &ftime0);
	size = filelength(fileno(fp));
	level = -1;
	if(*((uint*)&ftime) == *((uint*)&ftime0) && size == fsize_read)
		goto quit;
	if(*((uint*)&ftime_old) == *((uint*)&ftime0) && size_old == size)
		goto quit;
	*((uint*)&ftime_old) = *((uint*)&ftime0);
	size_old = size;
	level--;
	if(stricmp(dvifile.file_name + strlen(dvifile.file_name)-4, ".dvi"))
		goto quit1;
	if ((uchar)getc(fp) != PRE || (uchar)getc(fp) != ID)
		goto quit;
	level--;
	for (code = 0, pos = size; 
	  fseek(fp, --pos, SEEK_SET), (code = (uchar)getc(fp)) == END_DVI;);
	if (size - pos < 5 || (code != ID && code != ID_PTEX))
		goto quit;
quit1:
	level = 1;
quit:
	fclose(fp);
	if(!wait)
		return level;
	if(level < -1){
		for(f_dif=0;;){
			sleep0(500);			/* check every .5 sec */
			if((fp = fopenf(dvifile.file_name, "rb")) == NULL)
				return 0;
			pos = size;
			size = filelength(fileno(fp));
			if(pos == size){		/* stable */
				if(f_dif)			/* change -> stable */
					goto rep;
				if(count++ > INIT_WAIT){
					error(WARNING, "DVI file is not completed or correct!");
					fclose(fp);
					break;
				}
			}else{
				f_dif = 1;
				count = -MORE_WAIT;
			}
			fclose(fp);
		}
	}
	return level;
}

void touch(void)
{
	fsize_read = 0;
}

#ifdef	RAWOUT
void PrintRaw(int argc, char **argv)
{
	struct ftime ftime0;
	int old_spcolor, hdl, size;

	ChangeWaitCursor(1);
	if(dvifile.file_ptr != NULL)
		fclose(dvifile.file_ptr);
	if((dvifile.file_ptr = fopenf(dvifile.file_name, "rb")) == NULL)
		return;
	hdl = fileno(dvifile.file_ptr);
	getftime(hdl, &ftime0);
	size = filelength(hdl);
	if(*((uint*)&ftime) != *((uint*)&ftime0) || fsize_read != size){
		id_dvi++;
		*((uint*)&ftime) = *((uint*)&ftime0);
		fsize_read = size;
		f_init |= INIT_DVIREAD|INIT_CHKFONT;
	}
	f_dviprt = f_reading = TRUE;
	f_init |= INIT_METRIC;
	if(f_lbp_prt){
		font_flush();
		f_init |= INIT_CHKFONT;
	}
	old_spcolor = f_spcolor;
	f_spcolor = 0;
	initialize(&dvifile, &dviout_dimension);
	if(!g_nErrorCode){
		dviout_dimension.prt_type &= ~NO_PRN_OUT;
		put_dvi(&dvifile, &dviout_dimension, argc, argv);
	}else
		*((uint*)&ftime) = 0;
	if(f_lbp_prt){
		font_flush();
		f_init |= INIT_CHKFONT;
		SetPara("nf=^$", SET_OPTION);
		ff_flg = -1;
	}
	f_dviprt = f_rawerr = f_reading = FALSE;
	f_spcolor = old_spcolor;
	f_init |= INIT_METRIC;
	dviout_dimension.prt_type |= NO_PRN_OUT;
	ChangeWaitCursor(0);
}
#endif

FILE *PushDVI(int mode)
{
	static int pos;
	struct ftime ftime0;
	int hdl, size;

	if(mode == 1){
		if(dvifile.file_ptr != NULL){
			pos = ftell(dvifile.file_ptr);
			return dvifile.file_ptr;
		}
		pos = -1;
		if((dvifile.file_ptr = fopenf(dvifile.file_name, "rb")) == NULL)
			return NULL;
		hdl = fileno(dvifile.file_ptr);
		getftime(hdl, &ftime0);
		size = filelength(hdl);
		if(*((uint*)&ftime) != *((uint*)&ftime0) || fsize_read != size){
			fclose(dvifile.file_ptr);
			dvifile.file_ptr = NULL;
		}
	}else if(mode == -1){
		if(dvifile.file_ptr != NULL){
			if(pos >= 0){
				fseek(dvifile.file_ptr, pos, SEEK_SET);
				pos = -1;
				return dvifile.file_ptr;
			}else{
				fclose(dvifile.file_ptr);
				dvifile.file_ptr = NULL;
			}
		}
		pos = -1;
		return NULL;
	}
	return dvifile.file_ptr;
}

POINT GetCoverPos( void )
{
	return coverpos;
}

void SetCoverPos( int x, int y )
{
	SetCoverXPos(x);
	SetCoverYPos(y);
}

void SetCoverXPos( int x )
{
	coverpos.x = x;
}

void SetCoverYPos( int y )
{
	coverpos.y = y;
}

void CoverChange(int val)
{
	coverpos.x = coverpos.x*val/1000;
	coverpos.y = coverpos.y*val/1000;
	cover_old.x = cover_old.x*val/1000;
	cover_old.y = cover_old.y*val/1000;
}

static void KeepCoverPos(void)
{
	cover_old.x = coverpos.x;
	cover_old.y = coverpos.y;
}

BOOL CoverSuspend(void)
{
	if(b_presentation_mode && g_winData.dviwin.cover_sheet.fOn == CS_ON && !f_2page){
		KeepCoverPos();
		g_winData.dviwin.cover_sheet.fOn |= CS_SUSPEND;
		return TRUE;
	}
	return FALSE;
}

int CoverReturn(void)
{
	int mode;

	if(!b_presentation_mode || !g_szCoverName[0] || dvifile.file_name == NULL)
		return 0;
	if(strcmp(g_szCoverName, dvifile.file_name)){
		strcpy(g_szFileName, g_szCoverName);
		mode = 2;
	}else
		mode = 1;
	SetStartPage(cover_page);
	return mode;
}

void KeepCover(void)
{
	if(!(g_winData.dviwin.cover_sheet.fOn & CS_ON) || dvifile.file_name == NULL)
		return;
										// Cover Sheet for Presenation Mode
	if(strcmp(g_szCoverName, dvifile.file_name)){
		if(g_winData.dviwin.cover_sheet.fOn != CS_ON)
			return;
		strcpy(g_szCoverName, dvifile.file_name);
		goto new;
	}else if(cover_page != dviout_dimension.start_page){
		if(g_winData.dviwin.cover_sheet.fOn != CS_ON)
			return;
		if(cover_page < dviout_dimension.start_page){	// new page
new:		cover_page = dviout_dimension.start_page;
			coverpos.x = coverpos.y = 0;		// cover all
		}else{							// goto the former page
			KeepCoverPos();
			cover_old.y = coverpos.y;
			g_winData.dviwin.cover_sheet.fOn |= CS_SUSPEND;
		}
	}else{
		if(g_winData.dviwin.cover_sheet.fOn & CS_SUSPEND){
			coverpos.x = cover_old.x;
			coverpos.y = cover_old.y;
			g_winData.dviwin.cover_sheet.fOn = CS_ON;
		}
	}
}

void RenewCover(void)
{
	g_szCoverName[0] = 0;
	coverpos.x = coverpos.y = 0;
	KeepCover();
}

static int CheckDviId(void)
{

	struct ftime ftime0;
	BOOL f_tmp;
	int page, hdl, size;

	if(strcmp(g_szOldFileName, dvifile.file_name)){
		strcpy(g_szOldFileName, dvifile.file_name);
#ifdef	WIN32G
		RestoreParaSpecial();
		g_winData.dviwin.bStopRedrawByUser = FALSE;
		waiting_macro[0] = 0;
#endif
		goto dif;
	}
	f_tmp = f_reading;
	f_reading = FALSE;
	page = CheckRenew(1);
	f_reading = f_tmp;
	if(page != -1){
dif:	id_dvi++;
		hdl = fileno(dvifile.file_ptr);
		getftime(hdl, &ftime0);
		size = filelength(hdl);
		page = dviout_dimension.start_page;
		*((uint*)&ftime) = *((uint*)&ftime0);
		fsize_read = size;
		f_init |= INIT_DVIREAD|INIT_CHKFONT;
		resolve_font(NULL);
		ChgPage(page);
		return 1;
	}
	return 0;
}

BOOL ModifyDvi(char *name)
{
	static int color_id;
	int len;
	char cmd[1024];
	char *argv[2];

	if(id_dvi != color_id){
		color_id = id_dvi;
		len = strlen(name);
		sprintf(cmd, "color specials in this DVI file are not page-independent!\n%s\n",
			name);
		if(my_access("^x\\dvispc.exe", 4) || len < 4 || strcmp(name + strlen(name) - 4, ".dvi"))
			error(WARNING, cmd);
		else{
			strcat(cmd, "Modify this DVI file to be page-independent?");
			if(AskYes(cmd, "Warning")){
				argv[0] = cmd;
				argv[1] = NULL;
				SetPath(cmd, "^x\\dvispc.exe ");
				GetShortPathName(name, cmd + strlen(cmd), MAX_PATH);
				WinMinExecute(argv, 0, TRUE);
				return TRUE;
			}
		}
	}
	return FALSE;
}

void ExpandPage(void)
{
	int old_id_page, count=0;

	ChangeWaitCursor(1);
	if(!ff_macro)
		ff_macro = 1;
	else
		ff_macro |= 0x100;		// re-enty by ExecMacro(0)
	f_Expand = FALSE;
rep:
	if(dvifile.file_ptr != NULL)
		fclose(dvifile.file_ptr);
	if((dvifile.file_ptr = fopenf(dvifile.file_name, "rb")) == NULL){
		if(ff_macro <= 3)
			ff_macro = 0;
		ff_macro &= 0xff;
		return;
	}
	f_reading = TRUE;
	CheckDviId();
	if(f_init != 0)
		initialize(&dvifile, &dviout_dimension);
#ifdef	WIN32G
	if( (id_page & 0xffff) != (dviout_dimension.start_page & 0xffff))
		g_winData.dviwin.bStopRedrawByUser = FALSE;
#endif
	id_page = (id_dvi << 16)|dviout_dimension.start_page;
	CheckMacro();
	if(!g_nErrorCode){
		dviout_dimension.prt_type |= NO_PRN_OUT;
		reset_gsps();
		print_page(&dvifile, &dviout_dimension);
		if(ff_macro == 1){
			ff_macro = 2;
			old_id_page = id_page;
			if(ExecMacro(0)){
				ff_macro = 3;
				if(old_id_page == id_page)
					f_nopause = TRUE;
//				pause = GetPause(&page, &page, 2);
				goto rep;
			}
		}
//		f_dvioutspecial = TRUE;
	}else
		*((uint*)&ftime) = 0;
	fclose(dvifile.file_ptr);
	dvifile.file_ptr = NULL;
	f_reading = FALSE;
	if(colordepth && g_bCheckColorSpecial){
		if(ModifyDvi(dvifile.file_name) && !count++)
			goto rep;
	}
	if(ff_macro <= 3){
//		if(ff_macro == 3 && pause > 0 && old_id_page == id_page)
//			SetPause(pause);
		f_nopause = FALSE;
		ff_macro = 0;
	}
	ff_macro &= 0xff;
	if(b_presentation_mode)
		KeepCover();
	ChangeWaitCursor(0);
}

void ExpandPage0(void)
{
	BOOL f_d;

	f_d = f_dvioutspecial;
	f_dvioutspecial = FALSE;
	ExpandPage();
	f_dvioutspecial = f_d;
}

char *GotoPage(int page)
{
	page = CheckPage(page);
	if(page == dviout_dimension.start_page)
		return NULL;
	dviout_dimension.start_page = dviout_dimension.end_page = page;
	ExpandPage();
	return MakeBMP();
}

void StartDviout(void)
{
	int i;

	Startvfn();
	SetPara(INIT_PARA, SET_OPTION);
	for(i = 0; i < sizeof(inipara)/sizeof(struct INIPARA); i++)
		SetParaStringDirect(inipara[i].para, inipara[i].val);
}

int CheckPage(int page)
{
	if(page <= 0)
		page = 1;
	if(page > dviout_dimension.total_page)
		page = dviout_dimension.total_page;
	return page;
}

int OpenDvi(char *name)
{
	static char tmp[MAX_PATH];

	if(dvifile.file_ptr != NULL)
		fclose(dvifile.file_ptr);
	if((dvifile.file_ptr = fopenf(name, "rb")) == NULL)
		return -1;
	f_reading = TRUE;
	ToDviDir();
	if(dvifile.file_name == NULL){
		g_szOldFileName[0] = 0;
		dvifile.file_name = tmp;
	}else
		strcpy(g_szOldFileName, dvifile.file_name);
	strcpy(tmp, name);
	CheckDviId();
	f_init |= (INIT_DVIREAD|INIT_CHKFONT);
	initialize(&dvifile, &dviout_dimension);
	fclose(dvifile.file_ptr);
	dviout_dimension.prt_type |= NO_PRN_OUT;
	dvifile.file_ptr = NULL;
	f_reading = FALSE;
	if(dviout_dimension.total_page <= 0)
		return 0;
	dviout_dimension.start_page = dviout_dimension.end_page 
		= CheckPage(start_page);
	start_page = 0;
	return dviout_dimension.total_page;
}

void InitializeParameter(void)
{
	initialize(&dvifile, &dviout_dimension);
}

char *def_dvifile(int argc, char **argv, char *sub)
{
	char *name;
	int	len, pos;
	while (--argc > 0) {
		if (**++argv == OPTION_FLG)
			SetPara(*argv + 1, SET_OPTION);
		else{
			name = (no_extension(*argv))?
				name_link(*argv, default_dvifile_ext):*argv;
			if (argc > 1){
				if(argv[1][0] == '#'){
					if(argv[1][1] == 0 && argc == 4 && atoi(argv[2]) > 0){
						sprintf(common_work + 0x1000, "# %s %s", 
							argv[2], argv[3]);
						SetHyperTag(common_work + 0x1000);
					}else
						SetHyperTag(argv[1]);
				}else if(tolower(argv[1][0]) == 'h' && !argv[1][1]){
					name = SearchFileHistory(name, &start_page);
					if(name == NULL)
						name = "?  ?.dvi";
				}
				else
					start_page = atol(*(argv+1));
				len = strlen(*argv) - 4;
				if( sub != NULL &&
					(len <= 0 || strcmpi(*argv + len, ".dvi") != 0) ){
					for(pos = 0; pos < argc; pos++){
						len = strlen(argv[pos]) - 4;
						if(len >= 0 && strcmpi(argv[pos] + len, ".dvi") == 0){
							argc = pos;
							*sub = 0;
							for(pos = 0; pos <= argc; pos++){
								if((len = strlen(sub)) > 0){
									sub[len] = ' ';
									if(len + strlen(argv[pos]) >= MAX_PATH-2){
										*sub = 0;
										break;
									}
								}
								strcat(sub, argv[pos]);
							}
							break;
						}
					}
				}
			}
			return name;
		}
	}
	return NULL;
}

#ifdef WIN32
#define gray_number g_winData.dviwin.gamma
#else
extern int gray_number;
#endif

char *GrayReverse(void)
{
	SetGamma(-gray_number);
	return MakeBMP();
}

char *Blacker(void)
{
	if(gray_number > 50 || gray_number < -50)
		SetGamma(gray_number + ((gray_number > 0)?-50:50));
	return MakeBMP();
}

char *Whiter(void)
{
	SetGamma(gray_number + ((gray_number > 0)?50:-50));
	return MakeBMP();
}

char *nextpage(int num)
{
	return GotoPage(dviout_dimension.start_page + num);
}

void SetHyperTag(char *name)
{
	Free0(hypertag);
	hypertag = dup_string(name);
	f_hyper |= F_H_DVIOPEN;
}

void MarkPoint(unsigned char *pDib, int x, int y, int x_len, int y_len, unsigned char mode)
{
	if(x_len){
		FillBox(pDib, x-x_len, y, x+x_len, y, mode);
	}
	if(y_len){
		FillBox(pDib, x, y-y_len, x, y+y_len, mode);
	}
	if(!x_len && !y_len)
		FillBox(pDib, x, y, x, y, mode);
}

BOOL FillBox(unsigned char *pDib, int x1, int y1, int x2, int y2,
	unsigned char mode)
{
	int tmp, tmp2, x, y;
	WORD cxDib, cyDib;
	BYTE attr;
	
	if(  ((BITMAPINFOHEADER*)pDib)->biBitCount != 8			/* 256 color? */
	  || ((BITMAPINFOHEADER*)pDib)->biCompression != BI_RGB	/* compressed? */
	)
		return FALSE;
	cxDib = (WORD)((BITMAPINFOHEADER*)pDib)->biWidth;
	cyDib = (WORD)((BITMAPINFOHEADER*)pDib)->biHeight;
	if(mode & 1){
		tmp = GetXGray();
		x1 = (x1 + x1 + tmp)/(tmp2 = (tmp + tmp));
		x2 = (x2 + x2 + tmp)/tmp2;
		tmp = GetYGray();
		y1 = (y1 + y1 + tmp)/(tmp2 = (tmp + tmp));
		y2 = (y2 + y2 + tmp)/tmp2;
	}
	if(mode & 2){
		mode &= ~0x03;
		if(y1 != y2){
			if(x1 != x2)
				FillBox(pDib, x1, y1, x1, y2, mode);
			FillBox(pDib, x2, y1, x2, y2, mode);
			FillBox(pDib, x1, y1, x2, y1, mode);
		}
		FillBox(pDib, x1, y2, x2, y2, mode);
		return TRUE;
	}
	if(x1 > x2){
		tmp = x1; x1 = x2; x2 = tmp;
	}
	if(x1 < 0)
		x1 = 0;
	if(x2 > cxDib)
		x2 = cxDib-1;

	if(y1 > y2){
		tmp = y1; y1 = y2; y2 = tmp;
	}
	if(y1 < 0)
		y1 = 0;
	if(y2 >= cyDib)
		y2 = cyDib-1;
	if(x1 > x2 || y1 > y2)
		return FALSE;
	pDib += sizeof(BITMAPINFOHEADER) + 4*256
			 + ((tmp = (cxDib+3)&~3))*(cyDib-1-y1) + x1;
	tmp = tmp + x2 - x1 + 1;
	if(!(attr = mode & 0xf0) && !(mode & 8) )
		mode |= 4;
	switch(mode & 0x0c){
	  case 0:				/* merge */
		for(y = y2-y1; y-- >= 0; pDib -= tmp)
			for(x = x2-x1; x-- >= 0; x)
				*pDib++ |= attr;
		break;
	  case 4:				/* replace */
		for(y = y2-y1; y-- >= 0; pDib -= tmp)
			for(x = x2-x1; x-- >= 0; x){
				*pDib = (*pDib & 0x0f)|attr;
				pDib++;
			}
		break;
	  case 8:				/* convert */
		for(y = y2-y1; y-- >= 0; pDib -= tmp)
			for(x = x2-x1; x-- >= 0; ){
				*pDib ^= 0x0f;
				pDib++;
			}
		break;
	  default:
		break;
	}
	return TRUE;
}

void ColorS_Box(unsigned char *pDib, struct S_BOX *box, int mode)
{
	int	pos;

	if(mode & 0x10000){					/* character & underline */
		pos = ~mode & 0xf0;
		ColorS_Box(pDib, box, pos|(mode & 0xf));
		mode = (mode & 0xfffc)|5;		/* set color */
	}
	mode |= 1;							/* original coordinate */
	if(mode & 0x8000){					/* underline */
		if(box->dir & 1){
			pos = (!(mode & 0x4000))?box->x0:box->x1;
			FillBox(pDib, pos, (int)(box->y0), pos, (int)(box->y1), 
				(uchar)(mode & 0xff));
		}else{
			if(!(mode & 0x2000)){
				pos = (!(mode & 0x1000))?box->base:box->y1;
				FillBox(pDib, (int)(box->x0), pos, (int)(box->x1), pos, 
					(uchar)(mode & 0xff));
			}else
				FillBox(pDib, (int)box->x0, (int)(box->y0), (int)(box->x1), (int)(box->y0), (uchar)(mode & 0xff));
		}
	}else								/* box */
rep:	FillBox(pDib, (int)(box->x0) - 1, (int)(box->y0) - 1, (int)(box->x1) + 1, (int)(box->y1) + 1, 
			(uchar)(mode));
}

int IsTopSearchBox(int pos)
{
	return (pos<=0)?1:s_box[pos].sub.found - s_box[pos - 1].sub.found;
}

int CheckS_Box(struct S_BOX *box, int x, int y)
{
	if(box->dir & 1){
		if(x > box->x1 + box->dir/2)
			return -1;
		else if(x < box->x0)
			return 1;
		else if(y < box->y0)
			return -1;
		else
			return (y > box->y1)?1:0;
	}else{
		if(y < box->y0 - box->dir/2)
			return -1;
		else if(y > box->y1)
			return 1;
		else if(x < box->x0)
			return -1;
		else
			return (x > box->x1)?1:0;
	}
}

static int comp3byte(unsigned char *s, unsigned char *t)
{
	int	i, j;
	for(i = 0; i < 3; i++){
		j = (int)(s[i]) - (int)(t[i]);
		if(!j)
			return j;
	}
	return j;
}


int GetS_Count(int mode)
{
	int count, total, pos;

	if(count_src_special == 0 || (pos = IsHyperTag()) <= 0)
		return -mode;					// no source special
	total = h_box[pos].y1;
	pos   = h_box[pos].x1;
	count = 0;
	while(total-- > 0){
		if(IsSRCspecial(h_name[pos])){
			count++;
			if(mode == count)
				return pos;
		}
		pos++;
	}
	return count;
}

/*
 *  Source file & line number -> location in the current page (source special)
 *  
 *  0: no source special
 *  1: fined exact
 *  2: fined just after
 *  3: latter page?
 *  4: former page
 */ 
int GetS_NamePos(POINT *pt, char *name, uint line)
{
	int total, pos, dir, flag;
	unsigned char tmp[3];

	if(count_src_special == 0 || (pos = IsHyperTag()) <= 0)
		return 0;					// no source special
	dir = line/(255*255);
	tmp[0] = dir+1;
	line = line - dir*(255*255);
	dir = line/255;
	tmp[1] = dir+1;
	tmp[2] = line - dir*255 + 1;

	total = h_box[pos].y1;
	pos   = h_box[pos].x1;
	flag = 0;
	name_pos = -1;
	while(total-- >0){
		if(IsSRCspecial(h_name[pos]) &&				// <- source special ?
			(strcmp(h_name[pos].name + 8, name) == 0 || *name == '*')){
															// filename OK
			dir = comp3byte(h_name[pos].name+1, tmp);
			if(dir >= 0){
				flag = 1;
				pt->x = h_name[pos].x;
				pt->y = h_name[pos].y;
				name_pos = pos;
				if(!dir)
					return 1;		// fined exact
			}else if(flag)
				return 2;			// fined
			else
				return 4;			// former
		}
		pos++;
	}
	return flag?3:0;				// latter:no source special
}

/*
 *    Use name_pos
 *    mode  1: next
 *         -1: former
 *          0: current
 *          2: top
 *
 *    line <- line number
 *    *ret : source text
 *           NULL -> not found
 *    *[ret-4] : positon in DVI file (255)
 */
char *GetNextSource(int mode, int *line)
{
	unsigned char *tmp;
	int total, pos, i, j;

	pos = IsHyperTag();
	if(pos <= 0 || !GetS_Count(0) || name_pos < 0)
		return NULL;				// no source special
	total = h_box[pos].y1;
	pos   = h_box[pos].x1;
	if(mode == 1){
top:	i = (name_pos >= pos && name_pos < pos + total)?
			(name_pos+1):pos;
		for(; i < pos + total; i++){
			if(IsSRCspecial(h_name[i])){
end:			name_pos = i;
end2:			tmp = (unsigned char *)(h_name[name_pos].name);
				*line = (tmp[1]*255 + tmp[2] - 256)*255 + tmp[3] -1;
				return (char *)tmp + 8;
			}
		}
	}else if(mode == -1){
		for(i=-1, j = pos; j < name_pos && j < pos + total; j++){
			if(IsSRCspecial(h_name[j]))
				i = j;
		}
		if(i >= 0)
			goto end;
		return NULL;
	}else if(mode == 2){
		name_pos = -1;
		goto top;
	}else if(mode == 0 &&
			 name_pos >= pos && name_pos < pos + total &&
			 IsSRCspecial(h_name[name_pos]))
				goto end2;
	return NULL;
}

int GetShiftInt(unsigned char *tmp)
{
	return ((tmp[0]*255 + tmp[1] - 256)*255 + tmp[2] - 1)*255 + tmp[3] -1;
	
}

/*
 *  location in DVI file -> location in source (source special)
 *  search only in the current page
 *
 *  In  loc:    location in DVI file
 *  Out return: source file name
 *      *line:  line number
 */
char *GetDPos2Source(uint loc, int *line)
{
	int total, pos, i, total2, pos2;
	unsigned char *tmp;

	pos = IsHyperTag();
	if(pos <= 0 || !GetS_Count(0))
		return NULL;				// no source special
	total = h_box[pos].y1;
	pos   = h_box[pos].x1;
	name_pos  = -1;
	while(total-- > 0){
		if(IsSRCspecial(h_name[pos])){
			i = GetShiftInt(h_name[pos].name+4);
			if(i <= loc)
				name_pos = pos;
			else{
				if(name_pos < 0){
					if(dviout_dimension.start_page > 0 && 
						(i = dviout_dimension.page_index
							[dviout_dimension.start_page-1].hyper0) > 0){
						total2 = h_box[i].y1;
						pos2 = h_box[i].x1;
						for(i = pos2 + total2 - 1; i >= pos2; i--){
							if(IsSRCspecial(h_name[i])){
								pos++;
								name_pos = i;
								goto end;
							}
						}
					}
					name_pos = pos;
					pos = 0;
				}
end:			tmp = (unsigned char *)(h_name[name_pos].name);
				*line = (tmp[1]*255 + tmp[2] - 256)*255 + tmp[3] -1;
				if(pos == 0){
					*line = *line - 1;
					name_pos = -2;
				}
				return (char *)tmp + 8;
			}
		}
		pos++;
	}
	if(name_pos >= 0)
		goto end;
	else
		return NULL;			// no source special
}

/*
 *  coordinate in a page -> location in source (source special)
 *  search only in the current page
 *
 *  In  pt:     location in a page
 *  Out return: source file name
 *      *line:  line number
 */
char *GetWPos2Source(POINT *pt, int *line)
{
	int total, pos, dir, i, total2, pos2;
	unsigned char *tmp;

	pos = IsHyperTag();
	if(pos <= 0 || !GetS_Count(0))
		return NULL;				// no source special
	total = h_box[pos].y1;
	pos   = h_box[pos].x1;
	name_pos = -1;
	while(total-- >0){
		if((dir = h_name[pos].name[0]) != 0 && dir <= 2){
			if(dir == 1){			// horizontal
				if(h_name[pos].y <= pt->y)
					name_pos = pos;
				if(h_name[pos].y >= pt->y){
set:				if(name_pos < 0){
						if(dviout_dimension.start_page > 0 && 
							(i = dviout_dimension.page_index
								[dviout_dimension.start_page-1].hyper0) > 0){
							total2 = h_box[i].y1;
							pos2 = h_box[i].x1;
							for(i = pos2 + total2 - 1; i >= pos2; i--){
								if(IsSRCspecial(h_name[i])){
									pos++;
									name_pos = i;
									goto end;
								}
							}
						}
						name_pos = pos;
						pos = 0;
					}
end:				tmp = (unsigned char *)(h_name[name_pos].name);
					*line = (tmp[1]*255 + tmp[2] - 256)*255 + tmp[3] -1;
					if(pos == 0){
						*line = *line-1;
						name_pos = -2;
					}
					return (char *)tmp + 8;
				}
			}else{					// vertical
				if(h_name[pos].x >= pt->x)
					name_pos = pos;
				if(h_name[pos].x <= pt->x)
					goto set;
			}
		}
		pos++;
	}
	if(name_pos >= 0)
		goto end;
	else
		return NULL;			// no source special
}

/*
 *  Get the position of filename from pathname
 */
int GetNamePos(char *fname)
{
	int pos, pt;

	for(pt = pos = 0; fname[pt]; pt++){
		if(fname[pt] == '/' || fname[pt] == '\\' || fname[pt] == ':')
			pos = pt+1;
		else if(issjis1(fname[pt]) && fname[pt+1]
#ifdef	WIN32G
			&& IsJapanese()
#endif
		) pt++;
	}
	return pos;
}

/*
	#<name>  -> coordinate in the current page

	1: found, set *pt
	0: not exist, not set *pt
	2: later  (source special), set *pt
   -1: former (source special), not set *pt
 */
int GetH_NamePos(POINT *pt, char *name)
{
	int pos, total, line, lp, flag;
	unsigned char *tmp;
	char name0[MAX_PATH], *name1;

	pos = IsHyperTag();
	if(pos <= 0)
		return 0;
	line = flag = 0;
	if(name[0] == ' ' && (line = atoi(name+1)) > 0){	// line number for src special
		name++;
		while(*++name >= '0' && *name <= '9');			// skip number
		if(*name == '/')								// with a string search
			while((unsigned char)(*++name) > ' ');		// skip the firt word
		tmp = name;
		name1 = NULL;
		do{
			while(*tmp == ' ')
				tmp++;
			if(*name){
				name = tmp;
				if(*name == '\'')
					name1 = name+1;
			}
			while(*tmp && (unsigned char )(*++tmp) != ' ');
		}while(*tmp);
	}
	if(!f_srcspecial && line > 0)
		return 0;
	if(name[strlen(name)-1] == '\'' && name1){
		strncpy(name0, name1, MAX_PATH-1);
		name0[strlen(name0)-1] = 0;
	}else
		strncpy(name0, name, MAX_PATH-1);
	chg_pathsep(name0, '/');
	lp = MAX_PATH;
	if(line > 0
	  &&  (name[0] == '/'
		|| (isalpha(name0[0]) && name0[1] == ':' && name0[2] == '/')) ){
		if(name[0] != '/')
			name[0] |= 0x20;
		name1 = GetOpenName();
		lp = GetNamePos(name1);
		for(total = 0; total < lp; total++){
			if(name0[total] == name1[total])
				continue;
			if(name0[total] == '/' && name1[total] == '\\')
				continue;
			if(isalpha(name0[total]) && (name0[total]|0x20) == (name1[total]|0x20))
				continue;
			lp = GetNamePos(name0);
				break;
		}
	}
	name1 = (line > 0 && strlen(name0) > lp)?(name0 + lp):"";
	total = h_box[pos].y1;
	pos   = h_box[pos].x1;
	while(total-- > 0){
		if(line > 0){									/* src special */
			if(IsSRCspecial(h_name[pos]) &&
				(*name == '*'
				  || strcmp(h_name[pos].name + 8, name0) == 0
				  || strcmp(h_name[pos].name + 8, name1) == 0) ){
				tmp = (unsigned char *)h_name[pos].name+1;
				lp = (tmp[0]*255 + tmp[1]-256)*255 + tmp[2] -1;
				if(lp <= line){
					pt->x = h_name[pos].x;
					pt->y = h_name[pos].y;
					name_pos = pos;
					if(lp == line)
						return 1;
					flag = 1;
				}else
					return flag?1:-1;
			}
		}else if(strcmp(h_name[pos].name, name) == 0){	/* HyperTeX */
			pt->x = h_name[pos].x;
			pt->y = h_name[pos].y;
			return 1;
		}
		pos++;
	}
	return flag?2:0;
}

/*
 *  search the page containing the HyperTeX tag <name>
 *    allow <name> is  " <line_number> <source_file>" in source special
 *  RET   page number, -1: not found
 */
int SearchH_Name(char *name)
{
	POINT pt;
	int c_page, total, page, f_source, res, f_page;

	if((f_hyper & F_H_IGNORE) || !id_dvi
	  || dviout_dimension.page_index == NULL)
		return -1;

	c_page = GetCurrentPage();
	total = GetTotalPage();
	f_source = (name[0] == ' ' && atoi(name+1) > 0)?1:0;	// source special?
	if(f_source && !f_srcspecial)
		return -1;
	for(page = 1; page <= total; page++){
		ChgPage(page);
		if(f_source && !IsHyperTag())
			ExpandPage0();
		res = GetH_NamePos(&pt, name);
		if(res == 1)			// exact page
			return GetCurrentPage();
		if(res == 2){			// later page (source special)
			f_source = 2;
			f_page = GetCurrentPage();
			if(page == total)
				return f_page;
		}else if(res == -1)	// former page (source special)
			return(f_source == 2)?f_page:GetCurrentPage();
	}
	if(f_source)
		return -1;
	SetHyperSearch(name);
	for(page = 1; page <= total; page++){
		ChgPage(page);
		ExpandPage0();
		if(!(f_hyper & F_H_SEARCH))
			return GetCurrentPage();
	}
	f_hyper &= ~F_H_SEARCH;
	ChgPage(c_page);
	return -1;
}

void SimplifyFname(char *tname)
{
	char *s, *t, *u;

repeat:
	u = NULL;
	t = tname;
	while(1){
		s = u;
		while(*t != '\\' && *t != '/' && *t != ':'){
#ifndef	UNIX
			if(issjis1(*t) && IsJapanese())
				t++;
#endif
			if(!*t++)	return;
		}
		while(*++t == '\\' || *t == '/');
		if(*(u = t) == '.'){
			if(*++t == '.'){
				while(*++t == '\\' || *t == '/');
				if(t < u + 2)
					continue;
				if(s != NULL){
					while((*s++ = *t++) != 0);
					goto repeat;
				}else
					u = NULL;
			}else if(*t == '\\' || *t == '/'){
				while(*++t == '\\' || *t == '/');
				while((*u++ = *t++) != 0);
				goto repeat;
			}
		}
	}
}

char *IsExec(char *fname)
{
	char *u;

	if((u = strstr(fname, ".exe^s")) != NULL ||
	   (u = strstr(fname, ".com^s")) != NULL ||
	   (u = strstr(fname, ".cmd^s")) != NULL ||
	   (u = strstr(fname, ".bat^s")) != NULL ||
	   (u = strstr(fname, ".pif^s")) != NULL ||
	   (u = strstr(fname, ".scr^s")) != NULL ||
	   (u = strstr(fname, ".vbs^s")) != NULL ||
	   (u = strstr(fname, ".vbe^s")) != NULL ||
	   (u = strstr(fname, ".js^s"))  != NULL ||
	   (u = strstr(fname, ".jse^s")) != NULL ||
	   (u = strstr(fname, ".wsf^s")) != NULL ||
	   (u = strstr(fname, ".wsh^s")) != NULL)
		return u;
	else
		return NULL;
}

int CheckHrefDvi(char *href, char *name)
{
	int len, lent;
	char *s, *t, *u, *href0, ch;
	char tname[2*MAX_PATH];

	s = href0 = href;
	if( !strncmp(href, "dviout:", 7) ){
		strcpy(common_work, href + 7);
		return 0x10;
	}
	if(  tolower(*s++) == 'f'
	  && tolower(*s++) == 'i'
	  && tolower(*s++) == 'l'
	  && tolower(*s++) == 'e'
	  && *s++ == ':'){
		while(*s == ' ' || *s == '\t')
			s++;
file:	href = s;
		while((s = strstr(href, ".dvi")) != NULL
			||(s = strstr(href, ".DVI")) != NULL){
			t = (s += 4);
			while(*t == ' ' || *t == '\t')
				t++;
			if(*t == 0 || *t == '#' || !stricmp(t, ".tar"))
				break;
		}
		if(href[0] == '\\'){
			if(href[1] != '\\' && isalpha(name[0]) && name[1] == ':'){
				tname[0] = name[0];				/* set drive */
				tname[1] = ':';
				len = 2;
			}else
				len = 0;
		}else if(href[0] == '/' || (isalpha(href[0]) && href[1] == ':') )
			len = 0;							/* absolute path */
		else{									/* relative path */
			strncpy(tname, name, MAX_PATH);
			len = strlen(name);
			while(len >= 0 && tname[len] != '\\' && tname[len] != '/'
			  && tname[len] != ':')
				len--;
			len++;
		}
		lent = len;
		if(s){								// *.dvi
			while(len < 2*MAX_PATH-1 && href <= s)
				tname[len++] = *href++;		// copy until the end of *.dvi
			tname[len-1] = 0;
		}else{ 								// other files
											// foo.html       foo.htm
											// foo.html#name  foo.htm#name
			strncpy(tname+len, href, MAX_PATH);
			len = strlen(tname);
			while(len-- > 0){
				if(tname[len] == '#'){
					while(len > 0 && tname[len-1] <= ' ')
						len--;
					if(len > 4){
						t = tname + len - ((tolower(tname[len-1]) == 'l')?5:4);
						if(	 tolower(t[0]) != '.'
						  || tolower(t[1]) != 'h'
						  || tolower(t[2]) != 't'
						  || tolower(t[3]) != 'm'
						)
						len = 0;
					}else
						len = 0;
					break;
				}
			}
			if(len > 0){
				ch = tname[len];
				tname[len] = 0;
			}
		}
		exec_para = NULL;

		if( (u = IsExec(tname+lent)) != NULL ){
			u[4] = 0;
			strcpy(common_work, tname+lent);
			strcpy(exec_para = common_work + 0x800, u+6);
			s = NULL;
		}
#ifdef	DVI_ADD
		if( (u = GetAddFile(tname+lent)) != NULL ){
			if(s){							// *.dvi
				strcpy(name, u);
				goto add_dvi;
			}
			if(exec_para){
				strcpy(common_work, u);
				return 2;
			}
			sprintf(common_work, "file:%s", u);
			if(len > 0)						// *.htm[l]#...
				sprintf(common_work + strlen(common_work), 
					"%c%s", ch, tname+len+1);
			return 2;
		}
#endif
		if(href != href0 && strlen(tname) < MAX_PATH - 1){
			if(access(tname, 0) != 0){
				if(s){								// Ver.3.17.3
					for(len = strlen(tname); --len >= 0 && tname[len] != '?';);
					if(len >= 0){
						for(ch = '0'; ch <= '9'; ch++){
							tname[len] = ch;
							if(access(tname, 0) != FAILURE)
								goto found;
						}
						if(ch > '9')
							tname[len] = '?';
					}
				}
			}else{
				if(s){							// *.dvi
found:				SimplifyFname(tname);
					strcpy(name, tname);;
add_dvi:			if(*t == '#')
						SetHyperTag(t);
					return 5;
				}else{
					if(len > 0)
						tname[len] = ch;		// recover *.htm[l]#...
					SimplifyFname(tname);
					len = strlen(tname);
					sprintf(common_work, (exec_para)?"%s":"file:%s", tname);
					s = tname + len - 4;
					if(len >= 4){
						if(  !stricmp(s, ".exe")			// execute files
						  || !stricmp(s, ".cmd")
						  || !stricmp(s, ".bat")
						  || !stricmp(s, ".com")
						  || !stricmp(s, ".vbs")
						  || !stricmp(s, ".scr")
						  || !stricmp(s, ".pif")
						  || !stricmp(s, ".vbs")
						  || !stricmp(s, ".vbe")
						  || !stricmp(s, ".jse")
						  || !stricmp(s, ".wsf")
						  || !stricmp(s, ".wsh"))
							return 2;
					}else if(len >= 3){
						if(  !stricmp(s, ".js")
						  || !stricmp(s, ".ws") )
							return 2;
					}
					for( len = strlen(tname); --len > 0; ){
						if(tname[len] == '.'){			// exist extension
							if( !IsJapanese() || !issjis1(tname[len-1]) )
								return 4;
						}
						if(tname[len] == '\\')
							break;
					}
					return 3;							// no extension
				}
			}
			return 1;									// not found
		}
	}
	s = href;
	while(*s == ' ' || *s == '\t')
		s++;
	for(len = 0; ; ){
		if(s[len] == ':' && len > 1)				// ??...:??
			return 0;
		else if((s[len] & 0x80) || !s[len++])
			goto file;
	}
}


/*
  rc1 <- rc1 + rc2
  rc1 <- rc2  if (rc1.left >= rc1.right)
*/
void JoinBoundigBox(RECT *rc1, RECT *rc2)
{
	if(rc2->left >= rc2->right)
		return;
	if(rc1->left >= rc1->right){
		rc1->left   = rc2->left;
		rc1->right  = rc2->right;
		rc1->top    = rc2->top;
		rc1->bottom = rc2->bottom;
		return;
	}
	if(rc1->left > rc2->left)
		rc1->left = rc2->left;
	if(rc1->right < rc2->right)
		rc1->right = rc2->right;
	if(rc1->top > rc2->top)
		rc1->top = rc2->top;
	if(rc1->bottom < rc2->bottom)
		rc1->bottom = rc2->bottom;
}


/*
 -1: Cannot compare
  0: coinside
  1: Get the boundingBox of the difference
*/
int CompBMP(unsigned char *p, unsigned char *q, RECT *rc)
{
	int i, j, w0, width, height, col;

	if(memcmp(p,q,sizeof(BITMAPINFOHEADER)))
		return -1;
	if( (   ((BITMAPINFOHEADER*)p)->biBitCount != 8			/* 256 color? */
		&& ((BITMAPINFOHEADER*)p)->biBitCount != 4			/* 16 color? */
		)
	  || ((BITMAPINFOHEADER*)p)->biCompression != BI_RGB)
	  return -1;

	col    = ((BITMAPINFOHEADER *)p)->biBitCount;
	w0  = ((BITMAPINFOHEADER *)p)->biWidth;
	height = ((BITMAPINFOHEADER *)p)->biHeight;

//	if(col == 1)
//		w0 = (w0+7)/8;
//	else
	if(col == 4)
		w0 = (w0+1)/2;
//	else if(col == 24)
//		w0 = w0*3;
	else if(col != 8)
		return 0;
	width = (w0+3)&~3;

	p += sizeof(BITMAPINFOHEADER);
	q += sizeof(BITMAPINFOHEADER);
	if(col == 8){
		if(memcmp(p,q,4*256))
			return -1;
		p += 4*256;
		q += 4*256;
	}else if(col == 4){
		if(memcmp(p,q,4*16))
			return -1;
		p += 4*16;
		q += 4*16;
	}
	for(i = 0; i < height; i++){
		if(memcmp(p+i*width,q+i*width,width)){
			rc->top = rc->bottom = i;
			goto S1;
		}
	}
S1:
	if(i == height){	/* coincide */
		rc->top = rc->bottom;
		rc->left = rc->right = 0;
		return 0;
	}
	for(i = height-1; i > rc->top; i--){
		if(memcmp(p+i*width, q+i*width,width)){
			rc->bottom = i;
			goto S2;
		}
	}
S2:
	for(i = 0; i < width; i++){
		for(j = rc->top; j <= rc->bottom; j++){
			if(p[i+j*width] != q[i+j*width]){
				rc->left = rc->right = i;
				goto S3;
			}
		}
	}
S3:
	for(i = width-1; i>rc->left ; i--){
		for(j = rc->top; j <= rc->bottom; j++){
			if(p[i+j*width] != q[i+j*width]){
				rc->right = i;
				goto S4;
			}
		}
	}
S4:
	rc->right++;
	i = rc->top;
	rc->top = height - rc->bottom - 1;
	rc->bottom = height - i;
	if(col==4){
		rc->left *=2;
		rc->right = 2*rc->right+1;
	}
	return 1;
}

void GetModuleName(char *name)
{
	GetModuleFileName(NULL, name, MAX_PATH);
}

void GSPN(char *s, char *t)
{
	GetShortPathName(s, t, MAX_PATH);
}

int GetNextPages(int, char **, DIMENSION *, int *);
/*
 -2: out of range
 -1: null page
  0: end pages
  +: print page
*/
int GetPrintPage(char *par, int from, int to, int *flag){
	BOOL ret;
	int ts, te;
	static int argc, now, end, mode;
	static BOOL f_in;
	static char *var[5];
	static char pages[32];

	if(par){
		var[0] = var[1] = "";
		var[2] = par;
		var[3] = pages;
		sprintf(pages, "%d-%d", from, to);
		argc=(*par == 1)?3:4;
		var[argc] = NULL;
		f_in = TRUE;
	}else{
		if(!f_in)
			return 0;
		if(now++ < end){
			*flag = mode;
			goto end;
		}
	}
	ts = dviout_dimension.start_page;
	te = dviout_dimension.end_page;
	ret = GetNextPages(argc, par?var:NULL, &dviout_dimension, &mode);
	now = dviout_dimension.start_page;
	if(!ret || !now){
		f_in = FALSE;
		if(now > 0)
			now = 0;
	}
	end = dviout_dimension.end_page;
	if(now == -1 && end == 0)
		end = -1;
	dviout_dimension.start_page = ts;
	dviout_dimension.end_page = te;
	*flag = mode;
end:
	if(now > dviout_dimension.total_page){		// error in Page number 
		f_in = FALSE;
		return -2;
	}
	return now;
}

/*-------------------------------------------------------------------*/
void clear_buf(BUF_INFO *);
void clear_hyper_buf(void);
void clear_search_buf(void);
void flush_vfont(void);
void vir_buf_flush(void);
void FreeGray(void);
void FreeStack(void);
void tpic_init(PIXEL);
void FreeWinjTT(void);
void FreeETF(void);
void CutEdge(int);
int open_plugin(char *, int, char **, char **);
void Clear2Page(void);

extern char *option_str[];
extern char *youshi;
extern char *rectcut_org;

void AllFree(void)
{
	ARG_TABLE *tbl;
	int	i;

	clear_hyper_buf();
	clear_search_buf();
	ClearKeepBMP();
	ClearhBitmap();
#ifdef	WIN32G
	Clear2Page();
	FreeMacro();
	FreePause();
	Free0(keepspecial);
	FreeDvioutInitialize();
#endif
#ifdef	PLUG
	open_plugin(NULL, -1, NULL, NULL);
#endif
#ifdef	USE_ETF
	FreeETF();
#endif
	flush_vfont();
	vir_buf_flush();
	FreeWinjTT();
	font_flush();
	DefineFTT(NULL);
	Free0(path_cash);
	Free0(font_root_path);
	Free0(font_search_path);
	Free0(new_tfm_search_path);
#ifdef USE_SUBFONT
	Free0(new_subfont_path);
#endif
	FreeGray();
	FreeStack();
	tpic_init(0);
	CutEdge(-1);
	Free0(youshi);
	Free0(rectcut_org);
	Free0(dviout_dimension.page_index);
	KeepPara(-2);
	for(i=0; i < 3; i++){
		if(buffers[i].size)
			Free0(buffers[i].start);
	}
	SetPoint(0,0,-1);
	for(tbl = SetPara(NULL, GET_TOP); tbl->option; tbl++){
		switch(tbl->type){
			case STRING:
			case HIDDEN_STRING:
				Free0(*(char **)tbl->var);
				*(char **)tbl->var = NULL;
				break;

			case PROCEDURE:
			case HIDDEN_PROCEDURE:
				if((i = tbl->top) != 0)
					Free0(option_str[i]);
				option_str[i] = NULL;
				break;
		}
	}
#ifdef	USE_SUBFONT
	release_sfd_record();
#endif
#ifdef	DVI_ADD
	DeleteAddFile(1, "");
#endif
//	marea(0x12321);			// for debug by using CHKMEM in misc.c
}

#ifdef	WMF
#define	META32_SIGNATURE	0x464D4520      // ' EMF'
#define	ALDUS_ID			0x9AC6CDD7
#define APMSIZE				22

uint GetWMF(uchar *name, int mode)
{
	HMETAFILE hOld;
	HENHMETAFILE	hemf;
	HANDLE	hFile, hMapFile;
	LPVOID	pMapFile;
	int size;
	uchar *buf;
	LPENHMETAHEADER	pemh;

	if(mode==F_EMF)
		return (uint)GetEnhMetaFile(name);

	if( (hFile = CreateFile(name, GENERIC_READ, FILE_SHARE_READ, NULL, 
			OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL)) == (HANDLE)-1)
		return 0;
	if( (hMapFile = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, 
			"MapF")) == NULL){
		CloseHandle(hFile);
		return 0;
	}
	if( (pMapFile = MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, 0))
			== NULL ){
		CloseHandle(hMapFile);
		CloseHandle(hFile);
		return 0;
	}

	//	First check that if it is an enhanced metafile
	pemh = (LPENHMETAHEADER) pMapFile;
	if (pemh->dSignature == META32_SIGNATURE) {
		hemf = GetEnhMetaFile(name);
		goto exit;
	}

	//
	// If it has an ALDUS header skip it
	// Notice: APMSIZE is used because the HANDLE and RECT of the structure
	//         depends on the environment
	//
	if (*((LPDWORD)pemh) == ALDUS_ID) {

		size = *((LPDWORD) ((PBYTE)pMapFile + APMSIZE + 6));

		// Notice: mtSize is size of the file in word.
		// if LPMETAFILEPICT is NULL
		//    MM_ANISOTROPIC mode and default device size will be used.
		hemf = SetWinMetaFileBits(size*2L, (PBYTE)pMapFile + APMSIZE, 
			NULL, NULL);
		goto exit;
	}
	hOld = GetMetaFile(name);
	GetMetaFileBitsEx(hOld, 0, NULL);
	size = GetMetaFileBitsEx(hOld, 0, NULL);
	if(size == 0){
		hemf = 0;
		goto exit;
	}
	buf = (char*)marea(size);
	GetMetaFileBitsEx(hOld, size, buf);
	hemf = SetWinMetaFileBits(size, buf, NULL, NULL);
	Free0(buf);
	DeleteMetaFile(hOld);
exit:
	UnmapViewOfFile(pMapFile);
	CloseHandle(hMapFile);
	CloseHandle(hFile);
	return (uint)hemf;
}

void FreeWMF(uint hd, int mode)
{
	if(hd)
		DeleteEnhMetaFile((HENHMETAFILE)hd);
}
#endif
