#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <ctype.h>

#ifdef  UNIX
#include <stdlib.h>
#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>
#include <sys/stat.h>
# ifdef	_POSIX_VERSION
#include <termios.h>
# else
#include <sgtty.h>
#  ifndef CBREAK
#define CBREAK	0x00000002
#  endif
# endif
#else			/* !defined(UNIX) */
#include <dos.h>
# ifdef MSVC
#include "msvcdir.h"
# else
#include <dir.h>
# endif
#include <io.h>
# ifdef WIN32
# define MY_WIN32	1
# endif
# if defined(WIN32)||defined(WIN32C)
#include "dviout.h"
// # include <winbase.h>
# endif
#endif

#include "dd.h"

#ifdef MY_WIN32
#include "err.h"
#endif

/* p_out.c */
void pr_esc_chk(void);

/* vfont.c */
int AccessTT(char *);

extern int machine;
extern char *pr_port_str;
extern int pr_port_num;
extern int pr_port_wait;
extern int pr_ioport;

#define	PRN_BIOSP	0x1a	/* PC9801 */
#define	PRN_BIOSD	0x17	/* J3100, AX, DOSV ,DOSC */
#define	_ZF			0x40


#ifdef	MY_WIN32
#define	CHKMEM	0		// If this is TRUE, we cannot make LOG

#if	CHKMEM
#define	MAX_MEM		5000
static	char *chk_mem[MAX_MEM];
static	int   chk_num[MAX_MEM];
static	int mem_request;
static	int mem_num;
static	FILE *chk_fp;
#endif

void *marea(unsigned int size)
{
	char *ptr;
	MEMORYSTATUS	ms;

	if(size == 0)
		size = 1;

	if( (ptr = (char *)GlobalAlloc(GMEM_FIXED, size)) == NULL){
		ms.dwLength = sizeof(MEMORYSTATUS);
		GlobalMemoryStatus(&ms);
		error(NO_MEMORY, 	"Not enough memory\n"
					"Total  Phys.  : %d\n"
					"Avail Phys.  : %d\n"
					"Avail Virtual: %d\n"
					"Requested: %d",
					ms.dwTotalPhys, ms.dwAvailPhys, ms.dwAvailVirtual, 
					size);
	}
#if CHKMEM
	if(size > 0x10000){
		GlobalMemoryStatus(&ms);
# if CHKMEM & 1
		error(WARNING, "Memory\n"
					"Total  Phys.  : %d\n"
					"Avail Phys.  : %d\n"
					"Avail Virtual: %d\n"
					"Requested: %d",
					ms.dwTotalPhys, ms.dwAvailPhys, ms.dwAvailVirtual, 
					size);
# endif
		fprintf(chk_fp, "Memory\n"
					"Total  Phys. : %d\n"
					"Avail Phys.  : %d\n"
					"Avail Virtual: %d\n"
					"Requested: %d\n",
					ms.dwTotalPhys, ms.dwAvailPhys, ms.dwAvailVirtual, 
					size);
	}
	{
		int	i, j;

		if(chk_fp == NULL)
			chk_fp = fopenf("dvimem.log", "w");
		for(i=0; i < MAX_MEM; i++){
			if(chk_mem[i] == NULL){
				chk_mem[i] = (char*)ptr;
				chk_num[i] = ++mem_num;
				break;
			}
		}
		mem_request++;
# if CHKMEM & 1
		error(WARNING, "%d:%d %p(%d)", mem_num, mem_request, ptr, size);
# endif
		fprintf(chk_fp, "%3d:%3d %p(%d)\n", mem_num, mem_request, ptr, size);
		if(size == 0x12321){
			int k, nn;
			k = i;
			fprintf(chk_fp, "-- Used Memory list --\n");
			for(i=j=nn=0; i < MAX_MEM; i++){
				if(chk_mem[i] != NULL && i != k){
					nn++;
					error(C_MSG, "%d\n", chk_num[i]);
					fprintf(chk_fp, "%d ", chk_num[i]);
					if(!(++j & 0xf))
						fprintf(chk_fp, "\n");
				}
			}
			if(!nn)
				fprintf(chk_fp, "No unreleased memory");
# if CHKMEM & 1
			error(WARNING, "\n");
# endif
			fprintf(chk_fp, "\n");
		}
	}
#endif
	return ptr;
}

