[rec.games.hack] nethack for os9/68k 1 of 2

blarson@skat.usc.edu (Bob Larson) (02/01/88)

Here are the additional files and changes needed to make nethack 2.2
work on os9/68k, including a couple of minor bug fixes, portability fixes,
and some hacks to get it to work.  Read "readme.osk".

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:    Shell Archiver
#	Run the following text with /bin/sh to create:
#	config.h.osk
#	makefile.osk
#	readme.osk
#	osk.c
#	osktty.c
#	oskunix.c
# By:	Jim Omura ()
cat << \SHAR_EOF > config.h.osk
/*	SCCS Id: @(#)config.h	2.2	87/11/11
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */

#ifndef CONFIG	/* make sure the compiler does not see the typedefs twice */
#define	CONFIG

#define	CHDIR		/* delete if no chdir() available */

/*
 * Some include files are in a different place under SYSV
 * 	BSD		   SYSV
 * <strings.h>		<string.h>
 * <sys/time.h>		<time.h>
 * <sgtty.h>		<termio.h>
 * Some routines are called differently
 * index		strchr
 * rindex		strrchr
 * Also, the code for suspend and various ioctls is only given for BSD4.2
 */
/* #define MSDOS 	/* define for MS-DOS (actually defined by compiler) */
/* #define	UNIX		/* delete if no fork(), exec() available */
/* #define	GENIX		/* Yet Another Unix Clone */
/* #define BSD		/* defind for 4.n BSD  */
/* #define SYSV		/* define for System V */
/* #define OSK		/* defined by os9/68k C compiler */

/* #define BETA		/* if a beta-test copy  [MRS] */
#define VERSION	"2.2a"	/* version number. */

/* #define PYRAMID_BUG 	/* avoid a bug on the Pyramid */
/* #define APOLLO		/* same for the Apollo */
/* #define STUPID		/* avoid some complicated expressions if
			   your C compiler chokes on them */
/* #define TERMINFO		/* uses "curses" rather than termcap */

#ifdef __TURBOC__
#define	alloc	malloc
#define	signal	ssignal
#endif

#define WIZARD  "blarson" /* the person allowed to use the -D option */
#define RECORD	"record"/* the file containing the list of topscorers */
#define	NEWS	"news"	/* the file containing the latest hack news */
#define	HELP	"help"	/* the file containing a description of the commands */
#define	SHELP	"hh"	/* abbreviated form of the same */
#define	RUMORFILE	"rumors"	/* a file with fortune cookies */
#define	DATAFILE	"data"	/* a file giving the meaning of symbols used */
#ifdef OSK
#include <modes.h>
#define	FMASK	(S_IREAD|S_IWRITE)	/* file creation mask */
#else
#define	FMASK	0660	/* file creation mask */
#endif

#ifdef UNIX
/* #define	HLOCK	"perm"	/* an empty file used for locking purposes */
/* #define LLOCK	"safelock"	/* link to previous */

/*
 * Define DEF_PAGER as your default pager, e.g. "/bin/cat" or "/usr/ucb/more"
 * If defined, it can be overridden by the environment variable PAGER.
 * Hack will use its internal pager if DEF_PAGER is not defined.
 * (This might be preferable for security reasons.)
 * #define DEF_PAGER	".../mydir/mypager"
 */

/*
 * If you define MAIL, then the player will be notified of new mail
 * when it arrives. If you also define DEF_MAILREADER then this will
 * be the default mail reader, and can be overridden by the environment
 * variable MAILREADER; otherwise an internal pager will be used.
 * A stat system call is done on the mailbox every MAILCKFREQ moves.
 */
/* #define	MAIL */
/* #define	DEF_MAILREADER	"/usr/bin/mail"		/* or e.g. /bin/mail */
/* #define	MAILCKFREQ	1 */


#define SHELL		/* do not delete the '!' command */

#ifdef BSD
#define	SUSPEND		/* let ^Z suspend the game */
#endif

#ifdef BSD
/* Use the high quality random number routines. */
extern long random();
#define rand()	random()
#define srand(seed) srandom(seed)
#else
extern long lrand48();
#define rand()	lrand48()
#define srand(seed) srand48(seed)
#endif
#endif /* UNIX /**/

#ifdef CHDIR
/*
 * If you define HACKDIR, then this will be the default playground;
 * otherwise it will be the current directory.
 */
#define HACKDIR	"/dd/games/lib/nethackdir"

/*
 * Some system administrators are stupid enough to make Hack suid root
 * or suid daemon, where daemon has other powers besides that of reading or
 * writing Hack files. In such cases one should be careful with chdir's
 * since the user might create files in a directory of his choice.
 * Of course SECURE is meaningful only if HACKDIR is defined.
 */
/* #define SECURE			/* do setuid(getuid()) after chdir() */

/*
 * If it is desirable to limit the number of people that can play Hack
 * simultaneously, define HACKDIR, SECURE and MAX_NR_OF_PLAYERS.
 * #define MAX_NR_OF_PLAYERS	6
 */
#endif /* CHDIR /**/

/* size of terminal screen is (at least) (ROWNO+2) by COLNO */
#define	COLNO	80
#define	ROWNO	22

#ifdef BSD
#include <strings.h>		/* declarations for strcat etc. */
#define memcpy(d, s, n)		bcopy(s, d, n)
#define memcmp(s1, s2, n)	bcmp(s2, s1, n)
#else
#ifdef OSK
#include <strings.h>
#include <time.h>
#else
#include <string.h>		/* idem on System V */
#define	index	strchr
#define	rindex	strrchr
#endif
#endif

/*
 * small signed integers (8 bits suffice)
 *	typedef	char	schar;
 * will do when you have signed characters; otherwise use
 *	typedef	short int schar;
 */
typedef	char	schar;

