dd3fe9b2dc
so that we don't end up storing garbage accidentally and the next pointer is initialized. If there is an inconsistency in the file abort instead of dereferencing NULL.
601 lines
15 KiB
C
601 lines
15 KiB
C
/* $NetBSD: io.c,v 1.19 2006/03/21 17:14:15 christos Exp $ */
|
|
|
|
/*-
|
|
* Copyright (c) 1991, 1993
|
|
* The Regents of the University of California. All rights reserved.
|
|
*
|
|
* The game adventure was originally written in Fortran by Will Crowther
|
|
* and Don Woods. It was later translated to C and enhanced by Jim
|
|
* Gillogly. This code is derived from software contributed to Berkeley
|
|
* by Jim Gillogly at The Rand Corporation.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the University nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
#ifndef lint
|
|
#if 0
|
|
static char sccsid[] = "@(#)io.c 8.1 (Berkeley) 5/31/93";
|
|
#else
|
|
__RCSID("$NetBSD: io.c,v 1.19 2006/03/21 17:14:15 christos Exp $");
|
|
#endif
|
|
#endif /* not lint */
|
|
|
|
/* Re-coding of advent in C: file i/o and user i/o */
|
|
|
|
#include <err.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include "hdr.h"
|
|
#include "extern.h"
|
|
|
|
|
|
/* get command from user */
|
|
/* no prompt, usually */
|
|
void
|
|
getin(char **wrd1, char **wrd2)
|
|
{
|
|
char *s;
|
|
static char wd1buf[MAXSTR], wd2buf[MAXSTR];
|
|
int first, numch;
|
|
|
|
*wrd1 = wd1buf; /* return ptr to internal str */
|
|
*wrd2 = wd2buf;
|
|
wd2buf[0] = 0; /* in case it isn't set here */
|
|
for (s = wd1buf, first = 1, numch = 0;;) {
|
|
if ((*s = getchar()) >= 'A' && *s <= 'Z')
|
|
*s = *s - ('A' - 'a');
|
|
/* convert to upper case */
|
|
switch (*s) { /* start reading from user */
|
|
case '\n':
|
|
*s = 0;
|
|
return;
|
|
case ' ':
|
|
if (s == wd1buf || s == wd2buf) /* initial blank */
|
|
continue;
|
|
*s = 0;
|
|
if (first) { /* finished 1st wd; start 2nd */
|
|
first = numch = 0;
|
|
s = wd2buf;
|
|
break;
|
|
} else { /* finished 2nd word */
|
|
FLUSHLINE;
|
|
*s = 0;
|
|
return;
|
|
}
|
|
case EOF:
|
|
printf("user closed input stream, quitting...\n");
|
|
exit(0);
|
|
default:
|
|
if (++numch >= MAXSTR) { /* string too long */
|
|
printf("Give me a break!!\n");
|
|
wd1buf[0] = wd2buf[0] = 0;
|
|
FLUSHLINE;
|
|
return;
|
|
}
|
|
s++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* confirm with rspeak */
|
|
int
|
|
yes(int x, int y, int z)
|
|
{
|
|
int result = TRUE; /* pacify gcc */
|
|
int ch;
|
|
for (;;) {
|
|
rspeak(x); /* tell him what we want */
|
|
if ((ch = getchar()) == 'y')
|
|
result = TRUE;
|
|
else if (ch == 'n')
|
|
result = FALSE;
|
|
else if (ch == EOF) {
|
|
printf("user closed input stream, quitting...\n");
|
|
exit(0);
|
|
}
|
|
FLUSHLINE;
|
|
if (ch == 'y' || ch == 'n')
|
|
break;
|
|
printf("Please answer the question.\n");
|
|
}
|
|
if (result == TRUE)
|
|
rspeak(y);
|
|
if (result == FALSE)
|
|
rspeak(z);
|
|
return (result);
|
|
}
|
|
|
|
/* confirm with mspeak */
|
|
int
|
|
yesm(int x, int y, int z)
|
|
{
|
|
int result = TRUE; /* pacify gcc */
|
|
int ch;
|
|
for (;;) {
|
|
mspeak(x); /* tell him what we want */
|
|
if ((ch = getchar()) == 'y')
|
|
result = TRUE;
|
|
else if (ch == 'n')
|
|
result = FALSE;
|
|
else if (ch == EOF) {
|
|
printf("user closed input stream, quitting...\n");
|
|
exit(0);
|
|
}
|
|
FLUSHLINE;
|
|
if (ch == 'y' || ch == 'n')
|
|
break;
|
|
printf("Please answer the question.\n");
|
|
}
|
|
if (result == TRUE)
|
|
mspeak(y);
|
|
if (result == FALSE)
|
|
mspeak(z);
|
|
return (result);
|
|
}
|
|
/* FILE *inbuf,*outbuf; */
|
|
|
|
char *inptr; /* Pointer into virtual disk */
|
|
|
|
int outsw = 0; /* putting stuff to data file? */
|
|
|
|
const char iotape[] = "Ax3F'\003tt$8h\315qer*h\017nGKrX\207:!l";
|
|
const char *tape = iotape; /* pointer to encryption tape */
|
|
|
|
/* next virtual char, bump adr */
|
|
int
|
|
next(void)
|
|
{
|
|
int ch;
|
|
|
|
ch = (*inptr ^ random()) & 0xFF; /* Decrypt input data */
|
|
if (outsw) { /* putting data in tmp file */
|
|
if (*tape == 0)
|
|
tape = iotape; /* rewind encryption tape */
|
|
*inptr = ch ^ *tape++; /* re-encrypt and replace value */
|
|
}
|
|
inptr++;
|
|
return (ch);
|
|
}
|
|
|
|
char breakch; /* tell which char ended rnum */
|
|
|
|
/* "read" data from virtual file */
|
|
void
|
|
rdata(void)
|
|
{
|
|
int sect;
|
|
char ch;
|
|
|
|
inptr = data_file; /* Pointer to virtual data file */
|
|
srandom(SEED); /* which is lightly encrypted. */
|
|
|
|
clsses = 1;
|
|
for (;;) { /* read data sections */
|
|
sect = next() - '0'; /* 1st digit of section number */
|
|
#ifdef VERBOSE
|
|
printf("Section %c", sect + '0');
|
|
#endif
|
|
if ((ch = next()) != LF) { /* is there a second digit? */
|
|
FLUSHLF;
|
|
#ifdef VERBOSE
|
|
putchar(ch);
|
|
#endif
|
|
sect = 10 * sect + ch - '0';
|
|
}
|
|
#ifdef VERBOSE
|
|
putchar('\n');
|
|
#endif
|
|
switch (sect) {
|
|
case 0: /* finished reading database */
|
|
return;
|
|
case 1: /* long form descriptions */
|
|
rdesc(1);
|
|
break;
|
|
case 2: /* short form descriptions */
|
|
rdesc(2);
|
|
break;
|
|
case 3: /* travel table */
|
|
rtrav();
|
|
break;
|
|
case 4: /* vocabulary */
|
|
rvoc();
|
|
break;
|
|
case 5: /* object descriptions */
|
|
rdesc(5);
|
|
break;
|
|
case 6: /* arbitrary messages */
|
|
rdesc(6);
|
|
break;
|
|
case 7: /* object locations */
|
|
rlocs();
|
|
break;
|
|
case 8: /* action defaults */
|
|
rdflt();
|
|
break;
|
|
case 9: /* liquid assets */
|
|
rliq();
|
|
break;
|
|
case 10: /* class messages */
|
|
rdesc(10);
|
|
break;
|
|
case 11: /* hints */
|
|
rhints();
|
|
break;
|
|
case 12: /* magic messages */
|
|
rdesc(12);
|
|
break;
|
|
default:
|
|
printf("Invalid data section number: %d\n", sect);
|
|
for (;;)
|
|
putchar(next());
|
|
}
|
|
if (breakch != LF) /* routines return after "-1" */
|
|
FLUSHLF;
|
|
}
|
|
}
|
|
|
|
char nbf[12];
|
|
|
|
/* read initial location num */
|
|
int
|
|
rnum(void)
|
|
{
|
|
char *s;
|
|
tape = iotape; /* restart encryption tape */
|
|
for (s = nbf, *s = 0;; s++)
|
|
if ((*s = next()) == TAB || *s == '\n' || *s == LF)
|
|
break;
|
|
breakch = *s; /* save char for rtrav() */
|
|
*s = 0; /* got the number as ascii */
|
|
if (nbf[0] == '-')
|
|
return (-1); /* end of data */
|
|
return (atoi(nbf)); /* convert it to integer */
|
|
}
|
|
|
|
char *seekhere;
|
|
|
|
/* read description-format msgs */
|
|
void
|
|
rdesc(int sect)
|
|
{
|
|
int locc;
|
|
char *seekstart, *maystart;
|
|
|
|
seekhere = inptr; /* Where are we in virtual file? */
|
|
outsw = 1; /* these msgs go into tmp file */
|
|
for (oldloc = -1, seekstart = seekhere;;) {
|
|
maystart = inptr; /* maybe starting new entry */
|
|
if ((locc = rnum()) != oldloc && oldloc >= 0 /* finished msg */
|
|
/* unless sect 5 */
|
|
&& !(sect == 5 && (locc == 0 || locc >= 100))) {
|
|
switch (sect) { /* now put it into right table */
|
|
case 1:/* long descriptions */
|
|
ltext[oldloc].seekadr = seekhere;
|
|
ltext[oldloc].txtlen = maystart - seekstart;
|
|
break;
|
|
case 2:/* short descriptions */
|
|
stext[oldloc].seekadr = seekhere;
|
|
stext[oldloc].txtlen = maystart - seekstart;
|
|
break;
|
|
case 5:/* object descriptions */
|
|
ptext[oldloc].seekadr = seekhere;
|
|
ptext[oldloc].txtlen = maystart - seekstart;
|
|
break;
|
|
case 6:/* random messages */
|
|
if (oldloc >= RTXSIZ)
|
|
errx(1,"Too many random msgs");
|
|
rtext[oldloc].seekadr = seekhere;
|
|
rtext[oldloc].txtlen = maystart - seekstart;
|
|
break;
|
|
case 10: /* class messages */
|
|
ctext[clsses].seekadr = seekhere;
|
|
ctext[clsses].txtlen = maystart - seekstart;
|
|
cval[clsses++] = oldloc;
|
|
break;
|
|
case 12: /* magic messages */
|
|
if (oldloc >= MAGSIZ)
|
|
errx(1,"Too many magic msgs");
|
|
mtext[oldloc].seekadr = seekhere;
|
|
mtext[oldloc].txtlen = maystart - seekstart;
|
|
break;
|
|
default:
|
|
errx(1,"rdesc called with bad section");
|
|
}
|
|
seekhere += maystart - seekstart;
|
|
}
|
|
if (locc < 0) {
|
|
outsw = 0; /* turn off output */
|
|
seekhere += 3; /* -1<delimiter> */
|
|
return;
|
|
}
|
|
if (sect != 5 || (locc > 0 && locc < 100)) {
|
|
if (oldloc != locc) /* starting a new message */
|
|
seekstart = maystart;
|
|
oldloc = locc;
|
|
}
|
|
FLUSHLF; /* scan the line */
|
|
}
|
|
}
|
|
|
|
/* read travel table */
|
|
void
|
|
rtrav(void)
|
|
{
|
|
int locc;
|
|
struct travlist *t = NULL;
|
|
char *s;
|
|
char buf[12];
|
|
int len, m, n, entries = 0;
|
|
|
|
for (oldloc = -1;;) { /* get another line */
|
|
/* end of entry */
|
|
if ((locc = rnum()) != oldloc && oldloc >= 0 && t) {
|
|
t->next = 0; /* terminate the old entry */
|
|
/* printf("%d:%d entries\n",oldloc,entries); */
|
|
/* twrite(oldloc); */
|
|
}
|
|
if (locc == -1)
|
|
return;
|
|
if (locc != oldloc) { /* getting a new entry */
|
|
t = travel[locc] = calloc(1, sizeof(*t));
|
|
if (t == NULL)
|
|
err(1, NULL);
|
|
/* printf("New travel list for %d\n",locc); */
|
|
entries = 0;
|
|
oldloc = locc;
|
|
}
|
|
for (s = buf;; s++) /* get the newloc number /ASCII */
|
|
if ((*s = next()) == TAB || *s == LF)
|
|
break;
|
|
*s = 0;
|
|
len = length(buf) - 1; /* quad long number handling */
|
|
/* printf("Newloc: %s (%d chars)\n",buf,len); */
|
|
if (len < 4) { /* no "m" conditions */
|
|
m = 0;
|
|
n = atoi(buf); /* newloc mod 1000 = newloc */
|
|
} else { /* a long integer */
|
|
n = atoi(buf + len - 3);
|
|
buf[len - 3] = 0; /* terminate newloc/1000 */
|
|
m = atoi(buf);
|
|
}
|
|
while (breakch != LF) { /* only do one line at a time */
|
|
if (t == NULL)
|
|
abort();
|
|
if (entries++) {
|
|
t->next = calloc(1, sizeof(*t));
|
|
if (t->next == NULL)
|
|
err(1, NULL);
|
|
t = t->next;
|
|
}
|
|
t->tverb = rnum(); /* get verb from the file */
|
|
t->tloc = n; /* table entry mod 1000 */
|
|
t->conditions = m; /* table entry / 1000 */
|
|
/* printf("entry %d for %d\n",entries,locc); */
|
|
}
|
|
}
|
|
}
|
|
#ifdef DEBUG
|
|
|
|
/* travel options from this loc */
|
|
void
|
|
twrite(int loq)
|
|
{
|
|
struct travlist *t;
|
|
printf("If");
|
|
speak(<ext[loq]);
|
|
printf("then\n");
|
|
for (t = travel[loq]; t != 0; t = t->next) {
|
|
printf("verb %d takes you to ", t->tverb);
|
|
if (t->tloc <= 300)
|
|
speak(<ext[t->tloc]);
|
|
else
|
|
if (t->tloc <= 500)
|
|
printf("special code %d\n", t->tloc - 300);
|
|
else
|
|
rspeak(t->tloc - 500);
|
|
printf("under conditions %d\n", t->conditions);
|
|
}
|
|
}
|
|
#endif /* DEBUG */
|
|
|
|
/* read the vocabulary */
|
|
void
|
|
rvoc(void)
|
|
{
|
|
char *s;
|
|
int idx;
|
|
char buf[6];
|
|
for (;;) {
|
|
idx = rnum();
|
|
if (idx < 0)
|
|
break;
|
|
for (s = buf, *s = 0;; s++) /* get the word */
|
|
if ((*s = next()) == TAB || *s == '\n' || *s == LF
|
|
|| *s == ' ')
|
|
break;
|
|
/* terminate word with newline, LF, tab, blank */
|
|
if (*s != '\n' && *s != LF)
|
|
FLUSHLF;/* can be comments */
|
|
*s = 0;
|
|
/* printf("\"%s\"=%d\n",buf,idx); */
|
|
vocab(buf, -2, idx);
|
|
}
|
|
/* prht(); */
|
|
}
|
|
|
|
/* initial object locations */
|
|
void
|
|
rlocs(void)
|
|
{
|
|
for (;;) {
|
|
if ((obj = rnum()) < 0)
|
|
break;
|
|
plac[obj] = rnum(); /* initial loc for this obj */
|
|
if (breakch == TAB) /* there's another entry */
|
|
fixd[obj] = rnum();
|
|
else
|
|
fixd[obj] = 0;
|
|
}
|
|
}
|
|
|
|
/* default verb messages */
|
|
void
|
|
rdflt(void)
|
|
{
|
|
for (;;) {
|
|
if ((verb = rnum()) < 0)
|
|
break;
|
|
actspk[verb] = rnum();
|
|
}
|
|
}
|
|
|
|
/* liquid assets &c: cond bits */
|
|
void
|
|
rliq(void)
|
|
{
|
|
int bitnum;
|
|
for (;;) { /* read new bit list */
|
|
if ((bitnum = rnum()) < 0)
|
|
break;
|
|
for (;;) { /* read locs for bits */
|
|
int n = rnum();
|
|
if (n < 0)
|
|
break;
|
|
cond[n] |= setbit[bitnum];
|
|
if (breakch == LF)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
rhints(void)
|
|
{
|
|
int hintnum, i;
|
|
hntmax = 0;
|
|
for (;;) {
|
|
if ((hintnum = rnum()) < 0)
|
|
break;
|
|
for (i = 1; i < 5; i++)
|
|
hints[hintnum][i] = rnum();
|
|
if (hintnum > hntmax)
|
|
hntmax = hintnum;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
rspeak(int msg)
|
|
{
|
|
if (msg != 0)
|
|
speak(&rtext[msg]);
|
|
}
|
|
|
|
|
|
void
|
|
mspeak(int msg)
|
|
{
|
|
if (msg != 0)
|
|
speak(&mtext[msg]);
|
|
}
|
|
|
|
|
|
/* read, decrypt, and print a message (not ptext) */
|
|
/* msg is a pointer to seek address and length of mess */
|
|
void
|
|
speak(const struct text *msg)
|
|
{
|
|
char *s, nonfirst;
|
|
|
|
s = msg->seekadr;
|
|
nonfirst = 0;
|
|
while (s - msg->seekadr < msg->txtlen) { /* read a line at a time */
|
|
tape = iotape; /* restart decryption tape */
|
|
while ((*s++ ^ *tape++) != TAB); /* read past loc num */
|
|
/* assume tape is longer than location number */
|
|
/* plus the lookahead put together */
|
|
if ((*s ^ *tape) == '>' &&
|
|
(*(s + 1) ^ *(tape + 1)) == '$' &&
|
|
(*(s + 2) ^ *(tape + 2)) == '<')
|
|
break;
|
|
if (blklin && !nonfirst++)
|
|
putchar('\n');
|
|
do {
|
|
if (*tape == 0)
|
|
tape = iotape; /* rewind decryp tape */
|
|
putchar(*s ^ *tape);
|
|
} while ((*s++ ^ *tape++) != LF); /* better end with LF */
|
|
}
|
|
}
|
|
|
|
/* read, decrypt and print a ptext message */
|
|
/* msg is the number of all the p msgs for this place */
|
|
/* assumes object 1 doesn't have prop 1, obj 2 no prop 2 &c */
|
|
void
|
|
pspeak(int m, int skip)
|
|
{
|
|
char *s, nonfirst;
|
|
char *numst;
|
|
struct text *msg;
|
|
char *tbuf;
|
|
|
|
msg = &ptext[m];
|
|
if ((tbuf = (char *) malloc(msg->txtlen + 1)) == NULL)
|
|
err(1, NULL);
|
|
memcpy(tbuf, msg->seekadr, msg->txtlen + 1); /* Room to null */
|
|
s = tbuf;
|
|
|
|
nonfirst = 0;
|
|
while (s - tbuf < msg->txtlen) { /* read line at a time */
|
|
tape = iotape; /* restart decryption tape */
|
|
for (numst = s; (*s ^= *tape++) != TAB; s++); /* get number */
|
|
|
|
/* Temporarily trash the string (cringe) */
|
|
*s++ = 0; /* decrypting number within the string */
|
|
|
|
if (atoi(numst) != 100 * skip && skip >= 0) {
|
|
while ((*s++ ^ *tape++) != LF) /* flush the line */
|
|
if (*tape == 0)
|
|
tape = iotape;
|
|
continue;
|
|
}
|
|
if ((*s ^ *tape) == '>' && (*(s + 1) ^ *(tape + 1)) == '$' &&
|
|
(*(s + 2) ^ *(tape + 2)) == '<')
|
|
break;
|
|
if (blklin && !nonfirst++)
|
|
putchar('\n');
|
|
do {
|
|
if (*tape == 0)
|
|
tape = iotape;
|
|
putchar(*s ^ *tape);
|
|
} while ((*s++ ^ *tape++) != LF); /* better end with LF */
|
|
if (skip < 0)
|
|
break;
|
|
}
|
|
free(tbuf);
|
|
}
|