/*	SCCS Id: @(#)pcsys.c	3.4	2002/01/22		  */
/* NetHack may be freely redistributed.  See license for details. */

/*
 *  System related functions for MSDOS, OS/2, TOS, and Windows NT
 */

#define NEED_VARARGS
#include "hack.h"
#include "wintty.h"

#include <ctype.h>
#include <fcntl.h>
#if !defined(MSDOS) && !defined(WIN_CE) 	/* already done */
#include <process.h>
#endif
#ifdef __GO32__
#define P_WAIT		0
#define P_NOWAIT	1
#endif
#ifdef TOS
#include <osbind.h>
#endif
#if defined(MSDOS) && !defined(__GO32__)
#define findfirst findfirst_file
#define findnext findnext_file
#define filesize filesize_nh
#endif


#if defined(MICRO) || defined(WIN32) || defined(OS2)
void FDECL(nethack_exit,(int));
#else
#define nethack_exit exit
#endif
static void NDECL(msexit);


#ifdef MOVERLAY
extern void __far __cdecl _movepause( void );
extern void __far __cdecl _moveresume( void );
extern unsigned short __far __cdecl _movefpause;
extern unsigned short __far __cdecl _movefpaused;
#define     __MOVE_PAUSE_DISK	  2   /* Represents the executable file */
#define     __MOVE_PAUSE_CACHE	  4   /* Represents the cache memory */
#endif /* MOVERLAY */

#ifdef MFLOPPY
STATIC_DCL boolean NDECL(record_exists);
# ifndef TOS
STATIC_DCL boolean NDECL(comspec_exists);
# endif
#endif

#ifdef WIN32CON
extern int GUILaunched;    /* from nttty.c */
#endif

#ifdef MICRO

void
flushout()
{
	(void) fflush(stdout);
	return;
}

static const char *COMSPEC =
# ifdef TOS
"SHELL";
# else
"COMSPEC";
# endif

#define getcomspec() nh_getenv(COMSPEC)