/*
 * small unsigned integers (8 bits suffice - but 7 bits do not)
 * - these are usually object types; be careful with inequalities! -
 *	typedef	unsigned char	uchar;
 * will be satisfactory if you have an "unsigned char" type; otherwise use
 *	typedef unsigned short int uchar;
 */
typedef	unsigned char	uchar;

/*
 * small integers in the range 0 - 127, usually coordinates
 * although they are nonnegative they must not be declared unsigned
 * since otherwise comparisons with signed quantities are done incorrectly
 */
typedef schar	xchar;
typedef	xchar	boolean;		/* 0 or 1 */
#define	TRUE	1
#define	FALSE	0

/*
 * Declaration of bitfields in various structs; if your C compiler
 * doesnt handle bitfields well, e.g., if it is unable to initialize
 * structs containing bitfields, then you might use
 *	#define Bitfield(x,n)	uchar x
 * since the bitfields used never have more than 7 bits. (Most have 1 bit.)
 * otherwise:
 *	#define	Bitfield(x,n)	unsigned x:n
 */
#define	Bitfield(x,n)	uchar x

#define	SIZE(x)	(int)(sizeof(x) / sizeof(x[0]))

#ifdef MSDOS
#include <fcntl.h>
#define	exit	msexit		/* do chdir first */
#ifdef getchar
#	undef getchar
#endif /* getchar /**/
#define getchar tgetch
#define DGK			/* MS DOS specific enhancements by dgk */

#ifdef DGK
#  include "msdos.h"	/* contains necessary externs for msdos.c */
#  define SHELL		/* via exec of COMMAND.COM */
#  define PATHLEN	64	/* maximum pathlength */
#  define FILENAME 80	/* maximum filename length (conservative) */
#  define FROMPERM	1	/* for ramdisk use */
#  define TOPERM	2	/* for ramdisk use */
#  define glo(x)	name_file(lock, x)	/* name_file used for bones */
	extern char *configfile;
#endif /* DGK /**/
#endif /* MSDOS /**/

/*
 *	Conditional compilation of special options are controlled here.
 *	If you define the following flags, you will add not only to the
 *	complexity of the game but also to the size of the load module.
 */ 

#define	DOGNAME		/* Name of your first dog as an option */ 
#define	SPELLS		/* Spell casting by M. Stephenson */
#define	PRAYERS		/* Prayer code by M. Stephenson */
#define KAA		/* Various changes made by Ken Arromdee */
#define MARKER		/* Magic marker modification from Gil Neiger */
#define	NEWCLASS	/* Samurai/Ninja etc. by M. Stephenson */ 
#define	SAFE_ATTACK 	/* Safe attack code by Don Kneller */
#define	PROBING		/* Wand of probing code by Gil Neiger */
#define	DIAGS		/* Diagnostics after death/quit by Gil Neiger */
#define	SORTING		/* Sorted inventory by Don Kneller */
#define	DGKMOD		/* Additional features by Don Kneller */
#define REDO 		/* support for redoing last command - DGK */
#define	HARD		/* Enhanced wizard code by M. Stephenson */
#define	WALKIES		/* Leash code by M. Stephenson */
#define NEWTRAPS	/* Magic and Squeeky board traps by Scott R. Turner*/
#define FREEHAND	/* Cannot use Pick-axe without wielding it. */
#define SPIDERS		/* Spiders and webs by Scott R. Turner */
#define FOUNTAINS	/* Fountain code by SRT (+ GAN + EB) */
#define KOPS		/* Keystone Kops by Scott R. Turner */
#define ROCKMOLE	/* Rockmoles by Scott R. Turner */
#define COM_COMPL	/* Command line completion by John S. Bien */
#define GRAPHICS	/* Funky screen character support (Eric S. Raymond) */
#define HACKOPTIONS	/* Support DGK-style HACKOPTIONS processing (ESR) */
#define RPH		/* Various hacks by Richard P. Hughey */
#define KJSMODS		/* Various changes made by Kevin Sweet */
#define	BVH		/* Additions by Bruce Holloway */
#define SAC		/* Soldiers, barracks by Steve Creps */

#if defined(MSDOS) && defined(GRAPHICS)
#define MSDOSCOLOR
#endif

/*
 *	Status Line options.
 */

#define	GOLD_ON_BOTL
#define	EXP_ON_BOTL
	 
#ifdef REDO
#define DOAGAIN	'\001'		/* Used in tty.c and cmd.c */
#endif

#ifdef DGKMOD
#define LARGEST_INT	((1 << 15) - 1)
#endif

#endif /* CONFIG /**/
SHAR_EOF
cat << \SHAR_EOF > makefile.osk
# Hack or Quest Makefile.
# osk version (based on unix version) by blarson@ecla.usc.edu
# Warning: osk make doesn't beleive the dependencies!!!!!!
# you must make each .r file by hand!

# flags may have to be changed as required
CFLAGS = -dvoid=int
LFLAGS = -i

TERMLIB = -l=/dd/lib/termlib.l

# make NetHack
GAME     = nethack
GAMEUID  = games
GAMEGRP  = bin

# GAMEDIR also appears in config.h as "HACKDIR".
GAMEDIR  = /dd/games/lib/$(GAME)dir
SHELLDIR = /dd/games
MANDIR   = /dd/man
MANEXT   = man

HACKCSRC = alloc.c apply.c bones.c cmd.c decl.c do.c do_name.c\
 do_wear.c dog.c dogmove.c dothrow.c eat.c end.c engrave.c fight.c\
 fountain.c hack.c invent.c lev.c unixmain.c makemon.c mhitu.c\
 mklev.c mkmaze.c mkobj.c mkshop.c mon.c monmove.c monst.c o_init.c\
 objnam.c options.c osk.c pager.c polyself.c potion.c pray.c pri.c\
 prisym.c read.c rip.c rnd.c rumors.c save.c search.c shk.c shknam.c\
 sit.c spell.c steal.c termcap.c timeout.c topl.c topten.c track.c\
 trap.c osktty.c u_init.c oskunix.c vault.c version.c wield.c wizard.c\
 worm.c worn.c write.c zap.c