void Free(void *ptr)
{
#if	CHKMEM
	{
		int	i;

		for(i=0; i < MAX_MEM; i++){
			if(chk_mem[i] == (char*)ptr){
				chk_mem[i] = NULL;
				mem_request--;
# if CHKMEM & 1
				error(WARNING, "%d:%d Free(%p)", chk_num[i], mem_request, ptr);
# endif
				fprintf(chk_fp, "%3d:%3d Free(%p)\n", chk_num[i], mem_request, ptr);
				break;
			}
		}
	}
#endif
	GlobalFree(ptr);
}

void free_bmp(BUFFER *bmp)
{
#if	CHKMEM
	{
		int	i;

		for(i=0; i < MAX_MEM; i++){
			if(chk_mem[i] == (char*)bmp){
				chk_mem[i] = NULL;
				mem_request--;
# if CHKMEM & 1
				error(WARNING, "%3d: Free(%p)", chk_num[i], bmp);
# endif
				fprintf(chk_fp, "%3d: Free(%p)\n", chk_num[i], bmp);
				break;
			}
		}
		if(i == MAX_MEM){
			error(ERRPAUSE, "error in Free(%p)", bmp);
			fprintf(chk_fp, "error in Free(%p)", bmp);
		}
	}
#endif
	GlobalFree(bmp);
}

void CheckMem(void)
{
#if	CHKMEM
	int i;

	fprintf(chk_fp, "-- Result --\n");

	for(i=0; i < MAX_MEM; i++){
		if(chk_mem[i] != NULL){
# if CHKMEM & 1
			error(WARNING, "%3d: NotFree(%p)", chk_num[i], chk_mem[i]);
# endif
			fprintf(chk_fp, "%3d: NotFree(%p)\n", chk_num[i], chk_mem[i]);
		}
	}
#endif
}


char *dup_string(char *s)
{
	char *ptr;
#if	CHKMEM
		fprintf(chk_fp, "s ");
#endif
	if(s == NULL || (ptr = (char*)marea(strlen(s) + 1)) == NULL)
		return NULL;
	return strcpy(ptr, s);
}
#else
#define	Free(x)	free(x)
#endif

void Free0(void *pt)
{
	if(pt != NULL)
		Free(pt);
}


#if	defined(UNIX)
static DIR  *dir_p[MAX_KPS_DEPTH];
static char dir_name[MAX_KPS_DEPTH][MAXPATH];

static void FlushFindDir(int level)
{
	if(dir_p[level] != NULL){
		closedir(dir_p[level]);
		dir_p[level] = NULL;
	}
}

char *FindNextDir(int level)
{
	struct dirent* dp;
	char name[MAXPATH];
	struct stat st;

	for(dp = readdir(dir_p[level]); dp != NULL; dp = readdir(dir_p[level])){
			if(dp->d_name[0] == '.'){
		  if(dp->d_name[1] == 0)
			continue;
		  if(dp->d_name[1] == '.' && dp->d_name[2] == 0)
			continue;
		}
		sprintf(name, "%s/%s", dir_name[level], dp->d_name);
		stat(name, &st);
		if(S_ISDIR(st.st_mode))
		  return dp->d_name;
	}
	return NULL;
}

char *FindFirstDir(char *name, int level)
{
	FlushFindDir(level);
	if((dir_p[level] = opendir(name)) == NULL)
		return NULL;
	strcpy(dir_name[level], name);
	return FindNextDir(level);
}

void FlushAllFindDir(void)
{
  int level;

	for(level = 0; level < MAX_KPS_DEPTH; level++)
		FlushFindDir(level);
}

#elif defined(USE_WINAPI)

static WIN32_FIND_DATA	wfd;
static HANDLE hfind[MAX_KPS_DEPTH];
static HANDLE hfind32;

static void FlushFindDir(int level)
{
	if(hfind[level] != INVALID_HANDLE_VALUE && hfind[level] != NULL){
		FindClose(hfind[level]);
		hfind[level] = INVALID_HANDLE_VALUE;
	}
}

void FlushAllFindDir(void)
{
	int	level;

	for(level = 0; level < MAX_KPS_DEPTH; level++)
		FlushFindDir(level);
}

char *FindNextDir(int level)
{
	do{
		if(FindNextFile(hfind[level], &wfd) == FALSE)
			return NULL;
	}while((wfd.dwFileAttributes & 
		   (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_HIDDEN))
			!= FILE_ATTRIBUTE_DIRECTORY
			|| !strcmp(wfd.cFileName, ".")
			|| !strcmp(wfd.cFileName, "..")
	);
	return (char *)&(wfd.cFileName[0]);
}

