This brings things much closer to correct operation (I hope). - The shift to only moving the cursor on input (<Someone>'s changes) had a lot of complications, among them was that sometimes, there is no more input. When the program was exiting, or bombing the cursor synch never got done, so the final messages could end up strewn any place the cursor happened to be dwelling. - There were two competing output systems in use: the wintty stuff for the game, and the msmsg and error stuff used by the sys/share/pcsys, sys/share/pctty, and sys/share/pcunix routines. Those were meant to mimic output to stdout, where stuff just got sent to a sequential display. Over time, there were calls mixed in that depended on the cursor tracked stuff from the core game, so you really couldn't be sure where things were going to display. It wasn't as much of an issue before, because the cursor really did get moved around as expected. Everything now ends up in the same output system. - I even found a use of the real putchar() because sys/share/pcunix didn't #include wintty.h the same as the other files, and the macro never got defined. Who knows where that character was being put - the game certainly couldn't track it. While everything I knew to be wrong yesterday is now working, there may be some other glitches lurking that I haven't discovered yet. Please: test, test, test.
542 lines
10 KiB
C
542 lines
10 KiB
C
/* 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
|
|
|
|
#if defined(MICRO) || defined(WIN32)
|
|
|
|
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 */
|
|
# ifndef NOCWD_ASSUMPTIONS
|
|
chdirx(orgdir, 0);
|
|
# endif
|
|
# 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
|
|
# ifndef NOCWD_ASSUMPTIONS
|
|
chdirx(hackdir, 0);
|
|
# endif
|
|
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;
|
|
}
|
|
|
|
#ifdef WIN32
|
|
boolean getreturn_enabled;
|
|
#endif
|
|
|
|
void
|
|
getreturn(str)
|
|
const char *str;
|
|
{
|
|
#ifdef WIN32
|
|
if (!getreturn_enabled) return;
|
|
#endif
|
|
#ifdef TOS
|
|
msmsg("Hit <Return> %s.", str);
|
|
#else
|
|
msmsg("Hit <Enter> %s.", str);
|
|
#endif
|
|
while (Getchar() != '\n') ;
|
|
return;
|
|
}
|
|
|
|
#ifndef WIN32CON
|
|
void
|
|
msmsg VA_DECL(const char *, fmt)
|
|
VA_START(fmt);
|
|
VA_INIT(fmt, const char *);
|
|
# if defined(MSDOS) && defined(NO_TERMS)
|
|
if (iflags.grmode)
|
|
gr_finish();
|
|
# endif
|
|
Vprintf(fmt, VA_ARGS);
|
|
flushout();
|
|
VA_END();
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* 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();
|
|
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
|
|
#if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS)
|
|
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.
|
|
*/
|
|
synch_cursor();
|
|
if (GUILaunched) getreturn("to end");
|
|
synch_cursor();
|
|
#endif
|
|
return;
|
|
}
|
|
#endif /* MICRO || WIN32 || OS2 */
|