CSOURCES = $(HACKCSRC) makedefs.c

HSOURCES = config.h date.h edog.h eshk.h extern.h flag.h func_tab.h\
 gen.h gold.h hack.h mfndpos.h mkroom.h monst.h msdos.h\
 obj.h objclass.h objects.h onames.h permonst.h rm.h\
 spell.h trap.h wseg.h you.h

SOURCES = $(CSOURCES) $(HSOURCES)

AUX = help hh nethack.$(MANEXT) nethack.sh
VARAUX = data rumors

DISTR = $(SOURCES) $(AUX) $(VARAUX) README.OLD README\
 Makefile Makefile.pc Make.ini

HOBJ1 = alloc.r apply.r bones.r cmd.r decl.r do.r do_name.r\
 do_wear.r dog.r dogmove.r dothrow.r eat.r end.r engrave.r fight.r\
 fountain.r hack.r invent.r lev.r unixmain.r makemon.r mhitu.r\
 mklev.r mkmaze.r mkobj.r mkshop.r mon.r monmove.r monst.r o_init.r osk.r
HOBJ2 = objnam.r options.r pager.r polyself.r potion.r pray.r pri.r\
 prisym.r read.r rip.r rnd.r rumors.r save.r search.r shk.r shknam.r\
 sit.r spell.r steal.r termcap.r timeout.r topl.r topten.r track.r\
 trap.r osktty.r u_init.r oskunix.r vault.r version.r wield.r wizard.r\
 worm.r worn.r write.r zap.r

nethack: $(HOBJ1) hobj2.r
 cc $(LFLAGS) -f=$(GAME) $(HOBJ1) hobj2.r $(TERMLIB)

hobj2.r: $(HOBJ2)
  merge $(HOBJ2) >-hobj2.r

makedefs: makedefs.r alloc.r osk.r config.h
 cc -f=./makedefs alloc.r makedefs.r osk.r

RUMORFILES= rumors.base rumors.kaa rumors.mrx

rumors: config.h $(RUMORFILES) makedefs
 ./makedefs -r

data: config.h data.base makedefs
 ./makedefs -d

date.h: $(SOURCES) makedefs
 ./makedefs -D

trap.h: config.h makedefs
 ./makedefs -t

onames.h: makedefs objects.h
 ./makedefs -o

unixmain.r: unixmain.c

osktty.r: osktty.c

oskunix.r: oskunix.c