char *FindFirstDir(LPCTSTR name, int level)
{
	FlushFindDir(level);
	hfind[level] = FindFirstFile(name, &wfd);
	if(hfind[level] == INVALID_HANDLE_VALUE)
		return NULL;
	return((wfd.dwFileAttributes & 
		  (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_HIDDEN))
			== FILE_ATTRIBUTE_DIRECTORY
		  && strcmp(wfd.cFileName, ".") && strcmp(wfd.cFileName, ".."))?
		(char *)&(wfd.cFileName[0]):FindNextDir(level);
}

char *FindNextW32(void)
{
	do{
		if(FindNextFile(hfind32, &wfd) == FALSE)
			return NULL;
	}while((wfd.dwFileAttributes & 
		   (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_HIDDEN
			|FILE_ATTRIBUTE_SYSTEM)));
	return (char *)&(wfd.cFileName[0]);
}

char *FindFirstW32(char *name)
{
	if(hfind32 != NULL && hfind32 != INVALID_HANDLE_VALUE)
		FindClose(hfind32);
	if(name == NULL || 
	  (hfind32 = FindFirstFile(name, &wfd)) == INVALID_HANDLE_VALUE)
		return NULL;
	return( (wfd.dwFileAttributes & 
			(FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_HIDDEN
		   |FILE_ATTRIBUTE_SYSTEM))?
		FindNextW32():(char *)&(wfd.cFileName[0]));
}

int mult20(int m, int n)
{
	return MulDiv(m,n,0x100000);
}

void sleep0(int num)
{
	Sleep(num);
}

void CALLBACK Timer9Proc(HWND hWnd, UINT uMsg, UINT idEvent, DWORD dwTime)
{
	if(CheckRenew(0) == 1){
		id_dvi++;
		PostMessage( g_winData.dviwin.hWnd, WM_COMMAND, MAKEWPARAM(ID_REOPEN,0), 0L );
	}
}

void CALLBACK Timer7Proc(HWND hWnd, UINT uMsg, UINT idEvent, DWORD dwTime)
{
	KillTimer(g_winData.hMainWnd, 7);
	PostMessage( g_winData.dviwin.hWnd, WM_USER_EXECMACRO, 0, 0 );
}

void CALLBACK Timer10Proc(HWND hWnd, UINT uMsg, UINT idEvent, DWORD dwTime)
{
	KillTimer(g_winData.hMainWnd, 10);
	DisplayMessage(NULL);
}

char *Language(int mode)
{
	LANGID	langid;
	static char lang[64];
	static int japanese;

	if(!japanese || mode){
		// 修正 by 川崎高志 (Oct. 1999)
		// GetSystemDefaultLangID() からユーザの設定を尊重するように変更
		langid = GetUserDefaultLangID();
		VerLanguageName(langid, lang, 63);

		// japanese = strcmp(lang, "日本語")?1:2;
		// International Build では、"Japanese"と返される
		if(!japanese){
			japanese = 
				(strcmp(lang, "日本語") == 0 || strcmp(lang, "Japanese") == 0)?2:1;
			if(!(japanese & 2) && (!AccessTT("ＭＳ 明朝")) && (!AccessTT("ＭＳ ゴシック")))
				japanese = 2;
		}
		if(mode){
			if(mode == 2)
				japanese = 2;
			else if(mode == 3)
				japanese = 3;
			return lang;
		}
	}
	return(japanese&2)?lang:NULL;
}

BOOL IsJapanese(void)
{
	return (Language(0)!=NULL)?TRUE:FALSE;
}

  
/*
BOOL IsWindowsNT( void )
{
	OSVERSIONINFO osversion;
	memset( &osversion, 0, sizeof(OSVERSIONINFO) );
	osversion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
	GetVersionEx(&osversion);
	return (osversion.dwPlatformId == VER_PLATFORM_WIN32_NT)?TRUE:FALSE;
}
*/

BOOL IsPC98( void )
{
	int r  = GetKeyboardType(1) & 0xFF00;
	return (r==0x0D00)?TRUE:FALSE;
}

void get_windows_directory(char *buf)
{
	GetWindowsDirectory(buf, MAX_PATH);
}