# ifdef SHELL
int
dosh()
{
	extern char orgdir[];
	char *comspec;
# ifndef __GO32__
	int spawnstat;
# endif
#if defined(MSDOS) && defined(NO_TERMS)
	int grmode = iflags.grmode;
#endif
	if ((comspec = getcomspec())) {
#  ifndef TOS	/* TOS has a variety of shells */
		suspend_nhwindows("To return to NetHack, enter \"exit\" at the system prompt.\n");
#  else
#   if defined(MSDOS) && defined(NO_TERMS)
		grmode = iflags.grmode;
#   endif
		suspend_nhwindows((char *)0);
#  endif /* TOS */
		chdirx(orgdir, 0);
#  ifdef __GO32__
		if (system(comspec) < 0) {  /* wsu@eecs.umich.edu */
#  else
#   ifdef MOVERLAY
       /* Free the cache memory used by overlays, close .exe */
	_movefpause |= __MOVE_PAUSE_DISK;
	_movefpause |= __MOVE_PAUSE_CACHE;
	_movepause();
#   endif
		spawnstat = spawnl(P_WAIT, comspec, comspec, (char *)0);
#   ifdef MOVERLAY
		 _moveresume();
#   endif

		if ( spawnstat < 0) {
#  endif
			raw_printf("Can't spawn \"%s\"!", comspec);
			getreturn("to continue");
		}
#  ifdef TOS
/* Some shells (e.g. Gulam) turn the cursor off when they exit */
		if (iflags.BIOS)
			(void)Cursconf(1, -1);
#  endif
		chdirx(hackdir, 0);
		get_scr_size(); /* maybe the screen mode changed (TH) */
#  if defined(MSDOS) && defined(NO_TERMS)
		if (grmode) gr_init();
#  endif
		resume_nhwindows();
	} else
		pline("Can't find %s.",COMSPEC);
	return 0;
}
# endif /* SHELL */

# ifdef MFLOPPY

void
eraseall(path, files)
const char *path, *files;
{
	char buf[PATHLEN];
	char *foundfile;

	foundfile = foundfile_buffer();
	Sprintf(buf, "%s%s", path, files);
	if (findfirst(buf))
	    do {
	       Sprintf(buf, "%s%s", path, foundfile);
		(void) unlink(buf);
	    } while (findnext());
	return;
}

/*
 * Rewritten for version 3.3 to be faster
 */
void
copybones(mode)
int mode;
{
	char from[PATHLEN], to[PATHLEN], last[13];
	char *frompath, *topath;
	char *foundfile;
#  ifndef TOS
	int status;
	char copy[8], *comspec;
#  endif

	if (!ramdisk)
		return;

	/* Find the name of the last file to be transferred
	 */
	frompath = (mode != TOPERM) ? permbones : levels;
	foundfile = foundfile_buffer();
	last[0] = '\0';
	Sprintf(from, "%s%s", frompath, allbones);
	topath = (mode == TOPERM) ? permbones : levels;
#  ifdef TOS
	eraseall(topath, allbones);
#  endif
	if (findfirst(from))
		do {
#  ifdef TOS
			Sprintf(from, "%s%s", frompath, foundfile);
			Sprintf(to, "%s%s", topath, foundfile);
			if (_copyfile(from, to))
				goto error_copying;
#  endif
			Strcpy(last, foundfile);
		} while (findnext());
#  ifdef TOS
	else
		return;
#  else
	if (last[0]) {
		Sprintf(copy, "%cC copy",switchar());

		/* Remove any bones files in `to' directory.
		 */
		eraseall(topath, allbones);

		/* Copy `from' to `to' */
		Sprintf(to, "%s%s", topath, allbones);
		comspec = getcomspec();
		status =spawnl(P_WAIT, comspec, comspec, copy, from,
			to, "> nul", (char *)0);
	} else
		return;
#  endif /* TOS */

	/* See if the last file got there.  If so, remove the ramdisk bones
	 * files.
	 */
	Sprintf(to, "%s%s", topath, last);
	if (findfirst(to)) {
		if (mode == TOPERM)
			eraseall(frompath, allbones);
		return;
	}

#  ifdef TOS
error_copying:
#  endif
	/* Last file didn't get there.
	 */
	Sprintf(to, "%s%s", topath, allbones);
	msmsg("Can't copy \"%s\" to \"%s\" -- ", from, to);
#  ifndef TOS
	if (status < 0)
	    msmsg("can't spawn \"%s\"!", comspec);
	else
#  endif
	    msmsg((freediskspace(topath) < filesize(from)) ?
	    "insufficient disk space." : "bad path(s)?");
	if (mode == TOPERM) {
		msmsg("Bones will be left in \"%s\"\n",
			*levels ? levels : hackdir);
	} else {
		/* Remove all bones files on the RAMdisk */
		eraseall(levels, allbones);
		playwoRAMdisk();
	}
	return;
}

void
playwoRAMdisk()
{
	int c;

	msmsg("Do you wish to play without a RAMdisk? [yn] (n)");

	/* Set ramdisk false *before* exit-ing (because msexit calls
	 * copybones)
	 */
	ramdisk = FALSE;
	c = tgetch(); if (c == 'Y') c = 'y';
	if (c != 'y') {
		settty("Be seeing you...\n");
		nethack_exit(EXIT_SUCCESS);
	}
	set_lock_and_bones();
	return;
}

int
saveDiskPrompt(start)
int start;
{
	char buf[BUFSIZ], *bp;
	char qbuf[QBUFSZ];

	int fd;

	if (flags.asksavedisk) {
		/* Don't prompt if you can find the save file */
		if ((fd = open_savefile()) >= 0) {
			(void) close(fd);
			return 1;
		}
		clear_nhwindow(WIN_MESSAGE);
		pline("If save file is on a save disk, insert that disk now.");
		mark_synch();
		Sprintf(qbuf,"File name (default \"%s\"%s) ?", SAVEF,
			start ? "" : ", <Esc> cancels save");
		getlin(qbuf, buf);
		clear_nhwindow(WIN_MESSAGE);
		if (!start && *buf == '\033')
			return 0;

		/* Strip any whitespace. Also, if nothing was entered except
		 * whitespace, do not change the value of SAVEF.
		 */
		for (bp = buf; *bp; bp++)
			if (!isspace(*bp)) {
				strncpy(SAVEF, bp, PATHLEN);
				break;
			}
	}
	return 1;
}

/* Return 1 if the record file was found */
STATIC_OVL boolean
record_exists()
{
	FILE *fp;

	fp = fopen_datafile(RECORD, "r", TRUE);
	if (fp) {
		fclose(fp);
		return TRUE;
	}
	return FALSE;
}
#endif /* MFLOPPY */

# ifdef TOS
#define comspec_exists() 1
# else
#  ifdef MFLOPPY
/* Return 1 if the comspec was found */
STATIC_OVL boolean
comspec_exists()
{
	int fd;
	char *comspec;

	if ((comspec = getcomspec()))
		if ((fd = open(comspec, O_RDONLY)) >= 0) {
			(void) close(fd);
			return TRUE;
		}
	return FALSE;
}
#  endif /* MFLOPPY */
# endif


# ifdef MFLOPPY
/* Prompt for game disk, then check for record file.
 */
void
gameDiskPrompt()
{
	if (flags.asksavedisk) {
		if (record_exists() && comspec_exists())
			return;
		(void) putchar('\n');
		getreturn("when the game disk has been inserted");
	}
	if (comspec_exists() && record_exists())
		return;

	if (!comspec_exists())
		msmsg("\n\nWARNING: can't find command processor \"%s\"!\n", getcomspec());
	if (!record_exists())
		msmsg("\n\nWARNING: can't find record file \"%s\"!\n", RECORD);
	msmsg("If the game disk is not in, insert it now.\n");
	getreturn("to continue");
	return;
}
# endif /* MFLOPPY */
#endif /* MICRO */

/*
 * Add a backslash to any name not ending in /, \ or :	 There must
 * be room for the \
 */
void
append_slash(name)
char *name;
{
	char *ptr;

	if (!*name)
		return;
	ptr = name + (strlen(name) - 1);
	if (*ptr != '\\' && *ptr != '/' && *ptr != ':') {
		*++ptr = '\\';
		*++ptr = '\0';
	}
	return;
}

void
getreturn(str)
const char *str;
{
#ifdef TOS
	msmsg("Hit <Return> %s.", str);
#else
	msmsg("Hit <Enter> %s.", str);
#endif
	while (Getchar() != '\n') ;
	return;
}

void
msmsg VA_DECL(const char *, fmt)
	VA_START(fmt);
	VA_INIT(fmt, const char *);
# if defined(MSDOS)
	if (iflags.grmode)
		gr_finish();
# endif
	Vprintf(fmt, VA_ARGS);
	flushout();
	VA_END();
	return;
}