initial:
 deldir -q $(GAMEDIR)
 makdir $(SHELLDIR)
 makdir $(GAMEDIR) $(GAMEDIR)/save
 touch $(GAMEDIR)/perm
 touch $(GAMEDIR)/record
 attr -r -pr $(GAMEDIR)/*
 attr -r -w -pr -pw $(GAMEDIR) $(GAMEDIR)/save

install: $(VARAUX) $(GAME)
 makdir $(GAMEDIR)
 makdir $(GAMEDIR)/save
 del $(GAMEDIR)/$(GAME)
 del $(GAMEDIR)/bones* $(GAMEDIR)/alock* $(GAMEDIR)/wizard*
 del $(GAMEDIR)/save/*
 touch $(GAMEDIR)/perm $(GAMEDIR)/record
 copy -r -b=16 -w=$(GAMEDIR) help hh rumors data
 copy -r -b=16 -w=$(GAMEDIR)/$(GAME) $(GAME)
 attr -e -pe $(GAMEDIR)/$(GAME)
 copy -r -b=16 $(GAME).$(MANEXT) $(MANDIR)/$(GAME).$(MANEXT)

clean:
 del *.r

spotless: clean
 del $(GAME) makedefs
 del Makefile $(VARAUX)

osk.r:     osk.c
apply.r:   apply.c hack.h edog.h mkroom.h
bones.r:   bones.c hack.h
hack.r:    hack.c hack.h
cmd.r:     cmd.c hack.h func_tab.h
decl.r:    decl.c hack.h mkroom.h
do.r:      do.c hack.h
do_name.r: do_name.c hack.h
do_wear.r: do_wear.c hack.h
dog.r:     dog.c hack.h edog.h mkroom.h
dogmove.r: dogmove.c hack.h mfndpos.h edog.h mkroom.h
dothrow.r: dothrow.c hack.h
eat.r:     eat.c hack.h
end.r:     end.c hack.h
engrave.r: engrave.c hack.h
fight.r:   fight.c hack.h
fountain.r: fountain.c hack.h mkroom.h
invent.r:  invent.c hack.h wseg.h
lev.r:     lev.c hack.h mkroom.h wseg.h
makemon.r: makemon.c hack.h
mhitu.r:   mhitu.c hack.h
mklev.r:   mklev.c hack.h mkroom.h
mkmaze.r:  mkmaze.c hack.h mkroom.h
mkobj.r:   mkobj.c hack.h
mkshop.r:  mkshop.c hack.h mkroom.h eshk.h
mon.r:     mon.c hack.h mfndpos.h
monmove.r: monmove.c hack.h mfndpos.h
monst.r:   monst.c hack.h eshk.h
o_init.r:  o_init.c config.h objects.h onames.h
objnam.r:  objnam.c hack.h
options.r: options.c hack.h
pager.r:   pager.c hack.h
polyself.r: polyself.c hack.h
potion.r:  potion.c hack.h
pray.r:    pray.c hack.h
pri.r:     pri.c hack.h
prisym.r:  prisym.c hack.h wseg.h
read.r:    read.c hack.h
rip.r:     rip.c hack.h
rumors.r:  rumors.c hack.h
save.r:    save.c hack.h
search.r:  search.c hack.h
shk.r:     shk.c hack.h mfndpos.h mkroom.h eshk.h
shknam.r:  shknam.c hack.h
sit.r:     sit.c hack.h
spell.r:   spell.c hack.h
steal.r:   steal.c hack.h
termcap.r: termcap.c hack.h
timeout.r: timeout.c hack.h
topl.r:    topl.c hack.h
topten.r:  topten.c hack.h
track.r:   track.c hack.h
trap.r:    trap.c hack.h edog.h mkroom.h
u_init.r:  u_init.c hack.h
vault.r:   vault.c hack.h mkroom.h
wield.r:   wield.c hack.h
wizard.r:  wizard.c hack.h
worm.r:    worm.c hack.h wseg.h
worn.r:    worn.c hack.h
write.r:   write.c hack.h
zap.r:     zap.c hack.h
version.r: version.c hack.h date.h
rnd.r:     rnd.c config.h
extern.h: config.h spell.h obj.h
 touch extern.h
hack.h: extern.h flag.h gold.h monst.h objclass.h rm.h trap.h you.h 
 touch hack.h
objects.h:  config.h objclass.h
 touch objects.h
you.h: config.h onames.h permonst.h 
 touch you.h
SHAR_EOF
cat << \SHAR_EOF > readme.osk
os9/68k nethack notes

I couldn't get make to work properly, even with the -b options.
hobj2.r is an artifact of a shell limitation.  I wound up creating
empty files and requesting .r files be made as nessisary to get it to
work.

This is not a small program.  I had to deiniz my ramdisk to get it to
load or run.  (I have 1/2 meg.)  It also takes lots of disk space.
/dd/games/lib/nethackdir gets big.  You may want to put it elsewhere.
(see makefile.osk and config.h.osk)

The lock code does not work.  You have to enforce only one game at a time
some other way (such as not enough memory) until it gets fixed.

This is based on nethack 2.2, with all its known bugs.  (Such as the dogname
one.)  I've also noticed rooms near the left edge have a blank top wall,
this gets fixed in a redraw.
SHAR_EOF
cat << \SHAR_EOF > osk.c
#include <stdio.h>

rename(old, new)
char *old, *new;
{
    int os9fork();
    int child;
    char *eargv[4];
    extern char **environ;
    int status;

    eargv[0] = "rename";
    eargv[1] = old;
    eargv[2] = new;
    eargv[3] = (char *)NULL;
    unlink(new);
    child = os9exec(os9fork, eargv[0], eargv, environ, 0, 0);
    if(child == -1) return -1;
    while(wait(&status) != child) {}
    errno = status & 0xffff;
    return errno ? -1 : 0;
}

perror(s)
char *s;
{
    extern int errno;

    fprintf(stderr, "%s: %d\n", s, errno);
}

fputc(f)
FILE *f;
{
	return putc(f);
}

/* a pseuto-random number generator of questionable quality.  Even
 * if it was ok to begin with, I probably got the translation from
 * fortran wrong.
 */

static int cseed = 12345;
static int tseed = 1073;

srand(seed)
int seed;
{
	if(seed == 1) {
		cseed = 12345;
		tseed = 1073;
	} else {
		if(seed < 0) seed = -seed;
		cseed = tseed = seed;
	}
}

int rand()
{
	tseed = (tseed << 15) | (((unsigned)tseed >> 15) ^ (tseed >> 17));
	tseed = (tseed >> 17) | ((tseed << 17) ^ (tseed << 15));
	cseed = (cseed << 16) + ((unsigned)cseed >> 11) + (cseed << 10)
		+ (cseed << 8) + (cseed << 7) + (cseed << 6) + (cseed << 3) 
		+ (cseed << 2) + cseed;
	return (tseed ^ cseed) & 0x7fffffff;
}
SHAR_EOF
cat << \SHAR_EOF > osktty.c
/*	SCCS Id: @(#)unixtty.c	2.1	87/11/09
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* tty.c - (Unix) version */

/* With thanks to the people who sent code for SYSV - hpscdi!jon,
 * arnold@ucsf-cgl, wcs@bo95b, cbcephus!pds and others.
 */

#include	<stdio.h>
#include	"extern.h"
#include	"hack.h"
#include	"func_tab.h"

/*
 * Some systems may have getchar() return EOF for various reasons, and
 * we should not quit before seeing at least NR_OF_EOFS consecutive EOFs.
 */
#ifndef BSD
#define	NR_OF_EOFS	20
#endif

#include	<sgstat.h>
#define termstruct	sgbuf
#define	kill_sym	sg_dlnch
#define	erase_sym	sg_bspch
#define EXTABS		0xffffffff
#define tabflgs		sg_tabcr
#define	ECHO		0xffffffff
#define echoflgs	sg_echo
/* #define cbrkflgs	sg_flags	*/
/* #define CBRKMASK	CBREAK		*/
/* #define CBRKON		/* empty */
#define OSPEED(x)	(x).sg_baud
#define GTTY(x)		(_gs_opt(0, x))
#define STTY(x)		(_ss_opt(0, x))
#define	getioctls()	{}
#define	setioctls()	{}

extern short ospeed;
static char erase_char, kill_char;
static boolean settty_needed = FALSE;
struct termstruct inittyb, curttyb;

/*
 * Get initial state of terminal, set ospeed (for termcap routines)
 * and switch off tab expansion if necessary.
 * Called by startup() in termcap.c and after returning from ! or ^Z
 */