static void WaitProcess(PROCESS_INFORMATION *pi)
{
	if(pi->hProcess){
		WaitForSingleObject( pi->hProcess, INFINITE );
		CloseHandle(pi->hProcess);
		if(pi->hThread)
			CloseHandle(pi->hThread);
	}
	pi->hProcess = pi->hThread = NULL;
}

#ifdef	MY_WIN32
extern BOOL f_Wshow;
#endif

BOOL WinMinExecute(char **argv, int mode, BOOL hide)
{
	STARTUPINFO si;
	static PROCESS_INFORMATION pi;
	static int f_count;
	char var[1024];
	int argc;

	var[0] = 0;
	argc = 0;
	while(argv[argc] != NULL && strlen(var) + strlen(argv[argc] ) < 1020 ){
		strcat(var, " ");
		strcat(var, argv[argc++]);
	}
	if(f_count){
		WaitProcess( &pi );
		f_count = 0;
	}
	memset( &si, 0, sizeof(STARTUPINFO) );
	memset( &pi, 0, sizeof(PROCESS_INFORMATION) );
	si.cb = sizeof(STARTUPINFO);
#ifdef	MY_WIN32
	if(!f_Wshow && !(mode&2)){
		si.dwFlags = STARTF_USESHOWWINDOW;
		si.wShowWindow = hide?SW_HIDE:SW_SHOWMINIMIZED;
	}
#endif
	if(CreateProcess(NULL, var+1, NULL, NULL, FALSE,
		HIGH_PRIORITY_CLASS, // 0, 
		NULL, NULL, &si, &pi) == FALSE)
		return FALSE;
	if(!(mode&1))
		WaitProcess(&pi);
	else
		f_count = 1;
	return TRUE;
}

# ifdef WIN32C
int get_version()
{
	return GetVersion();
}
# endif
#endif			// END UNIX WIN32 WIN32C

#ifdef	GCC
long filelength(int fd)
{
  long pos = lseek(fd, 0, SEEK_CUR);
  long end = lseek(fd, 0, SEEK_END);
  lseek(fd, pos, SEEK_SET);
  return end;
}

int strcmpi(const char *s1, const char *s2)
{
	int	i, j;

	for(i=0; s2[i]; i++){
		if((j = toupper(s1[i]) - toupper(s2[i])) != 0)
		return j;
	}
	return s1[i];
}

#ifdef  UNIX

static char cbuf = -1;
void cbreak(int,int);

# if defined(HAVE_ON_EXIT) && !defined(HAVE_ATEXIT)
#define atexit(func)  on_exit(func, NULL);
void nocbreak(int status, void *dummy)
# else
void nocbreak(void)
# endif
{
	cbreak(0, 1);
}

# ifdef	_POSIX_VERSION

void cbreak(int fd, int off)    /* okumura */
{

	static int fdsave = -1;
	static struct termios tsave;
	struct termios t;

	if (! off) {                /* turn on CBREAK mode */
		if (fd < 0 || fdsave != -1) return; /* error */
		if (tcgetattr(fd, &tsave) < 0) return; /* error */
		t = tsave;              /* struct copy */
		t.c_lflag &= ~(ECHO | ICANON);
		t.c_cc[VMIN] = 0;
		t.c_cc[VTIME] = 0;
		if (tcsetattr(fd, TCSAFLUSH, &t) < 0) return; /* error */
		fdsave = fd;
		atexit(nocbreak);
	} else if (fd == fdsave) {
		fdsave = -1;
		if (tcsetattr(fd, TCSAFLUSH, &tsave) < 0) return; /* error */
	}
}
 
# else		/* !defined(_POSIX_VERSION) */

void cbreak(int fd, int on)
{

	struct sgttyb t;
	int flags, block, nonblock;

	ioctl(fd, TIOCGETP, &t);
	flags = fcntl(fd, F_GETFL, 0);
	block = flags & ~O_NONBLOCK;
	nonblock = flags | O_NONBLOCK;
		fcntl(fd, F_SETFL, on? block: nonblock);
	if(on)
		t.sg_flags &= ~CBREAK;   /* or RAW */
	else{
		atexit(nocbreak);
		t.sg_flags |= CBREAK;  /* or RAW */
	}
	ioctl(fd, TIOCSETP, &t);
}
# endif		/* eof _POSIX_VERSION */
int kbhit(void)
{
   char c;

   if (cbuf != -1)
	   return 1;
   switch (read(0, &c, 1)) {
	   case -1:
	   case 0:
			 return 0;
	   default:
			  break; 
	}
	return (cbuf = (c & 0377));
}

