Sync usage with man page.
[netbsd-mini2440.git] / games / adventure / io.c
blob372ebca3ac3e88fdf14b0d81e2919145d2a8bee1
1 /* $NetBSD: io.c,v 1.21 2009/08/12 04:28:27 dholland Exp $ */
3 /*-
4 * Copyright (c) 1991, 1993
5 * The Regents of the University of California. All rights reserved.
7 * The game adventure was originally written in Fortran by Will Crowther
8 * and Don Woods. It was later translated to C and enhanced by Jim
9 * Gillogly. This code is derived from software contributed to Berkeley
10 * by Jim Gillogly at The Rand Corporation.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
37 #include <sys/cdefs.h>
38 #ifndef lint
39 #if 0
40 static char sccsid[] = "@(#)io.c 8.1 (Berkeley) 5/31/93";
41 #else
42 __RCSID("$NetBSD: io.c,v 1.21 2009/08/12 04:28:27 dholland Exp $");
43 #endif
44 #endif /* not lint */
46 /* Re-coding of advent in C: file i/o and user i/o */
48 #include <err.h>
49 #include <stdio.h>
50 #include <string.h>
51 #include <stdlib.h>
52 #include "hdr.h"
53 #include "extern.h"
55 static int next(void);
56 static void rdesc(int);
57 static void rdefault(void);
58 static void rhints(void);
59 static void rliq(void);
60 static void rlocs(void);
61 static int rnum(void);
62 static void rtrav(void);
63 static void rvoc(void);
65 /* get command from user */
66 /* no prompt, usually */
67 void
68 getin(char **wrd1, char **wrd2)
70 char *s;
71 static char wd1buf[MAXSTR], wd2buf[MAXSTR];
72 int first, numch, c;
74 *wrd1 = wd1buf; /* return ptr to internal str */
75 *wrd2 = wd2buf;
76 wd2buf[0] = 0; /* in case it isn't set here */
77 for (s = wd1buf, first = 1, numch = 0;;) {
78 c = getchar();
79 if ((*s = (char)c) >= 'A' && *s <= 'Z')
80 *s = *s - ('A' - 'a');
81 /* convert to upper case */
82 switch (c) { /* start reading from user */
83 case '\n':
84 *s = 0;
85 return;
86 case ' ':
87 if (s == wd1buf || s == wd2buf) /* initial blank */
88 continue;
89 *s = 0;
90 if (first) { /* finished 1st wd; start 2nd */
91 first = numch = 0;
92 s = wd2buf;
93 break;
94 } else { /* finished 2nd word */
95 FLUSHLINE;
96 *s = 0;
97 return;
99 case EOF:
100 printf("user closed input stream, quitting...\n");
101 exit(0);
102 default:
103 if (++numch >= MAXSTR) { /* string too long */
104 printf("Give me a break!!\n");
105 wd1buf[0] = wd2buf[0] = 0;
106 FLUSHLINE;
107 return;
109 s++;
114 /* confirm with rspeak */
116 yes(int x, int y, int z)
118 int result = TRUE; /* pacify gcc */
119 int ch;
120 for (;;) {
121 rspeak(x); /* tell him what we want */
122 if ((ch = getchar()) == 'y')
123 result = TRUE;
124 else if (ch == 'n')
125 result = FALSE;
126 else if (ch == EOF) {
127 printf("user closed input stream, quitting...\n");
128 exit(0);
130 FLUSHLINE;
131 if (ch == 'y' || ch == 'n')
132 break;
133 printf("Please answer the question.\n");
135 if (result == TRUE)
136 rspeak(y);
137 if (result == FALSE)
138 rspeak(z);
139 return (result);
142 /* confirm with mspeak */
144 yesm(int x, int y, int z)
146 int result = TRUE; /* pacify gcc */
147 int ch;
148 for (;;) {
149 mspeak(x); /* tell him what we want */
150 if ((ch = getchar()) == 'y')
151 result = TRUE;
152 else if (ch == 'n')
153 result = FALSE;
154 else if (ch == EOF) {
155 printf("user closed input stream, quitting...\n");
156 exit(0);
158 FLUSHLINE;
159 if (ch == 'y' || ch == 'n')
160 break;
161 printf("Please answer the question.\n");
163 if (result == TRUE)
164 mspeak(y);
165 if (result == FALSE)
166 mspeak(z);
167 return (result);
169 /* FILE *inbuf,*outbuf; */
171 static char *inptr; /* Pointer into virtual disk */
173 static int outsw = 0; /* putting stuff to data file? */
175 static const char iotape[] = "Ax3F'\003tt$8h\315qer*h\017nGKrX\207:!l";
176 static const char *tape = iotape; /* pointer to encryption tape */
178 /* next virtual char, bump adr */
179 static int
180 next(void)
182 int ch;
184 ch = (*inptr ^ random()) & 0xFF; /* Decrypt input data */
185 if (outsw) { /* putting data in tmp file */
186 if (*tape == 0)
187 tape = iotape; /* rewind encryption tape */
188 *inptr = ch ^ *tape++; /* re-encrypt and replace value */
190 inptr++;
191 return (ch);
194 static char breakch; /* tell which char ended rnum */
196 /* "read" data from virtual file */
197 void
198 rdata(void)
200 int sect;
201 char ch;
203 inptr = data_file; /* Pointer to virtual data file */
204 srandom(SEED); /* which is lightly encrypted. */
206 classes = 1;
207 for (;;) { /* read data sections */
208 sect = next() - '0'; /* 1st digit of section number */
209 #ifdef VERBOSE
210 printf("Section %c", sect + '0');
211 #endif
212 if ((ch = next()) != LF) { /* is there a second digit? */
213 FLUSHLF;
214 #ifdef VERBOSE
215 putchar(ch);
216 #endif
217 sect = 10 * sect + ch - '0';
219 #ifdef VERBOSE
220 putchar('\n');
221 #endif
222 switch (sect) {
223 case 0: /* finished reading database */
224 return;
225 case 1: /* long form descriptions */
226 rdesc(1);
227 break;
228 case 2: /* short form descriptions */
229 rdesc(2);
230 break;
231 case 3: /* travel table */
232 rtrav();
233 break;
234 case 4: /* vocabulary */
235 rvoc();
236 break;
237 case 5: /* object descriptions */
238 rdesc(5);
239 break;
240 case 6: /* arbitrary messages */
241 rdesc(6);
242 break;
243 case 7: /* object locations */
244 rlocs();
245 break;
246 case 8: /* action defaults */
247 rdefault();
248 break;
249 case 9: /* liquid assets */
250 rliq();
251 break;
252 case 10: /* class messages */
253 rdesc(10);
254 break;
255 case 11: /* hints */
256 rhints();
257 break;
258 case 12: /* magic messages */
259 rdesc(12);
260 break;
261 default:
262 printf("Invalid data section number: %d\n", sect);
263 for (;;)
264 putchar(next());
266 if (breakch != LF) /* routines return after "-1" */
267 FLUSHLF;
271 static char nbf[12];
273 /* read initial location num */
274 static int
275 rnum(void)
277 char *s;
278 tape = iotape; /* restart encryption tape */
279 for (s = nbf, *s = 0;; s++)
280 if ((*s = next()) == TAB || *s == '\n' || *s == LF)
281 break;
282 breakch = *s; /* save char for rtrav() */
283 *s = 0; /* got the number as ascii */
284 if (nbf[0] == '-')
285 return (-1); /* end of data */
286 return (atoi(nbf)); /* convert it to integer */
289 static char *seekhere;
291 /* read description-format msgs */
292 static void
293 rdesc(int sect)
295 int locc;
296 char *seekstart, *maystart;
298 seekhere = inptr; /* Where are we in virtual file? */
299 outsw = 1; /* these msgs go into tmp file */
300 for (oldloc = -1, seekstart = seekhere;;) {
301 maystart = inptr; /* maybe starting new entry */
302 if ((locc = rnum()) != oldloc && oldloc >= 0 /* finished msg */
303 /* unless sect 5 */
304 && !(sect == 5 && (locc == 0 || locc >= 100))) {
305 switch (sect) { /* now put it into right table */
306 case 1:/* long descriptions */
307 ltext[oldloc].seekadr = seekhere;
308 ltext[oldloc].txtlen = maystart - seekstart;
309 break;
310 case 2:/* short descriptions */
311 stext[oldloc].seekadr = seekhere;
312 stext[oldloc].txtlen = maystart - seekstart;
313 break;
314 case 5:/* object descriptions */
315 ptext[oldloc].seekadr = seekhere;
316 ptext[oldloc].txtlen = maystart - seekstart;
317 break;
318 case 6:/* random messages */
319 if (oldloc >= RTXSIZE)
320 errx(1,"Too many random msgs");
321 rtext[oldloc].seekadr = seekhere;
322 rtext[oldloc].txtlen = maystart - seekstart;
323 break;
324 case 10: /* class messages */
325 ctext[classes].seekadr = seekhere;
326 ctext[classes].txtlen = maystart - seekstart;
327 cval[classes++] = oldloc;
328 break;
329 case 12: /* magic messages */
330 if (oldloc >= MAGSIZE)
331 errx(1,"Too many magic msgs");
332 mtext[oldloc].seekadr = seekhere;
333 mtext[oldloc].txtlen = maystart - seekstart;
334 break;
335 default:
336 errx(1,"rdesc called with bad section");
338 seekhere += maystart - seekstart;
340 if (locc < 0) {
341 outsw = 0; /* turn off output */
342 seekhere += 3; /* -1<delimiter> */
343 return;
345 if (sect != 5 || (locc > 0 && locc < 100)) {
346 if (oldloc != locc) /* starting a new message */
347 seekstart = maystart;
348 oldloc = locc;
350 FLUSHLF; /* scan the line */
354 /* read travel table */
355 static void
356 rtrav(void)
358 int locc;
359 struct travlist *t = NULL;
360 char *s;
361 char buf[12];
362 int len, m, n, entries = 0;
364 for (oldloc = -1;;) { /* get another line */
365 /* end of entry */
366 if ((locc = rnum()) != oldloc && oldloc >= 0 && t) {
367 t->next = 0; /* terminate the old entry */
368 /* printf("%d:%d entries\n",oldloc,entries); */
369 /* twrite(oldloc); */
371 if (locc == -1)
372 return;
373 if (locc != oldloc) { /* getting a new entry */
374 t = travel[locc] = calloc(1, sizeof(*t));
375 if (t == NULL)
376 err(1, NULL);
377 /* printf("New travel list for %d\n",locc); */
378 entries = 0;
379 oldloc = locc;
381 for (s = buf;; s++) /* get the newloc number /ASCII */
382 if ((*s = next()) == TAB || *s == LF)
383 break;
384 *s = 0;
385 len = length(buf) - 1; /* quad long number handling */
386 /* printf("Newloc: %s (%d chars)\n",buf,len); */
387 if (len < 4) { /* no "m" conditions */
388 m = 0;
389 n = atoi(buf); /* newloc mod 1000 = newloc */
390 } else { /* a long integer */
391 n = atoi(buf + len - 3);
392 buf[len - 3] = 0; /* terminate newloc/1000 */
393 m = atoi(buf);
395 while (breakch != LF) { /* only do one line at a time */
396 if (t == NULL)
397 abort();
398 if (entries++) {
399 t->next = calloc(1, sizeof(*t));
400 if (t->next == NULL)
401 err(1, NULL);
402 t = t->next;
404 t->tverb = rnum(); /* get verb from the file */
405 t->tloc = n; /* table entry mod 1000 */
406 t->conditions = m; /* table entry / 1000 */
407 /* printf("entry %d for %d\n",entries,locc); */
411 #ifdef DEBUG
413 /* travel options from this loc */
414 void
415 twrite(int loq)
417 struct travlist *t;
418 printf("If");
419 speak(&ltext[loq]);
420 printf("then\n");
421 for (t = travel[loq]; t != 0; t = t->next) {
422 printf("verb %d takes you to ", t->tverb);
423 if (t->tloc <= 300)
424 speak(&ltext[t->tloc]);
425 else
426 if (t->tloc <= 500)
427 printf("special code %d\n", t->tloc - 300);
428 else
429 rspeak(t->tloc - 500);
430 printf("under conditions %d\n", t->conditions);
433 #endif /* DEBUG */
435 /* read the vocabulary */
436 static void
437 rvoc(void)
439 char *s;
440 int idx;
441 char buf[6];
442 for (;;) {
443 idx = rnum();
444 if (idx < 0)
445 break;
446 for (s = buf, *s = 0;; s++) /* get the word */
447 if ((*s = next()) == TAB || *s == '\n' || *s == LF
448 || *s == ' ')
449 break;
450 /* terminate word with newline, LF, tab, blank */
451 if (*s != '\n' && *s != LF)
452 FLUSHLF;/* can be comments */
453 *s = 0;
454 /* printf("\"%s\"=%d\n",buf,idx); */
455 vocab(buf, -2, idx);
457 /* prht(); */
460 /* initial object locations */
461 static void
462 rlocs(void)
464 for (;;) {
465 if ((obj = rnum()) < 0)
466 break;
467 plac[obj] = rnum(); /* initial loc for this obj */
468 if (breakch == TAB) /* there's another entry */
469 fixd[obj] = rnum();
470 else
471 fixd[obj] = 0;
475 /* default verb messages */
476 static void
477 rdefault(void)
479 for (;;) {
480 if ((verb = rnum()) < 0)
481 break;
482 actspeak[verb] = rnum();
486 /* liquid assets &c: cond bits */
487 static void
488 rliq(void)
490 int bitnum;
491 for (;;) { /* read new bit list */
492 if ((bitnum = rnum()) < 0)
493 break;
494 for (;;) { /* read locs for bits */
495 int n = rnum();
496 if (n < 0)
497 break;
498 cond[n] |= setbit[bitnum];
499 if (breakch == LF)
500 break;
505 static void
506 rhints(void)
508 int hintnum, i;
509 hintmax = 0;
510 for (;;) {
511 if ((hintnum = rnum()) < 0)
512 break;
513 for (i = 1; i < 5; i++)
514 hints[hintnum][i] = rnum();
515 if (hintnum > hintmax)
516 hintmax = hintnum;
521 void
522 rspeak(int msg)
524 if (msg != 0)
525 speak(&rtext[msg]);
529 void
530 mspeak(int msg)
532 if (msg != 0)
533 speak(&mtext[msg]);
537 /* read, decrypt, and print a message (not ptext) */
538 /* msg is a pointer to seek address and length of mess */
539 void
540 speak(const struct text *msg)
542 char *s, nonfirst;
544 s = msg->seekadr;
545 nonfirst = 0;
546 while (s - msg->seekadr < msg->txtlen) { /* read a line at a time */
547 tape = iotape; /* restart decryption tape */
548 while ((*s++ ^ *tape++) != TAB); /* read past loc num */
549 /* assume tape is longer than location number */
550 /* plus the lookahead put together */
551 if ((*s ^ *tape) == '>' &&
552 (*(s + 1) ^ *(tape + 1)) == '$' &&
553 (*(s + 2) ^ *(tape + 2)) == '<')
554 break;
555 if (blklin && !nonfirst++)
556 putchar('\n');
557 do {
558 if (*tape == 0)
559 tape = iotape; /* rewind decryp tape */
560 putchar(*s ^ *tape);
561 } while ((*s++ ^ *tape++) != LF); /* better end with LF */
565 /* read, decrypt and print a ptext message */
566 /* msg is the number of all the p msgs for this place */
567 /* assumes object 1 doesn't have prop 1, obj 2 no prop 2 &c */
568 void
569 pspeak(int m, int skip)
571 char *s, nonfirst;
572 char *numst;
573 struct text *msg;
574 char *tbuf;
576 msg = &ptext[m];
577 if ((tbuf = (char *) malloc(msg->txtlen + 1)) == NULL)
578 err(1, NULL);
579 memcpy(tbuf, msg->seekadr, msg->txtlen + 1); /* Room to null */
580 s = tbuf;
582 nonfirst = 0;
583 while (s - tbuf < msg->txtlen) { /* read line at a time */
584 tape = iotape; /* restart decryption tape */
585 for (numst = s; (*s ^= *tape++) != TAB; s++); /* get number */
587 /* Temporarily trash the string (cringe) */
588 *s++ = 0; /* decrypting number within the string */
590 if (atoi(numst) != 100 * skip && skip >= 0) {
591 while ((*s++ ^ *tape++) != LF) /* flush the line */
592 if (*tape == 0)
593 tape = iotape;
594 continue;
596 if ((*s ^ *tape) == '>' && (*(s + 1) ^ *(tape + 1)) == '$' &&
597 (*(s + 2) ^ *(tape + 2)) == '<')
598 break;
599 if (blklin && !nonfirst++)
600 putchar('\n');
601 do {
602 if (*tape == 0)
603 tape = iotape;
604 putchar(*s ^ *tape);
605 } while ((*s++ ^ *tape++) != LF); /* better end with LF */
606 if (skip < 0)
607 break;
609 free(tbuf);