gettty(){
	if(GTTY(&inittyb) < 0)
		perror("Hack (gettty)");
	curttyb = inittyb;
	ospeed = OSPEED(inittyb);
	erase_char = inittyb.erase_sym;
	kill_char = inittyb.kill_sym;
	getioctls();

	/* do not expand tabs - they might be needed inside a cm sequence */
	if(curttyb.tabflgs & EXTABS) {
		curttyb.tabflgs &= ~EXTABS;
		setctty();
	}
	settty_needed = TRUE;
}

/* reset terminal to original state */
settty(s) char *s; {
	clear_screen();
	end_screen();
	if(s) printf(s);
	(void) fflush(stdout);
	if(STTY(&inittyb) < 0)
		perror("Hack (settty)");
	flags.echo = (inittyb.echoflgs & ECHO) ? ON : OFF;
#ifdef OSK
	flags.cbreak = stdin->_flag & _UNBUF ? ON : OFF;
#else
	flags.cbreak = (CBRKON(inittyb.cbrkflgs & CBRKMASK)) ? ON : OFF;
#endif
	setioctls();
}

setctty(){
	if(STTY(&curttyb) < 0)
		perror("Hack (setctty)");
}


setftty(){
register int ef = 0;			/* desired value of flags & ECHO */
register int change = 0;
#ifndef OSK
register int cf = CBRKON(CBRKMASK);	/* desired value of flags & CBREAK */
#endif
	flags.echo = OFF;
	flags.cbreak = ON;
	/* Should use (ECHO|CRMOD) here instead of ECHO */
	if((curttyb.echoflgs & ECHO) != ef){
		curttyb.echoflgs &= ~ECHO;
/*		curttyb.echoflgs |= ef;					*/
		change++;
	}
#ifdef OSK
	if(!(stdin->_flag & _UNBUF))
		stdin->_flag |= _UNBUF;
	if(curttyb.sg_eofch) curttyb.sg_eofch = 0, change++;
	if(curttyb.sg_kbich) curttyb.sg_kbich = 0, change++;
	if(curttyb.sg_kbach) curttyb.sg_kbach = 0, change++;
#else
	if((curttyb.cbrkflgs & CBRKMASK) != cf){
		curttyb.cbrkflgs &= ~CBRKMASK;
		curttyb.cbrkflgs |= cf;
#ifdef USG
		/* be satisfied with one character; no timeout */
		curttyb.c_cc[VMIN] = 1;		/* was VEOF */
		curttyb.c_cc[VTIME] = 0;	/* was VEOL */
#endif
		change++;
	}
#endif
	if(change){
		setctty();
	}
	start_screen();
}


/* fatal error */
/*VARARGS1*/
error(s,x,y) char *s; {
	if(settty_needed)
		settty((char *) 0);
	printf(s,x,y);
	putchar('\n');
	exit(1);
}

/*
 * Read a line closed with '\n' into the array char bufp[BUFSZ].
 * (The '\n' is not stored. The string is closed with a '\0'.)
 * Reading can be interrupted by an escape ('\033') - now the
 * resulting string is "\033".
 */
getlin(bufp)
register char *bufp;
{
	register char *obufp = bufp;
	register int c;

	flags.toplin = 2;		/* nonempty, no --More-- required */
	for(;;) {
		(void) fflush(stdout);
		if((c = getchar()) == EOF) {
			*bufp = 0;
			return;
		}
		if(c == '\033') {
			*obufp = c;
			obufp[1] = 0;
			return;
		}
		if(c == erase_char || c == '\b') {
			if(bufp != obufp) {
				bufp--;
				putstr("\b \b"); /* putsym converts \b */
			} else	bell();
		} else if(c == '\n') {
			*bufp = 0;
			return;
		} else if(' ' <= c && c < '\177') {
				/* avoid isprint() - some people don't have it
				   ' ' is not always a printing char */
			*bufp = c;
			bufp[1] = 0;
			putstr(bufp);
			if(bufp-obufp < BUFSZ-1 && bufp-obufp < COLNO)
				bufp++;
		} else if(c == kill_char || c == '\177') { /* Robert Viduya */
				/* this test last - @ might be the kill_char */
			while(bufp != obufp) {
				bufp--;
				putstr("\b \b");
			}
		} else
			bell();
	}
}

getret() {
	cgetret("");
}

cgetret(s)
register char *s;
{
	putsym('\n');
	if(flags.standout)
		standoutbeg();
	putstr("Hit ");
	putstr(flags.cbreak ? "space" : "return");
	putstr(" to continue: ");
	if(flags.standout)
		standoutend();
	xwaitforspace(s);
}

char morc;	/* tell the outside world what char he used */

xwaitforspace(s)
register char *s;	/* chars allowed besides space or return */
{
register int c;

	morc = 0;

	while((c = readchar()) != '\n') {
	    if(flags.cbreak) {
		if(c == ' ') break;
		if(s && index(s,c)) {
			morc = c;
			break;
		}
		bell();
	    }
	}
}

static int last_multi;

char *
parse()
{
	static char inline[COLNO];
	register foo;

	multi = 0;
	flags.move = 1;
	if(!Invisible) curs_on_u(); else home();
	while((foo = readchar()) >= '0' && foo <= '9') {
		multi = 10*multi+foo-'0';
#ifdef DGKMOD
		if (multi < 0 || multi > LARGEST_INT)
			multi = LARGEST_INT;
		if (multi > 9) {
			remember_topl();
			home();
			cl_end();
			printf("Count: %d", multi);
		}
#endif
		last_multi = multi;
	}
# ifdef REDO
	if (foo == DOAGAIN || in_doagain)
		multi = last_multi;
	else {
		savech(0);	/* reset input queue */
		savech(foo);
	}
# endif
	if(multi) {
		multi--;
		save_cm = inline;
	}
	inline[0] = foo;
	inline[1] = 0;
	if(foo == 'g' || foo == 'G'){
		inline[1] = getchar();
#ifdef QUEST
		if(inline[1] == foo) inline[2] = getchar(); else
#endif
		inline[2] = 0;
	}
	if(foo == 'm' || foo == 'M'){
		inline[1] = getchar();
		inline[2] = 0;
	}
	clrlin();
	return(inline);
}