int getch(void)
{
	char c;

	if (cbuf != -1) {
		c = cbuf;
		cbuf = -1;  
	}else {
 rep:
	  switch(read(0, &c, 1)){
		 case 0:
		 case -1:
			 goto rep;
	 default:
			 break;
	  }
	}
	return (c & 0377);
}

char *strupr(char *p)
{
  char *original = p;

  while (*p != '\0') {
	*p = toupper(*p);
	++p;
  }
  return original;
}

char *strlwr(char *p)
{
  char *original = p;

  while (*p != '\0') {
	*p = tolower(*p);
	++p;
  }
  return original;
}

#elif defined(GCC1)
int KbHit(void)
{
#if	defined(PC9801) && defined(DOSV)
	return (machine == M_PC98)? kbhit98():kbhit();
#else
# ifdef	PC9801
	return	kbhit98();
# else
	return	kbhit();
# endif
#endif
}

int	GetCh(void)
{
#if	defined(PC9801) && defined(DOSV)
	return (machine == M_PC98)? getch98():getch();
#else
# ifdef	PC9801
	return	getch98();
# else
	return	getch();
# endif
#endif
}
#endif
#endif

#if	!defined(NO_OUTP) && !defined(WIN32C)

static void dummy_wait()
{
	static int i;
	i++;
}

void PrFlush(char *pr_buf, int pr_buf_len)
{
	unsigned char c;
	int	i, k;
	union REGS inregs, outregs;
	struct SREGS sregs;

#ifdef PC9801
# ifndef	GCC
	BOOL repeat;
# endif

# ifdef	DOSV
	if(machine != M_PC98)
		goto no_pc98;
# endif

# ifdef	GCC
	for (i = 0; i < pr_buf_len; i++){
		while (!(inportb(0x42) & 0x04))
			pr_esc_chk();
		outportb(0x40, pr_buf[i]);
		outportb(0x46, 0x0e);	/* out strobe */
		for (k = pr_port_wait; k > 0; k--)
			dummy_wait();
		outportb(0x46, 0x0f);	/* reset strobe */
	}
# else
	inregs.x.cx = pr_buf_len;
	do {
		inregs.h.ah = 0x30;
		int86x(PRN_BIOSP, &inregs, &outregs, &sregs);
		{				/*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);
# endif
# ifdef DOSV
	return;
# endif
#endif  /* end PC9801 */

#if	!defined(PC9801) || defined(DOSV)		/* J3100, AX, DOS/V */
no_pc98:
	if (pr_port_num <= 0) {
# ifdef	J3100					/* J-3100 : BIOS 経由の出力 */
#  ifdef DOSV
		if(machine != M_J31) goto dosv;
#  endif
#  ifdef	GCC
		sregs.es = ((uint)pr_buf) >> 16;
		inregs.x.bx = (uint)pr_buf & 0xffff;
#  else
		sregs.es = FP_SEG(pr_buf);
		inregs.x.bx = FP_OFF(pr_buf);
#  endif
		inregs.x.cx = pr_buf_len;
		do {
			inregs.x.ax = 0x300;
			inregs.x.dx = 0;
			int86x(PRN_BIOSD, &inregs, &outregs, &sregs);
			inregs.x.bx = outregs.x.bx;
			inregs.x.cx = outregs.x.cx;
		} while (outregs.h.ah & 1);
#  ifdef	DOSV
		return;
#  endif
# endif	/* end J-3100 : BIOS */

# if defined(AX)|| defined(DOSV)		/* AX, DOS/V : BIOS 経由の出力 */
dosv:	for (i = 0; i < pr_buf_len; i++) {
			do {
				inregs.h.ah = 0x02;
				inregs.x.dx = (-pr_port_num);
				int86(0x17, &inregs, &outregs);
				pr_esc_chk();
			} while ((outregs.h.ah & 0x29) != 0 || (outregs.h.ah & 0x80) == 0);
			inregs.h.ah = 0;
			inregs.h.al = pr_buf[i];
			int86(PRN_BIOSD, &inregs, &outregs);
		}
# endif /* end AX, DOS/V : BIOS */

		/* J3100, AX, DOS/V : IO-PORT 直接アクセスによる出力 */
	}
	else {
		for (i = 0; i < pr_buf_len; i++) {
			while (!(inportb(pr_ioport + 1) & 0x80)) {
				pr_esc_chk();	/* 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 /* end J3100, AX, DOS/V */
}
#endif