/*
 * Follow the PATH, trying to fopen the file.
 */
#ifdef TOS
# ifdef __MINT__
#define PATHSEP ':'
# else
#define PATHSEP ','
# endif
#else
#define PATHSEP ';'
#endif

FILE *
fopenp(name, mode)
const char *name, *mode;
{
	char buf[BUFSIZ], *bp, *pp, lastch = 0;
	FILE *fp;

	/* Try the default directory first.  Then look along PATH.
	 */
	(void) strncpy(buf, name, BUFSIZ - 1);
	buf[BUFSIZ-1] = '\0';
	if ((fp = fopen(buf, mode)))
		return fp;
	else {
		int ccnt = 0;
		pp = getenv("PATH");
		while (pp && *pp) {
			bp = buf;
			while (*pp && *pp != PATHSEP) {
				lastch = *bp++ = *pp++;
				ccnt++;
			}
			if (lastch != '\\' && lastch != '/') {
				*bp++ = '\\';
				ccnt++;
			}
			(void) strncpy(bp, name, (BUFSIZ - ccnt) - 2);
			bp[BUFSIZ - ccnt - 1] = '\0';
			if ((fp = fopen(buf, mode)))
				return fp;
			if (*pp)
				pp++;
		}
	}
#ifdef OS2_CODEVIEW /* one more try for hackdir */
	(void) strncpy(buf, hackdir, BUFSZ);
	buf[BUFSZ-1] = '\0';
	if ((strlen(name) + 1 + strlen(buf)) < BUFSZ - 1) {
		append_slash(buf);
		Strcat(buf,name);
	} else 
		impossible("fopenp() buffer too small for complete filename!");
	if(fp = fopen(buf,mode))
		return fp;
#endif
	return (FILE *)0;
}

#if defined(MICRO) || defined(WIN32) || defined(OS2)
void nethack_exit(code)
int code;
{
	msexit();
#ifdef MTHREAD_VIEW
	if (iflags.mthreaded) nh_thread_exit(code);  /* no return from this */
	else
#endif
	exit(code);
}

/* Chdir back to original directory
 */
#ifdef TOS
extern boolean run_from_desktop;	/* set in pcmain.c */
#endif

static void msexit()
{
#ifdef CHDIR
	extern char orgdir[];
#endif

	flushout();
#ifndef TOS
# ifndef WIN32
	enable_ctrlP(); 	/* in case this wasn't done */
# endif
#endif
#ifdef MFLOPPY
	if (ramdisk) copybones(TOPERM);
#endif
#ifdef CHDIR
	chdir(orgdir);		/* chdir, not chdirx */
	chdrive(orgdir);
#endif
#ifdef TOS
	if (run_from_desktop)
	    getreturn("to continue"); /* so the user can read the score list */
# ifdef TEXTCOLOR
	if (colors_changed)
		restore_colors();
# endif
#endif
#ifdef WIN32CON
	/* Only if we started from the GUI, not the command prompt,
	 * we need to get one last return, so the score board does
	 * not vanish instantly after being created.
	 * GUILaunched is defined and set in nttty.c.
	 */

	if (GUILaunched) getreturn("to end");
#endif
	return;
}
#ifdef WIN32
/*
 * This is a kludge.  Just before the release of 3.3.0 the latest
 * version of a popular MAPI mail product was found to exhibit
 * a strange result where the current directory was changed out
 * from under NetHack resulting in a failure of all subsequent
 * file operations in NetHack.  This routine is called prior
 * to all file open/renames/deletes in file.c.
 *
 * A more elegant solution will be sought after 3.3.0 is released.
 */
void dircheck()
{
	char dirbuf[BUFSZ];
	dirbuf[0] = '\0';
	if (getcwd(dirbuf, sizeof dirbuf) != (char *)0)
		/* pline("%s,%s",dirbuf,hackdir); */
		if (strcmp(dirbuf,hackdir) != 0)
			chdir(hackdir);		/* chdir, not chdirx */
}
#endif
#endif /* MICRO || WIN32 || OS2 */