char
readchar() {
	register int sym;

	(void) fflush(stdout);
	if((sym = getchar()) == EOF)
#ifdef NR_OF_EOFS
	{ /*
	   * Some SYSV systems seem to return EOFs for various reasons
	   * (?like when one hits break or for interrupted systemcalls?),
	   * and we must see several before we quit.
	   */
		register int cnt = NR_OF_EOFS;
		while (cnt--) {
		    clearerr(stdin);	/* omit if clearerr is undefined */
		    if((sym = getchar()) != EOF) goto noteof;
		}
		end_of_input();
	     noteof:	;
	}
#else
		end_of_input();
#endif /* NR_OF_EOFS /**/
	if(flags.toplin == 1)
		flags.toplin = 2;
	return((char) sym);
}

end_of_input()
{
	settty("End of input?\n");
	clearlocks();
	exit(0);
}

#ifdef COM_COMPL
/* Read in an extended command - doing command line completion for
 * when enough character have been entered to make a unique command.
 * This is just a modified getlin().   -jsb
 */
get_ext_cmd(bufp)
register char *bufp;
{
	register char *obufp = bufp;
	register int c;
	int com_index, index;

	flags.toplin = 2;		/* nonempty, no --More-- required */

	for(;;) {
		(void) fflush(stdout);
		if((c = readchar()) == EOF) {
			*bufp = 0;
			return;
		}
		if(c == '\033') {
			*obufp = c;
			obufp[1] = 0;
			return;
		}
		if(c == erase_char || c == '\b') {
			if(bufp != obufp) {
				bufp--;
				putstr("\b \b"); /* putsym converts \b */
			} else	bell();
		} else if(c == '\n') {
			*bufp = 0;
			return;
		} else if(' ' <= c && c < '\177') {
				/* avoid isprint() - some people don't have it
				   ' ' is not always a printing char */
			*bufp = c;
			bufp[1] = 0;
			index = 0;
			com_index = -1;

			while(extcmdlist[index].ef_txt != (char *) 0){
				if(!strncmp(obufp, extcmdlist[index].ef_txt,
				strlen(obufp)))
					if(com_index == -1) /* No matches yet*/
					    com_index = index;
					else /* More than 1 match */
					    com_index = -2;
				index++;
			}
			if(com_index >= 0){
				strcpy(obufp,
				extcmdlist[com_index].ef_txt);
				/* finish print our string */
				putstr(bufp);
				bufp = obufp; /* reset it */
				if(strlen(obufp) < BUFSIZ-1 &&
				 strlen(obufp) < COLNO)
					/* set bufp at the end of our
					 * string
					 */
					bufp += strlen(obufp);
			} else {
				putstr(bufp);
				if(bufp-obufp < BUFSZ-1 && bufp-obufp < COLNO)
					bufp++;
			}
		} else if(c == kill_char || c == '\177') { /* Robert Viduya */
				/* this test last - @ might be the kill_char */
			while(bufp != obufp) {
				bufp--;
				putstr("\b \b");
			}
		} else
			bell();
	}

}
#endif COM_COMPL
SHAR_EOF
cat << \SHAR_EOF > oskunix.c
/*	SCCS Id: @(#)unixunix.c	1.4	87/08/08
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* hack.unix.c - version 1.0.3 */

/* This file collects some Unix dependencies; pager.c contains some more */

/*
 * The time is used for:
 *	- seed for rand()
 *	- year on tombstone and yymmdd in record file
 *	- phase of the moon (various monsters react to NEW_MOON or FULL_MOON)
 *	- night and midnight (the undead are dangerous at midnight)
 *	- determination of what files are "very old"
 */

#include <stdio.h>
#include <errno.h>
#include "hack.h"	/* mainly for index() which depends on BSD */

#ifdef OSK
#include <direct.h>
#else
#include	<sys/types.h>		/* for time_t and stat */
#include	<sys/stat.h>
#ifdef BSD
#include	<sys/time.h>
#else
#include	<time.h>
#endif
#endif

extern char *getenv();
extern time_t time();

setrandom()
{
	(void) srand((int) time ((time_t *) 0));
}

struct tm *
getlt()
{
	time_t date;
	struct tm *localtime();

	(void) time(&date);
	return(localtime(&date));
}

getyear()
{
	return(1900 + getlt()->tm_year);
}

char *
getdate()
{
	static char datestr[7];
	register struct tm *lt = getlt();

	(void) sprintf(datestr, "%2d%2d%2d",
		lt->tm_year, lt->tm_mon + 1, lt->tm_mday);
	if(datestr[2] == ' ') datestr[2] = '0';
	if(datestr[4] == ' ') datestr[4] = '0';
	return(datestr);
}

phase_of_the_moon()			/* 0-7, with 0: new, 4: full */
{					/* moon period: 29.5306 days */
					/* year: 365.2422 days */
	register struct tm *lt = getlt();
	register int epact, diy, golden;

	diy = lt->tm_yday;
	golden = (lt->tm_year % 19) + 1;
	epact = (11 * golden + 18) % 30;
	if ((epact == 25 && golden > 11) || epact == 24)
		epact++;

	return( (((((diy + epact) * 6) + 11) % 177) / 22) & 7 );
}

night()
{
	register int hour = getlt()->tm_hour;

	return(hour < 6 || hour > 21);
}

midnight()
{
	return(getlt()->tm_hour == 0);
}

#ifdef OSK
struct fildes buf, hbuf;
#else
struct stat buf, hbuf;
#endif

gethdate(name) char *name; {
/* old version - for people short of space */
/*
/* register char *np;
/*	if(stat(name, &hbuf))
/*		error("Cannot get status of %s.",
/*			(np = rindex(name, '/')) ? np+1 : name);
/*
/* version using PATH from: seismo!gregc@ucsf-cgl.ARPA (Greg Couch) */


/*
 * The problem with   #include	<sys/param.h>   is that this include file
 * does not exist on all systems, and moreover, that it sometimes includes
 * <sys/types.h> again, so that the compiler sees these typedefs twice.
 */
#define		MAXPATHLEN	1024

register char *np, *path;
char filename[MAXPATHLEN+1];
#ifdef OSK
int i;
#endif
	if (index(name, '/') != NULL || (path = getenv("PATH")) == NULL)
		path = "";

	for (;;) {
		if ((np = index(path, ':')) == NULL)
			np = path + strlen(path);	/* point to end str */
		if (np - path <= 1)			/* %% */
			(void) strcpy(filename, name);
		else {
			(void) strncpy(filename, path, np - path);
			filename[np - path] = '/';
			(void) strcpy(filename + (np - path) + 1, name);
		}
#ifdef OSK
		if ((i = open(filename, S_IREAD)) != -1)
			if(_gs_gfd(i, &hbuf, sizeof hbuf) != -1) {
				close(i);
				return;
			} else close(i);
#else
		if (stat(filename, &hbuf) == 0)
			return;
#endif
		if (*np == '\0')
			break;
		path = np + 1;
	}
	error("Cannot get status of %s.",
		(np = rindex(name, '/')) ? np+1 : name);
}

uptodate(fd) {
#ifdef OSK
	if(_gs_gfd(fd, &buf, sizeof buf) < 0) {
#else
	if(fstat(fd, &buf)) {
#endif
		pline("Cannot get status of saved level? ");
		return(0);
	}
#ifdef OSK
	if(memcmp(buf.fd_date, hbuf.fd_date, sizeof buf.fd_date) < 0) {
#else
	if(buf.st_mtime < hbuf.st_mtime) {
#endif
		pline("Saved level is out of date. ");
		return(0);
	}
	return(1);
}

/* see whether we should throw away this xlock file */
veryold(fd) {
#ifdef OSK
	return 0;	/* OSK should not need this */
#else
	register int i;
	time_t date;

	if(fstat(fd, &buf)) return(0);			/* cannot get status */
	if(buf.st_size != sizeof(int)) return(0);	/* not an xlock file */
	(void) time(&date);
	if(date - buf.st_mtime < 3L*24L*60L*60L) {	/* recent */
		extern int errno;
		int lockedpid;	/* should be the same size as hackpid */

		if(read(fd, (char *)&lockedpid, sizeof(lockedpid)) !=
			sizeof(lockedpid))
			/* strange ... */
			return(0);

		/* From: Rick Adams <seismo!rick>
		/* This will work on 4.1cbsd, 4.2bsd and system 3? & 5.
		/* It will do nothing on V7 or 4.1bsd. */
		if(!(kill(lockedpid, 0) == -1 && errno == ESRCH))
			return(0);
	}
	(void) close(fd);
	for(i = 1; i <= MAXLEVEL; i++) {		/* try to remove all */
		glo(i);
		(void) unlink(lock);
	}
	glo(0);
	if(unlink(lock)) return(0);			/* cannot remove it */
	return(1);					/* success! */
#endif
}

getlock()
{
#ifdef OSK
	/* implament later using OSK file locks */
	lock[0] = 'a';
#else
	extern int errno, hackpid, locknum;
	register int i = 0, fd;

	(void) fflush(stdout);

	/* we ignore QUIT and INT at this point */
	if (link(HLOCK, LLOCK) == -1) {
		register int errnosv = errno;

		perror(HLOCK);
		printf("Cannot link %s to %s\n", LLOCK, HLOCK);
		switch(errnosv) {
		case ENOENT:
		    printf("Perhaps there is no (empty) file %s ?\n", HLOCK);
		    break;
		case EACCES:
		    printf("It seems you don't have write permission here.\n");
		    break;
		case EEXIST:
		    printf("(Try again or rm %s.)\n", LLOCK);
		    break;
		default:
		    printf("I don't know what is wrong.");
		}
		getret();
		error("");
		/*NOTREACHED*/
	}

	regularize(lock);
	glo(0);
	if(locknum > 25) locknum = 25;

	do {
		if(locknum) lock[0] = 'a' + i++;

		if((fd = open(lock, 0)) == -1) {
			if(errno == ENOENT) goto gotlock;    /* no such file */
			perror(lock);
			(void) unlink(LLOCK);
			error("Cannot open %s", lock);
		}

		if(veryold(fd))	/* if true, this closes fd and unlinks lock */
			goto gotlock;
		(void) close(fd);
	} while(i < locknum);

	(void) unlink(LLOCK);
	error(locknum ? "Too many hacks running now."
		      : "There is a game in progress under your name.");
gotlock:
	fd = creat(lock, FMASK);
	if(unlink(LLOCK) == -1)
		error("Cannot unlink %s.", LLOCK);
	if(fd == -1) {
		error("cannot creat lock file.");
	} else {
		if(write(fd, (char *) &hackpid, sizeof(hackpid))
		    != sizeof(hackpid)){
			error("cannot write lock");
		}
		if(close(fd) == -1) {
			error("cannot close lock");
		}
	}
#endif
}	

#ifdef MAIL

/*
 * Notify user when new mail has arrived. [Idea from Merlyn Leroy, but
 * I don't know the details of his implementation.]
 * { Later note: he disliked my calling a general mailreader and felt that
 *   hack should do the paging itself. But when I get mail, I want to put it
 *   in some folder, reply, etc. - it would be unreasonable to put all these
 *   functions in hack. }
 * The mail daemon '2' is at present not a real monster, but only a visual
 * effect. Thus, makemon() is superfluous. This might become otherwise,
 * however. The motion of '2' is less restrained than usual: diagonal moves
 * from a DOOR are possible. He might also use SDOOR's. Also, '2' is visible
 * in a ROOM, even when you are Blind.
 * Its path should be longer when you are Telepat-hic and Blind.
 *
 * Interesting side effects:
 *	- You can get rich by sending yourself a lot of mail and selling
 *	  it to the shopkeeper. Unfortunately mail isn't very valuable.
 *	- You might die in case '2' comes along at a critical moment during
 *	  a fight and delivers a scroll the weight of which causes you to
 *	  collapse.
 *
 * Possible extensions:
 *	- Open the file MAIL and do fstat instead of stat for efficiency.
 *	  (But sh uses stat, so this cannot be too bad.)
 *	- Examine the mail and produce a scroll of mail called "From somebody".
 *	- Invoke MAILREADER in such a way that only this single letter is read.
 *
 *	- Make him lose his mail when a Nymph steals the letter.
 *	- Do something to the text when the scroll is enchanted or cancelled.
 */
#include	"mkroom.h"
static struct stat omstat,nmstat;
static char *mailbox;
static long laststattime;

getmailstatus() {
	if(!(mailbox = getenv("MAIL")))
		return;
	if(stat(mailbox, &omstat)){
#ifdef PERMANENT_MAILBOX
		pline("Cannot get status of MAIL=%s .", mailbox);
		mailbox = 0;
#else
		omstat.st_mtime = 0;
#endif
	}
}

ckmailstatus() {
	if(!mailbox
#ifdef MAILCKFREQ
		    || moves < laststattime + MAILCKFREQ
#endif
							)
		return;
	laststattime = moves;
	if(stat(mailbox, &nmstat)){
#ifdef PERMANENT_MAILBOX
		pline("Cannot get status of MAIL=%s anymore.", mailbox);
		mailbox = 0;
#else
		nmstat.st_mtime = 0;
#endif
	} else if(nmstat.st_mtime > omstat.st_mtime) {
		if(nmstat.st_size)
			newmail();
		getmailstatus();	/* might be too late ... */
	}
}

newmail() {
	/* produce a scroll of mail */
	register struct obj *obj;
	register struct monst *md;
	extern char plname[];
	extern struct obj *mksobj(), *addinv();
	extern struct monst *makemon();
	extern struct permonst pm_mail_daemon;

	obj = mksobj(SCR_MAIL);
	if(md = makemon(&pm_mail_daemon, u.ux, u.uy)) /* always succeeds */
		mdrush(md,0);

	pline("\"Hello, %s! I have some mail for you.\"", plname);
	if(md) {
		if(dist(md->mx,md->my) > 2)
			pline("\"Catch!\"");
		more();

		/* let him disappear again */
		mdrush(md,1);
		mondead(md);
	}

	obj = addinv(obj);
	(void) identify(obj);		/* set known and do prinv() */
}

/* make md run through the cave */
mdrush(md,away)
register struct monst *md;
boolean away;
{
	register int uroom = inroom(u.ux, u.uy);
	if(uroom >= 0) {
		register int tmp = rooms[uroom].fdoor;
		register int cnt = rooms[uroom].doorct;
		register int fx = u.ux, fy = u.uy;
		while(cnt--) {
			if(dist(fx,fy) < dist(doors[tmp].x, doors[tmp].y)){
				fx = doors[tmp].x;
				fy = doors[tmp].y;
			}
			tmp++;
		}
		tmp_at(-1, md->data->mlet);	/* open call */
		if(away) {	/* interchange origin and destination */
			unpmon(md);
			tmp = fx; fx = md->mx; md->mx = tmp;
			tmp = fy; fy = md->my; md->my = tmp;
		}
		while(fx != md->mx || fy != md->my) {
			register int dx,dy,nfx = fx,nfy = fy,d1,d2;

			tmp_at(fx,fy);
			d1 = DIST(fx,fy,md->mx,md->my);
			for(dx = -1; dx <= 1; dx++) for(dy = -1; dy <= 1; dy++)
			    if(dx || dy) {
				d2 = DIST(fx+dx,fy+dy,md->mx,md->my);
				if(d2 < d1) {
				    d1 = d2;
				    nfx = fx+dx;
				    nfy = fy+dy;
				}
			    }
			if(nfx != fx || nfy != fy) {
			    fx = nfx;
			    fy = nfy;
			} else {
			    if(!away) {
				md->mx = fx;
				md->my = fy;
			    }
			    break;
			} 
		}
		tmp_at(-1,-1);			/* close call */
	}
	if(!away)
		pmon(md);
}

readmail() {
#ifdef DEF_MAILREADER			/* This implies that UNIX is defined */
	register char *mr = 0;
	more();
	if(!(mr = getenv("MAILREADER")))
		mr = DEF_MAILREADER;
	if(child(1)){
		execl(mr, mr, (char *) 0);
		exit(1);
	}
#else
	(void) page_file(mailbox, FALSE);
#endif
	/* get new stat; not entirely correct: there is a small time
	   window where we do not see new mail */
	getmailstatus();
}
#endif /* MAIL /**/

regularize(s)	/* normalize file name - we don't like ..'s or /'s */
register char *s;
{
	register char *lp;

	while((lp = index(s, '.')) || (lp = index(s, '/')))
		*lp = '_';
}
SHAR_EOF
#	End of shell archive
exit 0
Bob Larson	Arpa: Blarson@Ecla.Usc.Edu	blarson@skat.usc.edu
Uucp: {sdcrdcf,cit-vax}!oberon!skat!blarson
Prime mailing list:	info-prime-request%fns1@ecla.usc.edu
			oberon!fns1!info-prime-request